diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 00000000..a2f4c549
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,29 @@
+{
+  "parserOptions": {
+    "ecmaVersion": 6
+  },
+  "rules": {
+    "keyword-spacing": 1,
+    "space-before-function-paren": [1, "never"],
+    "eqeqeq": 1,
+    "space-infix-ops": 1,
+    "comma-spacing": 1,
+    "brace-style": 1,
+    "no-multiple-empty-lines": 1,
+    "camelcase": 1,
+    "func-call-spacing": 1,
+    "key-spacing": 1,
+    "semi": 1,
+    "no-floating-decimal": 1,
+    "no-multi-spaces": 1,
+    "object-property-newline": 1,
+    "padded-blocks": [1, "never"],
+    "space-before-blocks": 1,
+    "space-in-parens": 1,
+    "spaced-comment": 1,
+    "quotes": [1, "single"],
+    "id-length": [1, { "exceptions": ["i", "j", "x"] }],
+    "indent": [1, 2],
+    "no-array-constructor": 1
+  }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..0af7592f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.DS_STORE
+node_modules/
+Thumbs.db
\ No newline at end of file
diff --git a/README.md b/README.md
index cc501c00..ecf4fae6 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,27 @@
-# E-commerce
+# D'papel
 
-* **Track:** _Especialización Front-end_
-* **Curso:** _CONSTRUYE UNA SINGLE PAGE APP (SPA) MULTI-USUARIO CONSUMIENDO DATA REMOTA_
-* **Unidad:** _Producto final_
+### Descripción
 
-***
+Es una Single Page Application de venta de productos de papelería desarrollada con el framework Sammy.js, la API de Stripe y un archivo json con el listado de productos.
 
-## Flujo de trabajo
+Se utilizaron dos templates que corresponden a las siguientes vistas de la página:
 
-1. Debes realizar un [**fork**](https://gist.github.com/ivandevp/1de47ae69a5e139a6622d78c882e1f74)
-   de este repositorio.
+### Previsualización
 
-2. Luego deberás **clonar** tu fork en tu máquina. Recuerda que el comando a usar
-   es `git clone` y su estructura normalmente se ve así:
+* Vista Principal
+![img](https://image.ibb.co/nCUMtH/vista_principal.png)
 
-   ```bash
-   git clone https://github.com/<nombre-de-usuario>/freelancer.git
-   ```
+* Detalle de Producto
+![img](https://image.ibb.co/dNPnYH/detalle.png)
 
-3. Cuando hayas terminado tu producto, envía un Pull Request a este repositorio
-   (puedes solicitar apoyo de tus profes para este paso).
+* Carrito de Compras con Log in
+![img](https://image.ibb.co/eoYEDH/login_carrito.png)
 
-> Nota: No olvides que es una buena práctica describir tu proyecto en este
-> archivo `README.md` y también desplegar tu web a Github Pages :smiley:.
+* Modal de botón de compra
+![img](https://image.ibb.co/btFmtH/screencapture_localhost_8080_1519733006154.png)
+
+##### Proyecto realizado por:
+  - Maria Paz Thompson
+  - Paulina Baeza
+  - Sabrina Villalobos
+  - Camila Cornejos
diff --git a/assets/js/payment.js b/assets/js/payment.js
new file mode 100644
index 00000000..cfce4ccd
--- /dev/null
+++ b/assets/js/payment.js
@@ -0,0 +1 @@
+$(".pay").append("")
diff --git a/assets/js/validate.min.js b/assets/js/validate.min.js
new file mode 100644
index 00000000..7453885e
--- /dev/null
+++ b/assets/js/validate.min.js
@@ -0,0 +1,9 @@
+/*!
+ * Validator v0.11.9 for Bootstrap 3, by @1000hz
+ * Copyright 2017 Cina Saffary
+ * Licensed under http://opensource.org/licenses/MIT
+ *
+ * https://github.com/1000hz/bootstrap-validator
+ */
+
++function(a){"use strict";function b(b){return b.is('[type="checkbox"]')?b.prop("checked"):b.is('[type="radio"]')?!!a('[name="'+b.attr("name")+'"]:checked').length:b.is("select[multiple]")?(b.val()||[]).length:b.val()}function c(b){return this.each(function(){var c=a(this),e=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b),f=c.data("bs.validator");(f||"destroy"!=b)&&(f||c.data("bs.validator",f=new d(this,e)),"string"==typeof b&&f[b]())})}var d=function(c,e){this.options=e,this.validators=a.extend({},d.VALIDATORS,e.custom),this.$element=a(c),this.$btn=a('button[type="submit"], input[type="submit"]').filter('[form="'+this.$element.attr("id")+'"]').add(this.$element.find('input[type="submit"], button[type="submit"]')),this.update(),this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator",a.proxy(this.onInput,this)),this.$element.on("submit.bs.validator",a.proxy(this.onSubmit,this)),this.$element.on("reset.bs.validator",a.proxy(this.reset,this)),this.$element.find("[data-match]").each(function(){var c=a(this),d=c.attr("data-match");a(d).on("input.bs.validator",function(){b(c)&&c.trigger("input.bs.validator")})}),this.$inputs.filter(function(){return b(a(this))&&!a(this).closest(".has-error").length}).trigger("focusout"),this.$element.attr("novalidate",!0)};d.VERSION="0.11.9",d.INPUT_SELECTOR=':input:not([type="hidden"], [type="submit"], [type="reset"], button)',d.FOCUS_OFFSET=20,d.DEFAULTS={delay:500,html:!1,disable:!0,focus:!0,custom:{},errors:{match:"Does not match",minlength:"Not long enough"},feedback:{success:"glyphicon-ok",error:"glyphicon-remove"}},d.VALIDATORS={"native":function(a){var b=a[0];return b.checkValidity?!b.checkValidity()&&!b.validity.valid&&(b.validationMessage||"error!"):void 0},match:function(b){var c=b.attr("data-match");return b.val()!==a(c).val()&&d.DEFAULTS.errors.match},minlength:function(a){var b=a.attr("data-minlength");return a.val().length<b&&d.DEFAULTS.errors.minlength}},d.prototype.update=function(){var b=this;return this.$inputs=this.$element.find(d.INPUT_SELECTOR).add(this.$element.find('[data-validate="true"]')).not(this.$element.find('[data-validate="false"]').each(function(){b.clearErrors(a(this))})),this.toggleSubmit(),this},d.prototype.onInput=function(b){var c=this,d=a(b.target),e="focusout"!==b.type;this.$inputs.is(d)&&this.validateInput(d,e).done(function(){c.toggleSubmit()})},d.prototype.validateInput=function(c,d){var e=(b(c),c.data("bs.validator.errors"));c.is('[type="radio"]')&&(c=this.$element.find('input[name="'+c.attr("name")+'"]'));var f=a.Event("validate.bs.validator",{relatedTarget:c[0]});if(this.$element.trigger(f),!f.isDefaultPrevented()){var g=this;return this.runValidators(c).done(function(b){c.data("bs.validator.errors",b),b.length?d?g.defer(c,g.showErrors):g.showErrors(c):g.clearErrors(c),e&&b.toString()===e.toString()||(f=b.length?a.Event("invalid.bs.validator",{relatedTarget:c[0],detail:b}):a.Event("valid.bs.validator",{relatedTarget:c[0],detail:e}),g.$element.trigger(f)),g.toggleSubmit(),g.$element.trigger(a.Event("validated.bs.validator",{relatedTarget:c[0]}))})}},d.prototype.runValidators=function(c){function d(a){return c.attr("data-"+a+"-error")}function e(){var a=c[0].validity;return a.typeMismatch?c.attr("data-type-error"):a.patternMismatch?c.attr("data-pattern-error"):a.stepMismatch?c.attr("data-step-error"):a.rangeOverflow?c.attr("data-max-error"):a.rangeUnderflow?c.attr("data-min-error"):a.valueMissing?c.attr("data-required-error"):null}function f(){return c.attr("data-error")}function g(a){return d(a)||e()||f()}var h=[],i=a.Deferred();return c.data("bs.validator.deferred")&&c.data("bs.validator.deferred").reject(),c.data("bs.validator.deferred",i),a.each(this.validators,a.proxy(function(a,d){var e=null;!b(c)&&!c.attr("required")||void 0===c.attr("data-"+a)&&"native"!=a||!(e=d.call(this,c))||(e=g(a)||e,!~h.indexOf(e)&&h.push(e))},this)),!h.length&&b(c)&&c.attr("data-remote")?this.defer(c,function(){var d={};d[c.attr("name")]=b(c),a.get(c.attr("data-remote"),d).fail(function(a,b,c){h.push(g("remote")||c)}).always(function(){i.resolve(h)})}):i.resolve(h),i.promise()},d.prototype.validate=function(){var b=this;return a.when(this.$inputs.map(function(){return b.validateInput(a(this),!1)})).then(function(){b.toggleSubmit(),b.focusError()}),this},d.prototype.focusError=function(){if(this.options.focus){var b=this.$element.find(".has-error :input:first");0!==b.length&&(a("html, body").animate({scrollTop:b.offset().top-d.FOCUS_OFFSET},250),b.focus())}},d.prototype.showErrors=function(b){var c=this.options.html?"html":"text",d=b.data("bs.validator.errors"),e=b.closest(".form-group"),f=e.find(".help-block.with-errors"),g=e.find(".form-control-feedback");d.length&&(d=a("<ul/>").addClass("list-unstyled").append(a.map(d,function(b){return a("<li/>")[c](b)})),void 0===f.data("bs.validator.originalContent")&&f.data("bs.validator.originalContent",f.html()),f.empty().append(d),e.addClass("has-error has-danger"),e.hasClass("has-feedback")&&g.removeClass(this.options.feedback.success)&&g.addClass(this.options.feedback.error)&&e.removeClass("has-success"))},d.prototype.clearErrors=function(a){var c=a.closest(".form-group"),d=c.find(".help-block.with-errors"),e=c.find(".form-control-feedback");d.html(d.data("bs.validator.originalContent")),c.removeClass("has-error has-danger has-success"),c.hasClass("has-feedback")&&e.removeClass(this.options.feedback.error)&&e.removeClass(this.options.feedback.success)&&b(a)&&e.addClass(this.options.feedback.success)&&c.addClass("has-success")},d.prototype.hasErrors=function(){function b(){return!!(a(this).data("bs.validator.errors")||[]).length}return!!this.$inputs.filter(b).length},d.prototype.isIncomplete=function(){function c(){var c=b(a(this));return!("string"==typeof c?a.trim(c):c)}return!!this.$inputs.filter("[required]").filter(c).length},d.prototype.onSubmit=function(a){this.validate(),(this.isIncomplete()||this.hasErrors())&&a.preventDefault()},d.prototype.toggleSubmit=function(){this.options.disable&&this.$btn.toggleClass("disabled",this.isIncomplete()||this.hasErrors())},d.prototype.defer=function(b,c){return c=a.proxy(c,this,b),this.options.delay?(window.clearTimeout(b.data("bs.validator.timeout")),void b.data("bs.validator.timeout",window.setTimeout(c,this.options.delay))):c()},d.prototype.reset=function(){return this.$element.find(".form-control-feedback").removeClass(this.options.feedback.error).removeClass(this.options.feedback.success),this.$inputs.removeData(["bs.validator.errors","bs.validator.deferred"]).each(function(){var b=a(this),c=b.data("bs.validator.timeout");window.clearTimeout(c)&&b.removeData("bs.validator.timeout")}),this.$element.find(".help-block.with-errors").each(function(){var b=a(this),c=b.data("bs.validator.originalContent");b.removeData("bs.validator.originalContent").html(c)}),this.$btn.removeClass("disabled"),this.$element.find(".has-error, .has-danger, .has-success").removeClass("has-error has-danger has-success"),this},d.prototype.destroy=function(){return this.reset(),this.$element.removeAttr("novalidate").removeData("bs.validator").off(".bs.validator"),this.$inputs.off(".bs.validator"),this.options=null,this.validators=null,this.$element=null,this.$btn=null,this.$inputs=null,this};var e=a.fn.validator;a.fn.validator=c,a.fn.validator.Constructor=d,a.fn.validator.noConflict=function(){return a.fn.validator=e,this},a(window).on("load",function(){a('form[data-toggle="validator"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery);
diff --git a/data/data.json b/data/data.json
new file mode 100644
index 00000000..83c877a4
--- /dev/null
+++ b/data/data.json
@@ -0,0 +1,202 @@
+[
+{
+  "title": "Cuaderno Anillado Globo Aerostático",
+  "image": "https://image.ibb.co/kCfvYH/1_200.png",
+  "large_image": "https://image.ibb.co/ekKNDH/1_500.png",
+  "price": 8990,
+  "description": "Déjate inspirar por este increíble cuaderno de Peter Pauper Press. Incluye 160 páginas levemente rayadas para escribir reflexiones personales, realizar sketches o simplemente anotar frases. Las hojas cuentan con calidad de archivo libre de ácidos que destaca la escritura. Se trata de un cuaderno práctico, duradero y personalizable. Cuenta con cubiertas que sirven de pizarrón, banda elástica para marcar páginas o mantenerlo cerrado y encuadernación chata con espiral. Medidas: 20.95 x 15.87 x 1.47 cm.",
+  "category": "notebooks",
+  "code": "PPA-2135",
+  "stock": "5"
+  },
+  {
+  "title": "Cuaderno My unicorn ate my homework",
+  "image": "https://image.ibb.co/mLjB7c/2_200.png",
+  "large_image":"https://image.ibb.co/dtPYLx/2_500.png",
+  "price": 3500,
+  "description": "Haz tus investigaciones en este maravilloso cuaderno. Incluye 100 páginas de composición, para que puedas escribir lo que quieras. ¡Llévalo a todas partes!. Medidas: 20 x 15.6 x 1.2 cm.",
+  "category": "notebooks",
+  "code": "GYA-3476",
+  "stock": "10"
+  },
+  {
+  "title": "Cuadernos American Crafts Memory Planner",
+  "image": "https://image.ibb.co/eWGutH/3_200.png",
+  "large_image": "https://image.ibb.co/hAHZtH/3_500.png",
+  "price": 3500,
+  "description": "Mini cuadernos para anotar recordatorios, fechas importantes y todo tipo de notas. Forman parte de la nueva colección de American Crafts Memory Planner. El kit incluye dos cuadernos de 9 x 10 cm.",
+  "category": "notebooks",
+  "code": "AMC-3039",
+  "stock": "10"
+  },
+  {
+  "title": "Cuaderno Cute, but psycho.",
+  "image": "https://image.ibb.co/hPeQ0x/4_200.png",
+  "large_image": "https://image.ibb.co/iixpSc/4_500.png",
+  "price": 3000,
+  "description": "Este cuaderno se caracteriza por su buen diseño y facilidad de transportar, ya que tiene un tamaño adecuado para cartera o mochila. Medidas: 18 x 10 x 1 cm.",
+  "category": "notebooks",
+  "code": "CUB-1274",
+  "stock": "6"
+  },
+  {
+  "title": "Block Chico The Fun Stuff",
+  "image": "https://image.ibb.co/fN43nc/5_200.png",
+  "large_image": "https://image.ibb.co/eQy7DH/5_500.png",
+  "price": 3290,
+  "description": "Un block imprescindible para llevar contigo a donde vayas gracias a su formato bolsillo. Podrás anotar ideas y listas de último momento. Cuenta con 75 páginas a todo color, cierre magnético, increíble diseño de Graphique de France y detalles en color oro. Medidas: 7.62 x 10 cm.",
+  "category": "notebooks",
+  "code": "GFR-1262",
+  "stock": "2"
+  },
+  {
+  "title": "Libreta diseño por Mauro Gómez",
+  "image": "https://image.ibb.co/i0U8Lx/6_200.png",
+  "large_image":"https://image.ibb.co/bPxTLx/6_500.png",
+  "price": 5900,
+  "description": "Libreta diseñada por Mauro Gómez, con una encuadernación rústica con un lomo forrado, este cuaderno es práctico para anotar todo tipo de cosas. Medidas: 18 x 15 x 2 cm.",
+  "category": "notebooks",
+  "code": "DCA-1253",
+  "stock": "12"
+  },
+  {
+  "title": "Block Chico Magnético Fucsia Life Moves",
+  "image": "https://image.ibb.co/fUGzSc/7_200.png",
+  "large_image": "https://image.ibb.co/hvvtnc/7_500.png",
+  "price": 3990,
+  "description": "Un block formato cartera o bolsillo para escribir lo que quieras. Desde tus ideas y pensamientos hasta una lista de compras; sirve para todo. Block tamaño bolsillo cierre magnético, hojas croquis. Traído de USA. Medidas: 76 x 100 x 10 mm, 70 gr.",
+  "category": "notebooks",
+  "code": "GFR-7841",
+  "stock": "5"
+  },
+  {
+  "title": "Pack 3 Cuadernos Ilustrados",
+  "image": "https://image.ibb.co/jKgoLx/8_200.png",
+  "large_image": "https://image.ibb.co/e4sQYH/8_500.png",
+  "price": 7990,
+  "description": "3 Cuadernos ilustrados y cocidos a mano, por la ilustradora Nuria Díaz. Por el tamaño de estos cuadernos, son prácticos para llevarlos a todas partes, toma notas, haz listas, puedes hacer sketch y serán un gran aporte en tu vida diaria.",
+  "category": "notebooks",
+  "code": "AGD-9623",
+  "stock": "notebooks"
+  },
+  {
+  "title": "Libreta Fuchsia Blooms",
+  "image": "https://image.ibb.co/ktCg7c/9_200.png",
+  "large_image": "https://image.ibb.co/kt2QYH/9_500.png",
+  "price": 6990,
+  "description": "Espectacular libreta de la colección Peter Pauper Press. Incluye 160 páginas levemente rayadas, cierre elástico en color celeste, tapa dura con diseño de flores, bolsillo interior para guardar notas y detalles en folia plateada. Cuenta con papel con calidad archivo y certificación libre de ácidos. Medidas: 12.7 x 17.78 x 1.27 cm ",
+  "category": "notebooks",
+  "code": "PPA-5595",
+  "stock": "15"
+  },
+  {
+  "title": "Cuaderno Flamencos",
+  "image": "https://image.ibb.co/c8rr7c/10_200.png",
+  "large_image": "https://image.ibb.co/bTo4Sc/10_500.png",
+  "price": 3990,
+  "description": "Cuaderno con una encuadernación rústica, para que anotes lo que quieras. Medidas 18 x 15.6 x 1 cm.",
+  "category": "notebooks",
+  "code": "ADG-1346",
+  "stock": "19"
+  },
+  {
+  "title": "Perforadora con Bordes Intercambiables Rosa",
+  "image": "https://image.ibb.co/bVC3Lx/11_200.png",
+  "large_image": "https://image.ibb.co/cCs3Lx/11_500.png",
+  "price": 9990,
+  "description": "Perforadora con bordes intercambiables de la colección Knock Outs de American Crafts. Los cartuchos son muy fáciles de cambiar ya que simplemente se encajan a presión en la base de la perforadora. De esta forma tienes 6 bordes diferentes para decorar tus papeles. Este espectacular kit contiene una perforadora, seis bordes intercambiables, 12 guías con forma de ala y un práctico estuche para su guardado y traslado. Es un regalo perfecto para cualquier aficionada al scrap o las manualidades.",
+  "category": "scrapbook",
+  "code": "AMC-8217",
+  "stock": "10"
+  },
+  {
+  "title": "Set de Timbres",
+  "image": "https://image.ibb.co/ekWcVx/12_200.png",
+  "large_image": "https://image.ibb.co/feqhxc/12_500.png",
+  "price": 2990,
+  "description": "Set incluye: 6 timbres de madera con base de goma, perfectos para Scrapbooking. 30mm de diámetro",
+  "category": "scrapbook",
+  "code": "YBN-2027",
+  "stock": "5"
+  },
+  {
+  "title": "Set Hojas de Papel Gather",
+  "image": "https://image.ibb.co/fx3kHc/13_200.png",
+  "large_image":"https://image.ibb.co/mg6GOH/13_500.png",
+  "price": 13990,
+  "description": "¡La adición perfecta para todos tus proyectos de diseño! Estas hojas para scrapbook reversibles vienen con diferentes diseños. Puedes encontrarlas en diversas variedades. Set hojas cartulina gruesa opaca reversible impresa con diferente diseño por tiro y retiro. Libre de ácido. Traído de USA. 305 x 305 mm 305grs",
+  "category": "scrapbook",
+  "code": "AMC-3758",
+  "stock": "13"
+  },
+  {
+  "title": "Set de Stickers",
+  "image": "https://image.ibb.co/hEKLiH/14_200.png",
+  "large_image": "https://image.ibb.co/cek4Ax/14_500.png",
+  "price": 3990,
+  "description": "Set incluye: 5 hojas de stickers. Dimensiones: 150 x 210 mm",
+  "category": "scrapbook",
+  "code": "FDM-3212",
+  "stock": "15"
+  },
+  {
+  "title": "Washi Tape Diseños Dorados",
+  "image": "https://image.ibb.co/dsDPAx/15_200.png",
+  "large_image": "https://image.ibb.co/cmZaHc/15_500.png",
+  "price": 1490,
+  "description": "Cinta adhesiva decorativa. 7 metros cada una, ancho 8mm",
+  "category": "scrapbook",
+  "code": "UGN-0048",
+  "stock": "28"
+  },
+  {
+  "title": "Set de Clips",
+  "image": "https://image.ibb.co/fAGGOH/16_200.png",
+  "large_image": "https://image.ibb.co/hjikHc/16_500.png",
+  "price": 2990,
+  "description": "Set incluye: 4 clips de metal color cobre.",
+  "category": "scrapbook",
+  "code": "LJM-0014",
+  "stock": "17"
+  },
+  {
+  "title": "Washi Tape Diseños Simples",
+  "image": "https://image.ibb.co/m6q4Ax/17_200.png",
+  "large_image": "https://image.ibb.co/f8VFHc/17_500.png",
+  "price": 1200,
+  "description": "Washi tapes con diferentes diseños.",
+  "category": "scrapbook",
+  "code": "3212861000689",
+  "stock": "14"
+  },
+  {
+  "title": "Perforadora Punch WR Diseño Bracket",
+  "image": "https://image.ibb.co/jUOViH/18_200.png",
+  "large_image": "https://image.ibb.co/dHUgqx/18_500.png",
+  "price": 6990,
+  "description": "La perforadora Mini 8 Punch de We R Memory Keepers es perfecta para utilizar con papeles de 15 x 15 cm. Transforma tu papel en 8 sencillos pasos. Se trata de una perforada de corte exacto. Es ideal para trabajos de scrapbooking y para confeccionar tarjetas. Crea y decora esquinas en cualquier tamaño de papel.",
+  "category": "scrapbook",
+  "code": "AMC-71298-5",
+  "stock": "3"
+  },
+  {
+  "title": "Set de Agujas Stitch Happy para Scrapbooking",
+  "image": "https://image.ibb.co/jRnsVx/19_200.png",
+  "large_image":"https://image.ibb.co/k0VCVx/19_500.png",
+  "price": 6990,
+  "description": "Set de seis agujas especialmente diseñadas para confección en distintos medios para utilizar con la máquina de coser Stitch Happy. Contiene tres agujas grandes #18 y tres #21 para hilos más gruesos con revestimiento para evitar pérdida de filo.",
+  "category": "scrapbook",
+  "code": "AMC-660394",
+  "stock": "6"
+  },
+  {
+  "title": "Set Banner para Coser Stitch Happy",
+  "image": "https://image.ibb.co/gdw2Vx/20_200.png",
+  "large_image": "https://image.ibb.co/g5wQiH/20_500.png",
+  "price": 5990,
+  "description": "Un increíble kit diseñado por We R Memory Keepers para coser piezas únicas. Contiene 12 banners de papel, 12 círculos de papel decorativos, 4 frases para celebrar (celebrate, welcome, congrats y baby) 3 corazones y 3 círculos de fieltro. ",
+  "category": "scrapbook",
+  "code": "AMC-660316",
+  "stock": "23"
+  }
+]
\ No newline at end of file
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 00000000..f683fabd
Binary files /dev/null and b/favicon.ico differ
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..9ced95be
--- /dev/null
+++ b/index.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <title>d'papel</title>
+    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
+    <link rel="icon" href="favicon.ico" type="image/x-icon">
+    <link rel="stylesheet" href="stylesheets/jsonstore.css" type="text/css" media="screen" charset="utf-8"/>
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
+    <script src="https://www.gstatic.com/firebasejs/4.10.1/firebase.js"></script>
+    <script>
+    // Initialize Firebase
+    var config = {
+    apiKey: "AIzaSyBp62l0Nv2rv8F9_MDgm2v7jWhPALlCkbI",
+    authDomain: "d-papel.firebaseapp.com",
+    databaseURL: "https://d-papel.firebaseio.com",
+    projectId: "d-papel",
+    storageBucket: "",
+    messagingSenderId: "412099125004"
+    };
+    firebase.initializeApp(config);
+    </script>
+  </head>
+  <body>
+    <!-- Modal Registro-->
+    <div class="modal fade" id="modalRegister" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+      <div class="modal-dialog" role="document">
+        <div class="modal-content">
+          <div class="modal-header">
+            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+            <h3 class="text-center">Registro</h3>
+          </div>
+          <div class="modal-body-register">
+            <form class="container-fluid" data-toggle="validator" role="form">
+              <div class="form-group">
+                <div class="row">
+                  <div class="row">
+                    <label for="inputEmail" class="control-label col-xs-offset-1 col-sm-offset-1 col-md-offset-1 col-lg-offset-1">Correo electrónico</label>
+                  </div>
+                  <div class="row">
+                    <input id="inputEmailReg" class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8" type="email" class="form-control"  aria-describedby="basic-addon2" placeholder="correo electrónico" data-error="Por favor ingresa un correo electrónico válido. Ej. ejemplo@mail.com" required>
+                  </div>
+                  <div class="row">
+                    <div class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8 help-block with-errors"></div>
+                  </div>
+                  </div> <!-- /row -->
+                  </div> <!-- /form-group -->
+                  <div class="form-group">
+                    <div class="row">
+                      <div class="row">
+                        <label for="inputPassword" class="control-label col-xs-offset-1 col-sm-offset-1 col-md-offset-1 col-lg-offset-1">Contraseña</label>
+                      </div>
+                      <div class="row">
+                        <input class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8" type="password" data-minlength="6" class="form-control"  aria-describedby="basic-addon2" id="inputPassReg" placeholder="Contraseña" required>
+                      </div>
+                      <div class="row">
+                        <div class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8 help-block">Mínimo de 6 caracteres</div>
+                      </div>
+                    </div>
+                    </div> <!-- /form-group -->
+                    <div class="form-group">
+                      <div class="row">
+                        <div class="row">
+                          <label for="inputPasswordConfirm" class="control-label col-xs-offset-1 col-sm-offset-1 col-md-offset-1 col-lg-offset-1">Confirma tu contraseña</label>
+                        </div>
+                        <div class="row">
+                          <input class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8" type="password" class="form-control" id="inputPasswordConfirm" data-match="#inputPassReg" data-match-error="Ups, las contraseñas no coinciden." data-error="Completa este campo" placeholder="Confirma tu contraseña" required>
+                        </div>
+                        <div class="row">
+                          <div class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8 help-block with-errors"></div>
+                        </div>
+                      </div>
+                      </div> <!-- /form-group -->
+                      <div class="form-group">
+                        <button id="register-btn" type="submit" data-dismiss="modal" class=" mb-1 btn btn-primary col-md-offset-3 col-lg-offset-3 col-md-6 col-lg-6 col-sm-offset-2 col-xs-offset-2 col-sm-8 col-xs-8 btn-registro">Enviar</button>
+                      </div>
+                    </form>
+                    <div class="modal-footer">
+                      <button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button>
+                    </div>
+                    </div> <!-- /modal-body -->
+                    </div> <!-- /modal-content -->
+                    </div> <!-- /modal-dialog -->
+                    </div><!-- /modal fade - fin modal de registro -->
+                    <!-- Modal Log In -->
+                    <div class="modal fade" id="modalLog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+                      <div class="modal-dialog" role="document">
+                        <div class="modal-content">
+                          <div class="modal-header">
+                            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                            <h3 class="text-center">Iniciar sesión</h3>
+                          </div>
+                          <div class="modal-body">
+                            <form data-toggle="validator" role="form">
+                              <div class="row">
+                                <div class="form-group">
+                                  <div class="row">
+                                    <label for="inputEmail" class="control-label col-xs-offset-1 col-sm-offset-1 col-md-offset-1 col-lg-offset-1">Correo electrónico</label>
+                                  </div>
+                                  <div class="row">
+                                    <input id="inputEmailLog" class="col-xs-offset-1 col-sm-offset-1 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8 " type="email" class="form-control"  aria-describedby="basic-addon2" placeholder="correo electrónico" data-error="Por favor ingresa un correo electrónico válido. Ej. ejemplo@mail.com" required>
+                                  </div>
+                                  <div class="row">
+                                    <div class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8 help-block with-errors"></div>
+                                  </div>
+                                  </div> <!-- /form-group -->
+                                  </div> <!-- /row -->
+                                  <div class="row">
+                                    <div class="form-group">
+                                      <div class="row">
+                                        <label for="inputPassword" class="control-label col-xs-offset-1 col-sm-offset-1 col-md-offset-1 col-lg-offset-1">Contraseña</label>
+                                      </div>
+                                      <div class="row">
+                                        <div class="row">
+                                          <input class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 col-sm-8 col-xs-8 col-lg-8 col-md-8" type="password" data-minlength="6" class="form-control" aria-describedby="basic-addon2" id="inputPassLog" placeholder="contraseña" required>
+                                        </div>
+                                        <div class="row">
+                                          <div class="col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2 help-block">Mínimo de 6 caracteres</div>
+                                        </div>
+                                      </div>
+                                      <div class="row">
+                                        <a class="col-xs-offset-2 col-sm-offset-2 col-md-offset-3 col-lg-offset-3 col-sm-8 col-xs-8 col-lg-8 col-md-8" id="forgotPass" href="">Olvidaste tu contraseña? Haz click aquí!</a>
+                                      </div>
+                                      </div> <!-- /form-group -->
+                                      </div> <!-- /row -->
+                                      <div class="row">
+                                        <div class="form-group">
+                                          <button id="log-btn" type="submit" class="btn btn-primary col-xs-offset-4 col-sm-offset-4 col-sm-4 col-xs-4 btn-ingreso" data-dismiss="modal">Iniciar sesión</button>
+                                        </div>
+                                      </div>
+                                    </form>
+                                    <div class="modal-footer">
+                                      <button type="button" class="btn btn-default" data-dismiss="modal">cerrar</button>
+                                    </div>
+                                    </div> <!-- /modal-body -->
+                                    </div> <!-- /modal-content -->
+                                    </div> <!-- /modal-dialog -->
+                                    </div> <!-- /modal fade -->
+                                    
+                                    <section id="navBar">
+                                      <div class="collapse navbar-collapse navbar-inverse" id="bs-example-navbar-collapse-1">
+                                        <ul class="nav navbar-nav navbar-left">
+                                          <img src="logo.png" alt="d'papel" />
+                                        </ul>
+                                        <ul class="nav navbar-nav navbar-right">
+                                          <li class=""><a id="logIn" data-toggle="modal" data-target="#modalLog" role="button">Iniciar sesión</a></li>
+                                          <li><a id="register" data-toggle="modal" data-target="#modalRegister" role="button">Registro</a></li>
+                                          <li><a id="logOut" role="button">Cerrar sesión</a></li>
+                                          <li> <div class="cart-info">
+                                            My Cart (<span class="cart-items">0</span> items)
+                                          </div></li>
+                                        </ul>
+                                      </div>
+                                    </section>
+                                    <div class="container">
+                                      <div id="main">
+                                      </div>
+                                    </div>
+                                    <script src="javascript/jquery-1.4.2.js" type="text/javascript" charset="utf-8"></script>
+                                    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
+                                    <script src="data/data.json"></script>
+                                    <script src="javascript/sammy/sammy.js" type="text/javascript" charset="utf-8"></script>
+                                    <script src="javascript/sammy/plugins/sammy.template.js" type="text/javascript" charset="utf-8"></script>
+                                    <script src="javascript/sammy/plugins/sammy.json.js" type="text/javascript" charset="utf-8"></script>
+                                    <script src="javascript/sammy/plugins/sammy.storage.js" type="text/javascript" charset="utf-8"></script>
+                                    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
+                                    <script src="javascript/validate.min.js"></script>
+                                    <script src="javascript/app.js"></script>
+                                  </body>
+                                </html>
\ No newline at end of file
diff --git a/javascript/app.js b/javascript/app.js
new file mode 100644
index 00000000..e8566a80
--- /dev/null
+++ b/javascript/app.js
@@ -0,0 +1,166 @@
+(function($) {
+  //inicializa sammy en #main
+  var app = $.sammy('#main', function() {
+    //sammy puede usar el plugin template y session en main
+    this.use('Template');
+    this.use('Session');
+    this.around(function(callback) {
+      var context = this;
+      this.load('data/data.json')
+        .then(function(items) {
+          context.items = items;
+        })
+        .then(callback);
+    });
+    // Cuando se cliqueen los botones en registro
+    $('#register-btn').click(function(e) {
+      e.preventDefault();
+      var emailReg = $('#inputEmailReg').val();
+      var passReg = $('#inputPassReg').val();
+      console.log(emailReg);
+      console.log('registrado');
+      firebase.auth().createUserWithEmailAndPassword(emailReg, passReg)
+        .then(function() {
+          var user = firebase.auth().currentUser;
+          user.sendEmailVerification().then(function() {
+          }).catch(function(error) {
+          });
+        })
+        .catch(function(error) {
+          // los errores van aquí.
+          var errorCode = error.code;
+          var errorMessage = error.message;
+        });
+    }); // fin funcion click en boton registro
+
+    // FIREBASE
+    $('#log-btn').click(function(e) {
+      e.preventDefault();
+      var emailLog = $('#inputEmailLog').val();
+      var passLog = $('#inputPassLog').val();
+      firebase.auth().signInWithEmailAndPassword(emailLog, passLog).catch(function(error) {
+        // los errores van aquí.
+        var errorCode = error.code;
+        var errorMessage = error.message;
+      });
+    }); // fin funcion click en boton log in
+    // ver si hay usuario activo
+    function observardor() {
+      firebase.auth().onAuthStateChanged(function(user) {
+        if (user) {
+          $('.cart-info').show();
+          $('#logOut').show();
+          $('#logIn').hide();
+          $('#register').hide();
+          // el usuario ingresó
+        } else {
+          $('.cart-info').hide();
+          $('#logOut').hide();
+          $('#logIn').show();
+          $('#register').show();
+          // el usuario salió
+          // ...
+        }
+      });
+    }; // fin funcion observador
+    observardor();
+
+    // desconectar
+    $('#logOut').click(function() {
+      firebase.auth().signOut().then(function() {
+      });
+      firebase.auth().signOut().catch(function(error) {
+      });
+    }); // fin funcion click en boton log out
+
+    $('#forgotPass').click(function() {
+      var auth = firebase.auth();
+      var emailAddress = prompt('Ingresa tu correo electrónico');
+      auth.sendPasswordResetEmail(emailAddress).then(function() {
+        // enviar mail
+      }).catch(function(error) {
+      });
+    }); // fin funcion click en olvidar contraseña
+
+    // validación de email
+    function validateEmail($email) {
+      var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
+      return emailReg.test($email);
+    }
+/*
+* ruta basica #, cuando el navegador está en #, la
+* funcion que definimos se ejecutará en un contexto sammy
+* muestra los items que están en la data appeneada en template
+*/
+    this.get('#/', function(context) {
+      context.app.swap('');
+      $.each(this.items, function(i, item) {
+        context.render('templates/item.template', {
+          id: i,
+          item: item
+        })
+          .appendTo(context.$element());
+      });
+    });
+
+    this.get('#/item/:id', function(context) {
+      this.item = this.items[this.params['id']];
+      if (!this.item) {
+        return this.notFound();
+      }
+      this.partial('templates/item_detail.template');
+    });
+
+    this.post('#/cart', function(context) {
+      var itemId = this.params['item_id'];
+      // fetch al carro actual
+      var cart = this.session('cart', function() {
+        return {};
+      });
+      if (!cart[itemId]) {
+        // este item no está aún en el carro
+        // inicializa la cantidad con 0
+        cart[itemId] = 0;
+      }
+      cart[itemId] += parseInt(this.params['quantity'], 10);
+      // almacena el carro
+      this.session('cart', cart);
+      this.trigger('update-cart');
+    });
+// funcion para actualizar carro
+    this.bind('update-cart', function() {
+      var sum = 0;
+      $.each(this.session('cart') || {}, function(id, quantity) {
+        sum += quantity;
+      });
+      $('.cart-info')
+        .find('.cart-items').text(sum).end()
+        .animate({paddingTop: '30px'})
+        .animate({paddingTop: '10px'});
+    });
+
+    this.bind('run', function() {
+      // muestra el carro actualizado
+      this.trigger('update-cart');
+    });
+  });
+  $(function() {
+    app.run('#/');
+  });
+})(jQuery);
+
+/*
+* botón de pagp por Stripe, se puso en item_detail.template
+* el botón llama al servidor de stripe con la API (data key)
+* de prueba que el sitio entrega a los desarrolladores
+* para que funcione hay que ingresar el número de TC de prueba
+* 4242 4242 4242 4242 más una fecha de vencimiento creíble
+* y 3 digitos al azar
+$(".pay").append("<script src='https://checkout.stripe.com/checkout.js'" + "class='stripe-button'" +
+    "data-key='pk_test_LMeQ66Q4hSaSh774qEv4EzwZ'" +
+    "data-image='/images/marketplace.png'" +
+    "data-name='SPA test'" +
+    "data-description='test de compra de 1 producto'" +
+    "data-amount='2000'" +
+    "data-label='Buy'></script>")
+*/
\ No newline at end of file
diff --git a/javascript/jquery-1.4.2.js b/javascript/jquery-1.4.2.js
new file mode 100644
index 00000000..38205810
--- /dev/null
+++ b/javascript/jquery-1.4.2.js
@@ -0,0 +1,6240 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function( window, undefined ) {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+    // The jQuery object is actually just the init constructor 'enhanced'
+    return new jQuery.fn.init( selector, context );
+  },
+
+  // Map over jQuery in case of overwrite
+  _jQuery = window.jQuery,
+
+  // Map over the $ in case of overwrite
+  _$ = window.$,
+
+  // Use the correct document accordingly with window argument (sandbox)
+  document = window.document,
+
+  // A central reference to the root jQuery(document)
+  rootjQuery,
+
+  // A simple way to check for HTML strings or ID strings
+  // (both of which we optimize for)
+  quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
+
+  // Is it a simple selector
+  isSimple = /^.[^:#\[\.,]*$/,
+
+  // Check if a string has a non-whitespace character in it
+  rnotwhite = /\S/,
+
+  // Used for trimming whitespace
+  rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
+
+  // Match a standalone tag
+  rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+  // Keep a UserAgent string for use with jQuery.browser
+  userAgent = navigator.userAgent,
+
+  // For matching the engine and version of the browser
+  browserMatch,
+
+  // Has the ready events already been bound?
+  readyBound = false,
+
+  // The functions to execute on DOM ready
+  readyList = [],
+
+  // The ready event handler
+  DOMContentLoaded,
+
+  // Save a reference to some core methods
+  toString = Object.prototype.toString,
+  hasOwnProperty = Object.prototype.hasOwnProperty,
+  push = Array.prototype.push,
+  slice = Array.prototype.slice,
+  indexOf = Array.prototype.indexOf;
+
+jQuery.fn = jQuery.prototype = {
+  init: function( selector, context ) {
+    var match, elem, ret, doc;
+
+    // Handle $(""), $(null), or $(undefined)
+    if ( !selector ) {
+      return this;
+    }
+
+    // Handle $(DOMElement)
+    if ( selector.nodeType ) {
+      this.context = this[0] = selector;
+      this.length = 1;
+      return this;
+    }
+
+    // The body element only exists once, optimize finding it
+    if ( selector === "body" && !context ) {
+      this.context = document;
+      this[0] = document.body;
+      this.selector = "body";
+      this.length = 1;
+      return this;
+    }
+
+    // Handle HTML strings
+    if ( typeof selector === "string" ) {
+      // Are we dealing with HTML string or an ID?
+      match = quickExpr.exec( selector );
+
+      // Verify a match, and that no context was specified for #id
+      if ( match && (match[1] || !context) ) {
+
+        // HANDLE: $(html) -> $(array)
+        if ( match[1] ) {
+          doc = (context ? context.ownerDocument || context : document);
+
+          // If a single string is passed in and it's a single tag
+          // just do a createElement and skip the rest
+          ret = rsingleTag.exec( selector );
+
+          if ( ret ) {
+            if ( jQuery.isPlainObject( context ) ) {
+              selector = [ document.createElement( ret[1] ) ];
+              jQuery.fn.attr.call( selector, context, true );
+
+            } else {
+              selector = [ doc.createElement( ret[1] ) ];
+            }
+
+          } else {
+            ret = buildFragment( [ match[1] ], [ doc ] );
+            selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
+          }
+
+          return jQuery.merge( this, selector );
+
+        // HANDLE: $("#id")
+        } else {
+          elem = document.getElementById( match[2] );
+
+          if ( elem ) {
+            // Handle the case where IE and Opera return items
+            // by name instead of ID
+            if ( elem.id !== match[2] ) {
+              return rootjQuery.find( selector );
+            }
+
+            // Otherwise, we inject the element directly into the jQuery object
+            this.length = 1;
+            this[0] = elem;
+          }
+
+          this.context = document;
+          this.selector = selector;
+          return this;
+        }
+
+      // HANDLE: $("TAG")
+      } else if ( !context && /^\w+$/.test( selector ) ) {
+        this.selector = selector;
+        this.context = document;
+        selector = document.getElementsByTagName( selector );
+        return jQuery.merge( this, selector );
+
+      // HANDLE: $(expr, $(...))
+      } else if ( !context || context.jquery ) {
+        return (context || rootjQuery).find( selector );
+
+      // HANDLE: $(expr, context)
+      // (which is just equivalent to: $(context).find(expr)
+      } else {
+        return jQuery( context ).find( selector );
+      }
+
+    // HANDLE: $(function)
+    // Shortcut for document ready
+    } else if ( jQuery.isFunction( selector ) ) {
+      return rootjQuery.ready( selector );
+    }
+
+    if (selector.selector !== undefined) {
+      this.selector = selector.selector;
+      this.context = selector.context;
+    }
+
+    return jQuery.makeArray( selector, this );
+  },
+
+  // Start with an empty selector
+  selector: "",
+
+  // The current version of jQuery being used
+  jquery: "1.4.2",
+
+  // The default length of a jQuery object is 0
+  length: 0,
+
+  // The number of elements contained in the matched element set
+  size: function() {
+    return this.length;
+  },
+
+  toArray: function() {
+    return slice.call( this, 0 );
+  },
+
+  // Get the Nth element in the matched element set OR
+  // Get the whole matched element set as a clean array
+  get: function( num ) {
+    return num == null ?
+
+      // Return a 'clean' array
+      this.toArray() :
+
+      // Return just the object
+      ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
+  },
+
+  // Take an array of elements and push it onto the stack
+  // (returning the new matched element set)
+  pushStack: function( elems, name, selector ) {
+    // Build a new jQuery matched element set
+    var ret = jQuery();
+
+    if ( jQuery.isArray( elems ) ) {
+      push.apply( ret, elems );
+
+    } else {
+      jQuery.merge( ret, elems );
+    }
+
+    // Add the old object onto the stack (as a reference)
+    ret.prevObject = this;
+
+    ret.context = this.context;
+
+    if ( name === "find" ) {
+      ret.selector = this.selector + (this.selector ? " " : "") + selector;
+    } else if ( name ) {
+      ret.selector = this.selector + "." + name + "(" + selector + ")";
+    }
+
+    // Return the newly-formed element set
+    return ret;
+  },
+
+  // Execute a callback for every element in the matched set.
+  // (You can seed the arguments with an array of args, but this is
+  // only used internally.)
+  each: function( callback, args ) {
+    return jQuery.each( this, callback, args );
+  },
+
+  ready: function( fn ) {
+    // Attach the listeners
+    jQuery.bindReady();
+
+    // If the DOM is already ready
+    if ( jQuery.isReady ) {
+      // Execute the function immediately
+      fn.call( document, jQuery );
+
+    // Otherwise, remember the function for later
+    } else if ( readyList ) {
+      // Add the function to the wait list
+      readyList.push( fn );
+    }
+
+    return this;
+  },
+
+  eq: function( i ) {
+    return i === -1 ?
+      this.slice( i ) :
+      this.slice( i, +i + 1 );
+  },
+
+  first: function() {
+    return this.eq( 0 );
+  },
+
+  last: function() {
+    return this.eq( -1 );
+  },
+
+  slice: function() {
+    return this.pushStack( slice.apply( this, arguments ),
+      "slice", slice.call(arguments).join(",") );
+  },
+
+  map: function( callback ) {
+    return this.pushStack( jQuery.map(this, function( elem, i ) {
+      return callback.call( elem, i, elem );
+    }));
+  },
+
+  end: function() {
+    return this.prevObject || jQuery(null);
+  },
+
+  // For internal use only.
+  // Behaves like an Array's method, not like a jQuery method.
+  push: push,
+  sort: [].sort,
+  splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+  // copy reference to target object
+  var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
+
+  // Handle a deep copy situation
+  if ( typeof target === "boolean" ) {
+    deep = target;
+    target = arguments[1] || {};
+    // skip the boolean and the target
+    i = 2;
+  }
+
+  // Handle case when target is a string or something (possible in deep copy)
+  if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+    target = {};
+  }
+
+  // extend jQuery itself if only one argument is passed
+  if ( length === i ) {
+    target = this;
+    --i;
+  }
+
+  for ( ; i < length; i++ ) {
+    // Only deal with non-null/undefined values
+    if ( (options = arguments[ i ]) != null ) {
+      // Extend the base object
+      for ( name in options ) {
+        src = target[ name ];
+        copy = options[ name ];
+
+        // Prevent never-ending loop
+        if ( target === copy ) {
+          continue;
+        }
+
+        // Recurse if we're merging object literal values or arrays
+        if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
+          var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
+            : jQuery.isArray(copy) ? [] : {};
+
+          // Never move original objects, clone them
+          target[ name ] = jQuery.extend( deep, clone, copy );
+
+        // Don't bring in undefined values
+        } else if ( copy !== undefined ) {
+          target[ name ] = copy;
+        }
+      }
+    }
+  }
+
+  // Return the modified object
+  return target;
+};
+
+jQuery.extend({
+  noConflict: function( deep ) {
+    window.$ = _$;
+
+    if ( deep ) {
+      window.jQuery = _jQuery;
+    }
+
+    return jQuery;
+  },
+
+  // Is the DOM ready to be used? Set to true once it occurs.
+  isReady: false,
+
+  // Handle when the DOM is ready
+  ready: function() {
+    // Make sure that the DOM is not already loaded
+    if ( !jQuery.isReady ) {
+      // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+      if ( !document.body ) {
+        return setTimeout( jQuery.ready, 13 );
+      }
+
+      // Remember that the DOM is ready
+      jQuery.isReady = true;
+
+      // If there are functions bound, to execute
+      if ( readyList ) {
+        // Execute all of them
+        var fn, i = 0;
+        while ( (fn = readyList[ i++ ]) ) {
+          fn.call( document, jQuery );
+        }
+
+        // Reset the list of functions
+        readyList = null;
+      }
+
+      // Trigger any bound ready events
+      if ( jQuery.fn.triggerHandler ) {
+        jQuery( document ).triggerHandler( "ready" );
+      }
+    }
+  },
+
+  bindReady: function() {
+    if ( readyBound ) {
+      return;
+    }
+
+    readyBound = true;
+
+    // Catch cases where $(document).ready() is called after the
+    // browser event has already occurred.
+    if ( document.readyState === "complete" ) {
+      return jQuery.ready();
+    }
+
+    // Mozilla, Opera and webkit nightlies currently support this event
+    if ( document.addEventListener ) {
+      // Use the handy event callback
+      document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+      // A fallback to window.onload, that will always work
+      window.addEventListener( "load", jQuery.ready, false );
+
+    // If IE event model is used
+    } else if ( document.attachEvent ) {
+      // ensure firing before onload,
+      // maybe late but safe also for iframes
+      document.attachEvent("onreadystatechange", DOMContentLoaded);
+
+      // A fallback to window.onload, that will always work
+      window.attachEvent( "onload", jQuery.ready );
+
+      // If IE and not a frame
+      // continually check to see if the document is ready
+      var toplevel = false;
+
+      try {
+        toplevel = window.frameElement == null;
+      } catch(e) {}
+
+      if ( document.documentElement.doScroll && toplevel ) {
+        doScrollCheck();
+      }
+    }
+  },
+
+  // See test/unit/core.js for details concerning isFunction.
+  // Since version 1.3, DOM methods and functions like alert
+  // aren't supported. They return false on IE (#2968).
+  isFunction: function( obj ) {
+    return toString.call(obj) === "[object Function]";
+  },
+
+  isArray: function( obj ) {
+    return toString.call(obj) === "[object Array]";
+  },
+
+  isPlainObject: function( obj ) {
+    // Must be an Object.
+    // Because of IE, we also have to check the presence of the constructor property.
+    // Make sure that DOM nodes and window objects don't pass through, as well
+    if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
+      return false;
+    }
+
+    // Not own constructor property must be Object
+    if ( obj.constructor
+      && !hasOwnProperty.call(obj, "constructor")
+      && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
+      return false;
+    }
+
+    // Own properties are enumerated firstly, so to speed up,
+    // if last one is own, then all properties are own.
+
+    var key;
+    for ( key in obj ) {}
+
+    return key === undefined || hasOwnProperty.call( obj, key );
+  },
+
+  isEmptyObject: function( obj ) {
+    for ( var name in obj ) {
+      return false;
+    }
+    return true;
+  },
+
+  error: function( msg ) {
+    throw msg;
+  },
+
+  parseJSON: function( data ) {
+    if ( typeof data !== "string" || !data ) {
+      return null;
+    }
+
+    // Make sure leading/trailing whitespace is removed (IE can't handle it)
+    data = jQuery.trim( data );
+
+    // Make sure the incoming data is actual JSON
+    // Logic borrowed from http://json.org/json2.js
+    if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
+      .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
+      .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
+
+      // Try to use the native JSON parser first
+      return window.JSON && window.JSON.parse ?
+        window.JSON.parse( data ) :
+        (new Function("return " + data))();
+
+    } else {
+      jQuery.error( "Invalid JSON: " + data );
+    }
+  },
+
+  noop: function() {},
+
+  // Evalulates a script in a global context
+  globalEval: function( data ) {
+    if ( data && rnotwhite.test(data) ) {
+      // Inspired by code by Andrea Giammarchi
+      // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+      var head = document.getElementsByTagName("head")[0] || document.documentElement,
+        script = document.createElement("script");
+
+      script.type = "text/javascript";
+
+      if ( jQuery.support.scriptEval ) {
+        script.appendChild( document.createTextNode( data ) );
+      } else {
+        script.text = data;
+      }
+
+      // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+      // This arises when a base node is used (#2709).
+      head.insertBefore( script, head.firstChild );
+      head.removeChild( script );
+    }
+  },
+
+  nodeName: function( elem, name ) {
+    return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+  },
+
+  // args is for internal usage only
+  each: function( object, callback, args ) {
+    var name, i = 0,
+      length = object.length,
+      isObj = length === undefined || jQuery.isFunction(object);
+
+    if ( args ) {
+      if ( isObj ) {
+        for ( name in object ) {
+          if ( callback.apply( object[ name ], args ) === false ) {
+            break;
+          }
+        }
+      } else {
+        for ( ; i < length; ) {
+          if ( callback.apply( object[ i++ ], args ) === false ) {
+            break;
+          }
+        }
+      }
+
+    // A special, fast, case for the most common use of each
+    } else {
+      if ( isObj ) {
+        for ( name in object ) {
+          if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+            break;
+          }
+        }
+      } else {
+        for ( var value = object[0];
+          i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
+      }
+    }
+
+    return object;
+  },
+
+  trim: function( text ) {
+    return (text || "").replace( rtrim, "" );
+  },
+
+  // results is for internal usage only
+  makeArray: function( array, results ) {
+    var ret = results || [];
+
+    if ( array != null ) {
+      // The window, strings (and functions) also have 'length'
+      // The extra typeof function check is to prevent crashes
+      // in Safari 2 (See: #3039)
+      if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
+        push.call( ret, array );
+      } else {
+        jQuery.merge( ret, array );
+      }
+    }
+
+    return ret;
+  },
+
+  inArray: function( elem, array ) {
+    if ( array.indexOf ) {
+      return array.indexOf( elem );
+    }
+
+    for ( var i = 0, length = array.length; i < length; i++ ) {
+      if ( array[ i ] === elem ) {
+        return i;
+      }
+    }
+
+    return -1;
+  },
+
+  merge: function( first, second ) {
+    var i = first.length, j = 0;
+
+    if ( typeof second.length === "number" ) {
+      for ( var l = second.length; j < l; j++ ) {
+        first[ i++ ] = second[ j ];
+      }
+
+    } else {
+      while ( second[j] !== undefined ) {
+        first[ i++ ] = second[ j++ ];
+      }
+    }
+
+    first.length = i;
+
+    return first;
+  },
+
+  grep: function( elems, callback, inv ) {
+    var ret = [];
+
+    // Go through the array, only saving the items
+    // that pass the validator function
+    for ( var i = 0, length = elems.length; i < length; i++ ) {
+      if ( !inv !== !callback( elems[ i ], i ) ) {
+        ret.push( elems[ i ] );
+      }
+    }
+
+    return ret;
+  },
+
+  // arg is for internal usage only
+  map: function( elems, callback, arg ) {
+    var ret = [], value;
+
+    // Go through the array, translating each of the items to their
+    // new value (or values).
+    for ( var i = 0, length = elems.length; i < length; i++ ) {
+      value = callback( elems[ i ], i, arg );
+
+      if ( value != null ) {
+        ret[ ret.length ] = value;
+      }
+    }
+
+    return ret.concat.apply( [], ret );
+  },
+
+  // A global GUID counter for objects
+  guid: 1,
+
+  proxy: function( fn, proxy, thisObject ) {
+    if ( arguments.length === 2 ) {
+      if ( typeof proxy === "string" ) {
+        thisObject = fn;
+        fn = thisObject[ proxy ];
+        proxy = undefined;
+
+      } else if ( proxy && !jQuery.isFunction( proxy ) ) {
+        thisObject = proxy;
+        proxy = undefined;
+      }
+    }
+
+    if ( !proxy && fn ) {
+      proxy = function() {
+        return fn.apply( thisObject || this, arguments );
+      };
+    }
+
+    // Set the guid of unique handler to the same of original handler, so it can be removed
+    if ( fn ) {
+      proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+    }
+
+    // So proxy can be declared as an argument
+    return proxy;
+  },
+
+  // Use of jQuery.browser is frowned upon.
+  // More details: http://docs.jquery.com/Utilities/jQuery.browser
+  uaMatch: function( ua ) {
+    ua = ua.toLowerCase();
+
+    var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+      /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
+      /(msie) ([\w.]+)/.exec( ua ) ||
+      !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
+        [];
+
+    return { browser: match[1] || "", version: match[2] || "0" };
+  },
+
+  browser: {}
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+  jQuery.browser[ browserMatch.browser ] = true;
+  jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+  jQuery.browser.safari = true;
+}
+
+if ( indexOf ) {
+  jQuery.inArray = function( elem, array ) {
+    return indexOf.call( array, elem );
+  };
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+  DOMContentLoaded = function() {
+    document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+    jQuery.ready();
+  };
+
+} else if ( document.attachEvent ) {
+  DOMContentLoaded = function() {
+    // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+    if ( document.readyState === "complete" ) {
+      document.detachEvent( "onreadystatechange", DOMContentLoaded );
+      jQuery.ready();
+    }
+  };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+  if ( jQuery.isReady ) {
+    return;
+  }
+
+  try {
+    // If IE is used, use the trick by Diego Perini
+    // http://javascript.nwbox.com/IEContentLoaded/
+    document.documentElement.doScroll("left");
+  } catch( error ) {
+    setTimeout( doScrollCheck, 1 );
+    return;
+  }
+
+  // and execute any waiting functions
+  jQuery.ready();
+}
+
+function evalScript( i, elem ) {
+  if ( elem.src ) {
+    jQuery.ajax({
+      url: elem.src,
+      async: false,
+      dataType: "script"
+    });
+  } else {
+    jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+  }
+
+  if ( elem.parentNode ) {
+    elem.parentNode.removeChild( elem );
+  }
+}
+
+// Mutifunctional method to get and set values to a collection
+// The value/s can be optionally by executed if its a function
+function access( elems, key, value, exec, fn, pass ) {
+  var length = elems.length;
+
+  // Setting many attributes
+  if ( typeof key === "object" ) {
+    for ( var k in key ) {
+      access( elems, k, key[k], exec, fn, value );
+    }
+    return elems;
+  }
+
+  // Setting one attribute
+  if ( value !== undefined ) {
+    // Optionally, function values get executed if exec is true
+    exec = !pass && exec && jQuery.isFunction(value);
+
+    for ( var i = 0; i < length; i++ ) {
+      fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+    }
+
+    return elems;
+  }
+
+  // Getting an attribute
+  return length ? fn( elems[0], key ) : undefined;
+}
+
+function now() {
+  return (new Date).getTime();
+}
+(function() {
+
+  jQuery.support = {};
+
+  var root = document.documentElement,
+    script = document.createElement("script"),
+    div = document.createElement("div"),
+    id = "script" + now();
+
+  div.style.display = "none";
+  div.innerHTML = "   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+  var all = div.getElementsByTagName("*"),
+    a = div.getElementsByTagName("a")[0];
+
+  // Can't get basic test support
+  if ( !all || !all.length || !a ) {
+    return;
+  }
+
+  jQuery.support = {
+    // IE strips leading whitespace when .innerHTML is used
+    leadingWhitespace: div.firstChild.nodeType === 3,
+
+    // Make sure that tbody elements aren't automatically inserted
+    // IE will insert them into empty tables
+    tbody: !div.getElementsByTagName("tbody").length,
+
+    // Make sure that link elements get serialized correctly by innerHTML
+    // This requires a wrapper element in IE
+    htmlSerialize: !!div.getElementsByTagName("link").length,
+
+    // Get the style information from getAttribute
+    // (IE uses .cssText insted)
+    style: /red/.test( a.getAttribute("style") ),
+
+    // Make sure that URLs aren't manipulated
+    // (IE normalizes it by default)
+    hrefNormalized: a.getAttribute("href") === "/a",
+
+    // Make sure that element opacity exists
+    // (IE uses filter instead)
+    // Use a regex to work around a WebKit issue. See #5145
+    opacity: /^0.55$/.test( a.style.opacity ),
+
+    // Verify style float existence
+    // (IE uses styleFloat instead of cssFloat)
+    cssFloat: !!a.style.cssFloat,
+
+    // Make sure that if no value is specified for a checkbox
+    // that it defaults to "on".
+    // (WebKit defaults to "" instead)
+    checkOn: div.getElementsByTagName("input")[0].value === "on",
+
+    // Make sure that a selected-by-default option has a working selected property.
+    // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+    optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
+
+    parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
+
+    // Will be defined later
+    deleteExpando: true,
+    checkClone: false,
+    scriptEval: false,
+    noCloneEvent: true,
+    boxModel: null
+  };
+
+  script.type = "text/javascript";
+  try {
+    script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+  } catch(e) {}
+
+  root.insertBefore( script, root.firstChild );
+
+  // Make sure that the execution of code works by injecting a script
+  // tag with appendChild/createTextNode
+  // (IE doesn't support this, fails, and uses .text instead)
+  if ( window[ id ] ) {
+    jQuery.support.scriptEval = true;
+    delete window[ id ];
+  }
+
+  // Test to see if it's possible to delete an expando from an element
+  // Fails in Internet Explorer
+  try {
+    delete script.test;
+
+  } catch(e) {
+    jQuery.support.deleteExpando = false;
+  }
+
+  root.removeChild( script );
+
+  if ( div.attachEvent && div.fireEvent ) {
+    div.attachEvent("onclick", function click() {
+      // Cloning a node shouldn't copy over any
+      // bound event handlers (IE does this)
+      jQuery.support.noCloneEvent = false;
+      div.detachEvent("onclick", click);
+    });
+    div.cloneNode(true).fireEvent("onclick");
+  }
+
+  div = document.createElement("div");
+  div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
+
+  var fragment = document.createDocumentFragment();
+  fragment.appendChild( div.firstChild );
+
+  // WebKit doesn't clone checked state correctly in fragments
+  jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
+
+  // Figure out if the W3C box model works as expected
+  // document.body must exist before we can do this
+  jQuery(function() {
+    var div = document.createElement("div");
+    div.style.width = div.style.paddingLeft = "1px";
+
+    document.body.appendChild( div );
+    jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+    document.body.removeChild( div ).style.display = 'none';
+
+    div = null;
+  });
+
+  // Technique from Juriy Zaytsev
+  // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+  var eventSupported = function( eventName ) {
+    var el = document.createElement("div");
+    eventName = "on" + eventName;
+
+    var isSupported = (eventName in el);
+    if ( !isSupported ) {
+      el.setAttribute(eventName, "return;");
+      isSupported = typeof el[eventName] === "function";
+    }
+    el = null;
+
+    return isSupported;
+  };
+
+  jQuery.support.submitBubbles = eventSupported("submit");
+  jQuery.support.changeBubbles = eventSupported("change");
+
+  // release memory in IE
+  root = script = div = all = a = null;
+})();
+
+jQuery.props = {
+  "for": "htmlFor",
+  "class": "className",
+  readonly: "readOnly",
+  maxlength: "maxLength",
+  cellspacing: "cellSpacing",
+  rowspan: "rowSpan",
+  colspan: "colSpan",
+  tabindex: "tabIndex",
+  usemap: "useMap",
+  frameborder: "frameBorder"
+};
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+  cache: {},
+
+  expando:expando,
+
+  // The following elements throw uncatchable exceptions if you
+  // attempt to add expando properties to them.
+  noData: {
+    "embed": true,
+    "object": true,
+    "applet": true
+  },
+
+  data: function( elem, name, data ) {
+    if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+      return;
+    }
+
+    elem = elem == window ?
+      windowData :
+      elem;
+
+    var id = elem[ expando ], cache = jQuery.cache, thisCache;
+
+    if ( !id && typeof name === "string" && data === undefined ) {
+      return null;
+    }
+
+    // Compute a unique ID for the element
+    if ( !id ) {
+      id = ++uuid;
+    }
+
+    // Avoid generating a new cache unless none exists and we
+    // want to manipulate it.
+    if ( typeof name === "object" ) {
+      elem[ expando ] = id;
+      thisCache = cache[ id ] = jQuery.extend(true, {}, name);
+
+    } else if ( !cache[ id ] ) {
+      elem[ expando ] = id;
+      cache[ id ] = {};
+    }
+
+    thisCache = cache[ id ];
+
+    // Prevent overriding the named cache with undefined values
+    if ( data !== undefined ) {
+      thisCache[ name ] = data;
+    }
+
+    return typeof name === "string" ? thisCache[ name ] : thisCache;
+  },
+
+  removeData: function( elem, name ) {
+    if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+      return;
+    }
+
+    elem = elem == window ?
+      windowData :
+      elem;
+
+    var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
+
+    // If we want to remove a specific section of the element's data
+    if ( name ) {
+      if ( thisCache ) {
+        // Remove the section of cache data
+        delete thisCache[ name ];
+
+        // If we've removed all the data, remove the element's cache
+        if ( jQuery.isEmptyObject(thisCache) ) {
+          jQuery.removeData( elem );
+        }
+      }
+
+    // Otherwise, we want to remove all of the element's data
+    } else {
+      if ( jQuery.support.deleteExpando ) {
+        delete elem[ jQuery.expando ];
+
+      } else if ( elem.removeAttribute ) {
+        elem.removeAttribute( jQuery.expando );
+      }
+
+      // Completely remove the data cache
+      delete cache[ id ];
+    }
+  }
+});
+
+jQuery.fn.extend({
+  data: function( key, value ) {
+    if ( typeof key === "undefined" && this.length ) {
+      return jQuery.data( this[0] );
+
+    } else if ( typeof key === "object" ) {
+      return this.each(function() {
+        jQuery.data( this, key );
+      });
+    }
+
+    var parts = key.split(".");
+    parts[1] = parts[1] ? "." + parts[1] : "";
+
+    if ( value === undefined ) {
+      var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+      if ( data === undefined && this.length ) {
+        data = jQuery.data( this[0], key );
+      }
+      return data === undefined && parts[1] ?
+        this.data( parts[0] ) :
+        data;
+    } else {
+      return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
+        jQuery.data( this, key, value );
+      });
+    }
+  },
+
+  removeData: function( key ) {
+    return this.each(function() {
+      jQuery.removeData( this, key );
+    });
+  }
+});
+jQuery.extend({
+  queue: function( elem, type, data ) {
+    if ( !elem ) {
+      return;
+    }
+
+    type = (type || "fx") + "queue";
+    var q = jQuery.data( elem, type );
+
+    // Speed up dequeue by getting out quickly if this is just a lookup
+    if ( !data ) {
+      return q || [];
+    }
+
+    if ( !q || jQuery.isArray(data) ) {
+      q = jQuery.data( elem, type, jQuery.makeArray(data) );
+
+    } else {
+      q.push( data );
+    }
+
+    return q;
+  },
+
+  dequeue: function( elem, type ) {
+    type = type || "fx";
+
+    var queue = jQuery.queue( elem, type ), fn = queue.shift();
+
+    // If the fx queue is dequeued, always remove the progress sentinel
+    if ( fn === "inprogress" ) {
+      fn = queue.shift();
+    }
+
+    if ( fn ) {
+      // Add a progress sentinel to prevent the fx queue from being
+      // automatically dequeued
+      if ( type === "fx" ) {
+        queue.unshift("inprogress");
+      }
+
+      fn.call(elem, function() {
+        jQuery.dequeue(elem, type);
+      });
+    }
+  }
+});
+
+jQuery.fn.extend({
+  queue: function( type, data ) {
+    if ( typeof type !== "string" ) {
+      data = type;
+      type = "fx";
+    }
+
+    if ( data === undefined ) {
+      return jQuery.queue( this[0], type );
+    }
+    return this.each(function( i, elem ) {
+      var queue = jQuery.queue( this, type, data );
+
+      if ( type === "fx" && queue[0] !== "inprogress" ) {
+        jQuery.dequeue( this, type );
+      }
+    });
+  },
+  dequeue: function( type ) {
+    return this.each(function() {
+      jQuery.dequeue( this, type );
+    });
+  },
+
+  // Based off of the plugin by Clint Helfers, with permission.
+  // http://blindsignals.com/index.php/2009/07/jquery-delay/
+  delay: function( time, type ) {
+    time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+    type = type || "fx";
+
+    return this.queue( type, function() {
+      var elem = this;
+      setTimeout(function() {
+        jQuery.dequeue( elem, type );
+      }, time );
+    });
+  },
+
+  clearQueue: function( type ) {
+    return this.queue( type || "fx", [] );
+  }
+});
+var rclass = /[\n\t]/g,
+  rspace = /\s+/,
+  rreturn = /\r/g,
+  rspecialurl = /href|src|style/,
+  rtype = /(button|input)/i,
+  rfocusable = /(button|input|object|select|textarea)/i,
+  rclickable = /^(a|area)$/i,
+  rradiocheck = /radio|checkbox/;
+
+jQuery.fn.extend({
+  attr: function( name, value ) {
+    return access( this, name, value, true, jQuery.attr );
+  },
+
+  removeAttr: function( name, fn ) {
+    return this.each(function(){
+      jQuery.attr( this, name, "" );
+      if ( this.nodeType === 1 ) {
+        this.removeAttribute( name );
+      }
+    });
+  },
+
+  addClass: function( value ) {
+    if ( jQuery.isFunction(value) ) {
+      return this.each(function(i) {
+        var self = jQuery(this);
+        self.addClass( value.call(this, i, self.attr("class")) );
+      });
+    }
+
+    if ( value && typeof value === "string" ) {
+      var classNames = (value || "").split( rspace );
+
+      for ( var i = 0, l = this.length; i < l; i++ ) {
+        var elem = this[i];
+
+        if ( elem.nodeType === 1 ) {
+          if ( !elem.className ) {
+            elem.className = value;
+
+          } else {
+            var className = " " + elem.className + " ", setClass = elem.className;
+            for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+              if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+                setClass += " " + classNames[c];
+              }
+            }
+            elem.className = jQuery.trim( setClass );
+          }
+        }
+      }
+    }
+
+    return this;
+  },
+
+  removeClass: function( value ) {
+    if ( jQuery.isFunction(value) ) {
+      return this.each(function(i) {
+        var self = jQuery(this);
+        self.removeClass( value.call(this, i, self.attr("class")) );
+      });
+    }
+
+    if ( (value && typeof value === "string") || value === undefined ) {
+      var classNames = (value || "").split(rspace);
+
+      for ( var i = 0, l = this.length; i < l; i++ ) {
+        var elem = this[i];
+
+        if ( elem.nodeType === 1 && elem.className ) {
+          if ( value ) {
+            var className = (" " + elem.className + " ").replace(rclass, " ");
+            for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+              className = className.replace(" " + classNames[c] + " ", " ");
+            }
+            elem.className = jQuery.trim( className );
+
+          } else {
+            elem.className = "";
+          }
+        }
+      }
+    }
+
+    return this;
+  },
+
+  toggleClass: function( value, stateVal ) {
+    var type = typeof value, isBool = typeof stateVal === "boolean";
+
+    if ( jQuery.isFunction( value ) ) {
+      return this.each(function(i) {
+        var self = jQuery(this);
+        self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+      });
+    }
+
+    return this.each(function() {
+      if ( type === "string" ) {
+        // toggle individual class names
+        var className, i = 0, self = jQuery(this),
+          state = stateVal,
+          classNames = value.split( rspace );
+
+        while ( (className = classNames[ i++ ]) ) {
+          // check each className given, space seperated list
+          state = isBool ? state : !self.hasClass( className );
+          self[ state ? "addClass" : "removeClass" ]( className );
+        }
+
+      } else if ( type === "undefined" || type === "boolean" ) {
+        if ( this.className ) {
+          // store className if set
+          jQuery.data( this, "__className__", this.className );
+        }
+
+        // toggle whole className
+        this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
+      }
+    });
+  },
+
+  hasClass: function( selector ) {
+    var className = " " + selector + " ";
+    for ( var i = 0, l = this.length; i < l; i++ ) {
+      if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  val: function( value ) {
+    if ( value === undefined ) {
+      var elem = this[0];
+
+      if ( elem ) {
+        if ( jQuery.nodeName( elem, "option" ) ) {
+          return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+        }
+
+        // We need to handle select boxes special
+        if ( jQuery.nodeName( elem, "select" ) ) {
+          var index = elem.selectedIndex,
+            values = [],
+            options = elem.options,
+            one = elem.type === "select-one";
+
+          // Nothing was selected
+          if ( index < 0 ) {
+            return null;
+          }
+
+          // Loop through all the selected options
+          for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+            var option = options[ i ];
+
+            if ( option.selected ) {
+              // Get the specifc value for the option
+              value = jQuery(option).val();
+
+              // We don't need an array for one selects
+              if ( one ) {
+                return value;
+              }
+
+              // Multi-Selects return an array
+              values.push( value );
+            }
+          }
+
+          return values;
+        }
+
+        // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+        if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
+          return elem.getAttribute("value") === null ? "on" : elem.value;
+        }
+
+
+        // Everything else, we just grab the value
+        return (elem.value || "").replace(rreturn, "");
+
+      }
+
+      return undefined;
+    }
+
+    var isFunction = jQuery.isFunction(value);
+
+    return this.each(function(i) {
+      var self = jQuery(this), val = value;
+
+      if ( this.nodeType !== 1 ) {
+        return;
+      }
+
+      if ( isFunction ) {
+        val = value.call(this, i, self.val());
+      }
+
+      // Typecast each time if the value is a Function and the appended
+      // value is therefore different each time.
+      if ( typeof val === "number" ) {
+        val += "";
+      }
+
+      if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
+        this.checked = jQuery.inArray( self.val(), val ) >= 0;
+
+      } else if ( jQuery.nodeName( this, "select" ) ) {
+        var values = jQuery.makeArray(val);
+
+        jQuery( "option", this ).each(function() {
+          this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+        });
+
+        if ( !values.length ) {
+          this.selectedIndex = -1;
+        }
+
+      } else {
+        this.value = val;
+      }
+    });
+  }
+});
+
+jQuery.extend({
+  attrFn: {
+    val: true,
+    css: true,
+    html: true,
+    text: true,
+    data: true,
+    width: true,
+    height: true,
+    offset: true
+  },
+
+  attr: function( elem, name, value, pass ) {
+    // don't set attributes on text and comment nodes
+    if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+      return undefined;
+    }
+
+    if ( pass && name in jQuery.attrFn ) {
+      return jQuery(elem)[name](value);
+    }
+
+    var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
+      // Whether we are setting (or getting)
+      set = value !== undefined;
+
+    // Try to normalize/fix the name
+    name = notxml && jQuery.props[ name ] || name;
+
+    // Only do all the following if this is a node (faster for style)
+    if ( elem.nodeType === 1 ) {
+      // These attributes require special treatment
+      var special = rspecialurl.test( name );
+
+      // Safari mis-reports the default selected property of an option
+      // Accessing the parent's selectedIndex property fixes it
+      if ( name === "selected" && !jQuery.support.optSelected ) {
+        var parent = elem.parentNode;
+        if ( parent ) {
+          parent.selectedIndex;
+
+          // Make sure that it also works with optgroups, see #5701
+          if ( parent.parentNode ) {
+            parent.parentNode.selectedIndex;
+          }
+        }
+      }
+
+      // If applicable, access the attribute via the DOM 0 way
+      if ( name in elem && notxml && !special ) {
+        if ( set ) {
+          // We can't allow the type property to be changed (since it causes problems in IE)
+          if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
+            jQuery.error( "type property can't be changed" );
+          }
+
+          elem[ name ] = value;
+        }
+
+        // browsers index elements by id/name on forms, give priority to attributes.
+        if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
+          return elem.getAttributeNode( name ).nodeValue;
+        }
+
+        // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+        // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+        if ( name === "tabIndex" ) {
+          var attributeNode = elem.getAttributeNode( "tabIndex" );
+
+          return attributeNode && attributeNode.specified ?
+            attributeNode.value :
+            rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+              0 :
+              undefined;
+        }
+
+        return elem[ name ];
+      }
+
+      if ( !jQuery.support.style && notxml && name === "style" ) {
+        if ( set ) {
+          elem.style.cssText = "" + value;
+        }
+
+        return elem.style.cssText;
+      }
+
+      if ( set ) {
+        // convert the value to a string (all browsers do this but IE) see #1070
+        elem.setAttribute( name, "" + value );
+      }
+
+      var attr = !jQuery.support.hrefNormalized && notxml && special ?
+          // Some attributes require a special call on IE
+          elem.getAttribute( name, 2 ) :
+          elem.getAttribute( name );
+
+      // Non-existent attributes return null, we normalize to undefined
+      return attr === null ? undefined : attr;
+    }
+
+    // elem is actually elem.style ... set the style
+    // Using attr for specific style information is now deprecated. Use style instead.
+    return jQuery.style( elem, name, value );
+  }
+});
+var rnamespaces = /\.(.*)$/,
+  fcleanup = function( nm ) {
+    return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+      return "\\" + ch;
+    });
+  };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+  // Bind an event to an element
+  // Original by Dean Edwards
+  add: function( elem, types, handler, data ) {
+    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+      return;
+    }
+
+    // For whatever reason, IE has trouble passing the window object
+    // around, causing it to be cloned in the process
+    if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
+      elem = window;
+    }
+
+    var handleObjIn, handleObj;
+
+    if ( handler.handler ) {
+      handleObjIn = handler;
+      handler = handleObjIn.handler;
+    }
+
+    // Make sure that the function being executed has a unique ID
+    if ( !handler.guid ) {
+      handler.guid = jQuery.guid++;
+    }
+
+    // Init the element's event structure
+    var elemData = jQuery.data( elem );
+
+    // If no elemData is found then we must be trying to bind to one of the
+    // banned noData elements
+    if ( !elemData ) {
+      return;
+    }
+
+    var events = elemData.events = elemData.events || {},
+      eventHandle = elemData.handle, eventHandle;
+
+    if ( !eventHandle ) {
+      elemData.handle = eventHandle = function() {
+        // Handle the second event of a trigger and when
+        // an event is called after a page has unloaded
+        return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+          jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+          undefined;
+      };
+    }
+
+    // Add elem as a property of the handle function
+    // This is to prevent a memory leak with non-native events in IE.
+    eventHandle.elem = elem;
+
+    // Handle multiple events separated by a space
+    // jQuery(...).bind("mouseover mouseout", fn);
+    types = types.split(" ");
+
+    var type, i = 0, namespaces;
+
+    while ( (type = types[ i++ ]) ) {
+      handleObj = handleObjIn ?
+        jQuery.extend({}, handleObjIn) :
+        { handler: handler, data: data };
+
+      // Namespaced event handlers
+      if ( type.indexOf(".") > -1 ) {
+        namespaces = type.split(".");
+        type = namespaces.shift();
+        handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+      } else {
+        namespaces = [];
+        handleObj.namespace = "";
+      }
+
+      handleObj.type = type;
+      handleObj.guid = handler.guid;
+
+      // Get the current list of functions bound to this event
+      var handlers = events[ type ],
+        special = jQuery.event.special[ type ] || {};
+
+      // Init the event handler queue
+      if ( !handlers ) {
+        handlers = events[ type ] = [];
+
+        // Check for a special event handler
+        // Only use addEventListener/attachEvent if the special
+        // events handler returns false
+        if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+          // Bind the global event handler to the element
+          if ( elem.addEventListener ) {
+            elem.addEventListener( type, eventHandle, false );
+
+          } else if ( elem.attachEvent ) {
+            elem.attachEvent( "on" + type, eventHandle );
+          }
+        }
+      }
+
+      if ( special.add ) {
+        special.add.call( elem, handleObj );
+
+        if ( !handleObj.handler.guid ) {
+          handleObj.handler.guid = handler.guid;
+        }
+      }
+
+      // Add the function to the element's handler list
+      handlers.push( handleObj );
+
+      // Keep track of which events have been used, for global triggering
+      jQuery.event.global[ type ] = true;
+    }
+
+    // Nullify elem to prevent memory leaks in IE
+    elem = null;
+  },
+
+  global: {},
+
+  // Detach an event or set of events from an element
+  remove: function( elem, types, handler, pos ) {
+    // don't do events on text and comment nodes
+    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+      return;
+    }
+
+    var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+      elemData = jQuery.data( elem ),
+      events = elemData && elemData.events;
+
+    if ( !elemData || !events ) {
+      return;
+    }
+
+    // types is actually an event object here
+    if ( types && types.type ) {
+      handler = types.handler;
+      types = types.type;
+    }
+
+    // Unbind all events for the element
+    if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+      types = types || "";
+
+      for ( type in events ) {
+        jQuery.event.remove( elem, type + types );
+      }
+
+      return;
+    }
+
+    // Handle multiple events separated by a space
+    // jQuery(...).unbind("mouseover mouseout", fn);
+    types = types.split(" ");
+
+    while ( (type = types[ i++ ]) ) {
+      origType = type;
+      handleObj = null;
+      all = type.indexOf(".") < 0;
+      namespaces = [];
+
+      if ( !all ) {
+        // Namespaced event handlers
+        namespaces = type.split(".");
+        type = namespaces.shift();
+
+        namespace = new RegExp("(^|\\.)" +
+          jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
+      }
+
+      eventType = events[ type ];
+
+      if ( !eventType ) {
+        continue;
+      }
+
+      if ( !handler ) {
+        for ( var j = 0; j < eventType.length; j++ ) {
+          handleObj = eventType[ j ];
+
+          if ( all || namespace.test( handleObj.namespace ) ) {
+            jQuery.event.remove( elem, origType, handleObj.handler, j );
+            eventType.splice( j--, 1 );
+          }
+        }
+
+        continue;
+      }
+
+      special = jQuery.event.special[ type ] || {};
+
+      for ( var j = pos || 0; j < eventType.length; j++ ) {
+        handleObj = eventType[ j ];
+
+        if ( handler.guid === handleObj.guid ) {
+          // remove the given handler for the given type
+          if ( all || namespace.test( handleObj.namespace ) ) {
+            if ( pos == null ) {
+              eventType.splice( j--, 1 );
+            }
+
+            if ( special.remove ) {
+              special.remove.call( elem, handleObj );
+            }
+          }
+
+          if ( pos != null ) {
+            break;
+          }
+        }
+      }
+
+      // remove generic event handler if no more handlers exist
+      if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+        if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+          removeEvent( elem, type, elemData.handle );
+        }
+
+        ret = null;
+        delete events[ type ];
+      }
+    }
+
+    // Remove the expando if it's no longer used
+    if ( jQuery.isEmptyObject( events ) ) {
+      var handle = elemData.handle;
+      if ( handle ) {
+        handle.elem = null;
+      }
+
+      delete elemData.events;
+      delete elemData.handle;
+
+      if ( jQuery.isEmptyObject( elemData ) ) {
+        jQuery.removeData( elem );
+      }
+    }
+  },
+
+  // bubbling is internal
+  trigger: function( event, data, elem /*, bubbling */ ) {
+    // Event object or event type
+    var type = event.type || event,
+      bubbling = arguments[3];
+
+    if ( !bubbling ) {
+      event = typeof event === "object" ?
+        // jQuery.Event object
+        event[expando] ? event :
+        // Object literal
+        jQuery.extend( jQuery.Event(type), event ) :
+        // Just the event type (string)
+        jQuery.Event(type);
+
+      if ( type.indexOf("!") >= 0 ) {
+        event.type = type = type.slice(0, -1);
+        event.exclusive = true;
+      }
+
+      // Handle a global trigger
+      if ( !elem ) {
+        // Don't bubble custom events when global (to avoid too much overhead)
+        event.stopPropagation();
+
+        // Only trigger if we've ever bound an event for it
+        if ( jQuery.event.global[ type ] ) {
+          jQuery.each( jQuery.cache, function() {
+            if ( this.events && this.events[type] ) {
+              jQuery.event.trigger( event, data, this.handle.elem );
+            }
+          });
+        }
+      }
+
+      // Handle triggering a single element
+
+      // don't do events on text and comment nodes
+      if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+        return undefined;
+      }
+
+      // Clean up in case it is reused
+      event.result = undefined;
+      event.target = elem;
+
+      // Clone the incoming data, if any
+      data = jQuery.makeArray( data );
+      data.unshift( event );
+    }
+
+    event.currentTarget = elem;
+
+    // Trigger the event, it is assumed that "handle" is a function
+    var handle = jQuery.data( elem, "handle" );
+    if ( handle ) {
+      handle.apply( elem, data );
+    }
+
+    var parent = elem.parentNode || elem.ownerDocument;
+
+    // Trigger an inline bound script
+    try {
+      if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
+        if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
+          event.result = false;
+        }
+      }
+
+    // prevent IE from throwing an error for some elements with some event types, see #3533
+    } catch (e) {}
+
+    if ( !event.isPropagationStopped() && parent ) {
+      jQuery.event.trigger( event, data, parent, true );
+
+    } else if ( !event.isDefaultPrevented() ) {
+      var target = event.target, old,
+        isClick = jQuery.nodeName(target, "a") && type === "click",
+        special = jQuery.event.special[ type ] || {};
+
+      if ( (!special._default || special._default.call( elem, event ) === false) &&
+        !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
+
+        try {
+          if ( target[ type ] ) {
+            // Make sure that we don't accidentally re-trigger the onFOO events
+            old = target[ "on" + type ];
+
+            if ( old ) {
+              target[ "on" + type ] = null;
+            }
+
+            jQuery.event.triggered = true;
+            target[ type ]();
+          }
+
+        // prevent IE from throwing an error for some elements with some event types, see #3533
+        } catch (e) {}
+
+        if ( old ) {
+          target[ "on" + type ] = old;
+        }
+
+        jQuery.event.triggered = false;
+      }
+    }
+  },
+
+  handle: function( event ) {
+    var all, handlers, namespaces, namespace, events;
+
+    event = arguments[0] = jQuery.event.fix( event || window.event );
+    event.currentTarget = this;
+
+    // Namespaced event handlers
+    all = event.type.indexOf(".") < 0 && !event.exclusive;
+
+    if ( !all ) {
+      namespaces = event.type.split(".");
+      event.type = namespaces.shift();
+      namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+    }
+
+    var events = jQuery.data(this, "events"), handlers = events[ event.type ];
+
+    if ( events && handlers ) {
+      // Clone the handlers to prevent manipulation
+      handlers = handlers.slice(0);
+
+      for ( var j = 0, l = handlers.length; j < l; j++ ) {
+        var handleObj = handlers[ j ];
+
+        // Filter the functions by class
+        if ( all || namespace.test( handleObj.namespace ) ) {
+          // Pass in a reference to the handler function itself
+          // So that we can later remove it
+          event.handler = handleObj.handler;
+          event.data = handleObj.data;
+          event.handleObj = handleObj;
+
+          var ret = handleObj.handler.apply( this, arguments );
+
+          if ( ret !== undefined ) {
+            event.result = ret;
+            if ( ret === false ) {
+              event.preventDefault();
+              event.stopPropagation();
+            }
+          }
+
+          if ( event.isImmediatePropagationStopped() ) {
+            break;
+          }
+        }
+      }
+    }
+
+    return event.result;
+  },
+
+  props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+  fix: function( event ) {
+    if ( event[ expando ] ) {
+      return event;
+    }
+
+    // store a copy of the original event object
+    // and "clone" to set read-only properties
+    var originalEvent = event;
+    event = jQuery.Event( originalEvent );
+
+    for ( var i = this.props.length, prop; i; ) {
+      prop = this.props[ --i ];
+      event[ prop ] = originalEvent[ prop ];
+    }
+
+    // Fix target property, if necessary
+    if ( !event.target ) {
+      event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+    }
+
+    // check if target is a textnode (safari)
+    if ( event.target.nodeType === 3 ) {
+      event.target = event.target.parentNode;
+    }
+
+    // Add relatedTarget, if necessary
+    if ( !event.relatedTarget && event.fromElement ) {
+      event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+    }
+
+    // Calculate pageX/Y if missing and clientX/Y available
+    if ( event.pageX == null && event.clientX != null ) {
+      var doc = document.documentElement, body = document.body;
+      event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+      event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
+    }
+
+    // Add which for key events
+    if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
+      event.which = event.charCode || event.keyCode;
+    }
+
+    // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+    if ( !event.metaKey && event.ctrlKey ) {
+      event.metaKey = event.ctrlKey;
+    }
+
+    // Add which for click: 1 === left; 2 === middle; 3 === right
+    // Note: button is not normalized, so don't use it
+    if ( !event.which && event.button !== undefined ) {
+      event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+    }
+
+    return event;
+  },
+
+  // Deprecated, use jQuery.guid instead
+  guid: 1E8,
+
+  // Deprecated, use jQuery.proxy instead
+  proxy: jQuery.proxy,
+
+  special: {
+    ready: {
+      // Make sure the ready event is setup
+      setup: jQuery.bindReady,
+      teardown: jQuery.noop
+    },
+
+    live: {
+      add: function( handleObj ) {
+        jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
+      },
+
+      remove: function( handleObj ) {
+        var remove = true,
+          type = handleObj.origType.replace(rnamespaces, "");
+
+        jQuery.each( jQuery.data(this, "events").live || [], function() {
+          if ( type === this.origType.replace(rnamespaces, "") ) {
+            remove = false;
+            return false;
+          }
+        });
+
+        if ( remove ) {
+          jQuery.event.remove( this, handleObj.origType, liveHandler );
+        }
+      }
+
+    },
+
+    beforeunload: {
+      setup: function( data, namespaces, eventHandle ) {
+        // We only want to do this special case on windows
+        if ( this.setInterval ) {
+          this.onbeforeunload = eventHandle;
+        }
+
+        return false;
+      },
+      teardown: function( namespaces, eventHandle ) {
+        if ( this.onbeforeunload === eventHandle ) {
+          this.onbeforeunload = null;
+        }
+      }
+    }
+  }
+};
+
+var removeEvent = document.removeEventListener ?
+  function( elem, type, handle ) {
+    elem.removeEventListener( type, handle, false );
+  } :
+  function( elem, type, handle ) {
+    elem.detachEvent( "on" + type, handle );
+  };
+
+jQuery.Event = function( src ) {
+  // Allow instantiation without the 'new' keyword
+  if ( !this.preventDefault ) {
+    return new jQuery.Event( src );
+  }
+
+  // Event object
+  if ( src && src.type ) {
+    this.originalEvent = src;
+    this.type = src.type;
+  // Event type
+  } else {
+    this.type = src;
+  }
+
+  // timeStamp is buggy for some events on Firefox(#3843)
+  // So we won't rely on the native value
+  this.timeStamp = now();
+
+  // Mark it as fixed
+  this[ expando ] = true;
+};
+
+function returnFalse() {
+  return false;
+}
+function returnTrue() {
+  return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+  preventDefault: function() {
+    this.isDefaultPrevented = returnTrue;
+
+    var e = this.originalEvent;
+    if ( !e ) {
+      return;
+    }
+
+    // if preventDefault exists run it on the original event
+    if ( e.preventDefault ) {
+      e.preventDefault();
+    }
+    // otherwise set the returnValue property of the original event to false (IE)
+    e.returnValue = false;
+  },
+  stopPropagation: function() {
+    this.isPropagationStopped = returnTrue;
+
+    var e = this.originalEvent;
+    if ( !e ) {
+      return;
+    }
+    // if stopPropagation exists run it on the original event
+    if ( e.stopPropagation ) {
+      e.stopPropagation();
+    }
+    // otherwise set the cancelBubble property of the original event to true (IE)
+    e.cancelBubble = true;
+  },
+  stopImmediatePropagation: function() {
+    this.isImmediatePropagationStopped = returnTrue;
+    this.stopPropagation();
+  },
+  isDefaultPrevented: returnFalse,
+  isPropagationStopped: returnFalse,
+  isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+  // Check if mouse(over|out) are still within the same parent element
+  var parent = event.relatedTarget;
+
+  // Firefox sometimes assigns relatedTarget a XUL element
+  // which we cannot access the parentNode property of
+  try {
+    // Traverse up the tree
+    while ( parent && parent !== this ) {
+      parent = parent.parentNode;
+    }
+
+    if ( parent !== this ) {
+      // set the correct event type
+      event.type = event.data;
+
+      // handle event if we actually just moused on to a non sub-element
+      jQuery.event.handle.apply( this, arguments );
+    }
+
+  // assuming we've left the element since we most likely mousedover a xul element
+  } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+  event.type = event.data;
+  jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+  mouseenter: "mouseover",
+  mouseleave: "mouseout"
+}, function( orig, fix ) {
+  jQuery.event.special[ orig ] = {
+    setup: function( data ) {
+      jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+    },
+    teardown: function( data ) {
+      jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+    }
+  };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+  jQuery.event.special.submit = {
+    setup: function( data, namespaces ) {
+      if ( this.nodeName.toLowerCase() !== "form" ) {
+        jQuery.event.add(this, "click.specialSubmit", function( e ) {
+          var elem = e.target, type = elem.type;
+
+          if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+            return trigger( "submit", this, arguments );
+          }
+        });
+
+        jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+          var elem = e.target, type = elem.type;
+
+          if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+            return trigger( "submit", this, arguments );
+          }
+        });
+
+      } else {
+        return false;
+      }
+    },
+
+    teardown: function( namespaces ) {
+      jQuery.event.remove( this, ".specialSubmit" );
+    }
+  };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+  var formElems = /textarea|input|select/i,
+
+  changeFilters,
+
+  getVal = function( elem ) {
+    var type = elem.type, val = elem.value;
+
+    if ( type === "radio" || type === "checkbox" ) {
+      val = elem.checked;
+
+    } else if ( type === "select-multiple" ) {
+      val = elem.selectedIndex > -1 ?
+        jQuery.map( elem.options, function( elem ) {
+          return elem.selected;
+        }).join("-") :
+        "";
+
+    } else if ( elem.nodeName.toLowerCase() === "select" ) {
+      val = elem.selectedIndex;
+    }
+
+    return val;
+  },
+
+  testChange = function testChange( e ) {
+    var elem = e.target, data, val;
+
+    if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
+      return;
+    }
+
+    data = jQuery.data( elem, "_change_data" );
+    val = getVal(elem);
+
+    // the current data will be also retrieved by beforeactivate
+    if ( e.type !== "focusout" || elem.type !== "radio" ) {
+      jQuery.data( elem, "_change_data", val );
+    }
+
+    if ( data === undefined || val === data ) {
+      return;
+    }
+
+    if ( data != null || val ) {
+      e.type = "change";
+      return jQuery.event.trigger( e, arguments[1], elem );
+    }
+  };
+
+  jQuery.event.special.change = {
+    filters: {
+      focusout: testChange,
+
+      click: function( e ) {
+        var elem = e.target, type = elem.type;
+
+        if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+          return testChange.call( this, e );
+        }
+      },
+
+      // Change has to be called before submit
+      // Keydown will be called before keypress, which is used in submit-event delegation
+      keydown: function( e ) {
+        var elem = e.target, type = elem.type;
+
+        if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+          (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+          type === "select-multiple" ) {
+          return testChange.call( this, e );
+        }
+      },
+
+      // Beforeactivate happens also before the previous element is blurred
+      // with this event you can't trigger a change event, but you can store
+      // information/focus[in] is not needed anymore
+      beforeactivate: function( e ) {
+        var elem = e.target;
+        jQuery.data( elem, "_change_data", getVal(elem) );
+      }
+    },
+
+    setup: function( data, namespaces ) {
+      if ( this.type === "file" ) {
+        return false;
+      }
+
+      for ( var type in changeFilters ) {
+        jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+      }
+
+      return formElems.test( this.nodeName );
+    },
+
+    teardown: function( namespaces ) {
+      jQuery.event.remove( this, ".specialChange" );
+
+      return formElems.test( this.nodeName );
+    }
+  };
+
+  changeFilters = jQuery.event.special.change.filters;
+}
+
+function trigger( type, elem, args ) {
+  args[0].type = type;
+  return jQuery.event.handle.apply( elem, args );
+}
+
+// Create "bubbling" focus and blur events
+if ( document.addEventListener ) {
+  jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+    jQuery.event.special[ fix ] = {
+      setup: function() {
+        this.addEventListener( orig, handler, true );
+      },
+      teardown: function() {
+        this.removeEventListener( orig, handler, true );
+      }
+    };
+
+    function handler( e ) {
+      e = jQuery.event.fix( e );
+      e.type = fix;
+      return jQuery.event.handle.call( this, e );
+    }
+  });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+  jQuery.fn[ name ] = function( type, data, fn ) {
+    // Handle object literals
+    if ( typeof type === "object" ) {
+      for ( var key in type ) {
+        this[ name ](key, data, type[key], fn);
+      }
+      return this;
+    }
+
+    if ( jQuery.isFunction( data ) ) {
+      fn = data;
+      data = undefined;
+    }
+
+    var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
+      jQuery( this ).unbind( event, handler );
+      return fn.apply( this, arguments );
+    }) : fn;
+
+    if ( type === "unload" && name !== "one" ) {
+      this.one( type, data, fn );
+
+    } else {
+      for ( var i = 0, l = this.length; i < l; i++ ) {
+        jQuery.event.add( this[i], type, handler, data );
+      }
+    }
+
+    return this;
+  };
+});
+
+jQuery.fn.extend({
+  unbind: function( type, fn ) {
+    // Handle object literals
+    if ( typeof type === "object" && !type.preventDefault ) {
+      for ( var key in type ) {
+        this.unbind(key, type[key]);
+      }
+
+    } else {
+      for ( var i = 0, l = this.length; i < l; i++ ) {
+        jQuery.event.remove( this[i], type, fn );
+      }
+    }
+
+    return this;
+  },
+
+  delegate: function( selector, types, data, fn ) {
+    return this.live( types, data, fn, selector );
+  },
+
+  undelegate: function( selector, types, fn ) {
+    if ( arguments.length === 0 ) {
+        return this.unbind( "live" );
+
+    } else {
+      return this.die( types, null, fn, selector );
+    }
+  },
+
+  trigger: function( type, data ) {
+    return this.each(function() {
+      jQuery.event.trigger( type, data, this );
+    });
+  },
+
+  triggerHandler: function( type, data ) {
+    if ( this[0] ) {
+      var event = jQuery.Event( type );
+      event.preventDefault();
+      event.stopPropagation();
+      jQuery.event.trigger( event, data, this[0] );
+      return event.result;
+    }
+  },
+
+  toggle: function( fn ) {
+    // Save reference to arguments for access in closure
+    var args = arguments, i = 1;
+
+    // link all the functions, so any of them can unbind this click handler
+    while ( i < args.length ) {
+      jQuery.proxy( fn, args[ i++ ] );
+    }
+
+    return this.click( jQuery.proxy( fn, function( event ) {
+      // Figure out which function to execute
+      var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+      jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+      // Make sure that clicks stop
+      event.preventDefault();
+
+      // and execute the function
+      return args[ lastToggle ].apply( this, arguments ) || false;
+    }));
+  },
+
+  hover: function( fnOver, fnOut ) {
+    return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+  }
+});
+
+var liveMap = {
+  focus: "focusin",
+  blur: "focusout",
+  mouseenter: "mouseover",
+  mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+  jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+    var type, i = 0, match, namespaces, preType,
+      selector = origSelector || this.selector,
+      context = origSelector ? this : jQuery( this.context );
+
+    if ( jQuery.isFunction( data ) ) {
+      fn = data;
+      data = undefined;
+    }
+
+    types = (types || "").split(" ");
+
+    while ( (type = types[ i++ ]) != null ) {
+      match = rnamespaces.exec( type );
+      namespaces = "";
+
+      if ( match )  {
+        namespaces = match[0];
+        type = type.replace( rnamespaces, "" );
+      }
+
+      if ( type === "hover" ) {
+        types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+        continue;
+      }
+
+      preType = type;
+
+      if ( type === "focus" || type === "blur" ) {
+        types.push( liveMap[ type ] + namespaces );
+        type = type + namespaces;
+
+      } else {
+        type = (liveMap[ type ] || type) + namespaces;
+      }
+
+      if ( name === "live" ) {
+        // bind live handler
+        context.each(function(){
+          jQuery.event.add( this, liveConvert( type, selector ),
+            { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+        });
+
+      } else {
+        // unbind live handler
+        context.unbind( liveConvert( type, selector ), fn );
+      }
+    }
+
+    return this;
+  }
+});
+
+function liveHandler( event ) {
+  var stop, elems = [], selectors = [], args = arguments,
+    related, match, handleObj, elem, j, i, l, data,
+    events = jQuery.data( this, "events" );
+
+  // Make sure we avoid non-left-click bubbling in Firefox (#3861)
+  if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+    return;
+  }
+
+  event.liveFired = this;
+
+  var live = events.live.slice(0);
+
+  for ( j = 0; j < live.length; j++ ) {
+    handleObj = live[j];
+
+    if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+      selectors.push( handleObj.selector );
+
+    } else {
+      live.splice( j--, 1 );
+    }
+  }
+
+  match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+  for ( i = 0, l = match.length; i < l; i++ ) {
+    for ( j = 0; j < live.length; j++ ) {
+      handleObj = live[j];
+
+      if ( match[i].selector === handleObj.selector ) {
+        elem = match[i].elem;
+        related = null;
+
+        // Those two events require additional checking
+        if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+          related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+        }
+
+        if ( !related || related !== elem ) {
+          elems.push({ elem: elem, handleObj: handleObj });
+        }
+      }
+    }
+  }
+
+  for ( i = 0, l = elems.length; i < l; i++ ) {
+    match = elems[i];
+    event.currentTarget = match.elem;
+    event.data = match.handleObj.data;
+    event.handleObj = match.handleObj;
+
+    if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
+      stop = false;
+      break;
+    }
+  }
+
+  return stop;
+}
+
+function liveConvert( type, selector ) {
+  return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+  "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+  "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+  // Handle event binding
+  jQuery.fn[ name ] = function( fn ) {
+    return fn ? this.bind( name, fn ) : this.trigger( name );
+  };
+
+  if ( jQuery.attrFn ) {
+    jQuery.attrFn[ name ] = true;
+  }
+});
+
+// Prevent memory leaks in IE
+// Window isn't included so as not to unbind existing unload events
+// More info:
+//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
+if ( window.attachEvent && !window.addEventListener ) {
+  window.attachEvent("onunload", function() {
+    for ( var id in jQuery.cache ) {
+      if ( jQuery.cache[ id ].handle ) {
+        // Try/Catch is to handle iframes being unloaded, see #4280
+        try {
+          jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+        } catch(e) {}
+      }
+    }
+  });
+}
+/*!
+ * Sizzle CSS Selector Engine - v1.0
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+  done = 0,
+  toString = Object.prototype.toString,
+  hasDuplicate = false,
+  baseHasDuplicate = true;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+//   Thus far that includes Google Chrome.
+[0, 0].sort(function(){
+  baseHasDuplicate = false;
+  return 0;
+});
+
+var Sizzle = function(selector, context, results, seed) {
+  results = results || [];
+  var origContext = context = context || document;
+
+  if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+    return [];
+  }
+
+  if ( !selector || typeof selector !== "string" ) {
+    return results;
+  }
+
+  var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
+    soFar = selector;
+
+  // Reset the position of the chunker regexp (start from head)
+  while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
+    soFar = m[3];
+
+    parts.push( m[1] );
+
+    if ( m[2] ) {
+      extra = m[3];
+      break;
+    }
+  }
+
+  if ( parts.length > 1 && origPOS.exec( selector ) ) {
+    if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+      set = posProcess( parts[0] + parts[1], context );
+    } else {
+      set = Expr.relative[ parts[0] ] ?
+        [ context ] :
+        Sizzle( parts.shift(), context );
+
+      while ( parts.length ) {
+        selector = parts.shift();
+
+        if ( Expr.relative[ selector ] ) {
+          selector += parts.shift();
+        }
+
+        set = posProcess( selector, set );
+      }
+    }
+  } else {
+    // Take a shortcut and set the context if the root selector is an ID
+    // (but not if it'll be faster if the inner selector is an ID)
+    if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+        Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+      var ret = Sizzle.find( parts.shift(), context, contextXML );
+      context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
+    }
+
+    if ( context ) {
+      var ret = seed ?
+        { expr: parts.pop(), set: makeArray(seed) } :
+        Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+      set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
+
+      if ( parts.length > 0 ) {
+        checkSet = makeArray(set);
+      } else {
+        prune = false;
+      }
+
+      while ( parts.length ) {
+        var cur = parts.pop(), pop = cur;
+
+        if ( !Expr.relative[ cur ] ) {
+          cur = "";
+        } else {
+          pop = parts.pop();
+        }
+
+        if ( pop == null ) {
+          pop = context;
+        }
+
+        Expr.relative[ cur ]( checkSet, pop, contextXML );
+      }
+    } else {
+      checkSet = parts = [];
+    }
+  }
+
+  if ( !checkSet ) {
+    checkSet = set;
+  }
+
+  if ( !checkSet ) {
+    Sizzle.error( cur || selector );
+  }
+
+  if ( toString.call(checkSet) === "[object Array]" ) {
+    if ( !prune ) {
+      results.push.apply( results, checkSet );
+    } else if ( context && context.nodeType === 1 ) {
+      for ( var i = 0; checkSet[i] != null; i++ ) {
+        if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+          results.push( set[i] );
+        }
+      }
+    } else {
+      for ( var i = 0; checkSet[i] != null; i++ ) {
+        if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+          results.push( set[i] );
+        }
+      }
+    }
+  } else {
+    makeArray( checkSet, results );
+  }
+
+  if ( extra ) {
+    Sizzle( extra, origContext, results, seed );
+    Sizzle.uniqueSort( results );
+  }
+
+  return results;
+};
+
+Sizzle.uniqueSort = function(results){
+  if ( sortOrder ) {
+    hasDuplicate = baseHasDuplicate;
+    results.sort(sortOrder);
+
+    if ( hasDuplicate ) {
+      for ( var i = 1; i < results.length; i++ ) {
+        if ( results[i] === results[i-1] ) {
+          results.splice(i--, 1);
+        }
+      }
+    }
+  }
+
+  return results;
+};
+
+Sizzle.matches = function(expr, set){
+  return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+  var set, match;
+
+  if ( !expr ) {
+    return [];
+  }
+
+  for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+    var type = Expr.order[i], match;
+
+    if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+      var left = match[1];
+      match.splice(1,1);
+
+      if ( left.substr( left.length - 1 ) !== "\\" ) {
+        match[1] = (match[1] || "").replace(/\\/g, "");
+        set = Expr.find[ type ]( match, context, isXML );
+        if ( set != null ) {
+          expr = expr.replace( Expr.match[ type ], "" );
+          break;
+        }
+      }
+    }
+  }
+
+  if ( !set ) {
+    set = context.getElementsByTagName("*");
+  }
+
+  return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+  var old = expr, result = [], curLoop = set, match, anyFound,
+    isXMLFilter = set && set[0] && isXML(set[0]);
+
+  while ( expr && set.length ) {
+    for ( var type in Expr.filter ) {
+      if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+        var filter = Expr.filter[ type ], found, item, left = match[1];
+        anyFound = false;
+
+        match.splice(1,1);
+
+        if ( left.substr( left.length - 1 ) === "\\" ) {
+          continue;
+        }
+
+        if ( curLoop === result ) {
+          result = [];
+        }
+
+        if ( Expr.preFilter[ type ] ) {
+          match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+          if ( !match ) {
+            anyFound = found = true;
+          } else if ( match === true ) {
+            continue;
+          }
+        }
+
+        if ( match ) {
+          for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+            if ( item ) {
+              found = filter( item, match, i, curLoop );
+              var pass = not ^ !!found;
+
+              if ( inplace && found != null ) {
+                if ( pass ) {
+                  anyFound = true;
+                } else {
+                  curLoop[i] = false;
+                }
+              } else if ( pass ) {
+                result.push( item );
+                anyFound = true;
+              }
+            }
+          }
+        }
+
+        if ( found !== undefined ) {
+          if ( !inplace ) {
+            curLoop = result;
+          }
+
+          expr = expr.replace( Expr.match[ type ], "" );
+
+          if ( !anyFound ) {
+            return [];
+          }
+
+          break;
+        }
+      }
+    }
+
+    // Improper expression
+    if ( expr === old ) {
+      if ( anyFound == null ) {
+        Sizzle.error( expr );
+      } else {
+        break;
+      }
+    }
+
+    old = expr;
+  }
+
+  return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+  throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+  order: [ "ID", "NAME", "TAG" ],
+  match: {
+    ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+    CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+    NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
+    ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+    TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
+    CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+    POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+    PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+  },
+  leftMatch: {},
+  attrMap: {
+    "class": "className",
+    "for": "htmlFor"
+  },
+  attrHandle: {
+    href: function(elem){
+      return elem.getAttribute("href");
+    }
+  },
+  relative: {
+    "+": function(checkSet, part){
+      var isPartStr = typeof part === "string",
+        isTag = isPartStr && !/\W/.test(part),
+        isPartStrNotTag = isPartStr && !isTag;
+
+      if ( isTag ) {
+        part = part.toLowerCase();
+      }
+
+      for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+        if ( (elem = checkSet[i]) ) {
+          while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+          checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+            elem || false :
+            elem === part;
+        }
+      }
+
+      if ( isPartStrNotTag ) {
+        Sizzle.filter( part, checkSet, true );
+      }
+    },
+    ">": function(checkSet, part){
+      var isPartStr = typeof part === "string";
+
+      if ( isPartStr && !/\W/.test(part) ) {
+        part = part.toLowerCase();
+
+        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+          var elem = checkSet[i];
+          if ( elem ) {
+            var parent = elem.parentNode;
+            checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+          }
+        }
+      } else {
+        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+          var elem = checkSet[i];
+          if ( elem ) {
+            checkSet[i] = isPartStr ?
+              elem.parentNode :
+              elem.parentNode === part;
+          }
+        }
+
+        if ( isPartStr ) {
+          Sizzle.filter( part, checkSet, true );
+        }
+      }
+    },
+    "": function(checkSet, part, isXML){
+      var doneName = done++, checkFn = dirCheck;
+
+      if ( typeof part === "string" && !/\W/.test(part) ) {
+        var nodeCheck = part = part.toLowerCase();
+        checkFn = dirNodeCheck;
+      }
+
+      checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+    },
+    "~": function(checkSet, part, isXML){
+      var doneName = done++, checkFn = dirCheck;
+
+      if ( typeof part === "string" && !/\W/.test(part) ) {
+        var nodeCheck = part = part.toLowerCase();
+        checkFn = dirNodeCheck;
+      }
+
+      checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+    }
+  },
+  find: {
+    ID: function(match, context, isXML){
+      if ( typeof context.getElementById !== "undefined" && !isXML ) {
+        var m = context.getElementById(match[1]);
+        return m ? [m] : [];
+      }
+    },
+    NAME: function(match, context){
+      if ( typeof context.getElementsByName !== "undefined" ) {
+        var ret = [], results = context.getElementsByName(match[1]);
+
+        for ( var i = 0, l = results.length; i < l; i++ ) {
+          if ( results[i].getAttribute("name") === match[1] ) {
+            ret.push( results[i] );
+          }
+        }
+
+        return ret.length === 0 ? null : ret;
+      }
+    },
+    TAG: function(match, context){
+      return context.getElementsByTagName(match[1]);
+    }
+  },
+  preFilter: {
+    CLASS: function(match, curLoop, inplace, result, not, isXML){
+      match = " " + match[1].replace(/\\/g, "") + " ";
+
+      if ( isXML ) {
+        return match;
+      }
+
+      for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+        if ( elem ) {
+          if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+            if ( !inplace ) {
+              result.push( elem );
+            }
+          } else if ( inplace ) {
+            curLoop[i] = false;
+          }
+        }
+      }
+
+      return false;
+    },
+    ID: function(match){
+      return match[1].replace(/\\/g, "");
+    },
+    TAG: function(match, curLoop){
+      return match[1].toLowerCase();
+    },
+    CHILD: function(match){
+      if ( match[1] === "nth" ) {
+        // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+        var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+          match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+          !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+        // calculate the numbers (first)n+(last) including if they are negative
+        match[2] = (test[1] + (test[2] || 1)) - 0;
+        match[3] = test[3] - 0;
+      }
+
+      // TODO: Move to normal caching system
+      match[0] = done++;
+
+      return match;
+    },
+    ATTR: function(match, curLoop, inplace, result, not, isXML){
+      var name = match[1].replace(/\\/g, "");
+
+      if ( !isXML && Expr.attrMap[name] ) {
+        match[1] = Expr.attrMap[name];
+      }
+
+      if ( match[2] === "~=" ) {
+        match[4] = " " + match[4] + " ";
+      }
+
+      return match;
+    },
+    PSEUDO: function(match, curLoop, inplace, result, not){
+      if ( match[1] === "not" ) {
+        // If we're dealing with a complex expression, or a simple one
+        if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+          match[3] = Sizzle(match[3], null, null, curLoop);
+        } else {
+          var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+          if ( !inplace ) {
+            result.push.apply( result, ret );
+          }
+          return false;
+        }
+      } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+        return true;
+      }
+
+      return match;
+    },
+    POS: function(match){
+      match.unshift( true );
+      return match;
+    }
+  },
+  filters: {
+    enabled: function(elem){
+      return elem.disabled === false && elem.type !== "hidden";
+    },
+    disabled: function(elem){
+      return elem.disabled === true;
+    },
+    checked: function(elem){
+      return elem.checked === true;
+    },
+    selected: function(elem){
+      // Accessing this property makes selected-by-default
+      // options in Safari work properly
+      elem.parentNode.selectedIndex;
+      return elem.selected === true;
+    },
+    parent: function(elem){
+      return !!elem.firstChild;
+    },
+    empty: function(elem){
+      return !elem.firstChild;
+    },
+    has: function(elem, i, match){
+      return !!Sizzle( match[3], elem ).length;
+    },
+    header: function(elem){
+      return /h\d/i.test( elem.nodeName );
+    },
+    text: function(elem){
+      return "text" === elem.type;
+    },
+    radio: function(elem){
+      return "radio" === elem.type;
+    },
+    checkbox: function(elem){
+      return "checkbox" === elem.type;
+    },
+    file: function(elem){
+      return "file" === elem.type;
+    },
+    password: function(elem){
+      return "password" === elem.type;
+    },
+    submit: function(elem){
+      return "submit" === elem.type;
+    },
+    image: function(elem){
+      return "image" === elem.type;
+    },
+    reset: function(elem){
+      return "reset" === elem.type;
+    },
+    button: function(elem){
+      return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+    },
+    input: function(elem){
+      return /input|select|textarea|button/i.test(elem.nodeName);
+    }
+  },
+  setFilters: {
+    first: function(elem, i){
+      return i === 0;
+    },
+    last: function(elem, i, match, array){
+      return i === array.length - 1;
+    },
+    even: function(elem, i){
+      return i % 2 === 0;
+    },
+    odd: function(elem, i){
+      return i % 2 === 1;
+    },
+    lt: function(elem, i, match){
+      return i < match[3] - 0;
+    },
+    gt: function(elem, i, match){
+      return i > match[3] - 0;
+    },
+    nth: function(elem, i, match){
+      return match[3] - 0 === i;
+    },
+    eq: function(elem, i, match){
+      return match[3] - 0 === i;
+    }
+  },
+  filter: {
+    PSEUDO: function(elem, match, i, array){
+      var name = match[1], filter = Expr.filters[ name ];
+
+      if ( filter ) {
+        return filter( elem, i, match, array );
+      } else if ( name === "contains" ) {
+        return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+      } else if ( name === "not" ) {
+        var not = match[3];
+
+        for ( var i = 0, l = not.length; i < l; i++ ) {
+          if ( not[i] === elem ) {
+            return false;
+          }
+        }
+
+        return true;
+      } else {
+        Sizzle.error( "Syntax error, unrecognized expression: " + name );
+      }
+    },
+    CHILD: function(elem, match){
+      var type = match[1], node = elem;
+      switch (type) {
+        case 'only':
+        case 'first':
+          while ( (node = node.previousSibling) )   {
+            if ( node.nodeType === 1 ) {
+              return false;
+            }
+          }
+          if ( type === "first" ) {
+            return true;
+          }
+          node = elem;
+        case 'last':
+          while ( (node = node.nextSibling) )   {
+            if ( node.nodeType === 1 ) {
+              return false;
+            }
+          }
+          return true;
+        case 'nth':
+          var first = match[2], last = match[3];
+
+          if ( first === 1 && last === 0 ) {
+            return true;
+          }
+
+          var doneName = match[0],
+            parent = elem.parentNode;
+
+          if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+            var count = 0;
+            for ( node = parent.firstChild; node; node = node.nextSibling ) {
+              if ( node.nodeType === 1 ) {
+                node.nodeIndex = ++count;
+              }
+            }
+            parent.sizcache = doneName;
+          }
+
+          var diff = elem.nodeIndex - last;
+          if ( first === 0 ) {
+            return diff === 0;
+          } else {
+            return ( diff % first === 0 && diff / first >= 0 );
+          }
+      }
+    },
+    ID: function(elem, match){
+      return elem.nodeType === 1 && elem.getAttribute("id") === match;
+    },
+    TAG: function(elem, match){
+      return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+    },
+    CLASS: function(elem, match){
+      return (" " + (elem.className || elem.getAttribute("class")) + " ")
+        .indexOf( match ) > -1;
+    },
+    ATTR: function(elem, match){
+      var name = match[1],
+        result = Expr.attrHandle[ name ] ?
+          Expr.attrHandle[ name ]( elem ) :
+          elem[ name ] != null ?
+            elem[ name ] :
+            elem.getAttribute( name ),
+        value = result + "",
+        type = match[2],
+        check = match[4];
+
+      return result == null ?
+        type === "!=" :
+        type === "=" ?
+        value === check :
+        type === "*=" ?
+        value.indexOf(check) >= 0 :
+        type === "~=" ?
+        (" " + value + " ").indexOf(check) >= 0 :
+        !check ?
+        value && result !== false :
+        type === "!=" ?
+        value !== check :
+        type === "^=" ?
+        value.indexOf(check) === 0 :
+        type === "$=" ?
+        value.substr(value.length - check.length) === check :
+        type === "|=" ?
+        value === check || value.substr(0, check.length + 1) === check + "-" :
+        false;
+    },
+    POS: function(elem, match, i, array){
+      var name = match[2], filter = Expr.setFilters[ name ];
+
+      if ( filter ) {
+        return filter( elem, i, match, array );
+      }
+    }
+  }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+  Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+  Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
+    return "\\" + (num - 0 + 1);
+  }));
+}
+
+var makeArray = function(array, results) {
+  array = Array.prototype.slice.call( array, 0 );
+
+  if ( results ) {
+    results.push.apply( results, array );
+    return results;
+  }
+
+  return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+  Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch(e){
+  makeArray = function(array, results) {
+    var ret = results || [];
+
+    if ( toString.call(array) === "[object Array]" ) {
+      Array.prototype.push.apply( ret, array );
+    } else {
+      if ( typeof array.length === "number" ) {
+        for ( var i = 0, l = array.length; i < l; i++ ) {
+          ret.push( array[i] );
+        }
+      } else {
+        for ( var i = 0; array[i]; i++ ) {
+          ret.push( array[i] );
+        }
+      }
+    }
+
+    return ret;
+  };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+  sortOrder = function( a, b ) {
+    if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+      if ( a == b ) {
+        hasDuplicate = true;
+      }
+      return a.compareDocumentPosition ? -1 : 1;
+    }
+
+    var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+    if ( ret === 0 ) {
+      hasDuplicate = true;
+    }
+    return ret;
+  };
+} else if ( "sourceIndex" in document.documentElement ) {
+  sortOrder = function( a, b ) {
+    if ( !a.sourceIndex || !b.sourceIndex ) {
+      if ( a == b ) {
+        hasDuplicate = true;
+      }
+      return a.sourceIndex ? -1 : 1;
+    }
+
+    var ret = a.sourceIndex - b.sourceIndex;
+    if ( ret === 0 ) {
+      hasDuplicate = true;
+    }
+    return ret;
+  };
+} else if ( document.createRange ) {
+  sortOrder = function( a, b ) {
+    if ( !a.ownerDocument || !b.ownerDocument ) {
+      if ( a == b ) {
+        hasDuplicate = true;
+      }
+      return a.ownerDocument ? -1 : 1;
+    }
+
+    var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+    aRange.setStart(a, 0);
+    aRange.setEnd(a, 0);
+    bRange.setStart(b, 0);
+    bRange.setEnd(b, 0);
+    var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+    if ( ret === 0 ) {
+      hasDuplicate = true;
+    }
+    return ret;
+  };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+function getText( elems ) {
+  var ret = "", elem;
+
+  for ( var i = 0; elems[i]; i++ ) {
+    elem = elems[i];
+
+    // Get the text from text nodes and CDATA nodes
+    if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+      ret += elem.nodeValue;
+
+    // Traverse everything else, except comment nodes
+    } else if ( elem.nodeType !== 8 ) {
+      ret += getText( elem.childNodes );
+    }
+  }
+
+  return ret;
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+  // We're going to inject a fake input element with a specified name
+  var form = document.createElement("div"),
+    id = "script" + (new Date).getTime();
+  form.innerHTML = "<a name='" + id + "'/>";
+
+  // Inject it into the root element, check its status, and remove it quickly
+  var root = document.documentElement;
+  root.insertBefore( form, root.firstChild );
+
+  // The workaround has to do additional checks after a getElementById
+  // Which slows things down for other browsers (hence the branching)
+  if ( document.getElementById( id ) ) {
+    Expr.find.ID = function(match, context, isXML){
+      if ( typeof context.getElementById !== "undefined" && !isXML ) {
+        var m = context.getElementById(match[1]);
+        return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+      }
+    };
+
+    Expr.filter.ID = function(elem, match){
+      var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+      return elem.nodeType === 1 && node && node.nodeValue === match;
+    };
+  }
+
+  root.removeChild( form );
+  root = form = null; // release memory in IE
+})();
+
+(function(){
+  // Check to see if the browser returns only elements
+  // when doing getElementsByTagName("*")
+
+  // Create a fake element
+  var div = document.createElement("div");
+  div.appendChild( document.createComment("") );
+
+  // Make sure no comments are found
+  if ( div.getElementsByTagName("*").length > 0 ) {
+    Expr.find.TAG = function(match, context){
+      var results = context.getElementsByTagName(match[1]);
+
+      // Filter out possible comments
+      if ( match[1] === "*" ) {
+        var tmp = [];
+
+        for ( var i = 0; results[i]; i++ ) {
+          if ( results[i].nodeType === 1 ) {
+            tmp.push( results[i] );
+          }
+        }
+
+        results = tmp;
+      }
+
+      return results;
+    };
+  }
+
+  // Check to see if an attribute returns normalized href attributes
+  div.innerHTML = "<a href='#'></a>";
+  if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+      div.firstChild.getAttribute("href") !== "#" ) {
+    Expr.attrHandle.href = function(elem){
+      return elem.getAttribute("href", 2);
+    };
+  }
+
+  div = null; // release memory in IE
+})();
+
+if ( document.querySelectorAll ) {
+  (function(){
+    var oldSizzle = Sizzle, div = document.createElement("div");
+    div.innerHTML = "<p class='TEST'></p>";
+
+    // Safari can't handle uppercase or unicode characters when
+    // in quirks mode.
+    if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+      return;
+    }
+
+    Sizzle = function(query, context, extra, seed){
+      context = context || document;
+
+      // Only use querySelectorAll on non-XML documents
+      // (ID selectors don't work in non-HTML documents)
+      if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+        try {
+          return makeArray( context.querySelectorAll(query), extra );
+        } catch(e){}
+      }
+
+      return oldSizzle(query, context, extra, seed);
+    };
+
+    for ( var prop in oldSizzle ) {
+      Sizzle[ prop ] = oldSizzle[ prop ];
+    }
+
+    div = null; // release memory in IE
+  })();
+}
+
+(function(){
+  var div = document.createElement("div");
+
+  div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+  // Opera can't find a second classname (in 9.6)
+  // Also, make sure that getElementsByClassName actually exists
+  if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+    return;
+  }
+
+  // Safari caches class attributes, doesn't catch changes (in 3.2)
+  div.lastChild.className = "e";
+
+  if ( div.getElementsByClassName("e").length === 1 ) {
+    return;
+  }
+
+  Expr.order.splice(1, 0, "CLASS");
+  Expr.find.CLASS = function(match, context, isXML) {
+    if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+      return context.getElementsByClassName(match[1]);
+    }
+  };
+
+  div = null; // release memory in IE
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+    var elem = checkSet[i];
+    if ( elem ) {
+      elem = elem[dir];
+      var match = false;
+
+      while ( elem ) {
+        if ( elem.sizcache === doneName ) {
+          match = checkSet[elem.sizset];
+          break;
+        }
+
+        if ( elem.nodeType === 1 && !isXML ){
+          elem.sizcache = doneName;
+          elem.sizset = i;
+        }
+
+        if ( elem.nodeName.toLowerCase() === cur ) {
+          match = elem;
+          break;
+        }
+
+        elem = elem[dir];
+      }
+
+      checkSet[i] = match;
+    }
+  }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+    var elem = checkSet[i];
+    if ( elem ) {
+      elem = elem[dir];
+      var match = false;
+
+      while ( elem ) {
+        if ( elem.sizcache === doneName ) {
+          match = checkSet[elem.sizset];
+          break;
+        }
+
+        if ( elem.nodeType === 1 ) {
+          if ( !isXML ) {
+            elem.sizcache = doneName;
+            elem.sizset = i;
+          }
+          if ( typeof cur !== "string" ) {
+            if ( elem === cur ) {
+              match = true;
+              break;
+            }
+
+          } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+            match = elem;
+            break;
+          }
+        }
+
+        elem = elem[dir];
+      }
+
+      checkSet[i] = match;
+    }
+  }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+  return !!(a.compareDocumentPosition(b) & 16);
+} : function(a, b){
+  return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+  // documentElement is verified for cases where it doesn't yet exist
+  // (such as loading iframes in IE - #4833)
+  var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+  return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function(selector, context){
+  var tmpSet = [], later = "", match,
+    root = context.nodeType ? [context] : context;
+
+  // Position selectors must be done after the filter
+  // And so must :not(positional) so we move all PSEUDOs to the end
+  while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+    later += match[0];
+    selector = selector.replace( Expr.match.PSEUDO, "" );
+  }
+
+  selector = Expr.relative[selector] ? selector + "*" : selector;
+
+  for ( var i = 0, l = root.length; i < l; i++ ) {
+    Sizzle( selector, root[i], tmpSet );
+  }
+
+  return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = getText;
+jQuery.isXMLDoc = isXML;
+jQuery.contains = contains;
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+var runtil = /Until$/,
+  rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+  // Note: This RegExp should be improved, or likely pulled from Sizzle
+  rmultiselector = /,/,
+  slice = Array.prototype.slice;
+
+// Implement the identical functionality for filter and not
+var winnow = function( elements, qualifier, keep ) {
+  if ( jQuery.isFunction( qualifier ) ) {
+    return jQuery.grep(elements, function( elem, i ) {
+      return !!qualifier.call( elem, i, elem ) === keep;
+    });
+
+  } else if ( qualifier.nodeType ) {
+    return jQuery.grep(elements, function( elem, i ) {
+      return (elem === qualifier) === keep;
+    });
+
+  } else if ( typeof qualifier === "string" ) {
+    var filtered = jQuery.grep(elements, function( elem ) {
+      return elem.nodeType === 1;
+    });
+
+    if ( isSimple.test( qualifier ) ) {
+      return jQuery.filter(qualifier, filtered, !keep);
+    } else {
+      qualifier = jQuery.filter( qualifier, filtered );
+    }
+  }
+
+  return jQuery.grep(elements, function( elem, i ) {
+    return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+  });
+};
+
+jQuery.fn.extend({
+  find: function( selector ) {
+    var ret = this.pushStack( "", "find", selector ), length = 0;
+
+    for ( var i = 0, l = this.length; i < l; i++ ) {
+      length = ret.length;
+      jQuery.find( selector, this[i], ret );
+
+      if ( i > 0 ) {
+        // Make sure that the results are unique
+        for ( var n = length; n < ret.length; n++ ) {
+          for ( var r = 0; r < length; r++ ) {
+            if ( ret[r] === ret[n] ) {
+              ret.splice(n--, 1);
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    return ret;
+  },
+
+  has: function( target ) {
+    var targets = jQuery( target );
+    return this.filter(function() {
+      for ( var i = 0, l = targets.length; i < l; i++ ) {
+        if ( jQuery.contains( this, targets[i] ) ) {
+          return true;
+        }
+      }
+    });
+  },
+
+  not: function( selector ) {
+    return this.pushStack( winnow(this, selector, false), "not", selector);
+  },
+
+  filter: function( selector ) {
+    return this.pushStack( winnow(this, selector, true), "filter", selector );
+  },
+
+  is: function( selector ) {
+    return !!selector && jQuery.filter( selector, this ).length > 0;
+  },
+
+  closest: function( selectors, context ) {
+    if ( jQuery.isArray( selectors ) ) {
+      var ret = [], cur = this[0], match, matches = {}, selector;
+
+      if ( cur && selectors.length ) {
+        for ( var i = 0, l = selectors.length; i < l; i++ ) {
+          selector = selectors[i];
+
+          if ( !matches[selector] ) {
+            matches[selector] = jQuery.expr.match.POS.test( selector ) ?
+              jQuery( selector, context || this.context ) :
+              selector;
+          }
+        }
+
+        while ( cur && cur.ownerDocument && cur !== context ) {
+          for ( selector in matches ) {
+            match = matches[selector];
+
+            if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
+              ret.push({ selector: selector, elem: cur });
+              delete matches[selector];
+            }
+          }
+          cur = cur.parentNode;
+        }
+      }
+
+      return ret;
+    }
+
+    var pos = jQuery.expr.match.POS.test( selectors ) ?
+      jQuery( selectors, context || this.context ) : null;
+
+    return this.map(function( i, cur ) {
+      while ( cur && cur.ownerDocument && cur !== context ) {
+        if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
+          return cur;
+        }
+        cur = cur.parentNode;
+      }
+      return null;
+    });
+  },
+
+  // Determine the position of an element within
+  // the matched set of elements
+  index: function( elem ) {
+    if ( !elem || typeof elem === "string" ) {
+      return jQuery.inArray( this[0],
+        // If it receives a string, the selector is used
+        // If it receives nothing, the siblings are used
+        elem ? jQuery( elem ) : this.parent().children() );
+    }
+    // Locate the position of the desired element
+    return jQuery.inArray(
+      // If it receives a jQuery object, the first element is used
+      elem.jquery ? elem[0] : elem, this );
+  },
+
+  add: function( selector, context ) {
+    var set = typeof selector === "string" ?
+        jQuery( selector, context || this.context ) :
+        jQuery.makeArray( selector ),
+      all = jQuery.merge( this.get(), set );
+
+    return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+      all :
+      jQuery.unique( all ) );
+  },
+
+  andSelf: function() {
+    return this.add( this.prevObject );
+  }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+  return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+  parent: function( elem ) {
+    var parent = elem.parentNode;
+    return parent && parent.nodeType !== 11 ? parent : null;
+  },
+  parents: function( elem ) {
+    return jQuery.dir( elem, "parentNode" );
+  },
+  parentsUntil: function( elem, i, until ) {
+    return jQuery.dir( elem, "parentNode", until );
+  },
+  next: function( elem ) {
+    return jQuery.nth( elem, 2, "nextSibling" );
+  },
+  prev: function( elem ) {
+    return jQuery.nth( elem, 2, "previousSibling" );
+  },
+  nextAll: function( elem ) {
+    return jQuery.dir( elem, "nextSibling" );
+  },
+  prevAll: function( elem ) {
+    return jQuery.dir( elem, "previousSibling" );
+  },
+  nextUntil: function( elem, i, until ) {
+    return jQuery.dir( elem, "nextSibling", until );
+  },
+  prevUntil: function( elem, i, until ) {
+    return jQuery.dir( elem, "previousSibling", until );
+  },
+  siblings: function( elem ) {
+    return jQuery.sibling( elem.parentNode.firstChild, elem );
+  },
+  children: function( elem ) {
+    return jQuery.sibling( elem.firstChild );
+  },
+  contents: function( elem ) {
+    return jQuery.nodeName( elem, "iframe" ) ?
+      elem.contentDocument || elem.contentWindow.document :
+      jQuery.makeArray( elem.childNodes );
+  }
+}, function( name, fn ) {
+  jQuery.fn[ name ] = function( until, selector ) {
+    var ret = jQuery.map( this, fn, until );
+
+    if ( !runtil.test( name ) ) {
+      selector = until;
+    }
+
+    if ( selector && typeof selector === "string" ) {
+      ret = jQuery.filter( selector, ret );
+    }
+
+    ret = this.length > 1 ? jQuery.unique( ret ) : ret;
+
+    if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+      ret = ret.reverse();
+    }
+
+    return this.pushStack( ret, name, slice.call(arguments).join(",") );
+  };
+});
+
+jQuery.extend({
+  filter: function( expr, elems, not ) {
+    if ( not ) {
+      expr = ":not(" + expr + ")";
+    }
+
+    return jQuery.find.matches(expr, elems);
+  },
+
+  dir: function( elem, dir, until ) {
+    var matched = [], cur = elem[dir];
+    while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+      if ( cur.nodeType === 1 ) {
+        matched.push( cur );
+      }
+      cur = cur[dir];
+    }
+    return matched;
+  },
+
+  nth: function( cur, result, dir, elem ) {
+    result = result || 1;
+    var num = 0;
+
+    for ( ; cur; cur = cur[dir] ) {
+      if ( cur.nodeType === 1 && ++num === result ) {
+        break;
+      }
+    }
+
+    return cur;
+  },
+
+  sibling: function( n, elem ) {
+    var r = [];
+
+    for ( ; n; n = n.nextSibling ) {
+      if ( n.nodeType === 1 && n !== elem ) {
+        r.push( n );
+      }
+    }
+
+    return r;
+  }
+});
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+  rleadingWhitespace = /^\s+/,
+  rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
+  rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
+  rtagName = /<([\w:]+)/,
+  rtbody = /<tbody/i,
+  rhtml = /<|&#?\w+;/,
+  rnocache = /<script|<object|<embed|<option|<style/i,
+  rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,  // checked="checked" or checked (html5)
+  fcloseTag = function( all, front, tag ) {
+    return rselfClosing.test( tag ) ?
+      all :
+      front + "></" + tag + ">";
+  },
+  wrapMap = {
+    option: [ 1, "<select multiple='multiple'>", "</select>" ],
+    legend: [ 1, "<fieldset>", "</fieldset>" ],
+    thead: [ 1, "<table>", "</table>" ],
+    tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+    td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+    col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+    area: [ 1, "<map>", "</map>" ],
+    _default: [ 0, "", "" ]
+  };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+  wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+  text: function( text ) {
+    if ( jQuery.isFunction(text) ) {
+      return this.each(function(i) {
+        var self = jQuery(this);
+        self.text( text.call(this, i, self.text()) );
+      });
+    }
+
+    if ( typeof text !== "object" && text !== undefined ) {
+      return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+    }
+
+    return jQuery.text( this );
+  },
+
+  wrapAll: function( html ) {
+    if ( jQuery.isFunction( html ) ) {
+      return this.each(function(i) {
+        jQuery(this).wrapAll( html.call(this, i) );
+      });
+    }
+
+    if ( this[0] ) {
+      // The elements to wrap the target around
+      var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+      if ( this[0].parentNode ) {
+        wrap.insertBefore( this[0] );
+      }
+
+      wrap.map(function() {
+        var elem = this;
+
+        while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+          elem = elem.firstChild;
+        }
+
+        return elem;
+      }).append(this);
+    }
+
+    return this;
+  },
+
+  wrapInner: function( html ) {
+    if ( jQuery.isFunction( html ) ) {
+      return this.each(function(i) {
+        jQuery(this).wrapInner( html.call(this, i) );
+      });
+    }
+
+    return this.each(function() {
+      var self = jQuery( this ), contents = self.contents();
+
+      if ( contents.length ) {
+        contents.wrapAll( html );
+
+      } else {
+        self.append( html );
+      }
+    });
+  },
+
+  wrap: function( html ) {
+    return this.each(function() {
+      jQuery( this ).wrapAll( html );
+    });
+  },
+
+  unwrap: function() {
+    return this.parent().each(function() {
+      if ( !jQuery.nodeName( this, "body" ) ) {
+        jQuery( this ).replaceWith( this.childNodes );
+      }
+    }).end();
+  },
+
+  append: function() {
+    return this.domManip(arguments, true, function( elem ) {
+      if ( this.nodeType === 1 ) {
+        this.appendChild( elem );
+      }
+    });
+  },
+
+  prepend: function() {
+    return this.domManip(arguments, true, function( elem ) {
+      if ( this.nodeType === 1 ) {
+        this.insertBefore( elem, this.firstChild );
+      }
+    });
+  },
+
+  before: function() {
+    if ( this[0] && this[0].parentNode ) {
+      return this.domManip(arguments, false, function( elem ) {
+        this.parentNode.insertBefore( elem, this );
+      });
+    } else if ( arguments.length ) {
+      var set = jQuery(arguments[0]);
+      set.push.apply( set, this.toArray() );
+      return this.pushStack( set, "before", arguments );
+    }
+  },
+
+  after: function() {
+    if ( this[0] && this[0].parentNode ) {
+      return this.domManip(arguments, false, function( elem ) {
+        this.parentNode.insertBefore( elem, this.nextSibling );
+      });
+    } else if ( arguments.length ) {
+      var set = this.pushStack( this, "after", arguments );
+      set.push.apply( set, jQuery(arguments[0]).toArray() );
+      return set;
+    }
+  },
+
+  // keepData is for internal use only--do not document
+  remove: function( selector, keepData ) {
+    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+      if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+        if ( !keepData && elem.nodeType === 1 ) {
+          jQuery.cleanData( elem.getElementsByTagName("*") );
+          jQuery.cleanData( [ elem ] );
+        }
+
+        if ( elem.parentNode ) {
+           elem.parentNode.removeChild( elem );
+        }
+      }
+    }
+
+    return this;
+  },
+
+  empty: function() {
+    for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+      // Remove element nodes and prevent memory leaks
+      if ( elem.nodeType === 1 ) {
+        jQuery.cleanData( elem.getElementsByTagName("*") );
+      }
+
+      // Remove any remaining nodes
+      while ( elem.firstChild ) {
+        elem.removeChild( elem.firstChild );
+      }
+    }
+
+    return this;
+  },
+
+  clone: function( events ) {
+    // Do the clone
+    var ret = this.map(function() {
+      if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+        // IE copies events bound via attachEvent when
+        // using cloneNode. Calling detachEvent on the
+        // clone will also remove the events from the orignal
+        // In order to get around this, we use innerHTML.
+        // Unfortunately, this means some modifications to
+        // attributes in IE that are actually only stored
+        // as properties will not be copied (such as the
+        // the name attribute on an input).
+        var html = this.outerHTML, ownerDocument = this.ownerDocument;
+        if ( !html ) {
+          var div = ownerDocument.createElement("div");
+          div.appendChild( this.cloneNode(true) );
+          html = div.innerHTML;
+        }
+
+        return jQuery.clean([html.replace(rinlinejQuery, "")
+          // Handle the case in IE 8 where action=/test/> self-closes a tag
+          .replace(/=([^="'>\s]+\/)>/g, '="$1">')
+          .replace(rleadingWhitespace, "")], ownerDocument)[0];
+      } else {
+        return this.cloneNode(true);
+      }
+    });
+
+    // Copy the events from the original to the clone
+    if ( events === true ) {
+      cloneCopyEvent( this, ret );
+      cloneCopyEvent( this.find("*"), ret.find("*") );
+    }
+
+    // Return the cloned set
+    return ret;
+  },
+
+  html: function( value ) {
+    if ( value === undefined ) {
+      return this[0] && this[0].nodeType === 1 ?
+        this[0].innerHTML.replace(rinlinejQuery, "") :
+        null;
+
+    // See if we can take a shortcut and just use innerHTML
+    } else if ( typeof value === "string" && !rnocache.test( value ) &&
+      (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+      !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+      value = value.replace(rxhtmlTag, fcloseTag);
+
+      try {
+        for ( var i = 0, l = this.length; i < l; i++ ) {
+          // Remove element nodes and prevent memory leaks
+          if ( this[i].nodeType === 1 ) {
+            jQuery.cleanData( this[i].getElementsByTagName("*") );
+            this[i].innerHTML = value;
+          }
+        }
+
+      // If using innerHTML throws an exception, use the fallback method
+      } catch(e) {
+        this.empty().append( value );
+      }
+
+    } else if ( jQuery.isFunction( value ) ) {
+      this.each(function(i){
+        var self = jQuery(this), old = self.html();
+        self.empty().append(function(){
+          return value.call( this, i, old );
+        });
+      });
+
+    } else {
+      this.empty().append( value );
+    }
+
+    return this;
+  },
+
+  replaceWith: function( value ) {
+    if ( this[0] && this[0].parentNode ) {
+      // Make sure that the elements are removed from the DOM before they are inserted
+      // this can help fix replacing a parent with child elements
+      if ( jQuery.isFunction( value ) ) {
+        return this.each(function(i) {
+          var self = jQuery(this), old = self.html();
+          self.replaceWith( value.call( this, i, old ) );
+        });
+      }
+
+      if ( typeof value !== "string" ) {
+        value = jQuery(value).detach();
+      }
+
+      return this.each(function() {
+        var next = this.nextSibling, parent = this.parentNode;
+
+        jQuery(this).remove();
+
+        if ( next ) {
+          jQuery(next).before( value );
+        } else {
+          jQuery(parent).append( value );
+        }
+      });
+    } else {
+      return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
+    }
+  },
+
+  detach: function( selector ) {
+    return this.remove( selector, true );
+  },
+
+  domManip: function( args, table, callback ) {
+    var results, first, value = args[0], scripts = [], fragment, parent;
+
+    // We can't cloneNode fragments that contain checked, in WebKit
+    if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+      return this.each(function() {
+        jQuery(this).domManip( args, table, callback, true );
+      });
+    }
+
+    if ( jQuery.isFunction(value) ) {
+      return this.each(function(i) {
+        var self = jQuery(this);
+        args[0] = value.call(this, i, table ? self.html() : undefined);
+        self.domManip( args, table, callback );
+      });
+    }
+
+    if ( this[0] ) {
+      parent = value && value.parentNode;
+
+      // If we're in a fragment, just use that instead of building a new one
+      if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+        results = { fragment: parent };
+
+      } else {
+        results = buildFragment( args, this, scripts );
+      }
+
+      fragment = results.fragment;
+
+      if ( fragment.childNodes.length === 1 ) {
+        first = fragment = fragment.firstChild;
+      } else {
+        first = fragment.firstChild;
+      }
+
+      if ( first ) {
+        table = table && jQuery.nodeName( first, "tr" );
+
+        for ( var i = 0, l = this.length; i < l; i++ ) {
+          callback.call(
+            table ?
+              root(this[i], first) :
+              this[i],
+            i > 0 || results.cacheable || this.length > 1  ?
+              fragment.cloneNode(true) :
+              fragment
+          );
+        }
+      }
+
+      if ( scripts.length ) {
+        jQuery.each( scripts, evalScript );
+      }
+    }
+
+    return this;
+
+    function root( elem, cur ) {
+      return jQuery.nodeName(elem, "table") ?
+        (elem.getElementsByTagName("tbody")[0] ||
+        elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+        elem;
+    }
+  }
+});
+
+function cloneCopyEvent(orig, ret) {
+  var i = 0;
+
+  ret.each(function() {
+    if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
+      return;
+    }
+
+    var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
+
+    if ( events ) {
+      delete curData.handle;
+      curData.events = {};
+
+      for ( var type in events ) {
+        for ( var handler in events[ type ] ) {
+          jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+        }
+      }
+    }
+  });
+}
+
+function buildFragment( args, nodes, scripts ) {
+  var fragment, cacheable, cacheresults,
+    doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+  // Only cache "small" (1/2 KB) strings that are associated with the main document
+  // Cloning options loses the selected state, so don't cache them
+  // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+  // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+  if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+    !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+    cacheable = true;
+    cacheresults = jQuery.fragments[ args[0] ];
+    if ( cacheresults ) {
+      if ( cacheresults !== 1 ) {
+        fragment = cacheresults;
+      }
+    }
+  }
+
+  if ( !fragment ) {
+    fragment = doc.createDocumentFragment();
+    jQuery.clean( args, doc, fragment, scripts );
+  }
+
+  if ( cacheable ) {
+    jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+  }
+
+  return { fragment: fragment, cacheable: cacheable };
+}
+
+jQuery.fragments = {};
+
+jQuery.each({
+  appendTo: "append",
+  prependTo: "prepend",
+  insertBefore: "before",
+  insertAfter: "after",
+  replaceAll: "replaceWith"
+}, function( name, original ) {
+  jQuery.fn[ name ] = function( selector ) {
+    var ret = [], insert = jQuery( selector ),
+      parent = this.length === 1 && this[0].parentNode;
+
+    if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+      insert[ original ]( this[0] );
+      return this;
+
+    } else {
+      for ( var i = 0, l = insert.length; i < l; i++ ) {
+        var elems = (i > 0 ? this.clone(true) : this).get();
+        jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+        ret = ret.concat( elems );
+      }
+
+      return this.pushStack( ret, name, insert.selector );
+    }
+  };
+});
+
+jQuery.extend({
+  clean: function( elems, context, fragment, scripts ) {
+    context = context || document;
+
+    // !context.createElement fails in IE with an error but returns typeof 'object'
+    if ( typeof context.createElement === "undefined" ) {
+      context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+    }
+
+    var ret = [];
+
+    for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+      if ( typeof elem === "number" ) {
+        elem += "";
+      }
+
+      if ( !elem ) {
+        continue;
+      }
+
+      // Convert html string into DOM nodes
+      if ( typeof elem === "string" && !rhtml.test( elem ) ) {
+        elem = context.createTextNode( elem );
+
+      } else if ( typeof elem === "string" ) {
+        // Fix "XHTML"-style tags in all browsers
+        elem = elem.replace(rxhtmlTag, fcloseTag);
+
+        // Trim whitespace, otherwise indexOf won't work as expected
+        var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+          wrap = wrapMap[ tag ] || wrapMap._default,
+          depth = wrap[0],
+          div = context.createElement("div");
+
+        // Go to html and back, then peel off extra wrappers
+        div.innerHTML = wrap[1] + elem + wrap[2];
+
+        // Move to the right depth
+        while ( depth-- ) {
+          div = div.lastChild;
+        }
+
+        // Remove IE's autoinserted <tbody> from table fragments
+        if ( !jQuery.support.tbody ) {
+
+          // String was a <table>, *may* have spurious <tbody>
+          var hasBody = rtbody.test(elem),
+            tbody = tag === "table" && !hasBody ?
+              div.firstChild && div.firstChild.childNodes :
+
+              // String was a bare <thead> or <tfoot>
+              wrap[1] === "<table>" && !hasBody ?
+                div.childNodes :
+                [];
+
+          for ( var j = tbody.length - 1; j >= 0 ; --j ) {
+            if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+              tbody[ j ].parentNode.removeChild( tbody[ j ] );
+            }
+          }
+
+        }
+
+        // IE completely kills leading whitespace when innerHTML is used
+        if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+          div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+        }
+
+        elem = div.childNodes;
+      }
+
+      if ( elem.nodeType ) {
+        ret.push( elem );
+      } else {
+        ret = jQuery.merge( ret, elem );
+      }
+    }
+
+    if ( fragment ) {
+      for ( var i = 0; ret[i]; i++ ) {
+        if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+          scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+
+        } else {
+          if ( ret[i].nodeType === 1 ) {
+            ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+          }
+          fragment.appendChild( ret[i] );
+        }
+      }
+    }
+
+    return ret;
+  },
+
+  cleanData: function( elems ) {
+    var data, id, cache = jQuery.cache,
+      special = jQuery.event.special,
+      deleteExpando = jQuery.support.deleteExpando;
+
+    for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+      id = elem[ jQuery.expando ];
+
+      if ( id ) {
+        data = cache[ id ];
+
+        if ( data.events ) {
+          for ( var type in data.events ) {
+            if ( special[ type ] ) {
+              jQuery.event.remove( elem, type );
+
+            } else {
+              removeEvent( elem, type, data.handle );
+            }
+          }
+        }
+
+        if ( deleteExpando ) {
+          delete elem[ jQuery.expando ];
+
+        } else if ( elem.removeAttribute ) {
+          elem.removeAttribute( jQuery.expando );
+        }
+
+        delete cache[ id ];
+      }
+    }
+  }
+});
+// exclude the following css properties to add px
+var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+  ralpha = /alpha\([^)]*\)/,
+  ropacity = /opacity=([^)]*)/,
+  rfloat = /float/i,
+  rdashAlpha = /-([a-z])/ig,
+  rupper = /([A-Z])/g,
+  rnumpx = /^-?\d+(?:px)?$/i,
+  rnum = /^-?\d/,
+
+  cssShow = { position: "absolute", visibility: "hidden", display:"block" },
+  cssWidth = [ "Left", "Right" ],
+  cssHeight = [ "Top", "Bottom" ],
+
+  // cache check for defaultView.getComputedStyle
+  getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
+  // normalize float css property
+  styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
+  fcamelCase = function( all, letter ) {
+    return letter.toUpperCase();
+  };
+
+jQuery.fn.css = function( name, value ) {
+  return access( this, name, value, true, function( elem, name, value ) {
+    if ( value === undefined ) {
+      return jQuery.curCSS( elem, name );
+    }
+
+    if ( typeof value === "number" && !rexclude.test(name) ) {
+      value += "px";
+    }
+
+    jQuery.style( elem, name, value );
+  });
+};
+
+jQuery.extend({
+  style: function( elem, name, value ) {
+    // don't set styles on text and comment nodes
+    if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+      return undefined;
+    }
+
+    // ignore negative width and height values #1599
+    if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
+      value = undefined;
+    }
+
+    var style = elem.style || elem, set = value !== undefined;
+
+    // IE uses filters for opacity
+    if ( !jQuery.support.opacity && name === "opacity" ) {
+      if ( set ) {
+        // IE has trouble with opacity if it does not have layout
+        // Force it by setting the zoom level
+        style.zoom = 1;
+
+        // Set the alpha filter to set the opacity
+        var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
+        var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
+        style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
+      }
+
+      return style.filter && style.filter.indexOf("opacity=") >= 0 ?
+        (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
+        "";
+    }
+
+    // Make sure we're using the right name for getting the float value
+    if ( rfloat.test( name ) ) {
+      name = styleFloat;
+    }
+
+    name = name.replace(rdashAlpha, fcamelCase);
+
+    if ( set ) {
+      style[ name ] = value;
+    }
+
+    return style[ name ];
+  },
+
+  css: function( elem, name, force, extra ) {
+    if ( name === "width" || name === "height" ) {
+      var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
+
+      function getWH() {
+        val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+        if ( extra === "border" ) {
+          return;
+        }
+
+        jQuery.each( which, function() {
+          if ( !extra ) {
+            val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+          }
+
+          if ( extra === "margin" ) {
+            val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+          } else {
+            val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+          }
+        });
+      }
+
+      if ( elem.offsetWidth !== 0 ) {
+        getWH();
+      } else {
+        jQuery.swap( elem, props, getWH );
+      }
+
+      return Math.max(0, Math.round(val));
+    }
+
+    return jQuery.curCSS( elem, name, force );
+  },
+
+  curCSS: function( elem, name, force ) {
+    var ret, style = elem.style, filter;
+
+    // IE uses filters for opacity
+    if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
+      ret = ropacity.test(elem.currentStyle.filter || "") ?
+        (parseFloat(RegExp.$1) / 100) + "" :
+        "";
+
+      return ret === "" ?
+        "1" :
+        ret;
+    }
+
+    // Make sure we're using the right name for getting the float value
+    if ( rfloat.test( name ) ) {
+      name = styleFloat;
+    }
+
+    if ( !force && style && style[ name ] ) {
+      ret = style[ name ];
+
+    } else if ( getComputedStyle ) {
+
+      // Only "float" is needed here
+      if ( rfloat.test( name ) ) {
+        name = "float";
+      }
+
+      name = name.replace( rupper, "-$1" ).toLowerCase();
+
+      var defaultView = elem.ownerDocument.defaultView;
+
+      if ( !defaultView ) {
+        return null;
+      }
+
+      var computedStyle = defaultView.getComputedStyle( elem, null );
+
+      if ( computedStyle ) {
+        ret = computedStyle.getPropertyValue( name );
+      }
+
+      // We should always get a number back from opacity
+      if ( name === "opacity" && ret === "" ) {
+        ret = "1";
+      }
+
+    } else if ( elem.currentStyle ) {
+      var camelCase = name.replace(rdashAlpha, fcamelCase);
+
+      ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+      // From the awesome hack by Dean Edwards
+      // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+      // If we're not dealing with a regular pixel number
+      // but a number that has a weird ending, we need to convert it to pixels
+      if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+        // Remember the original values
+        var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+        // Put in the new values to get a computed value out
+        elem.runtimeStyle.left = elem.currentStyle.left;
+        style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
+        ret = style.pixelLeft + "px";
+
+        // Revert the changed values
+        style.left = left;
+        elem.runtimeStyle.left = rsLeft;
+      }
+    }
+
+    return ret;
+  },
+
+  // A method for quickly swapping in/out CSS properties to get correct calculations
+  swap: function( elem, options, callback ) {
+    var old = {};
+
+    // Remember the old values, and insert the new ones
+    for ( var name in options ) {
+      old[ name ] = elem.style[ name ];
+      elem.style[ name ] = options[ name ];
+    }
+
+    callback.call( elem );
+
+    // Revert the old values
+    for ( var name in options ) {
+      elem.style[ name ] = old[ name ];
+    }
+  }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+  jQuery.expr.filters.hidden = function( elem ) {
+    var width = elem.offsetWidth, height = elem.offsetHeight,
+      skip = elem.nodeName.toLowerCase() === "tr";
+
+    return width === 0 && height === 0 && !skip ?
+      true :
+      width > 0 && height > 0 && !skip ?
+        false :
+        jQuery.curCSS(elem, "display") === "none";
+  };
+
+  jQuery.expr.filters.visible = function( elem ) {
+    return !jQuery.expr.filters.hidden( elem );
+  };
+}
+var jsc = now(),
+  rscript = /<script(.|\s)*?\/script>/gi,
+  rselectTextarea = /select|textarea/i,
+  rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
+  jsre = /=\?(&|$)/,
+  rquery = /\?/,
+  rts = /(\?|&)_=.*?(&|$)/,
+  rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+  r20 = /%20/g,
+
+  // Keep a copy of the old load method
+  _load = jQuery.fn.load;
+
+jQuery.fn.extend({
+  load: function( url, params, callback ) {
+    if ( typeof url !== "string" ) {
+      return _load.call( this, url );
+
+    // Don't do a request if no elements are being requested
+    } else if ( !this.length ) {
+      return this;
+    }
+
+    var off = url.indexOf(" ");
+    if ( off >= 0 ) {
+      var selector = url.slice(off, url.length);
+      url = url.slice(0, off);
+    }
+
+    // Default to a GET request
+    var type = "GET";
+
+    // If the second parameter was provided
+    if ( params ) {
+      // If it's a function
+      if ( jQuery.isFunction( params ) ) {
+        // We assume that it's the callback
+        callback = params;
+        params = null;
+
+      // Otherwise, build a param string
+      } else if ( typeof params === "object" ) {
+        params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+        type = "POST";
+      }
+    }
+
+    var self = this;
+
+    // Request the remote document
+    jQuery.ajax({
+      url: url,
+      type: type,
+      dataType: "html",
+      data: params,
+      complete: function( res, status ) {
+        // If successful, inject the HTML into all the matched elements
+        if ( status === "success" || status === "notmodified" ) {
+          // See if a selector was specified
+          self.html( selector ?
+            // Create a dummy div to hold the results
+            jQuery("<div />")
+              // inject the contents of the document in, removing the scripts
+              // to avoid any 'Permission Denied' errors in IE
+              .append(res.responseText.replace(rscript, ""))
+
+              // Locate the specified elements
+              .find(selector) :
+
+            // If not, just inject the full result
+            res.responseText );
+        }
+
+        if ( callback ) {
+          self.each( callback, [res.responseText, status, res] );
+        }
+      }
+    });
+
+    return this;
+  },
+
+  serialize: function() {
+    return jQuery.param(this.serializeArray());
+  },
+  serializeArray: function() {
+    return this.map(function() {
+      return this.elements ? jQuery.makeArray(this.elements) : this;
+    })
+    .filter(function() {
+      return this.name && !this.disabled &&
+        (this.checked || rselectTextarea.test(this.nodeName) ||
+          rinput.test(this.type));
+    })
+    .map(function( i, elem ) {
+      var val = jQuery(this).val();
+
+      return val == null ?
+        null :
+        jQuery.isArray(val) ?
+          jQuery.map( val, function( val, i ) {
+            return { name: elem.name, value: val };
+          }) :
+          { name: elem.name, value: val };
+    }).get();
+  }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
+  jQuery.fn[o] = function( f ) {
+    return this.bind(o, f);
+  };
+});
+
+jQuery.extend({
+
+  get: function( url, data, callback, type ) {
+    // shift arguments if data argument was omited
+    if ( jQuery.isFunction( data ) ) {
+      type = type || callback;
+      callback = data;
+      data = null;
+    }
+
+    return jQuery.ajax({
+      type: "GET",
+      url: url,
+      data: data,
+      success: callback,
+      dataType: type
+    });
+  },
+
+  getScript: function( url, callback ) {
+    return jQuery.get(url, null, callback, "script");
+  },
+
+  getJSON: function( url, data, callback ) {
+    return jQuery.get(url, data, callback, "json");
+  },
+
+  post: function( url, data, callback, type ) {
+    // shift arguments if data argument was omited
+    if ( jQuery.isFunction( data ) ) {
+      type = type || callback;
+      callback = data;
+      data = {};
+    }
+
+    return jQuery.ajax({
+      type: "POST",
+      url: url,
+      data: data,
+      success: callback,
+      dataType: type
+    });
+  },
+
+  ajaxSetup: function( settings ) {
+    jQuery.extend( jQuery.ajaxSettings, settings );
+  },
+
+  ajaxSettings: {
+    url: location.href,
+    global: true,
+    type: "GET",
+    contentType: "application/x-www-form-urlencoded",
+    processData: true,
+    async: true,
+    /*
+    timeout: 0,
+    data: null,
+    username: null,
+    password: null,
+    traditional: false,
+    */
+    // Create the request object; Microsoft failed to properly
+    // implement the XMLHttpRequest in IE7 (can't request local files),
+    // so we use the ActiveXObject when it is available
+    // This function can be overriden by calling jQuery.ajaxSetup
+    xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
+      function() {
+        return new window.XMLHttpRequest();
+      } :
+      function() {
+        try {
+          return new window.ActiveXObject("Microsoft.XMLHTTP");
+        } catch(e) {}
+      },
+    accepts: {
+      xml: "application/xml, text/xml",
+      html: "text/html",
+      script: "text/javascript, application/javascript",
+      json: "application/json, text/javascript",
+      text: "text/plain",
+      _default: "*/*"
+    }
+  },
+
+  // Last-Modified header cache for next request
+  lastModified: {},
+  etag: {},
+
+  ajax: function( origSettings ) {
+    var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
+
+    var jsonp, status, data,
+      callbackContext = origSettings && origSettings.context || s,
+      type = s.type.toUpperCase();
+
+    // convert data if not already a string
+    if ( s.data && s.processData && typeof s.data !== "string" ) {
+      s.data = jQuery.param( s.data, s.traditional );
+    }
+
+    // Handle JSONP Parameter Callbacks
+    if ( s.dataType === "jsonp" ) {
+      if ( type === "GET" ) {
+        if ( !jsre.test( s.url ) ) {
+          s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+        }
+      } else if ( !s.data || !jsre.test(s.data) ) {
+        s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+      }
+      s.dataType = "json";
+    }
+
+    // Build temporary JSONP function
+    if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+      jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+      // Replace the =? sequence both in the query string and the data
+      if ( s.data ) {
+        s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+      }
+
+      s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+      // We need to make sure
+      // that a JSONP style response is executed properly
+      s.dataType = "script";
+
+      // Handle JSONP-style loading
+      window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+        data = tmp;
+        success();
+        complete();
+        // Garbage collect
+        window[ jsonp ] = undefined;
+
+        try {
+          delete window[ jsonp ];
+        } catch(e) {}
+
+        if ( head ) {
+          head.removeChild( script );
+        }
+      };
+    }
+
+    if ( s.dataType === "script" && s.cache === null ) {
+      s.cache = false;
+    }
+
+    if ( s.cache === false && type === "GET" ) {
+      var ts = now();
+
+      // try replacing _= if it is there
+      var ret = s.url.replace(rts, "$1_=" + ts + "$2");
+
+      // if nothing was replaced, add timestamp to the end
+      s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
+    }
+
+    // If data is available, append data to url for get requests
+    if ( s.data && type === "GET" ) {
+      s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
+    }
+
+    // Watch for a new set of requests
+    if ( s.global && ! jQuery.active++ ) {
+      jQuery.event.trigger( "ajaxStart" );
+    }
+
+    // Matches an absolute URL, and saves the domain
+    var parts = rurl.exec( s.url ),
+      remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
+
+    // If we're requesting a remote document
+    // and trying to load JSON or Script with a GET
+    if ( s.dataType === "script" && type === "GET" && remote ) {
+      var head = document.getElementsByTagName("head")[0] || document.documentElement;
+      var script = document.createElement("script");
+      script.src = s.url;
+      if ( s.scriptCharset ) {
+        script.charset = s.scriptCharset;
+      }
+
+      // Handle Script loading
+      if ( !jsonp ) {
+        var done = false;
+
+        // Attach handlers for all browsers
+        script.onload = script.onreadystatechange = function() {
+          if ( !done && (!this.readyState ||
+              this.readyState === "loaded" || this.readyState === "complete") ) {
+            done = true;
+            success();
+            complete();
+
+            // Handle memory leak in IE
+            script.onload = script.onreadystatechange = null;
+            if ( head && script.parentNode ) {
+              head.removeChild( script );
+            }
+          }
+        };
+      }
+
+      // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
+      // This arises when a base node is used (#2709 and #4378).
+      head.insertBefore( script, head.firstChild );
+
+      // We handle everything using the script element injection
+      return undefined;
+    }
+
+    var requestDone = false;
+
+    // Create the request object
+    var xhr = s.xhr();
+
+    if ( !xhr ) {
+      return;
+    }
+
+    // Open the socket
+    // Passing null username, generates a login popup on Opera (#2865)
+    if ( s.username ) {
+      xhr.open(type, s.url, s.async, s.username, s.password);
+    } else {
+      xhr.open(type, s.url, s.async);
+    }
+
+    // Need an extra try/catch for cross domain requests in Firefox 3
+    try {
+      // Set the correct header, if data is being sent
+      if ( s.data || origSettings && origSettings.contentType ) {
+        xhr.setRequestHeader("Content-Type", s.contentType);
+      }
+
+      // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+      if ( s.ifModified ) {
+        if ( jQuery.lastModified[s.url] ) {
+          xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
+        }
+
+        if ( jQuery.etag[s.url] ) {
+          xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
+        }
+      }
+
+      // Set header so the called script knows that it's an XMLHttpRequest
+      // Only send the header if it's not a remote XHR
+      if ( !remote ) {
+        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+      }
+
+      // Set the Accepts header for the server, depending on the dataType
+      xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+        s.accepts[ s.dataType ] + ", */*" :
+        s.accepts._default );
+    } catch(e) {}
+
+    // Allow custom headers/mimetypes and early abort
+    if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
+      // Handle the global AJAX counter
+      if ( s.global && ! --jQuery.active ) {
+        jQuery.event.trigger( "ajaxStop" );
+      }
+
+      // close opended socket
+      xhr.abort();
+      return false;
+    }
+
+    if ( s.global ) {
+      trigger("ajaxSend", [xhr, s]);
+    }
+
+    // Wait for a response to come back
+    var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
+      // The request was aborted
+      if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
+        // Opera doesn't call onreadystatechange before this point
+        // so we simulate the call
+        if ( !requestDone ) {
+          complete();
+        }
+
+        requestDone = true;
+        if ( xhr ) {
+          xhr.onreadystatechange = jQuery.noop;
+        }
+
+      // The transfer is complete and the data is available, or the request timed out
+      } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
+        requestDone = true;
+        xhr.onreadystatechange = jQuery.noop;
+
+        status = isTimeout === "timeout" ?
+          "timeout" :
+          !jQuery.httpSuccess( xhr ) ?
+            "error" :
+            s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
+              "notmodified" :
+              "success";
+
+        var errMsg;
+
+        if ( status === "success" ) {
+          // Watch for, and catch, XML document parse errors
+          try {
+            // process the data (runs the xml through httpData regardless of callback)
+            data = jQuery.httpData( xhr, s.dataType, s );
+          } catch(err) {
+            status = "parsererror";
+            errMsg = err;
+          }
+        }
+
+        // Make sure that the request was successful or notmodified
+        if ( status === "success" || status === "notmodified" ) {
+          // JSONP handles its own success callback
+          if ( !jsonp ) {
+            success();
+          }
+        } else {
+          jQuery.handleError(s, xhr, status, errMsg);
+        }
+
+        // Fire the complete handlers
+        complete();
+
+        if ( isTimeout === "timeout" ) {
+          xhr.abort();
+        }
+
+        // Stop memory leaks
+        if ( s.async ) {
+          xhr = null;
+        }
+      }
+    };
+
+    // Override the abort handler, if we can (IE doesn't allow it, but that's OK)
+    // Opera doesn't fire onreadystatechange at all on abort
+    try {
+      var oldAbort = xhr.abort;
+      xhr.abort = function() {
+        if ( xhr ) {
+          oldAbort.call( xhr );
+        }
+
+        onreadystatechange( "abort" );
+      };
+    } catch(e) { }
+
+    // Timeout checker
+    if ( s.async && s.timeout > 0 ) {
+      setTimeout(function() {
+        // Check to see if the request is still happening
+        if ( xhr && !requestDone ) {
+          onreadystatechange( "timeout" );
+        }
+      }, s.timeout);
+    }
+
+    // Send the data
+    try {
+      xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
+    } catch(e) {
+      jQuery.handleError(s, xhr, null, e);
+      // Fire the complete handlers
+      complete();
+    }
+
+    // firefox 1.5 doesn't fire statechange for sync requests
+    if ( !s.async ) {
+      onreadystatechange();
+    }
+
+    function success() {
+      // If a local callback was specified, fire it and pass it the data
+      if ( s.success ) {
+        s.success.call( callbackContext, data, status, xhr );
+      }
+
+      // Fire the global callback
+      if ( s.global ) {
+        trigger( "ajaxSuccess", [xhr, s] );
+      }
+    }
+
+    function complete() {
+      // Process result
+      if ( s.complete ) {
+        s.complete.call( callbackContext, xhr, status);
+      }
+
+      // The request was completed
+      if ( s.global ) {
+        trigger( "ajaxComplete", [xhr, s] );
+      }
+
+      // Handle the global AJAX counter
+      if ( s.global && ! --jQuery.active ) {
+        jQuery.event.trigger( "ajaxStop" );
+      }
+    }
+
+    function trigger(type, args) {
+      (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
+    }
+
+    // return XMLHttpRequest to allow aborting the request etc.
+    return xhr;
+  },
+
+  handleError: function( s, xhr, status, e ) {
+    // If a local callback was specified, fire it
+    if ( s.error ) {
+      s.error.call( s.context || s, xhr, status, e );
+    }
+
+    // Fire the global callback
+    if ( s.global ) {
+      (s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
+    }
+  },
+
+  // Counter for holding the number of active queries
+  active: 0,
+
+  // Determines if an XMLHttpRequest was successful or not
+  httpSuccess: function( xhr ) {
+    try {
+      // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+      return !xhr.status && location.protocol === "file:" ||
+        // Opera returns 0 when status is 304
+        ( xhr.status >= 200 && xhr.status < 300 ) ||
+        xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
+    } catch(e) {}
+
+    return false;
+  },
+
+  // Determines if an XMLHttpRequest returns NotModified
+  httpNotModified: function( xhr, url ) {
+    var lastModified = xhr.getResponseHeader("Last-Modified"),
+      etag = xhr.getResponseHeader("Etag");
+
+    if ( lastModified ) {
+      jQuery.lastModified[url] = lastModified;
+    }
+
+    if ( etag ) {
+      jQuery.etag[url] = etag;
+    }
+
+    // Opera returns 0 when status is 304
+    return xhr.status === 304 || xhr.status === 0;
+  },
+
+  httpData: function( xhr, type, s ) {
+    var ct = xhr.getResponseHeader("content-type") || "",
+      xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
+      data = xml ? xhr.responseXML : xhr.responseText;
+
+    if ( xml && data.documentElement.nodeName === "parsererror" ) {
+      jQuery.error( "parsererror" );
+    }
+
+    // Allow a pre-filtering function to sanitize the response
+    // s is checked to keep backwards compatibility
+    if ( s && s.dataFilter ) {
+      data = s.dataFilter( data, type );
+    }
+
+    // The filter can actually parse the response
+    if ( typeof data === "string" ) {
+      // Get the JavaScript object, if JSON is used.
+      if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
+        data = jQuery.parseJSON( data );
+
+      // If the type is "script", eval it in global context
+      } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
+        jQuery.globalEval( data );
+      }
+    }
+
+    return data;
+  },
+
+  // Serialize an array of form elements or a set of
+  // key/values into a query string
+  param: function( a, traditional ) {
+    var s = [];
+
+    // Set traditional to true for jQuery <= 1.3.2 behavior.
+    if ( traditional === undefined ) {
+      traditional = jQuery.ajaxSettings.traditional;
+    }
+
+    // If an array was passed in, assume that it is an array of form elements.
+    if ( jQuery.isArray(a) || a.jquery ) {
+      // Serialize the form elements
+      jQuery.each( a, function() {
+        add( this.name, this.value );
+      });
+
+    } else {
+      // If traditional, encode the "old" way (the way 1.3.2 or older
+      // did it), otherwise encode params recursively.
+      for ( var prefix in a ) {
+        buildParams( prefix, a[prefix] );
+      }
+    }
+
+    // Return the resulting serialization
+    return s.join("&").replace(r20, "+");
+
+    function buildParams( prefix, obj ) {
+      if ( jQuery.isArray(obj) ) {
+        // Serialize array item.
+        jQuery.each( obj, function( i, v ) {
+          if ( traditional || /\[\]$/.test( prefix ) ) {
+            // Treat each array item as a scalar.
+            add( prefix, v );
+          } else {
+            // If array item is non-scalar (array or object), encode its
+            // numeric index to resolve deserialization ambiguity issues.
+            // Note that rack (as of 1.0.0) can't currently deserialize
+            // nested arrays properly, and attempting to do so may cause
+            // a server error. Possible fixes are to modify rack's
+            // deserialization algorithm or to provide an option or flag
+            // to force array serialization to be shallow.
+            buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
+          }
+        });
+
+      } else if ( !traditional && obj != null && typeof obj === "object" ) {
+        // Serialize object item.
+        jQuery.each( obj, function( k, v ) {
+          buildParams( prefix + "[" + k + "]", v );
+        });
+
+      } else {
+        // Serialize scalar item.
+        add( prefix, obj );
+      }
+    }
+
+    function add( key, value ) {
+      // If value is a function, invoke it and return its value
+      value = jQuery.isFunction(value) ? value() : value;
+      s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
+    }
+  }
+});
+var elemdisplay = {},
+  rfxtypes = /toggle|show|hide/,
+  rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
+  timerId,
+  fxAttrs = [
+    // height animations
+    [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+    // width animations
+    [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+    // opacity animations
+    [ "opacity" ]
+  ];
+
+jQuery.fn.extend({
+  show: function( speed, callback ) {
+    if ( speed || speed === 0) {
+      return this.animate( genFx("show", 3), speed, callback);
+
+    } else {
+      for ( var i = 0, l = this.length; i < l; i++ ) {
+        var old = jQuery.data(this[i], "olddisplay");
+
+        this[i].style.display = old || "";
+
+        if ( jQuery.css(this[i], "display") === "none" ) {
+          var nodeName = this[i].nodeName, display;
+
+          if ( elemdisplay[ nodeName ] ) {
+            display = elemdisplay[ nodeName ];
+
+          } else {
+            var elem = jQuery("<" + nodeName + " />").appendTo("body");
+
+            display = elem.css("display");
+
+            if ( display === "none" ) {
+              display = "block";
+            }
+
+            elem.remove();
+
+            elemdisplay[ nodeName ] = display;
+          }
+
+          jQuery.data(this[i], "olddisplay", display);
+        }
+      }
+
+      // Set the display of the elements in a second loop
+      // to avoid the constant reflow
+      for ( var j = 0, k = this.length; j < k; j++ ) {
+        this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
+      }
+
+      return this;
+    }
+  },
+
+  hide: function( speed, callback ) {
+    if ( speed || speed === 0 ) {
+      return this.animate( genFx("hide", 3), speed, callback);
+
+    } else {
+      for ( var i = 0, l = this.length; i < l; i++ ) {
+        var old = jQuery.data(this[i], "olddisplay");
+        if ( !old && old !== "none" ) {
+          jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+        }
+      }
+
+      // Set the display of the elements in a second loop
+      // to avoid the constant reflow
+      for ( var j = 0, k = this.length; j < k; j++ ) {
+        this[j].style.display = "none";
+      }
+
+      return this;
+    }
+  },
+
+  // Save the old toggle function
+  _toggle: jQuery.fn.toggle,
+
+  toggle: function( fn, fn2 ) {
+    var bool = typeof fn === "boolean";
+
+    if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+      this._toggle.apply( this, arguments );
+
+    } else if ( fn == null || bool ) {
+      this.each(function() {
+        var state = bool ? fn : jQuery(this).is(":hidden");
+        jQuery(this)[ state ? "show" : "hide" ]();
+      });
+
+    } else {
+      this.animate(genFx("toggle", 3), fn, fn2);
+    }
+
+    return this;
+  },
+
+  fadeTo: function( speed, to, callback ) {
+    return this.filter(":hidden").css("opacity", 0).show().end()
+          .animate({opacity: to}, speed, callback);
+  },
+
+  animate: function( prop, speed, easing, callback ) {
+    var optall = jQuery.speed(speed, easing, callback);
+
+    if ( jQuery.isEmptyObject( prop ) ) {
+      return this.each( optall.complete );
+    }
+
+    return this[ optall.queue === false ? "each" : "queue" ](function() {
+      var opt = jQuery.extend({}, optall), p,
+        hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
+        self = this;
+
+      for ( p in prop ) {
+        var name = p.replace(rdashAlpha, fcamelCase);
+
+        if ( p !== name ) {
+          prop[ name ] = prop[ p ];
+          delete prop[ p ];
+          p = name;
+        }
+
+        if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
+          return opt.complete.call(this);
+        }
+
+        if ( ( p === "height" || p === "width" ) && this.style ) {
+          // Store display property
+          opt.display = jQuery.css(this, "display");
+
+          // Make sure that nothing sneaks out
+          opt.overflow = this.style.overflow;
+        }
+
+        if ( jQuery.isArray( prop[p] ) ) {
+          // Create (if needed) and add to specialEasing
+          (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
+          prop[p] = prop[p][0];
+        }
+      }
+
+      if ( opt.overflow != null ) {
+        this.style.overflow = "hidden";
+      }
+
+      opt.curAnim = jQuery.extend({}, prop);
+
+      jQuery.each( prop, function( name, val ) {
+        var e = new jQuery.fx( self, opt, name );
+
+        if ( rfxtypes.test(val) ) {
+          e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+
+        } else {
+          var parts = rfxnum.exec(val),
+            start = e.cur(true) || 0;
+
+          if ( parts ) {
+            var end = parseFloat( parts[2] ),
+              unit = parts[3] || "px";
+
+            // We need to compute starting value
+            if ( unit !== "px" ) {
+              self.style[ name ] = (end || 1) + unit;
+              start = ((end || 1) / e.cur(true)) * start;
+              self.style[ name ] = start + unit;
+            }
+
+            // If a +=/-= token was provided, we're doing a relative animation
+            if ( parts[1] ) {
+              end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
+            }
+
+            e.custom( start, end, unit );
+
+          } else {
+            e.custom( start, val, "" );
+          }
+        }
+      });
+
+      // For JS strict compliance
+      return true;
+    });
+  },
+
+  stop: function( clearQueue, gotoEnd ) {
+    var timers = jQuery.timers;
+
+    if ( clearQueue ) {
+      this.queue([]);
+    }
+
+    this.each(function() {
+      // go in reverse order so anything added to the queue during the loop is ignored
+      for ( var i = timers.length - 1; i >= 0; i-- ) {
+        if ( timers[i].elem === this ) {
+          if (gotoEnd) {
+            // force the next step to be the last
+            timers[i](true);
+          }
+
+          timers.splice(i, 1);
+        }
+      }
+    });
+
+    // start the next in the queue if the last step wasn't forced
+    if ( !gotoEnd ) {
+      this.dequeue();
+    }
+
+    return this;
+  }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+  slideDown: genFx("show", 1),
+  slideUp: genFx("hide", 1),
+  slideToggle: genFx("toggle", 1),
+  fadeIn: { opacity: "show" },
+  fadeOut: { opacity: "hide" }
+}, function( name, props ) {
+  jQuery.fn[ name ] = function( speed, callback ) {
+    return this.animate( props, speed, callback );
+  };
+});
+
+jQuery.extend({
+  speed: function( speed, easing, fn ) {
+    var opt = speed && typeof speed === "object" ? speed : {
+      complete: fn || !fn && easing ||
+        jQuery.isFunction( speed ) && speed,
+      duration: speed,
+      easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+    };
+
+    opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+      jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+    // Queueing
+    opt.old = opt.complete;
+    opt.complete = function() {
+      if ( opt.queue !== false ) {
+        jQuery(this).dequeue();
+      }
+      if ( jQuery.isFunction( opt.old ) ) {
+        opt.old.call( this );
+      }
+    };
+
+    return opt;
+  },
+
+  easing: {
+    linear: function( p, n, firstNum, diff ) {
+      return firstNum + diff * p;
+    },
+    swing: function( p, n, firstNum, diff ) {
+      return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+    }
+  },
+
+  timers: [],
+
+  fx: function( elem, options, prop ) {
+    this.options = options;
+    this.elem = elem;
+    this.prop = prop;
+
+    if ( !options.orig ) {
+      options.orig = {};
+    }
+  }
+
+});
+
+jQuery.fx.prototype = {
+  // Simple function for setting a style value
+  update: function() {
+    if ( this.options.step ) {
+      this.options.step.call( this.elem, this.now, this );
+    }
+
+    (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+    // Set display property to block for height/width animations
+    if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
+      this.elem.style.display = "block";
+    }
+  },
+
+  // Get the current size
+  cur: function( force ) {
+    if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+      return this.elem[ this.prop ];
+    }
+
+    var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+    return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+  },
+
+  // Start an animation from one number to another
+  custom: function( from, to, unit ) {
+    this.startTime = now();
+    this.start = from;
+    this.end = to;
+    this.unit = unit || this.unit || "px";
+    this.now = this.start;
+    this.pos = this.state = 0;
+
+    var self = this;
+    function t( gotoEnd ) {
+      return self.step(gotoEnd);
+    }
+
+    t.elem = this.elem;
+
+    if ( t() && jQuery.timers.push(t) && !timerId ) {
+      timerId = setInterval(jQuery.fx.tick, 13);
+    }
+  },
+
+  // Simple 'show' function
+  show: function() {
+    // Remember where we started, so that we can go back to it later
+    this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+    this.options.show = true;
+
+    // Begin the animation
+    // Make sure that we start at a small width/height to avoid any
+    // flash of content
+    this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+    // Start by showing the element
+    jQuery( this.elem ).show();
+  },
+
+  // Simple 'hide' function
+  hide: function() {
+    // Remember where we started, so that we can go back to it later
+    this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+    this.options.hide = true;
+
+    // Begin the animation
+    this.custom(this.cur(), 0);
+  },
+
+  // Each step of an animation
+  step: function( gotoEnd ) {
+    var t = now(), done = true;
+
+    if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+      this.now = this.end;
+      this.pos = this.state = 1;
+      this.update();
+
+      this.options.curAnim[ this.prop ] = true;
+
+      for ( var i in this.options.curAnim ) {
+        if ( this.options.curAnim[i] !== true ) {
+          done = false;
+        }
+      }
+
+      if ( done ) {
+        if ( this.options.display != null ) {
+          // Reset the overflow
+          this.elem.style.overflow = this.options.overflow;
+
+          // Reset the display
+          var old = jQuery.data(this.elem, "olddisplay");
+          this.elem.style.display = old ? old : this.options.display;
+
+          if ( jQuery.css(this.elem, "display") === "none" ) {
+            this.elem.style.display = "block";
+          }
+        }
+
+        // Hide the element if the "hide" operation was done
+        if ( this.options.hide ) {
+          jQuery(this.elem).hide();
+        }
+
+        // Reset the properties, if the item has been hidden or shown
+        if ( this.options.hide || this.options.show ) {
+          for ( var p in this.options.curAnim ) {
+            jQuery.style(this.elem, p, this.options.orig[p]);
+          }
+        }
+
+        // Execute the complete function
+        this.options.complete.call( this.elem );
+      }
+
+      return false;
+
+    } else {
+      var n = t - this.startTime;
+      this.state = n / this.options.duration;
+
+      // Perform the easing function, defaults to swing
+      var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
+      var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
+      this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
+      this.now = this.start + ((this.end - this.start) * this.pos);
+
+      // Perform the next step of the animation
+      this.update();
+    }
+
+    return true;
+  }
+};
+
+jQuery.extend( jQuery.fx, {
+  tick: function() {
+    var timers = jQuery.timers;
+
+    for ( var i = 0; i < timers.length; i++ ) {
+      if ( !timers[i]() ) {
+        timers.splice(i--, 1);
+      }
+    }
+
+    if ( !timers.length ) {
+      jQuery.fx.stop();
+    }
+  },
+
+  stop: function() {
+    clearInterval( timerId );
+    timerId = null;
+  },
+
+  speeds: {
+    slow: 600,
+     fast: 200,
+     // Default speed
+     _default: 400
+  },
+
+  step: {
+    opacity: function( fx ) {
+      jQuery.style(fx.elem, "opacity", fx.now);
+    },
+
+    _default: function( fx ) {
+      if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+        fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+      } else {
+        fx.elem[ fx.prop ] = fx.now;
+      }
+    }
+  }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+  jQuery.expr.filters.animated = function( elem ) {
+    return jQuery.grep(jQuery.timers, function( fn ) {
+      return elem === fn.elem;
+    }).length;
+  };
+}
+
+function genFx( type, num ) {
+  var obj = {};
+
+  jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+    obj[ this ] = type;
+  });
+
+  return obj;
+}
+if ( "getBoundingClientRect" in document.documentElement ) {
+  jQuery.fn.offset = function( options ) {
+    var elem = this[0];
+
+    if ( options ) {
+      return this.each(function( i ) {
+        jQuery.offset.setOffset( this, options, i );
+      });
+    }
+
+    if ( !elem || !elem.ownerDocument ) {
+      return null;
+    }
+
+    if ( elem === elem.ownerDocument.body ) {
+      return jQuery.offset.bodyOffset( elem );
+    }
+
+    var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
+      clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+      top  = box.top  + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
+      left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+
+    return { top: top, left: left };
+  };
+
+} else {
+  jQuery.fn.offset = function( options ) {
+    var elem = this[0];
+
+    if ( options ) {
+      return this.each(function( i ) {
+        jQuery.offset.setOffset( this, options, i );
+      });
+    }
+
+    if ( !elem || !elem.ownerDocument ) {
+      return null;
+    }
+
+    if ( elem === elem.ownerDocument.body ) {
+      return jQuery.offset.bodyOffset( elem );
+    }
+
+    jQuery.offset.initialize();
+
+    var offsetParent = elem.offsetParent, prevOffsetParent = elem,
+      doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+      body = doc.body, defaultView = doc.defaultView,
+      prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+      top = elem.offsetTop, left = elem.offsetLeft;
+
+    while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+      if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+        break;
+      }
+
+      computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+      top  -= elem.scrollTop;
+      left -= elem.scrollLeft;
+
+      if ( elem === offsetParent ) {
+        top  += elem.offsetTop;
+        left += elem.offsetLeft;
+
+        if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
+          top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+          left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+        }
+
+        prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+      }
+
+      if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+        top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+        left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+      }
+
+      prevComputedStyle = computedStyle;
+    }
+
+    if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+      top  += body.offsetTop;
+      left += body.offsetLeft;
+    }
+
+    if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+      top  += Math.max( docElem.scrollTop, body.scrollTop );
+      left += Math.max( docElem.scrollLeft, body.scrollLeft );
+    }
+
+    return { top: top, left: left };
+  };
+}
+
+jQuery.offset = {
+  initialize: function() {
+    var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
+      html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+    jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+    container.innerHTML = html;
+    body.insertBefore( container, body.firstChild );
+    innerDiv = container.firstChild;
+    checkDiv = innerDiv.firstChild;
+    td = innerDiv.nextSibling.firstChild.firstChild;
+
+    this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+    this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+    checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
+    // safari subtracts parent border width here which is 5px
+    this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+    checkDiv.style.position = checkDiv.style.top = "";
+
+    innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
+    this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+    this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+    body.removeChild( container );
+    body = container = innerDiv = checkDiv = table = td = null;
+    jQuery.offset.initialize = jQuery.noop;
+  },
+
+  bodyOffset: function( body ) {
+    var top = body.offsetTop, left = body.offsetLeft;
+
+    jQuery.offset.initialize();
+
+    if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+      top  += parseFloat( jQuery.curCSS(body, "marginTop",  true) ) || 0;
+      left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
+    }
+
+    return { top: top, left: left };
+  },
+
+  setOffset: function( elem, options, i ) {
+    // set position first, in-case top/left are set even on static elem
+    if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
+      elem.style.position = "relative";
+    }
+    var curElem   = jQuery( elem ),
+      curOffset = curElem.offset(),
+      curTop    = parseInt( jQuery.curCSS( elem, "top",  true ), 10 ) || 0,
+      curLeft   = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
+
+    if ( jQuery.isFunction( options ) ) {
+      options = options.call( elem, i, curOffset );
+    }
+
+    var props = {
+      top:  (options.top  - curOffset.top)  + curTop,
+      left: (options.left - curOffset.left) + curLeft
+    };
+
+    if ( "using" in options ) {
+      options.using.call( elem, props );
+    } else {
+      curElem.css( props );
+    }
+  }
+};
+
+
+jQuery.fn.extend({
+  position: function() {
+    if ( !this[0] ) {
+      return null;
+    }
+
+    var elem = this[0],
+
+    // Get *real* offsetParent
+    offsetParent = this.offsetParent(),
+
+    // Get correct offsets
+    offset       = this.offset(),
+    parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+    // Subtract element margins
+    // note: when an element has margin: auto the offsetLeft and marginLeft
+    // are the same in Safari causing offset.left to incorrectly be 0
+    offset.top  -= parseFloat( jQuery.curCSS(elem, "marginTop",  true) ) || 0;
+    offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
+
+    // Add offsetParent borders
+    parentOffset.top  += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth",  true) ) || 0;
+    parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
+
+    // Subtract the two offsets
+    return {
+      top:  offset.top  - parentOffset.top,
+      left: offset.left - parentOffset.left
+    };
+  },
+
+  offsetParent: function() {
+    return this.map(function() {
+      var offsetParent = this.offsetParent || document.body;
+      while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+        offsetParent = offsetParent.offsetParent;
+      }
+      return offsetParent;
+    });
+  }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+  var method = "scroll" + name;
+
+  jQuery.fn[ method ] = function(val) {
+    var elem = this[0], win;
+
+    if ( !elem ) {
+      return null;
+    }
+
+    if ( val !== undefined ) {
+      // Set the scroll offset
+      return this.each(function() {
+        win = getWindow( this );
+
+        if ( win ) {
+          win.scrollTo(
+            !i ? val : jQuery(win).scrollLeft(),
+             i ? val : jQuery(win).scrollTop()
+          );
+
+        } else {
+          this[ method ] = val;
+        }
+      });
+    } else {
+      win = getWindow( elem );
+
+      // Return the scroll offset
+      return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+        jQuery.support.boxModel && win.document.documentElement[ method ] ||
+          win.document.body[ method ] :
+        elem[ method ];
+    }
+  };
+});
+
+function getWindow( elem ) {
+  return ("scrollTo" in elem && elem.document) ?
+    elem :
+    elem.nodeType === 9 ?
+      elem.defaultView || elem.parentWindow :
+      false;
+}
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+  var type = name.toLowerCase();
+
+  // innerHeight and innerWidth
+  jQuery.fn["inner" + name] = function() {
+    return this[0] ?
+      jQuery.css( this[0], type, false, "padding" ) :
+      null;
+  };
+
+  // outerHeight and outerWidth
+  jQuery.fn["outer" + name] = function( margin ) {
+    return this[0] ?
+      jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
+      null;
+  };
+
+  jQuery.fn[ type ] = function( size ) {
+    // Get window width or height
+    var elem = this[0];
+    if ( !elem ) {
+      return size == null ? null : this;
+    }
+
+    if ( jQuery.isFunction( size ) ) {
+      return this.each(function( i ) {
+        var self = jQuery( this );
+        self[ type ]( size.call( this, i, self[ type ]() ) );
+      });
+    }
+
+    return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
+      // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+      elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
+      elem.document.body[ "client" + name ] :
+
+      // Get document width or height
+      (elem.nodeType === 9) ? // is it a document
+        // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+        Math.max(
+          elem.documentElement["client" + name],
+          elem.body["scroll" + name], elem.documentElement["scroll" + name],
+          elem.body["offset" + name], elem.documentElement["offset" + name]
+        ) :
+
+        // Get or set width or height on the element
+        size === undefined ?
+          // Get width or height on the element
+          jQuery.css( elem, type ) :
+
+          // Set the width or height on the element (default to pixels if value is unitless)
+          this.css( type, typeof size === "string" ? size : size + "px" );
+  };
+
+});
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+})(window);
diff --git a/javascript/sammy/min/plugins/sammy.cache-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.cache-0.6.2.min.js
new file mode 100644
index 00000000..9e47f8c7
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.cache-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.cache.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:38 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.MemoryCacheProxy=function(b){this._cache=b||{}};a.extend(Sammy.MemoryCacheProxy.prototype,{exists:function(b){return(typeof this._cache[b]!="undefined")},set:function(b,c){return this._cache[b]=c},get:function(b){return this._cache[b]},clear:function(b){delete this._cache[b]}});Sammy.DataCacheProxy=function(c,b){c=c||{};this.$element=b;a.each(c,function(d,e){b.data("cache."+d,e)})};a.extend(Sammy.DataCacheProxy.prototype,{exists:function(b){return(typeof this.$element.data("cache."+b)!="undefined")},set:function(b,c){return this.$element.data("cache."+b,c)},get:function(b){return this.$element.data("cache."+b)},clear:function(b){this.$element.removeData("cache."+b)}});Sammy.Cache=function(c,b){c.log("**WARNING:** This version of Sammy.Cache has been deprecated in favor of using the version in Sammy.Storage and will be removed in 1.0");if(b=="data"){this.cache_proxy=new Sammy.DataCacheProxy({},this.$element())}else{this.cache_proxy=new Sammy.MemoryCacheProxy({})}c.cache_partials=true;a.extend(c,{cache:function(d,e){if(typeof e=="undefined"){return this.cache_proxy.get(d)}else{if(a.isFunction(e)&&!this.cache_proxy.exists(d)){return this.cache_proxy.set(d,e.apply(this))}else{return this.cache_proxy.set(d,e)}}},clearCache:function(d){return this.cache_proxy.clear(d)}});c.helpers({cache:function(d,e){return this.app.cache(d,e)}})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.cache-latest.min.js b/javascript/sammy/min/plugins/sammy.cache-latest.min.js
new file mode 100644
index 00000000..9e47f8c7
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.cache-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.cache.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:38 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.MemoryCacheProxy=function(b){this._cache=b||{}};a.extend(Sammy.MemoryCacheProxy.prototype,{exists:function(b){return(typeof this._cache[b]!="undefined")},set:function(b,c){return this._cache[b]=c},get:function(b){return this._cache[b]},clear:function(b){delete this._cache[b]}});Sammy.DataCacheProxy=function(c,b){c=c||{};this.$element=b;a.each(c,function(d,e){b.data("cache."+d,e)})};a.extend(Sammy.DataCacheProxy.prototype,{exists:function(b){return(typeof this.$element.data("cache."+b)!="undefined")},set:function(b,c){return this.$element.data("cache."+b,c)},get:function(b){return this.$element.data("cache."+b)},clear:function(b){this.$element.removeData("cache."+b)}});Sammy.Cache=function(c,b){c.log("**WARNING:** This version of Sammy.Cache has been deprecated in favor of using the version in Sammy.Storage and will be removed in 1.0");if(b=="data"){this.cache_proxy=new Sammy.DataCacheProxy({},this.$element())}else{this.cache_proxy=new Sammy.MemoryCacheProxy({})}c.cache_partials=true;a.extend(c,{cache:function(d,e){if(typeof e=="undefined"){return this.cache_proxy.get(d)}else{if(a.isFunction(e)&&!this.cache_proxy.exists(d)){return this.cache_proxy.set(d,e.apply(this))}else{return this.cache_proxy.set(d,e)}}},clearCache:function(d){return this.cache_proxy.clear(d)}});c.helpers({cache:function(d,e){return this.app.cache(d,e)}})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.data_location_proxy-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.data_location_proxy-0.6.2.min.js
new file mode 100644
index 00000000..59c42142
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.data_location_proxy-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.data_location_proxy.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:39 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.DataLocationProxy=function(d,c,b){this.app=d;this.data_name=c||"sammy-location";this.href_attribute=b};Sammy.DataLocationProxy.prototype={bind:function(){var b=this;this.app.$element().bind("setData",function(f,c,d){if(c==b.data_name){b.app.$element().each(function(){a.data(this,b.data_name,d)});b.app.trigger("location-changed")}});if(this.href_attribute){this.app.$element().delegate("["+this.href_attribute+"]","click",function(c){c.preventDefault();b.setLocation(a(this).attr(b.href_attribute))})}},unbind:function(){if(this.href_attribute){this.app.$element().undelegate("["+this.href_attribute+"]","click")}this.app.$element().unbind("setData")},getLocation:function(){return this.app.$element().data(this.data_name)||""},setLocation:function(b){return this.app.$element().data(this.data_name,b)}}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.data_location_proxy-latest.min.js b/javascript/sammy/min/plugins/sammy.data_location_proxy-latest.min.js
new file mode 100644
index 00000000..59c42142
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.data_location_proxy-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.data_location_proxy.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:39 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.DataLocationProxy=function(d,c,b){this.app=d;this.data_name=c||"sammy-location";this.href_attribute=b};Sammy.DataLocationProxy.prototype={bind:function(){var b=this;this.app.$element().bind("setData",function(f,c,d){if(c==b.data_name){b.app.$element().each(function(){a.data(this,b.data_name,d)});b.app.trigger("location-changed")}});if(this.href_attribute){this.app.$element().delegate("["+this.href_attribute+"]","click",function(c){c.preventDefault();b.setLocation(a(this).attr(b.href_attribute))})}},unbind:function(){if(this.href_attribute){this.app.$element().undelegate("["+this.href_attribute+"]","click")}this.app.$element().unbind("setData")},getLocation:function(){return this.app.$element().data(this.data_name)||""},setLocation:function(b){return this.app.$element().data(this.data_name,b)}}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.ejs-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.ejs-0.6.2.min.js
new file mode 100644
index 00000000..a9eb0bdc
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.ejs-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.ejs.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:40 -0700 2010
+(function($){var rsplit=function(string,regex){var result=regex.exec(string),retArr=new Array(),first_idx,last_idx,first_bit;while(result!=null){first_idx=result.index;last_idx=regex.lastIndex;if((first_idx)!=0){first_bit=string.substring(0,first_idx);retArr.push(string.substring(0,first_idx));string=string.slice(first_idx)}retArr.push(result[0]);string=string.slice(result[0].length);result=regex.exec(string)}if(!string==""){retArr.push(string)}return retArr};var chop=function(string){return string.substr(0,string.length-1)};var extend=function(d,s){for(var n in s){if(s.hasOwnProperty(n)){d[n]=s[n]}}};EJS=function(options){options=(typeof(options)=="string")?{view:options}:options;this.set_options(options);if(options.precompiled){this.template={};this.template.process=options.precompiled;EJS.update(this.name,this);return}if(options.element){if(typeof(options.element)=="string"){var name=options.element;options.element=document.getElementById(options.element);if(options.element==null){throw name+"does not exist!"}}if(options.element.value){this.text=options.element.value}else{this.text=options.element.innerHTML}this.name=options.element.id;this.type="["}else{if(options.url){options.url=EJS.endExt(options.url,this.extMatch);this.name=this.name?this.name:options.url;var url=options.url;var template=EJS.get(this.name,this.cache);if(template){return template}if(template==EJS.INVALID_PATH){return null}try{this.text=EJS.request(url+(this.cache?"":"?"+Math.random()))}catch(e){}if(this.text==null){throw ({type:"EJS",message:"There is no template at "+url})}}}var template=new EJS.Compiler(this.text,this.type);template.compile(options,this.name);EJS.update(this.name,this);this.template=template};EJS.prototype={render:function(object,extra_helpers){object=object||{};this._extra_helpers=extra_helpers;var v=new EJS.Helpers(object,extra_helpers||{});return this.template.process.call(object,object,v)},update:function(element,options){if(typeof(element)=="string"){element=document.getElementById(element)}if(options==null){_template=this;return function(object){EJS.prototype.update.call(_template,element,object)}}if(typeof(options)=="string"){params={};params.url=options;_template=this;params.onComplete=function(request){var object=eval(request.responseText);EJS.prototype.update.call(_template,element,object)};EJS.ajax_request(params)}else{element.innerHTML=this.render(options)}},out:function(){return this.template.out},set_options:function(options){this.type=options.type||EJS.type;this.cache=(options.cache!=null)?options.cache:EJS.cache;this.text=options.text||null;this.name=options.name||null;this.ext=options.ext||EJS.ext;this.extMatch=new RegExp(this.ext.replace(/\./,"."))}};EJS.endExt=function(path,match){if(!path){return null}match.lastIndex=0;return path+(match.test(path)?"":this.ext)};EJS.Scanner=function(source,left,right){extend(this,{left_delimiter:left+"%",right_delimiter:"%"+right,double_left:left+"%%",double_right:"%%"+right,left_equal:left+"%=",left_comment:left+"%#"});this.SplitRegexp=(left=="[")?/(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/:new RegExp("("+this.double_left+")|(%%"+this.double_right+")|("+this.left_equal+")|("+this.left_comment+")|("+this.left_delimiter+")|("+this.right_delimiter+"\n)|("+this.right_delimiter+")|(\n)");this.source=source;this.stag=null;this.lines=0};EJS.Scanner.to_text=function(input){if(input==null||input===undefined){return""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString()}return""};EJS.Scanner.prototype={scan:function(block){scanline=this.scanline;regex=this.SplitRegexp;if(!this.source==""){var source_split=rsplit(this.source,/\n/);for(var i=0;i<source_split.length;i++){var item=source_split[i];this.scanline(item,regex,block)}}},scanline:function(line,regex,block){this.lines++;var line_split=rsplit(line,regex);for(var i=0;i<line_split.length;i++){var token=line_split[i];if(token!=null){try{block(token,this)}catch(e){throw {type:"EJS.Scanner",line:this.lines}}}}}};EJS.Buffer=function(pre_cmd,post_cmd){this.line=new Array();this.script="";this.pre_cmd=pre_cmd;this.post_cmd=post_cmd;for(var i=0;i<this.pre_cmd.length;i++){this.push(pre_cmd[i])}};EJS.Buffer.prototype={push:function(cmd){this.line.push(cmd)},cr:function(){this.script=this.script+this.line.join("; ");this.line=new Array();this.script=this.script+"\n"},close:function(){if(this.line.length>0){for(var i=0;i<this.post_cmd.length;i++){this.push(pre_cmd[i])}this.script=this.script+this.line.join("; ");line=null}}};EJS.Compiler=function(source,left){this.pre_cmd=["var ___ViewO = [];"];this.post_cmd=new Array();this.source=" ";if(source!=null){if(typeof(source)=="string"){source=source.replace(/\r\n/g,"\n");source=source.replace(/\r/g,"\n");this.source=source}else{if(source.innerHTML){this.source=source.innerHTML}}if(typeof this.source!="string"){this.source=""}}left=left||"<";var right=">";switch(left){case"[":right="]";break;case"<":break;default:throw left+" is not a supported deliminator";break}this.scanner=new EJS.Scanner(this.source,left,right);this.out=""};EJS.Compiler.prototype={compile:function(options,name){options=options||{};this.out="";var put_cmd="___ViewO.push(";var insert_cmd=put_cmd;var buff=new EJS.Buffer(this.pre_cmd,this.post_cmd);var content="";var clean=function(content){content=content.replace(/\\/g,"\\\\");content=content.replace(/\n/g,"\\n");content=content.replace(/"/g,'\\"');return content};this.scanner.scan(function(token,scanner){if(scanner.stag==null){switch(token){case"\n":content=content+"\n";buff.push(put_cmd+'"'+clean(content)+'");');buff.cr();content="";break;case scanner.left_delimiter:case scanner.left_equal:case scanner.left_comment:scanner.stag=token;if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}content="";break;case scanner.double_left:content=content+scanner.left_delimiter;break;default:content=content+token;break}}else{switch(token){case scanner.right_delimiter:switch(scanner.stag){case scanner.left_delimiter:if(content[content.length-1]=="\n"){content=chop(content);buff.push(content);buff.cr()}else{buff.push(content)}break;case scanner.left_equal:buff.push(insert_cmd+"(EJS.Scanner.to_text("+content+")))");break}scanner.stag=null;content="";break;case scanner.double_right:content=content+scanner.right_delimiter;break;default:content=content+token;break}}});if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}buff.close();this.out=buff.script+";";var to_be_evaled="/*"+name+"*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {"+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";try{eval(to_be_evaled)}catch(e){if(typeof JSLINT!="undefined"){JSLINT(this.out);for(var i=0;i<JSLINT.errors.length;i++){var error=JSLINT.errors[i];if(error.reason!="Unnecessary semicolon."){error.line++;var e=new Error();e.lineNumber=error.line;e.message=error.reason;if(options.view){e.fileName=options.view}throw e}}}else{throw e}}}};EJS.config=function(options){EJS.cache=(options.cache!=null)?options.cache:EJS.cache;EJS.type=(options.type!=null)?options.type:EJS.type;EJS.ext=(options.ext!=null)?options.ext:EJS.ext;var templates_directory=EJS.templates_directory||{};EJS.templates_directory=templates_directory;EJS.get=function(path,cache){if(cache==false){return null}if(templates_directory[path]){return templates_directory[path]}return null};EJS.update=function(path,template){if(path==null){return}templates_directory[path]=template};EJS.INVALID_PATH=-1};EJS.config({cache:true,type:"<",ext:".ejs"});EJS.Helpers=function(data,extras){this._data=data;this._extras=extras;extend(this,extras)};EJS.Helpers.prototype={view:function(options,data,helpers){if(!helpers){helpers=this._extras}if(!data){data=this._data}return new EJS(options).render(data,helpers)},to_text:function(input,null_text){if(input==null||input===undefined){return null_text||""}if(input instanceof (Date)){return input.toDateString()}if(input.toString){return input.toString().replace(/\n/g,"<br />").replace(/''/g,"'")}return""}};EJS.newRequest=function(){var factories=[function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var i=0;i<factories.length;i++){try{var request=factories[i]();if(request!=null){return request}}catch(e){continue}}};EJS.request=function(path){var request=new EJS.newRequest();request.open("GET",path,false);try{request.send(null)}catch(e){return null}if(request.status==404||request.status==2||(request.status==0&&request.responseText=="")){return null}return request.responseText};EJS.ajax_request=function(params){params.method=(params.method?params.method:"GET");var request=new EJS.newRequest();request.onreadystatechange=function(){if(request.readyState==4){if(request.status==200){params.onComplete(request)}else{params.onComplete(request)}}};request.open(params.method,params.url);request.send(null)};EJS.Helpers.prototype.date_tag=function(name,value,html_options){if(!(value instanceof (Date))){value=new Date()}var month_names=["January","February","March","April","May","June","July","August","September","October","November","December"];var years=[],months=[],days=[];var year=value.getFullYear();var month=value.getMonth();var day=value.getDate();for(var y=year-15;y<year+15;y++){years.push({value:y,text:y})}for(var m=0;m<12;m++){months.push({value:(m),text:month_names[m]})}for(var d=0;d<31;d++){days.push({value:(d+1),text:(d+1)})}var year_select=this.select_tag(name+"[year]",year,years,{id:name+"[year]"});var month_select=this.select_tag(name+"[month]",month,months,{id:name+"[month]"});var day_select=this.select_tag(name+"[day]",day,days,{id:name+"[day]"});return year_select+month_select+day_select};EJS.Helpers.prototype.form_tag=function(action,html_options){html_options=html_options||{};html_options.action=action;if(html_options.multipart==true){html_options.method="post";html_options.enctype="multipart/form-data"}return this.start_tag_for("form",html_options)};EJS.Helpers.prototype.form_tag_end=function(){return this.tag_end("form")};EJS.Helpers.prototype.hidden_field_tag=function(name,value,html_options){return this.input_field_tag(name,value,"hidden",html_options)};EJS.Helpers.prototype.input_field_tag=function(name,value,inputType,html_options){html_options=html_options||{};html_options.id=html_options.id||name;html_options.value=value||"";html_options.type=inputType||"text";html_options.name=name;return this.single_tag_for("input",html_options)};EJS.Helpers.prototype.is_current_page=function(url){return(window.location.href==url||window.location.pathname==url?true:false)};EJS.Helpers.prototype.link_to=function(name,url,html_options){if(!name){var name="null"}if(!html_options){var html_options={}}if(html_options.confirm){html_options.onclick=' var ret_confirm = confirm("'+html_options.confirm+'"); if(!ret_confirm){ return false;} ';html_options.confirm=null}html_options.href=url;return this.start_tag_for("a",html_options)+name+this.tag_end("a")};EJS.Helpers.prototype.submit_link_to=function(name,url,html_options){if(!name){var name="null"}if(!html_options){var html_options={}}html_options.onclick=html_options.onclick||"";if(html_options.confirm){html_options.onclick=' var ret_confirm = confirm("'+html_options.confirm+'"); if(!ret_confirm){ return false;} ';html_options.confirm=null}html_options.value=name;html_options.type="submit";html_options.onclick=html_options.onclick+(url?this.url_for(url):"")+"return false;";return this.start_tag_for("input",html_options)};EJS.Helpers.prototype.link_to_if=function(condition,name,url,html_options,post,block){return this.link_to_unless((condition==false),name,url,html_options,post,block)};EJS.Helpers.prototype.link_to_unless=function(condition,name,url,html_options,block){html_options=html_options||{};if(condition){if(block&&typeof(block)=="function"){return block(name,url,html_options,block)}else{return name}}else{return this.link_to(name,url,html_options)}};EJS.Helpers.prototype.link_to_unless_current=function(name,url,html_options,block){html_options=html_options||{};return this.link_to_unless(this.is_current_page(url),name,url,html_options,block)};EJS.Helpers.prototype.password_field_tag=function(name,value,html_options){return this.input_field_tag(name,value,"password",html_options)};EJS.Helpers.prototype.select_tag=function(name,value,choices,html_options){html_options=html_options||{};html_options.id=html_options.id||name;html_options.value=value;html_options.name=name;var txt="";txt+=this.start_tag_for("select",html_options);for(var i=0;i<choices.length;i++){var choice=choices[i];var optionOptions={value:choice.value};if(choice.value==value){optionOptions.selected="selected"}txt+=this.start_tag_for("option",optionOptions)+choice.text+this.tag_end("option")}txt+=this.tag_end("select");return txt};EJS.Helpers.prototype.single_tag_for=function(tag,html_options){return this.tag(tag,html_options,"/>")};EJS.Helpers.prototype.start_tag_for=function(tag,html_options){return this.tag(tag,html_options)};EJS.Helpers.prototype.submit_tag=function(name,html_options){html_options=html_options||{};html_options.type=html_options.type||"submit";html_options.value=name||"Submit";return this.single_tag_for("input",html_options)};EJS.Helpers.prototype.tag=function(tag,html_options,end){if(!end){var end=">"}var txt=" ";for(var attr in html_options){if(html_options[attr]!=null){var value=html_options[attr].toString()}else{var value=""}if(attr=="Class"){attr="class"}if(value.indexOf("'")!=-1){txt+=attr+'="'+value+'" '}else{txt+=attr+"='"+value+"' "}}return"<"+tag+txt+end};EJS.Helpers.prototype.tag_end=function(tag){return"</"+tag+">"};EJS.Helpers.prototype.text_area_tag=function(name,value,html_options){html_options=html_options||{};html_options.id=html_options.id||name;html_options.name=html_options.name||name;value=value||"";if(html_options.size){html_options.cols=html_options.size.split("x")[0];html_options.rows=html_options.size.split("x")[1];delete html_options.size}html_options.cols=html_options.cols||50;html_options.rows=html_options.rows||4;return this.start_tag_for("textarea",html_options)+value+this.tag_end("textarea")};EJS.Helpers.prototype.text_tag=EJS.Helpers.prototype.text_area_tag;EJS.Helpers.prototype.text_field_tag=function(name,value,html_options){return this.input_field_tag(name,value,"text",html_options)};EJS.Helpers.prototype.url_for=function(url){return'window.location="'+url+'";'};EJS.Helpers.prototype.img_tag=function(image_location,alt,options){options=options||{};options.src=image_location;options.alt=alt;return this.single_tag_for("img",options)};Sammy=Sammy||{};Sammy.EJS=function(app,method_alias){var template=function(template,data,name){if(typeof name=="undefined"){name=template}return new EJS({text:template,name:name}).render(data)};if(!method_alias){method_alias="ejs"}app.helper(method_alias,template)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.ejs-latest.min.js b/javascript/sammy/min/plugins/sammy.ejs-latest.min.js
new file mode 100644
index 00000000..a9eb0bdc
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.ejs-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.ejs.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:40 -0700 2010
+(function($){var rsplit=function(string,regex){var result=regex.exec(string),retArr=new Array(),first_idx,last_idx,first_bit;while(result!=null){first_idx=result.index;last_idx=regex.lastIndex;if((first_idx)!=0){first_bit=string.substring(0,first_idx);retArr.push(string.substring(0,first_idx));string=string.slice(first_idx)}retArr.push(result[0]);string=string.slice(result[0].length);result=regex.exec(string)}if(!string==""){retArr.push(string)}return retArr};var chop=function(string){return string.substr(0,string.length-1)};var extend=function(d,s){for(var n in s){if(s.hasOwnProperty(n)){d[n]=s[n]}}};EJS=function(options){options=(typeof(options)=="string")?{view:options}:options;this.set_options(options);if(options.precompiled){this.template={};this.template.process=options.precompiled;EJS.update(this.name,this);return}if(options.element){if(typeof(options.element)=="string"){var name=options.element;options.element=document.getElementById(options.element);if(options.element==null){throw name+"does not exist!"}}if(options.element.value){this.text=options.element.value}else{this.text=options.element.innerHTML}this.name=options.element.id;this.type="["}else{if(options.url){options.url=EJS.endExt(options.url,this.extMatch);this.name=this.name?this.name:options.url;var url=options.url;var template=EJS.get(this.name,this.cache);if(template){return template}if(template==EJS.INVALID_PATH){return null}try{this.text=EJS.request(url+(this.cache?"":"?"+Math.random()))}catch(e){}if(this.text==null){throw ({type:"EJS",message:"There is no template at "+url})}}}var template=new EJS.Compiler(this.text,this.type);template.compile(options,this.name);EJS.update(this.name,this);this.template=template};EJS.prototype={render:function(object,extra_helpers){object=object||{};this._extra_helpers=extra_helpers;var v=new EJS.Helpers(object,extra_helpers||{});return this.template.process.call(object,object,v)},update:function(element,options){if(typeof(element)=="string"){element=document.getElementById(element)}if(options==null){_template=this;return function(object){EJS.prototype.update.call(_template,element,object)}}if(typeof(options)=="string"){params={};params.url=options;_template=this;params.onComplete=function(request){var object=eval(request.responseText);EJS.prototype.update.call(_template,element,object)};EJS.ajax_request(params)}else{element.innerHTML=this.render(options)}},out:function(){return this.template.out},set_options:function(options){this.type=options.type||EJS.type;this.cache=(options.cache!=null)?options.cache:EJS.cache;this.text=options.text||null;this.name=options.name||null;this.ext=options.ext||EJS.ext;this.extMatch=new RegExp(this.ext.replace(/\./,"."))}};EJS.endExt=function(path,match){if(!path){return null}match.lastIndex=0;return path+(match.test(path)?"":this.ext)};EJS.Scanner=function(source,left,right){extend(this,{left_delimiter:left+"%",right_delimiter:"%"+right,double_left:left+"%%",double_right:"%%"+right,left_equal:left+"%=",left_comment:left+"%#"});this.SplitRegexp=(left=="[")?/(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/:new RegExp("("+this.double_left+")|(%%"+this.double_right+")|("+this.left_equal+")|("+this.left_comment+")|("+this.left_delimiter+")|("+this.right_delimiter+"\n)|("+this.right_delimiter+")|(\n)");this.source=source;this.stag=null;this.lines=0};EJS.Scanner.to_text=function(input){if(input==null||input===undefined){return""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString()}return""};EJS.Scanner.prototype={scan:function(block){scanline=this.scanline;regex=this.SplitRegexp;if(!this.source==""){var source_split=rsplit(this.source,/\n/);for(var i=0;i<source_split.length;i++){var item=source_split[i];this.scanline(item,regex,block)}}},scanline:function(line,regex,block){this.lines++;var line_split=rsplit(line,regex);for(var i=0;i<line_split.length;i++){var token=line_split[i];if(token!=null){try{block(token,this)}catch(e){throw {type:"EJS.Scanner",line:this.lines}}}}}};EJS.Buffer=function(pre_cmd,post_cmd){this.line=new Array();this.script="";this.pre_cmd=pre_cmd;this.post_cmd=post_cmd;for(var i=0;i<this.pre_cmd.length;i++){this.push(pre_cmd[i])}};EJS.Buffer.prototype={push:function(cmd){this.line.push(cmd)},cr:function(){this.script=this.script+this.line.join("; ");this.line=new Array();this.script=this.script+"\n"},close:function(){if(this.line.length>0){for(var i=0;i<this.post_cmd.length;i++){this.push(pre_cmd[i])}this.script=this.script+this.line.join("; ");line=null}}};EJS.Compiler=function(source,left){this.pre_cmd=["var ___ViewO = [];"];this.post_cmd=new Array();this.source=" ";if(source!=null){if(typeof(source)=="string"){source=source.replace(/\r\n/g,"\n");source=source.replace(/\r/g,"\n");this.source=source}else{if(source.innerHTML){this.source=source.innerHTML}}if(typeof this.source!="string"){this.source=""}}left=left||"<";var right=">";switch(left){case"[":right="]";break;case"<":break;default:throw left+" is not a supported deliminator";break}this.scanner=new EJS.Scanner(this.source,left,right);this.out=""};EJS.Compiler.prototype={compile:function(options,name){options=options||{};this.out="";var put_cmd="___ViewO.push(";var insert_cmd=put_cmd;var buff=new EJS.Buffer(this.pre_cmd,this.post_cmd);var content="";var clean=function(content){content=content.replace(/\\/g,"\\\\");content=content.replace(/\n/g,"\\n");content=content.replace(/"/g,'\\"');return content};this.scanner.scan(function(token,scanner){if(scanner.stag==null){switch(token){case"\n":content=content+"\n";buff.push(put_cmd+'"'+clean(content)+'");');buff.cr();content="";break;case scanner.left_delimiter:case scanner.left_equal:case scanner.left_comment:scanner.stag=token;if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}content="";break;case scanner.double_left:content=content+scanner.left_delimiter;break;default:content=content+token;break}}else{switch(token){case scanner.right_delimiter:switch(scanner.stag){case scanner.left_delimiter:if(content[content.length-1]=="\n"){content=chop(content);buff.push(content);buff.cr()}else{buff.push(content)}break;case scanner.left_equal:buff.push(insert_cmd+"(EJS.Scanner.to_text("+content+")))");break}scanner.stag=null;content="";break;case scanner.double_right:content=content+scanner.right_delimiter;break;default:content=content+token;break}}});if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}buff.close();this.out=buff.script+";";var to_be_evaled="/*"+name+"*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {"+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";try{eval(to_be_evaled)}catch(e){if(typeof JSLINT!="undefined"){JSLINT(this.out);for(var i=0;i<JSLINT.errors.length;i++){var error=JSLINT.errors[i];if(error.reason!="Unnecessary semicolon."){error.line++;var e=new Error();e.lineNumber=error.line;e.message=error.reason;if(options.view){e.fileName=options.view}throw e}}}else{throw e}}}};EJS.config=function(options){EJS.cache=(options.cache!=null)?options.cache:EJS.cache;EJS.type=(options.type!=null)?options.type:EJS.type;EJS.ext=(options.ext!=null)?options.ext:EJS.ext;var templates_directory=EJS.templates_directory||{};EJS.templates_directory=templates_directory;EJS.get=function(path,cache){if(cache==false){return null}if(templates_directory[path]){return templates_directory[path]}return null};EJS.update=function(path,template){if(path==null){return}templates_directory[path]=template};EJS.INVALID_PATH=-1};EJS.config({cache:true,type:"<",ext:".ejs"});EJS.Helpers=function(data,extras){this._data=data;this._extras=extras;extend(this,extras)};EJS.Helpers.prototype={view:function(options,data,helpers){if(!helpers){helpers=this._extras}if(!data){data=this._data}return new EJS(options).render(data,helpers)},to_text:function(input,null_text){if(input==null||input===undefined){return null_text||""}if(input instanceof (Date)){return input.toDateString()}if(input.toString){return input.toString().replace(/\n/g,"<br />").replace(/''/g,"'")}return""}};EJS.newRequest=function(){var factories=[function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var i=0;i<factories.length;i++){try{var request=factories[i]();if(request!=null){return request}}catch(e){continue}}};EJS.request=function(path){var request=new EJS.newRequest();request.open("GET",path,false);try{request.send(null)}catch(e){return null}if(request.status==404||request.status==2||(request.status==0&&request.responseText=="")){return null}return request.responseText};EJS.ajax_request=function(params){params.method=(params.method?params.method:"GET");var request=new EJS.newRequest();request.onreadystatechange=function(){if(request.readyState==4){if(request.status==200){params.onComplete(request)}else{params.onComplete(request)}}};request.open(params.method,params.url);request.send(null)};EJS.Helpers.prototype.date_tag=function(name,value,html_options){if(!(value instanceof (Date))){value=new Date()}var month_names=["January","February","March","April","May","June","July","August","September","October","November","December"];var years=[],months=[],days=[];var year=value.getFullYear();var month=value.getMonth();var day=value.getDate();for(var y=year-15;y<year+15;y++){years.push({value:y,text:y})}for(var m=0;m<12;m++){months.push({value:(m),text:month_names[m]})}for(var d=0;d<31;d++){days.push({value:(d+1),text:(d+1)})}var year_select=this.select_tag(name+"[year]",year,years,{id:name+"[year]"});var month_select=this.select_tag(name+"[month]",month,months,{id:name+"[month]"});var day_select=this.select_tag(name+"[day]",day,days,{id:name+"[day]"});return year_select+month_select+day_select};EJS.Helpers.prototype.form_tag=function(action,html_options){html_options=html_options||{};html_options.action=action;if(html_options.multipart==true){html_options.method="post";html_options.enctype="multipart/form-data"}return this.start_tag_for("form",html_options)};EJS.Helpers.prototype.form_tag_end=function(){return this.tag_end("form")};EJS.Helpers.prototype.hidden_field_tag=function(name,value,html_options){return this.input_field_tag(name,value,"hidden",html_options)};EJS.Helpers.prototype.input_field_tag=function(name,value,inputType,html_options){html_options=html_options||{};html_options.id=html_options.id||name;html_options.value=value||"";html_options.type=inputType||"text";html_options.name=name;return this.single_tag_for("input",html_options)};EJS.Helpers.prototype.is_current_page=function(url){return(window.location.href==url||window.location.pathname==url?true:false)};EJS.Helpers.prototype.link_to=function(name,url,html_options){if(!name){var name="null"}if(!html_options){var html_options={}}if(html_options.confirm){html_options.onclick=' var ret_confirm = confirm("'+html_options.confirm+'"); if(!ret_confirm){ return false;} ';html_options.confirm=null}html_options.href=url;return this.start_tag_for("a",html_options)+name+this.tag_end("a")};EJS.Helpers.prototype.submit_link_to=function(name,url,html_options){if(!name){var name="null"}if(!html_options){var html_options={}}html_options.onclick=html_options.onclick||"";if(html_options.confirm){html_options.onclick=' var ret_confirm = confirm("'+html_options.confirm+'"); if(!ret_confirm){ return false;} ';html_options.confirm=null}html_options.value=name;html_options.type="submit";html_options.onclick=html_options.onclick+(url?this.url_for(url):"")+"return false;";return this.start_tag_for("input",html_options)};EJS.Helpers.prototype.link_to_if=function(condition,name,url,html_options,post,block){return this.link_to_unless((condition==false),name,url,html_options,post,block)};EJS.Helpers.prototype.link_to_unless=function(condition,name,url,html_options,block){html_options=html_options||{};if(condition){if(block&&typeof(block)=="function"){return block(name,url,html_options,block)}else{return name}}else{return this.link_to(name,url,html_options)}};EJS.Helpers.prototype.link_to_unless_current=function(name,url,html_options,block){html_options=html_options||{};return this.link_to_unless(this.is_current_page(url),name,url,html_options,block)};EJS.Helpers.prototype.password_field_tag=function(name,value,html_options){return this.input_field_tag(name,value,"password",html_options)};EJS.Helpers.prototype.select_tag=function(name,value,choices,html_options){html_options=html_options||{};html_options.id=html_options.id||name;html_options.value=value;html_options.name=name;var txt="";txt+=this.start_tag_for("select",html_options);for(var i=0;i<choices.length;i++){var choice=choices[i];var optionOptions={value:choice.value};if(choice.value==value){optionOptions.selected="selected"}txt+=this.start_tag_for("option",optionOptions)+choice.text+this.tag_end("option")}txt+=this.tag_end("select");return txt};EJS.Helpers.prototype.single_tag_for=function(tag,html_options){return this.tag(tag,html_options,"/>")};EJS.Helpers.prototype.start_tag_for=function(tag,html_options){return this.tag(tag,html_options)};EJS.Helpers.prototype.submit_tag=function(name,html_options){html_options=html_options||{};html_options.type=html_options.type||"submit";html_options.value=name||"Submit";return this.single_tag_for("input",html_options)};EJS.Helpers.prototype.tag=function(tag,html_options,end){if(!end){var end=">"}var txt=" ";for(var attr in html_options){if(html_options[attr]!=null){var value=html_options[attr].toString()}else{var value=""}if(attr=="Class"){attr="class"}if(value.indexOf("'")!=-1){txt+=attr+'="'+value+'" '}else{txt+=attr+"='"+value+"' "}}return"<"+tag+txt+end};EJS.Helpers.prototype.tag_end=function(tag){return"</"+tag+">"};EJS.Helpers.prototype.text_area_tag=function(name,value,html_options){html_options=html_options||{};html_options.id=html_options.id||name;html_options.name=html_options.name||name;value=value||"";if(html_options.size){html_options.cols=html_options.size.split("x")[0];html_options.rows=html_options.size.split("x")[1];delete html_options.size}html_options.cols=html_options.cols||50;html_options.rows=html_options.rows||4;return this.start_tag_for("textarea",html_options)+value+this.tag_end("textarea")};EJS.Helpers.prototype.text_tag=EJS.Helpers.prototype.text_area_tag;EJS.Helpers.prototype.text_field_tag=function(name,value,html_options){return this.input_field_tag(name,value,"text",html_options)};EJS.Helpers.prototype.url_for=function(url){return'window.location="'+url+'";'};EJS.Helpers.prototype.img_tag=function(image_location,alt,options){options=options||{};options.src=image_location;options.alt=alt;return this.single_tag_for("img",options)};Sammy=Sammy||{};Sammy.EJS=function(app,method_alias){var template=function(template,data,name){if(typeof name=="undefined"){name=template}return new EJS({text:template,name:name}).render(data)};if(!method_alias){method_alias="ejs"}app.helper(method_alias,template)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.form-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.form-0.6.2.min.js
new file mode 100644
index 00000000..4c690009
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.form-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.form.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:41 -0700 2010
+(function(b){Sammy=Sammy||{};function a(d,e){if(typeof e==="undefined"){return""}else{if(b.isFunction(e)){e=e.apply(d)}}return e.toString()}function c(d,e,g){var f="<";f+=d;if(typeof e!="undefined"){b.each(e,function(h,i){if(i!=null){f+=" "+h+"='";f+=a(e,i).replace(/\'/g,"'");f+="'"}})}if(g===false){f+=">"}else{if(typeof g!="undefined"){f+=">";f+=a(this,g);f+="</"+d+">"}else{f+=" />"}}return f}Sammy.FormBuilder=function(e,d){this.name=e;this.object=d};b.extend(Sammy.FormBuilder.prototype,{open:function(d){return c("form",b.extend({method:"post",action:"#/"+this.name+"s"},d),false)},close:function(){return"</form>"},label:function(e,g,d){var f={"for":this._attributesForKeyPath(e).name};return c("label",b.extend(f,d),g)},hidden:function(e,d){d=b.extend({type:"hidden"},this._attributesForKeyPath(e),d);return c("input",d)},text:function(e,d){d=b.extend({type:"text"},this._attributesForKeyPath(e),d);return c("input",d)},textarea:function(e,d){var f;d=b.extend(this._attributesForKeyPath(e),d);f=d.value;delete d.value;return c("textarea",d,f)},password:function(e,d){return this.text(e,b.extend({type:"password"},d))},select:function(e,f,d){var h="",g;d=b.extend(this._attributesForKeyPath(e),d);g=d.value;delete d.value;b.each(f,function(j,k){var l,n,m;if(b.isArray(k)){l=k[1],n=k[0]}else{l=k,n=k}m={value:a(this.object,l)};if(l===g){m.selected="selected"}h+=c("option",m,n)});return c("select",d,h)},radio:function(e,g,d){var f;d=b.extend(this._attributesForKeyPath(e),d);f=d.value;d.value=a(this.object,g);if(f==d.value){d.checked="checked"}return c("input",b.extend({type:"radio"},d))},checkbox:function(e,g,d){var f="";if(!d){d={}}if(d.hidden_element!==false){f+=this.hidden(e,{value:!g})}delete d.hidden_element;f+=this.radio(e,g,b.extend({type:"checkbox"},d));return f},submit:function(d){return c("input",b.extend({type:"submit"},d))},_attributesForKeyPath:function(e){var d=this,g=b.isArray(e)?e:e.split(/\./),f=d.name,i=d.object,h=d.name;b.each(g,function(k,j){if((typeof i==="undefined")||i==""){i=""}else{if(typeof j=="number"||j.match(/^\d+$/)){i=i[parseInt(j,10)]}else{i=i[j]}}f+="["+j+"]";h+="-"+j});return{name:f,value:a(d.object,i),"class":h}}});Sammy.Form=function(d){d.helpers({simple_element:c,formFor:function(g,f,h){var e;if(b.isFunction(f)){h=f;f=this[g]}e=new Sammy.FormBuilder(g,f),h.apply(this,[e]);return e}})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.form-latest.min.js b/javascript/sammy/min/plugins/sammy.form-latest.min.js
new file mode 100644
index 00000000..4c690009
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.form-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.form.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:41 -0700 2010
+(function(b){Sammy=Sammy||{};function a(d,e){if(typeof e==="undefined"){return""}else{if(b.isFunction(e)){e=e.apply(d)}}return e.toString()}function c(d,e,g){var f="<";f+=d;if(typeof e!="undefined"){b.each(e,function(h,i){if(i!=null){f+=" "+h+"='";f+=a(e,i).replace(/\'/g,"'");f+="'"}})}if(g===false){f+=">"}else{if(typeof g!="undefined"){f+=">";f+=a(this,g);f+="</"+d+">"}else{f+=" />"}}return f}Sammy.FormBuilder=function(e,d){this.name=e;this.object=d};b.extend(Sammy.FormBuilder.prototype,{open:function(d){return c("form",b.extend({method:"post",action:"#/"+this.name+"s"},d),false)},close:function(){return"</form>"},label:function(e,g,d){var f={"for":this._attributesForKeyPath(e).name};return c("label",b.extend(f,d),g)},hidden:function(e,d){d=b.extend({type:"hidden"},this._attributesForKeyPath(e),d);return c("input",d)},text:function(e,d){d=b.extend({type:"text"},this._attributesForKeyPath(e),d);return c("input",d)},textarea:function(e,d){var f;d=b.extend(this._attributesForKeyPath(e),d);f=d.value;delete d.value;return c("textarea",d,f)},password:function(e,d){return this.text(e,b.extend({type:"password"},d))},select:function(e,f,d){var h="",g;d=b.extend(this._attributesForKeyPath(e),d);g=d.value;delete d.value;b.each(f,function(j,k){var l,n,m;if(b.isArray(k)){l=k[1],n=k[0]}else{l=k,n=k}m={value:a(this.object,l)};if(l===g){m.selected="selected"}h+=c("option",m,n)});return c("select",d,h)},radio:function(e,g,d){var f;d=b.extend(this._attributesForKeyPath(e),d);f=d.value;d.value=a(this.object,g);if(f==d.value){d.checked="checked"}return c("input",b.extend({type:"radio"},d))},checkbox:function(e,g,d){var f="";if(!d){d={}}if(d.hidden_element!==false){f+=this.hidden(e,{value:!g})}delete d.hidden_element;f+=this.radio(e,g,b.extend({type:"checkbox"},d));return f},submit:function(d){return c("input",b.extend({type:"submit"},d))},_attributesForKeyPath:function(e){var d=this,g=b.isArray(e)?e:e.split(/\./),f=d.name,i=d.object,h=d.name;b.each(g,function(k,j){if((typeof i==="undefined")||i==""){i=""}else{if(typeof j=="number"||j.match(/^\d+$/)){i=i[parseInt(j,10)]}else{i=i[j]}}f+="["+j+"]";h+="-"+j});return{name:f,value:a(d.object,i),"class":h}}});Sammy.Form=function(d){d.helpers({simple_element:c,formFor:function(g,f,h){var e;if(b.isFunction(f)){h=f;f=this[g]}e=new Sammy.FormBuilder(g,f),h.apply(this,[e]);return e}})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.googleanalytics-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.googleanalytics-0.6.2.min.js
new file mode 100644
index 00000000..db86ca75
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.googleanalytics-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.googleanalytics.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:41 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.GoogleAnalytics=function(g,e){var c=e||window.pageTracker,b=true;function f(){b=false}function d(){b=true}this.helpers({noTrack:function(){f()},track:function(h){if(typeof c!="undefined"&&b){this.log("tracking",h);c._trackPageview(h)}}});this.bind("event-context-after",function(){this.track(this.path);d()})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.googleanalytics-latest.min.js b/javascript/sammy/min/plugins/sammy.googleanalytics-latest.min.js
new file mode 100644
index 00000000..db86ca75
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.googleanalytics-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.googleanalytics.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:41 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.GoogleAnalytics=function(g,e){var c=e||window.pageTracker,b=true;function f(){b=false}function d(){b=true}this.helpers({noTrack:function(){f()},track:function(h){if(typeof c!="undefined"&&b){this.log("tracking",h);c._trackPageview(h)}}});this.bind("event-context-after",function(){this.track(this.path);d()})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.haml-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.haml-0.6.2.min.js
new file mode 100644
index 00000000..9d08bffa
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.haml-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.haml.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:42 -0700 2010
+(function($){var matchers,self_close_tags,embedder,forceXML;function html_escape(text){return(text+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\"/g,"&quot;")}function render_attribs(attribs){var key,value,result=[];for(key in attribs){if(key!=="_content"&&attribs.hasOwnProperty(key)){switch(attribs[key]){case"undefined":case"false":case"null":case'""':break;default:try{value=JSON.parse("["+attribs[key]+"]")[0];if(value===true){value=key}else{if(typeof value==="string"&&embedder.test(value)){value='" +\n'+parse_interpol(html_escape(value))+' +\n"'}else{value=html_escape(value)}}result.push(" "+key+'=\\"'+value+'\\"')}catch(e){result.push(" "+key+'=\\"" + html_escape('+attribs[key]+') + "\\"')}}}}return result.join("")}function parse_attribs(line){var attributes={},l=line.length,i,c,count=1,quote=false,skip=false,open,close,joiner,seperator,pair={start:1,middle:null,end:null};if(!(l>0&&(line.charAt(0)==="{"||line.charAt(0)==="("))){return{_content:line[0]===" "?line.substr(1,l):line}}open=line.charAt(0);close=(open==="{")?"}":")";joiner=(open==="{")?":":"=";seperator=(open==="{")?",":" ";function process_pair(){if(typeof pair.start==="number"&&typeof pair.middle==="number"&&typeof pair.end==="number"){var key=line.substr(pair.start,pair.middle-pair.start).trim(),value=line.substr(pair.middle+1,pair.end-pair.middle-1).trim();attributes[key]=value}pair={start:null,middle:null,end:null}}for(i=1;count>0;i+=1){if(i>l){throw"Malformed attribute block"}c=line.charAt(i);if(skip){skip=false}else{if(quote){if(c==="\\"){skip=true}if(c===quote){quote=false}}else{if(c==='"'||c==="'"){quote=c}if(count===1){if(c===joiner){pair.middle=i}if(c===seperator||c===close){pair.end=i;process_pair();if(c===seperator){pair.start=i+1}}}if(c===open||c==="("){count+=1}if(c===close||(count>1&&c===")")){count-=1}}}}attributes._content=line.substr(i,line.length);return attributes}function parse_interpol(value){var items=[],pos=0,next=0,match;while(true){next=value.substr(pos).search(embedder);if(next<0){if(pos<value.length){items.push(JSON.stringify(value.substr(pos)))}break}items.push(JSON.stringify(value.substr(pos,next)));pos+=next;match=value.substr(pos).match(embedder);next=match[0].length;if(next<0){break}items.push(match[1]||match[2]);pos+=next}return items.filter(function(part){return part&&part.length>0}).join(" +\n")}embedder=/\#\{([^}]*)\}/;self_close_tags=["meta","img","link","br","hr","input","area","base"];matchers=[{regexp:/^(\s*)((?:[.#%][a-z_\-][a-z0-9_:\-]*)+)(.*)$/i,process:function(){var tag,classes,ids,attribs,content;tag=this.matches[2];classes=tag.match(/\.([a-z_\-][a-z0-9_\-]*)/gi);ids=tag.match(/\#([a-z_\-][a-z0-9_\-]*)/gi);tag=tag.match(/\%([a-z_\-][a-z0-9_:\-]*)/gi);tag=tag?tag[0].substr(1,tag[0].length):"div";attribs=this.matches[3];if(attribs){attribs=parse_attribs(attribs);if(attribs._content){this.contents.unshift(attribs._content.trim());delete (attribs._content)}}else{attribs={}}if(classes){classes=classes.map(function(klass){return klass.substr(1,klass.length)}).join(" ");if(attribs["class"]){try{attribs["class"]=JSON.stringify(classes+" "+JSON.parse(attribs["class"]))}catch(e){attribs["class"]=JSON.stringify(classes+" ")+" + "+attribs["class"]}}else{attribs["class"]=JSON.stringify(classes)}}if(ids){ids=ids.map(function(id){return id.substr(1,id.length)}).join(" ");if(attribs.id){attribs.id=JSON.stringify(ids+" ")+attribs.id}else{attribs.id=JSON.stringify(ids)}}attribs=render_attribs(attribs);content=this.render_contents();if(content==='""'){content=""}if(forceXML?content.length>0:self_close_tags.indexOf(tag)==-1){return'"<'+tag+attribs+'>"'+(content.length>0?" + \n"+content:"")+' + \n"</'+tag+'>"'}else{return'"<'+tag+attribs+' />"'}}},{regexp:/^(\s*)(?::for|:each)\s+(?:([a-z_][a-z_\-]*),\s*)?([a-z_][a-z_\-]*)\s+in\s+(.*)(\s*)$/i,process:function(){var ivar=this.matches[2]||"__key__",vvar=this.matches[3],avar=this.matches[4],rvar="__result__";if(this.matches[5]){this.contents.unshift(this.matches[5])}return"(function () { var "+rvar+" = [], "+ivar+", "+vvar+"; for ("+ivar+" in "+avar+") { if ("+avar+".hasOwnProperty("+ivar+")) { "+vvar+" = "+avar+"["+ivar+"]; "+rvar+".push(\n"+(this.render_contents()||"''")+"\n); } } return "+rvar+'.join(""); }).call(this)'}},{regexp:/^(\s*):if\s+(.*)\s*$/i,process:function(){var condition=this.matches[2];return"(function () { if ("+condition+") { return (\n"+(this.render_contents()||"")+'\n);} else { return ""; } }).call(this)'}},{regexp:/^()!!!(?:\s*(.*))\s*$/,process:function(){var line="";switch((this.matches[2]||"").toLowerCase()){case"":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';break;case"strict":case"1.0":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';break;case"frameset":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';break;case"5":line="<!DOCTYPE html>";break;case"1.1":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';break;case"basic":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">';break;case"mobile":line='<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">';break;case"xml":line="<?xml version='1.0' encoding='utf-8' ?>";break;case"xml iso-8859-1":line="<?xml version='1.0' encoding='iso-8859-1' ?>";break}return JSON.stringify(line+"\n")}},{regexp:/^(\s*):markdown\s*$/i,process:function(){return parse_interpol(exports.Markdown.encode(this.contents.join("\n")))}},{regexp:/^(\s*):(?:java)?script\s*$/,process:function(){return parse_interpol('\n<script type="text/javascript">\n//<![CDATA[\n'+this.contents.join("\n")+"\n//]]>\n<\/script>\n")}},{regexp:/^(\s*):css\s*$/,process:function(){return JSON.stringify('\n<style type="text/css">\n'+this.contents.join("\n")+"\n</style>\n")}},];function compile(lines){var block=false,output=[];if(typeof lines==="string"){lines=lines.trim().replace(/\n\r|\r/g,"\n").split("\n")}lines.forEach(function(line){var match,found=false;if(block){match=block.check_indent(line);if(match){block.contents.push(match[1]||"");return}else{output.push(block.process());block=false}}matchers.forEach(function(matcher){if(!found){match=matcher.regexp(line);if(match){block={contents:[],matches:match,check_indent:new RegExp("^(?:\\s*|"+match[1]+"  (.*))$"),process:matcher.process,render_contents:function(){return compile(this.contents)}};found=true}}});if(!found){output.push(function(){if(line[0]==="\\"){return parse_interpol(line.substr(1,line.length))}if(line[0]==="="){line=line.substr(1,line.length).trim();try{return parse_interpol(JSON.parse(line))}catch(e){return line}}if(line.substr(0,2)==="&="){line=line.substr(2,line.length).trim();try{return JSON.stringify(html_escape(JSON.parse(line)))}catch(e2){return"html_escape("+line+")"}}return parse_interpol(line)}())}});if(block){output.push(block.process())}return output.filter(function(part){return part&&part.length>0}).join(" +\n")}function optimize(js){var new_js=[],buffer=[],part,end;function flush(){if(buffer.length>0){new_js.push(JSON.stringify(buffer.join(""))+end);buffer=[]}}js.replace(/\n\r|\r/g,"\n").split("\n").forEach(function(line){part=line.match(/^(\".*\")(\s*\+\s*)?$/);if(!part){flush();new_js.push(line);return}end=part[2]||"";part=part[1];try{buffer.push(JSON.parse(part))}catch(e){flush();new_js.push(line)}});flush();return new_js.join("\n")}function render(text,options){options=options||{};text=text||"";var js=compile(text);if(options.optimize){js=Haml.optimize(js)}return execute(js,options.context||Haml,options.locals)}function execute(js,self,locals){return(function(){with(locals||{}){try{return eval("("+js+")")}catch(e){return"\n<pre class='error'>"+html_escape(e.stack)+"</pre>\n"}}}).call(self)}Haml=function Haml(haml,xml){forceXML=xml;var js=optimize(compile(haml));return new Function("locals",html_escape+"\nwith(locals || {}) {\n  try {\n    return ("+js+');\n  } catch (e) {\n    return "\\n<pre class=\'error\'>" + html_escape(e.stack) + "</pre>\\n";\n  }\n}')};Sammy=Sammy||{};Sammy.Haml=function(app,method_alias){app.use(Sammy.JSON);var haml_cache={};var haml=function(template,data,name){if(typeof name=="undefined"){name=template}var fn=haml_cache[name];if(!fn){fn=haml_cache[name]=Haml(template)}return fn($.extend({},this,data))};if(!method_alias){method_alias="haml"}app.helper(method_alias,haml)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.haml-latest.min.js b/javascript/sammy/min/plugins/sammy.haml-latest.min.js
new file mode 100644
index 00000000..9d08bffa
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.haml-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.haml.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:42 -0700 2010
+(function($){var matchers,self_close_tags,embedder,forceXML;function html_escape(text){return(text+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\"/g,"&quot;")}function render_attribs(attribs){var key,value,result=[];for(key in attribs){if(key!=="_content"&&attribs.hasOwnProperty(key)){switch(attribs[key]){case"undefined":case"false":case"null":case'""':break;default:try{value=JSON.parse("["+attribs[key]+"]")[0];if(value===true){value=key}else{if(typeof value==="string"&&embedder.test(value)){value='" +\n'+parse_interpol(html_escape(value))+' +\n"'}else{value=html_escape(value)}}result.push(" "+key+'=\\"'+value+'\\"')}catch(e){result.push(" "+key+'=\\"" + html_escape('+attribs[key]+') + "\\"')}}}}return result.join("")}function parse_attribs(line){var attributes={},l=line.length,i,c,count=1,quote=false,skip=false,open,close,joiner,seperator,pair={start:1,middle:null,end:null};if(!(l>0&&(line.charAt(0)==="{"||line.charAt(0)==="("))){return{_content:line[0]===" "?line.substr(1,l):line}}open=line.charAt(0);close=(open==="{")?"}":")";joiner=(open==="{")?":":"=";seperator=(open==="{")?",":" ";function process_pair(){if(typeof pair.start==="number"&&typeof pair.middle==="number"&&typeof pair.end==="number"){var key=line.substr(pair.start,pair.middle-pair.start).trim(),value=line.substr(pair.middle+1,pair.end-pair.middle-1).trim();attributes[key]=value}pair={start:null,middle:null,end:null}}for(i=1;count>0;i+=1){if(i>l){throw"Malformed attribute block"}c=line.charAt(i);if(skip){skip=false}else{if(quote){if(c==="\\"){skip=true}if(c===quote){quote=false}}else{if(c==='"'||c==="'"){quote=c}if(count===1){if(c===joiner){pair.middle=i}if(c===seperator||c===close){pair.end=i;process_pair();if(c===seperator){pair.start=i+1}}}if(c===open||c==="("){count+=1}if(c===close||(count>1&&c===")")){count-=1}}}}attributes._content=line.substr(i,line.length);return attributes}function parse_interpol(value){var items=[],pos=0,next=0,match;while(true){next=value.substr(pos).search(embedder);if(next<0){if(pos<value.length){items.push(JSON.stringify(value.substr(pos)))}break}items.push(JSON.stringify(value.substr(pos,next)));pos+=next;match=value.substr(pos).match(embedder);next=match[0].length;if(next<0){break}items.push(match[1]||match[2]);pos+=next}return items.filter(function(part){return part&&part.length>0}).join(" +\n")}embedder=/\#\{([^}]*)\}/;self_close_tags=["meta","img","link","br","hr","input","area","base"];matchers=[{regexp:/^(\s*)((?:[.#%][a-z_\-][a-z0-9_:\-]*)+)(.*)$/i,process:function(){var tag,classes,ids,attribs,content;tag=this.matches[2];classes=tag.match(/\.([a-z_\-][a-z0-9_\-]*)/gi);ids=tag.match(/\#([a-z_\-][a-z0-9_\-]*)/gi);tag=tag.match(/\%([a-z_\-][a-z0-9_:\-]*)/gi);tag=tag?tag[0].substr(1,tag[0].length):"div";attribs=this.matches[3];if(attribs){attribs=parse_attribs(attribs);if(attribs._content){this.contents.unshift(attribs._content.trim());delete (attribs._content)}}else{attribs={}}if(classes){classes=classes.map(function(klass){return klass.substr(1,klass.length)}).join(" ");if(attribs["class"]){try{attribs["class"]=JSON.stringify(classes+" "+JSON.parse(attribs["class"]))}catch(e){attribs["class"]=JSON.stringify(classes+" ")+" + "+attribs["class"]}}else{attribs["class"]=JSON.stringify(classes)}}if(ids){ids=ids.map(function(id){return id.substr(1,id.length)}).join(" ");if(attribs.id){attribs.id=JSON.stringify(ids+" ")+attribs.id}else{attribs.id=JSON.stringify(ids)}}attribs=render_attribs(attribs);content=this.render_contents();if(content==='""'){content=""}if(forceXML?content.length>0:self_close_tags.indexOf(tag)==-1){return'"<'+tag+attribs+'>"'+(content.length>0?" + \n"+content:"")+' + \n"</'+tag+'>"'}else{return'"<'+tag+attribs+' />"'}}},{regexp:/^(\s*)(?::for|:each)\s+(?:([a-z_][a-z_\-]*),\s*)?([a-z_][a-z_\-]*)\s+in\s+(.*)(\s*)$/i,process:function(){var ivar=this.matches[2]||"__key__",vvar=this.matches[3],avar=this.matches[4],rvar="__result__";if(this.matches[5]){this.contents.unshift(this.matches[5])}return"(function () { var "+rvar+" = [], "+ivar+", "+vvar+"; for ("+ivar+" in "+avar+") { if ("+avar+".hasOwnProperty("+ivar+")) { "+vvar+" = "+avar+"["+ivar+"]; "+rvar+".push(\n"+(this.render_contents()||"''")+"\n); } } return "+rvar+'.join(""); }).call(this)'}},{regexp:/^(\s*):if\s+(.*)\s*$/i,process:function(){var condition=this.matches[2];return"(function () { if ("+condition+") { return (\n"+(this.render_contents()||"")+'\n);} else { return ""; } }).call(this)'}},{regexp:/^()!!!(?:\s*(.*))\s*$/,process:function(){var line="";switch((this.matches[2]||"").toLowerCase()){case"":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';break;case"strict":case"1.0":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';break;case"frameset":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';break;case"5":line="<!DOCTYPE html>";break;case"1.1":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';break;case"basic":line='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">';break;case"mobile":line='<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">';break;case"xml":line="<?xml version='1.0' encoding='utf-8' ?>";break;case"xml iso-8859-1":line="<?xml version='1.0' encoding='iso-8859-1' ?>";break}return JSON.stringify(line+"\n")}},{regexp:/^(\s*):markdown\s*$/i,process:function(){return parse_interpol(exports.Markdown.encode(this.contents.join("\n")))}},{regexp:/^(\s*):(?:java)?script\s*$/,process:function(){return parse_interpol('\n<script type="text/javascript">\n//<![CDATA[\n'+this.contents.join("\n")+"\n//]]>\n<\/script>\n")}},{regexp:/^(\s*):css\s*$/,process:function(){return JSON.stringify('\n<style type="text/css">\n'+this.contents.join("\n")+"\n</style>\n")}},];function compile(lines){var block=false,output=[];if(typeof lines==="string"){lines=lines.trim().replace(/\n\r|\r/g,"\n").split("\n")}lines.forEach(function(line){var match,found=false;if(block){match=block.check_indent(line);if(match){block.contents.push(match[1]||"");return}else{output.push(block.process());block=false}}matchers.forEach(function(matcher){if(!found){match=matcher.regexp(line);if(match){block={contents:[],matches:match,check_indent:new RegExp("^(?:\\s*|"+match[1]+"  (.*))$"),process:matcher.process,render_contents:function(){return compile(this.contents)}};found=true}}});if(!found){output.push(function(){if(line[0]==="\\"){return parse_interpol(line.substr(1,line.length))}if(line[0]==="="){line=line.substr(1,line.length).trim();try{return parse_interpol(JSON.parse(line))}catch(e){return line}}if(line.substr(0,2)==="&="){line=line.substr(2,line.length).trim();try{return JSON.stringify(html_escape(JSON.parse(line)))}catch(e2){return"html_escape("+line+")"}}return parse_interpol(line)}())}});if(block){output.push(block.process())}return output.filter(function(part){return part&&part.length>0}).join(" +\n")}function optimize(js){var new_js=[],buffer=[],part,end;function flush(){if(buffer.length>0){new_js.push(JSON.stringify(buffer.join(""))+end);buffer=[]}}js.replace(/\n\r|\r/g,"\n").split("\n").forEach(function(line){part=line.match(/^(\".*\")(\s*\+\s*)?$/);if(!part){flush();new_js.push(line);return}end=part[2]||"";part=part[1];try{buffer.push(JSON.parse(part))}catch(e){flush();new_js.push(line)}});flush();return new_js.join("\n")}function render(text,options){options=options||{};text=text||"";var js=compile(text);if(options.optimize){js=Haml.optimize(js)}return execute(js,options.context||Haml,options.locals)}function execute(js,self,locals){return(function(){with(locals||{}){try{return eval("("+js+")")}catch(e){return"\n<pre class='error'>"+html_escape(e.stack)+"</pre>\n"}}}).call(self)}Haml=function Haml(haml,xml){forceXML=xml;var js=optimize(compile(haml));return new Function("locals",html_escape+"\nwith(locals || {}) {\n  try {\n    return ("+js+');\n  } catch (e) {\n    return "\\n<pre class=\'error\'>" + html_escape(e.stack) + "</pre>\\n";\n  }\n}')};Sammy=Sammy||{};Sammy.Haml=function(app,method_alias){app.use(Sammy.JSON);var haml_cache={};var haml=function(template,data,name){if(typeof name=="undefined"){name=template}var fn=haml_cache[name];if(!fn){fn=haml_cache[name]=Haml(template)}return fn($.extend({},this,data))};if(!method_alias){method_alias="haml"}app.helper(method_alias,haml)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.handlebars-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.handlebars-0.6.2.min.js
new file mode 100644
index 00000000..17cc5b88
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.handlebars-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.handlebars.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:43 -0700 2010
+(function(b){var c={compilerCache:{},compile:function(d){if(c.compilerCache[d]==null){var f=c.compileFunctionBody(d);var e=new Function("context","fallback","Handlebars",f);c.compilerCache[d]=function(g,h){return e(g,h,c)}}return c.compilerCache[d]},compileToString:function(d){var e=c.compileFunctionBody(d);return"function(context, fallback) { "+e+"}"},compileFunctionBody:function(d){var e=new c.Compiler(d);e.compile();return"fallback = fallback || {}; var stack = [];"+e.fn},isFunction:function(d){return Object.prototype.toString.call(d)=="[object Function]"},trim:function(d){return d.replace(/^\s+|\s+$/g,"")},escapeText:function(d){d=d.replace(/'/g,"\\'");d=d.replace(/\"/g,'\\"');return d},escapeExpression:function(d){if(d instanceof c.SafeString){return d.toString()}else{if(d===null){d=""}}return d.toString().replace(/&(?!\w+;)|["\\<>]/g,function(e){switch(e){case"&":return"&amp;";break;case'"':return'"';case"\\":return"\\\\";break;case"<":return"&lt;";break;case">":return"&gt;";break;default:return e}})},compilePartial:function(d){if(c.isFunction(d)){compiled=d}else{compiled=c.compile(d)}return compiled},evalExpression:function(h,f,d){var k=c.parsePath(h);var j=k[0];var g=k[1];if(j>d.length){f=null}else{if(j>0){f=d[d.length-j]}}for(var e=0;e<g.length&&f!==undefined;e++){f=f[g[e]]}return f},buildContext:function(f,d){var e=function(g){this.__stack__=g.slice(0);this.__get__=function(h){return c.evalExpression(h,this,this.__stack__)}};e.prototype=f;return new e(d)},pathPatterns:{},parsePath:function(k){if(k==null){return[0,[]]}else{if(c.pathPatterns[k]!=null){return c.pathPatterns[k]}}var h=k.split("/");var m=false;var l=0;var f=[];for(var g=0,e=h.length;g<e;g++){switch(h[g]){case"..":if(m){throw new c.Exception("Cannot jump out of context after moving into a context.")}else{l+=1}break;case".":case"this":break;default:m=true;f.push(h[g])}}var d=[l,f];c.pathPatterns[k]=d;return d},isEmpty:function(d){if(typeof d==="undefined"){return true}else{if(!d){return true}else{if(Object.prototype.toString.call(d)==="[object Array]"&&d.length==0){return true}else{return false}}}},filterOutput:function(e,d){if(c.isEmpty(e)){return""}else{if(d){return c.escapeExpression(e)}else{return e}}},handleBlock:function(i,g,d,h,e){var f="";if(c.isFunction(i)){f=f+i.call(g,d,h);if(e!=null&&c.isFunction(i.not)){f=f+i.not.call(g,d,e)}}else{if(!c.isEmpty(i)){f=f+c.helperMissing.call(d,i,h)}if(e!=null){f=f+c.helperMissing.not.call(d,i,e)}}return f},handleExpression:function(h,g,d,e){var f="";if(c.isFunction(h)){f=f+c.filterOutput(h.call(g,d),e)}else{if(!c.isEmpty(h)){f=f+c.filterOutput(h,e)}}return f},handleInvertedSection:function(g,e,f){var d="";if(c.isFunction(g)&&c.isEmpty(g())){d=d+f(e)}else{if(c.isEmpty(g)){d=d+f(e)}}return d}};c.Compiler=function(d){this.string=d;this.pointer=-1;this.mustache=false;this.text="";this.fn="var out = ''; var lookup; ";this.newlines="";this.comment=false;this.escaped=true;this.partial=false;this.inverted=false;this.endCondition=null;this.continueInverted=false};c.Exception=function(d){this.message=d};c.SafeString=function(d){this.string=d};c.SafeString.prototype.toString=function(){return this.string.toString()};c.helperMissing=function(f,h){var e="";if(f===true){return h(this)}else{if(f===false){return""}else{if(Object.prototype.toString.call(f)==="[object Array]"){for(var g=0,d=f.length;g<d;g++){e=e+h(f[g])}return e}else{return h(f)}}}};c.helperMissing.not=function(d,e){return e(d)};c.Compiler.prototype={getChar:function(e){var d=this.peek(e);this.pointer=this.pointer+(e||1);return d},peek:function(e){e=e||1;var d=this.pointer+1;return this.string.slice(d,d+e)},compile:function(e){if(!e||!e(this)){var d;while(d=this.getChar()){if(d==="{"&&this.peek()==="{"&&!this.mustache){this.getChar();this.parseMustache()}else{if(d==="\n"){this.newlines=this.newlines+"\n";d="\\n"}else{if(d==="\r"){this.newlines=this.newlines+"\r";d="\\r"}else{if(d==="\\"){d="\\\\"}}}this.text=this.text+d}if(e&&this.peek(5)=="{{^}}"){this.continueInverted=true;this.getChar(5);break}else{if(e&&e(this)){break}}}}this.addText();this.fn+="return out;";return},addText:function(){if(this.text){this.fn=this.fn+'out = out + "'+c.escapeText(this.text)+'"; ';this.fn=this.fn+this.newlines;this.newlines="";this.text=""}},addExpression:function(d,f){f=f||null;var e=this.lookupFor(d);this.fn+="var proxy = Handlebars.buildContext(context, stack);";this.fn+="out = out + Handlebars.handleExpression("+e+", proxy, "+f+", "+this.escaped+");"},addInvertedSection:function(g){var f=this.compileToEndOfBlock(g);var d=f.fn;var e="fn"+this.pointer.toString();this.fn+="var "+e+" = function(context) {"+d+"}; ";this.fn+="lookup = "+this.lookupFor(g)+"; ";this.fn+="out = out + Handlebars.handleInvertedSection(lookup, context, "+e+");";this.openBlock=false;this.inverted=false},lookupFor:function(g){var d=c.parsePath(g);var f=d[0];var e=d[1];if(f>0||e.length>1){return"(Handlebars.evalExpression('"+g+"', context, stack))"}else{if(e.length==1){return"(context['"+e[0]+"'] || fallback['"+e[0]+"'])"}else{return"(context || fallback)"}}},compileToEndOfBlock:function(e){var d=new c.Compiler(this.string.slice(this.pointer+1));d.compile(function(f){if(f.peek(3)==="{{/"){if(f.peek(e.length+5)==="{{/"+e+"}}"){f.getChar(e.length+5);return true}else{throw new c.Exception("Mismatched block close: expected "+e+".")}}});this.pointer+=d.pointer+1;return d},addBlock:function(h,j,i){var g=this.compileToEndOfBlock(h);var d=g.fn;var f="fn"+this.pointer.toString();this.fn+="var wrappedContext = Handlebars.buildContext(context, stack);";this.fn+="stack.push(context);";this.fn+="var "+f+" = function(context) {"+d+"}; ";this.fn+="lookup = "+this.lookupFor(h)+"; ";if(g.continueInverted){var e=this.compileToEndOfBlock(h);this.fn+=" var "+f+"Not = function(context) { "+e.fn+" };"}else{this.fn+=" var "+f+"Not = null;"}this.fn+="out = out + Handlebars.handleBlock(lookup, wrappedContext, "+j+", "+f+", "+f+"Not);";this.fn+="stack.pop();";this.openBlock=false},addPartial:function(d,e){this.fn+="if (typeof fallback['partials'] === 'undefined' || typeof fallback['partials']['"+d+"'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: "+d+"');";this.fn+="out = out + Handlebars.compilePartial(fallback['partials']['"+d+"'])("+e+", fallback);"},parseMustache:function(){var f,d,g,i;var e=this.peek();if(e==="!"){this.comment=true;this.getChar()}else{if(e==="#"){this.openBlock=true;this.getChar()}else{if(e===">"){this.partial=true;this.getChar()}else{if(e==="^"){this.inverted=true;this.openBlock=true;this.getChar()}else{if(e==="{"||e==="&"){this.escaped=false;this.getChar()}}}}}this.addText();this.mustache=" ";while(f=this.getChar()){if(this.mustache&&f==="}"&&this.peek()==="}"){var h=c.trim(this.mustache).split(/\s+/);g=h[0];i=this.lookupFor(h[1]);this.mustache=false;this.getChar();if(!this.escaped&&this.peek()==="}"){this.getChar()}if(this.comment){this.comment=false;return}else{if(this.partial){this.addPartial(g,i);this.partial=false;return}else{if(this.inverted){this.addInvertedSection(g);this.inverted=false;return}else{if(this.openBlock){this.addBlock(g,i,h);return}else{return this.addExpression(g,i)}}}}this.escaped=true}else{if(this.comment){}else{this.mustache=this.mustache+f}}}}};var a=a||{};a.compile=c.compile;a.compileToString=c.compileToString;Sammy=Sammy||{};Sammy.Handlebars=function(g,e){var f={};var d=function(k,l,i,h){if(typeof h=="undefined"){h=k}var j=f[h];if(!j){j=f[h]=c.compile(k)}l=b.extend({},this,l);i=b.extend({},l.partials,i);return j(l,{partials:i})};if(!e){e="handlebars"}g.helper(e,d)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.handlebars-latest.min.js b/javascript/sammy/min/plugins/sammy.handlebars-latest.min.js
new file mode 100644
index 00000000..17cc5b88
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.handlebars-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.handlebars.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:43 -0700 2010
+(function(b){var c={compilerCache:{},compile:function(d){if(c.compilerCache[d]==null){var f=c.compileFunctionBody(d);var e=new Function("context","fallback","Handlebars",f);c.compilerCache[d]=function(g,h){return e(g,h,c)}}return c.compilerCache[d]},compileToString:function(d){var e=c.compileFunctionBody(d);return"function(context, fallback) { "+e+"}"},compileFunctionBody:function(d){var e=new c.Compiler(d);e.compile();return"fallback = fallback || {}; var stack = [];"+e.fn},isFunction:function(d){return Object.prototype.toString.call(d)=="[object Function]"},trim:function(d){return d.replace(/^\s+|\s+$/g,"")},escapeText:function(d){d=d.replace(/'/g,"\\'");d=d.replace(/\"/g,'\\"');return d},escapeExpression:function(d){if(d instanceof c.SafeString){return d.toString()}else{if(d===null){d=""}}return d.toString().replace(/&(?!\w+;)|["\\<>]/g,function(e){switch(e){case"&":return"&amp;";break;case'"':return'"';case"\\":return"\\\\";break;case"<":return"&lt;";break;case">":return"&gt;";break;default:return e}})},compilePartial:function(d){if(c.isFunction(d)){compiled=d}else{compiled=c.compile(d)}return compiled},evalExpression:function(h,f,d){var k=c.parsePath(h);var j=k[0];var g=k[1];if(j>d.length){f=null}else{if(j>0){f=d[d.length-j]}}for(var e=0;e<g.length&&f!==undefined;e++){f=f[g[e]]}return f},buildContext:function(f,d){var e=function(g){this.__stack__=g.slice(0);this.__get__=function(h){return c.evalExpression(h,this,this.__stack__)}};e.prototype=f;return new e(d)},pathPatterns:{},parsePath:function(k){if(k==null){return[0,[]]}else{if(c.pathPatterns[k]!=null){return c.pathPatterns[k]}}var h=k.split("/");var m=false;var l=0;var f=[];for(var g=0,e=h.length;g<e;g++){switch(h[g]){case"..":if(m){throw new c.Exception("Cannot jump out of context after moving into a context.")}else{l+=1}break;case".":case"this":break;default:m=true;f.push(h[g])}}var d=[l,f];c.pathPatterns[k]=d;return d},isEmpty:function(d){if(typeof d==="undefined"){return true}else{if(!d){return true}else{if(Object.prototype.toString.call(d)==="[object Array]"&&d.length==0){return true}else{return false}}}},filterOutput:function(e,d){if(c.isEmpty(e)){return""}else{if(d){return c.escapeExpression(e)}else{return e}}},handleBlock:function(i,g,d,h,e){var f="";if(c.isFunction(i)){f=f+i.call(g,d,h);if(e!=null&&c.isFunction(i.not)){f=f+i.not.call(g,d,e)}}else{if(!c.isEmpty(i)){f=f+c.helperMissing.call(d,i,h)}if(e!=null){f=f+c.helperMissing.not.call(d,i,e)}}return f},handleExpression:function(h,g,d,e){var f="";if(c.isFunction(h)){f=f+c.filterOutput(h.call(g,d),e)}else{if(!c.isEmpty(h)){f=f+c.filterOutput(h,e)}}return f},handleInvertedSection:function(g,e,f){var d="";if(c.isFunction(g)&&c.isEmpty(g())){d=d+f(e)}else{if(c.isEmpty(g)){d=d+f(e)}}return d}};c.Compiler=function(d){this.string=d;this.pointer=-1;this.mustache=false;this.text="";this.fn="var out = ''; var lookup; ";this.newlines="";this.comment=false;this.escaped=true;this.partial=false;this.inverted=false;this.endCondition=null;this.continueInverted=false};c.Exception=function(d){this.message=d};c.SafeString=function(d){this.string=d};c.SafeString.prototype.toString=function(){return this.string.toString()};c.helperMissing=function(f,h){var e="";if(f===true){return h(this)}else{if(f===false){return""}else{if(Object.prototype.toString.call(f)==="[object Array]"){for(var g=0,d=f.length;g<d;g++){e=e+h(f[g])}return e}else{return h(f)}}}};c.helperMissing.not=function(d,e){return e(d)};c.Compiler.prototype={getChar:function(e){var d=this.peek(e);this.pointer=this.pointer+(e||1);return d},peek:function(e){e=e||1;var d=this.pointer+1;return this.string.slice(d,d+e)},compile:function(e){if(!e||!e(this)){var d;while(d=this.getChar()){if(d==="{"&&this.peek()==="{"&&!this.mustache){this.getChar();this.parseMustache()}else{if(d==="\n"){this.newlines=this.newlines+"\n";d="\\n"}else{if(d==="\r"){this.newlines=this.newlines+"\r";d="\\r"}else{if(d==="\\"){d="\\\\"}}}this.text=this.text+d}if(e&&this.peek(5)=="{{^}}"){this.continueInverted=true;this.getChar(5);break}else{if(e&&e(this)){break}}}}this.addText();this.fn+="return out;";return},addText:function(){if(this.text){this.fn=this.fn+'out = out + "'+c.escapeText(this.text)+'"; ';this.fn=this.fn+this.newlines;this.newlines="";this.text=""}},addExpression:function(d,f){f=f||null;var e=this.lookupFor(d);this.fn+="var proxy = Handlebars.buildContext(context, stack);";this.fn+="out = out + Handlebars.handleExpression("+e+", proxy, "+f+", "+this.escaped+");"},addInvertedSection:function(g){var f=this.compileToEndOfBlock(g);var d=f.fn;var e="fn"+this.pointer.toString();this.fn+="var "+e+" = function(context) {"+d+"}; ";this.fn+="lookup = "+this.lookupFor(g)+"; ";this.fn+="out = out + Handlebars.handleInvertedSection(lookup, context, "+e+");";this.openBlock=false;this.inverted=false},lookupFor:function(g){var d=c.parsePath(g);var f=d[0];var e=d[1];if(f>0||e.length>1){return"(Handlebars.evalExpression('"+g+"', context, stack))"}else{if(e.length==1){return"(context['"+e[0]+"'] || fallback['"+e[0]+"'])"}else{return"(context || fallback)"}}},compileToEndOfBlock:function(e){var d=new c.Compiler(this.string.slice(this.pointer+1));d.compile(function(f){if(f.peek(3)==="{{/"){if(f.peek(e.length+5)==="{{/"+e+"}}"){f.getChar(e.length+5);return true}else{throw new c.Exception("Mismatched block close: expected "+e+".")}}});this.pointer+=d.pointer+1;return d},addBlock:function(h,j,i){var g=this.compileToEndOfBlock(h);var d=g.fn;var f="fn"+this.pointer.toString();this.fn+="var wrappedContext = Handlebars.buildContext(context, stack);";this.fn+="stack.push(context);";this.fn+="var "+f+" = function(context) {"+d+"}; ";this.fn+="lookup = "+this.lookupFor(h)+"; ";if(g.continueInverted){var e=this.compileToEndOfBlock(h);this.fn+=" var "+f+"Not = function(context) { "+e.fn+" };"}else{this.fn+=" var "+f+"Not = null;"}this.fn+="out = out + Handlebars.handleBlock(lookup, wrappedContext, "+j+", "+f+", "+f+"Not);";this.fn+="stack.pop();";this.openBlock=false},addPartial:function(d,e){this.fn+="if (typeof fallback['partials'] === 'undefined' || typeof fallback['partials']['"+d+"'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: "+d+"');";this.fn+="out = out + Handlebars.compilePartial(fallback['partials']['"+d+"'])("+e+", fallback);"},parseMustache:function(){var f,d,g,i;var e=this.peek();if(e==="!"){this.comment=true;this.getChar()}else{if(e==="#"){this.openBlock=true;this.getChar()}else{if(e===">"){this.partial=true;this.getChar()}else{if(e==="^"){this.inverted=true;this.openBlock=true;this.getChar()}else{if(e==="{"||e==="&"){this.escaped=false;this.getChar()}}}}}this.addText();this.mustache=" ";while(f=this.getChar()){if(this.mustache&&f==="}"&&this.peek()==="}"){var h=c.trim(this.mustache).split(/\s+/);g=h[0];i=this.lookupFor(h[1]);this.mustache=false;this.getChar();if(!this.escaped&&this.peek()==="}"){this.getChar()}if(this.comment){this.comment=false;return}else{if(this.partial){this.addPartial(g,i);this.partial=false;return}else{if(this.inverted){this.addInvertedSection(g);this.inverted=false;return}else{if(this.openBlock){this.addBlock(g,i,h);return}else{return this.addExpression(g,i)}}}}this.escaped=true}else{if(this.comment){}else{this.mustache=this.mustache+f}}}}};var a=a||{};a.compile=c.compile;a.compileToString=c.compileToString;Sammy=Sammy||{};Sammy.Handlebars=function(g,e){var f={};var d=function(k,l,i,h){if(typeof h=="undefined"){h=k}var j=f[h];if(!j){j=f[h]=c.compile(k)}l=b.extend({},this,l);i=b.extend({},l.partials,i);return j(l,{partials:i})};if(!e){e="handlebars"}g.helper(e,d)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.json-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.json-0.6.2.min.js
new file mode 100644
index 00000000..03c1f35a
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.json-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.json.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:43 -0700 2010
+(function($){if(!window.JSON){window.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());Sammy=Sammy||{};Sammy.JSON=function(app){app.helpers({json:function(object){if(typeof object=="string"){return JSON.parse(object)}else{return JSON.stringify(object)}}})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.json-latest.min.js b/javascript/sammy/min/plugins/sammy.json-latest.min.js
new file mode 100644
index 00000000..03c1f35a
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.json-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.json.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:43 -0700 2010
+(function($){if(!window.JSON){window.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z"};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());Sammy=Sammy||{};Sammy.JSON=function(app){app.helpers({json:function(object){if(typeof object=="string"){return JSON.parse(object)}else{return JSON.stringify(object)}}})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.meld-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.meld-0.6.2.min.js
new file mode 100644
index 00000000..34181583
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.meld-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.meld.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:44 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.Meld=function(d,c){var e={selector:function(f){return"."+f},remove_false:true};var b=function(h,i,g){var f=a(h);g=a.extend(e,g||{});if(typeof i==="string"){f.html(i)}else{a.each(i,function(s,q){var k=g.selector(s),p=f.filter(k),t,r,m=false,n=f.index(p);if(p.length===0){p=f.find(k)}if(p.length>0){if(a.isArray(q)){t=a("<div/>");if(p.is("ol, ul")){m=true;r=p.children("li:first");if(r.length==0){r=a("<li/>")}}else{if(p.children().length==1){m=true;r=p.children(":first").clone()}else{r=p.clone()}}for(var l=0;l<q.length;l++){t.append(b(r.clone(),q[l],g))}if(m){p.html(t.html())}else{if(p[0]==f[0]){f=a(t.html())}else{if(n>=0){var o=[n,1];o=o.concat(t.children().get());f.splice.apply(f,o)}}}}else{if(g.remove_false&&q===false){f.splice(n,1)}else{if(typeof q==="object"){if(p.is(":empty")){p.attr(q,true)}else{p.html(b(p.html(),q,g))}}else{p.html(q.toString())}}}}else{f.attr(s,q,true)}})}var j=f;return j};if(!c){c="meld"}d.helper(c,b)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.meld-latest.min.js b/javascript/sammy/min/plugins/sammy.meld-latest.min.js
new file mode 100644
index 00000000..34181583
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.meld-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.meld.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:44 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.Meld=function(d,c){var e={selector:function(f){return"."+f},remove_false:true};var b=function(h,i,g){var f=a(h);g=a.extend(e,g||{});if(typeof i==="string"){f.html(i)}else{a.each(i,function(s,q){var k=g.selector(s),p=f.filter(k),t,r,m=false,n=f.index(p);if(p.length===0){p=f.find(k)}if(p.length>0){if(a.isArray(q)){t=a("<div/>");if(p.is("ol, ul")){m=true;r=p.children("li:first");if(r.length==0){r=a("<li/>")}}else{if(p.children().length==1){m=true;r=p.children(":first").clone()}else{r=p.clone()}}for(var l=0;l<q.length;l++){t.append(b(r.clone(),q[l],g))}if(m){p.html(t.html())}else{if(p[0]==f[0]){f=a(t.html())}else{if(n>=0){var o=[n,1];o=o.concat(t.children().get());f.splice.apply(f,o)}}}}else{if(g.remove_false&&q===false){f.splice(n,1)}else{if(typeof q==="object"){if(p.is(":empty")){p.attr(q,true)}else{p.html(b(p.html(),q,g))}}else{p.html(q.toString())}}}}else{f.attr(s,q,true)}})}var j=f;return j};if(!c){c="meld"}d.helper(c,b)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.mustache-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.mustache-0.6.2.min.js
new file mode 100644
index 00000000..2129b0c4
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.mustache-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.mustache.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:45 -0700 2010
+(function(b){if(!window.Mustache){var a=function(){var c=function(){};c.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":true},context:{},render:function(g,f,e,h){if(!h){this.context=f;this.buffer=[]}if(!this.includes("",g)){if(h){return g}else{this.send(g);return}}g=this.render_pragmas(g);var d=this.render_section(g,f,e);if(h){return this.render_tags(d,f,e,h)}this.render_tags(d,f,e,h)},send:function(d){if(d!=""){this.buffer.push(d)}},render_pragmas:function(d){if(!this.includes("%",d)){return d}var f=this;var e=new RegExp(this.otag+"%([\\w-]+) ?([\\w]+=[\\w]+)?"+this.ctag);return d.replace(e,function(i,g,h){if(!f.pragmas_implemented[g]){throw ({message:"This implementation of mustache doesn't understand the '"+g+"' pragma"})}f.pragmas[g]={};if(h){var j=h.split("=");f.pragmas[g][j[0]]=j[1]}return""})},render_partial:function(d,f,e){d=this.trim(d);if(!e||e[d]===undefined){throw ({message:"unknown_partial '"+d+"'"})}if(typeof(f[d])!="object"){return this.render(e[d],f,e,true)}return this.render(e[d],f[d],e,true)},render_section:function(f,e,d){if(!this.includes("#",f)&&!this.includes("^",f)){return f}var h=this;var g=new RegExp(this.otag+"(\\^|\\#)\\s*(.+)\\s*"+this.ctag+"\n*([\\s\\S]+?)"+this.otag+"\\/\\s*\\2\\s*"+this.ctag+"\\s*","mg");return f.replace(g,function(j,k,i,l){var m=h.find(i,e);if(k=="^"){if(!m||h.is_array(m)&&m.length===0){return h.render(l,e,d,true)}else{return""}}else{if(k=="#"){if(h.is_array(m)){return h.map(m,function(n){return h.render(l,h.create_context(n),d,true)}).join("")}else{if(h.is_object(m)){return h.render(l,h.create_context(m),d,true)}else{if(typeof m==="function"){return m.call(e,l,function(n){return h.render(n,e,d,true)})}else{if(m){return h.render(l,e,d,true)}else{return""}}}}}}})},render_tags:function(m,d,f,h){var g=this;var l=function(){return new RegExp(g.otag+"(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?"+g.ctag+"+","g")};var j=l();var k=function(p,i,o){switch(i){case"!":return"";case"=":g.set_delimiters(o);j=l();return"";case">":return g.render_partial(o,d,f);case"{":return g.find(o,d);default:return g.escape(g.find(o,d))}};var n=m.split("\n");for(var e=0;e<n.length;e++){n[e]=n[e].replace(j,k,this);if(!h){this.send(n[e])}}if(h){return n.join("\n")}},set_delimiters:function(e){var d=e.split(" ");this.otag=this.escape_regex(d[0]);this.ctag=this.escape_regex(d[1])},escape_regex:function(e){if(!arguments.callee.sRE){var d=["/",".","*","+","?","|","(",")","[","]","{","}","\\"];arguments.callee.sRE=new RegExp("(\\"+d.join("|\\")+")","g")}return e.replace(arguments.callee.sRE,"\\$1")},find:function(e,f){e=this.trim(e);function d(h){return h===false||h===0||h}var g;if(d(f[e])){g=f[e]}else{if(d(this.context[e])){g=this.context[e]}}if(typeof g==="function"){return g.apply(f)}if(g!==undefined){return g}return""},includes:function(e,d){return d.indexOf(this.otag+e)!=-1},escape:function(d){d=String(d===null?"":d);return d.replace(/&(?!\w+;)|["<>\\]/g,function(e){switch(e){case"&":return"&amp;";case"\\":return"\\\\";case'"':return'"';case"<":return"&lt;";case">":return"&gt;";default:return e}})},create_context:function(e){if(this.is_object(e)){return e}else{var f=".";if(this.pragmas["IMPLICIT-ITERATOR"]){f=this.pragmas["IMPLICIT-ITERATOR"].iterator}var d={};d[f]=e;return d}},is_object:function(d){return d&&typeof d=="object"},is_array:function(d){return Object.prototype.toString.call(d)==="[object Array]"},trim:function(d){return d.replace(/^\s*|\s*$/g,"")},map:function(h,f){if(typeof h.map=="function"){return h.map(f)}else{var g=[];var d=h.length;for(var e=0;e<d;e++){g.push(f(h[e]))}return g}}};return({name:"mustache.js",version:"0.3.1-dev",to_html:function(f,d,e,h){var g=new c();if(h){g.send=h}g.render(f,d,e);if(!h){return g.buffer.join("\n")}}})}()}Sammy=Sammy||{};Sammy.Mustache=function(e,c){var d=function(g,h,f){h=b.extend({},this,h);f=b.extend({},h.partials,f);return a.to_html(g,h,f)};if(!c){c="mustache"}e.helper(c,d)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.mustache-latest.min.js b/javascript/sammy/min/plugins/sammy.mustache-latest.min.js
new file mode 100644
index 00000000..2129b0c4
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.mustache-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.mustache.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:45 -0700 2010
+(function(b){if(!window.Mustache){var a=function(){var c=function(){};c.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":true},context:{},render:function(g,f,e,h){if(!h){this.context=f;this.buffer=[]}if(!this.includes("",g)){if(h){return g}else{this.send(g);return}}g=this.render_pragmas(g);var d=this.render_section(g,f,e);if(h){return this.render_tags(d,f,e,h)}this.render_tags(d,f,e,h)},send:function(d){if(d!=""){this.buffer.push(d)}},render_pragmas:function(d){if(!this.includes("%",d)){return d}var f=this;var e=new RegExp(this.otag+"%([\\w-]+) ?([\\w]+=[\\w]+)?"+this.ctag);return d.replace(e,function(i,g,h){if(!f.pragmas_implemented[g]){throw ({message:"This implementation of mustache doesn't understand the '"+g+"' pragma"})}f.pragmas[g]={};if(h){var j=h.split("=");f.pragmas[g][j[0]]=j[1]}return""})},render_partial:function(d,f,e){d=this.trim(d);if(!e||e[d]===undefined){throw ({message:"unknown_partial '"+d+"'"})}if(typeof(f[d])!="object"){return this.render(e[d],f,e,true)}return this.render(e[d],f[d],e,true)},render_section:function(f,e,d){if(!this.includes("#",f)&&!this.includes("^",f)){return f}var h=this;var g=new RegExp(this.otag+"(\\^|\\#)\\s*(.+)\\s*"+this.ctag+"\n*([\\s\\S]+?)"+this.otag+"\\/\\s*\\2\\s*"+this.ctag+"\\s*","mg");return f.replace(g,function(j,k,i,l){var m=h.find(i,e);if(k=="^"){if(!m||h.is_array(m)&&m.length===0){return h.render(l,e,d,true)}else{return""}}else{if(k=="#"){if(h.is_array(m)){return h.map(m,function(n){return h.render(l,h.create_context(n),d,true)}).join("")}else{if(h.is_object(m)){return h.render(l,h.create_context(m),d,true)}else{if(typeof m==="function"){return m.call(e,l,function(n){return h.render(n,e,d,true)})}else{if(m){return h.render(l,e,d,true)}else{return""}}}}}}})},render_tags:function(m,d,f,h){var g=this;var l=function(){return new RegExp(g.otag+"(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?"+g.ctag+"+","g")};var j=l();var k=function(p,i,o){switch(i){case"!":return"";case"=":g.set_delimiters(o);j=l();return"";case">":return g.render_partial(o,d,f);case"{":return g.find(o,d);default:return g.escape(g.find(o,d))}};var n=m.split("\n");for(var e=0;e<n.length;e++){n[e]=n[e].replace(j,k,this);if(!h){this.send(n[e])}}if(h){return n.join("\n")}},set_delimiters:function(e){var d=e.split(" ");this.otag=this.escape_regex(d[0]);this.ctag=this.escape_regex(d[1])},escape_regex:function(e){if(!arguments.callee.sRE){var d=["/",".","*","+","?","|","(",")","[","]","{","}","\\"];arguments.callee.sRE=new RegExp("(\\"+d.join("|\\")+")","g")}return e.replace(arguments.callee.sRE,"\\$1")},find:function(e,f){e=this.trim(e);function d(h){return h===false||h===0||h}var g;if(d(f[e])){g=f[e]}else{if(d(this.context[e])){g=this.context[e]}}if(typeof g==="function"){return g.apply(f)}if(g!==undefined){return g}return""},includes:function(e,d){return d.indexOf(this.otag+e)!=-1},escape:function(d){d=String(d===null?"":d);return d.replace(/&(?!\w+;)|["<>\\]/g,function(e){switch(e){case"&":return"&amp;";case"\\":return"\\\\";case'"':return'"';case"<":return"&lt;";case">":return"&gt;";default:return e}})},create_context:function(e){if(this.is_object(e)){return e}else{var f=".";if(this.pragmas["IMPLICIT-ITERATOR"]){f=this.pragmas["IMPLICIT-ITERATOR"].iterator}var d={};d[f]=e;return d}},is_object:function(d){return d&&typeof d=="object"},is_array:function(d){return Object.prototype.toString.call(d)==="[object Array]"},trim:function(d){return d.replace(/^\s*|\s*$/g,"")},map:function(h,f){if(typeof h.map=="function"){return h.map(f)}else{var g=[];var d=h.length;for(var e=0;e<d;e++){g.push(f(h[e]))}return g}}};return({name:"mustache.js",version:"0.3.1-dev",to_html:function(f,d,e,h){var g=new c();if(h){g.send=h}g.render(f,d,e);if(!h){return g.buffer.join("\n")}}})}()}Sammy=Sammy||{};Sammy.Mustache=function(e,c){var d=function(g,h,f){h=b.extend({},this,h);f=b.extend({},h.partials,f);return a.to_html(g,h,f)};if(!c){c="mustache"}e.helper(c,d)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.nested_params-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.nested_params-0.6.2.min.js
new file mode 100644
index 00000000..c412f780
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.nested_params-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.nested_params.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:45 -0700 2010
+(function(b){Sammy=Sammy||{};function a(d){d=unescape(d);if(d==="true"){return true}else{if(d==="false"){return false}else{return d}}}function c(i,h,g){var e,d,f;if(h.match(/^[^\[]+$/)){i[h]=a(g)}else{if(e=h.match(/^([^\[]+)\[\](.*)$/)){d=e[1];f=e[2];if(i[d]&&!b.isArray(i[d])){throw ("400 Bad Request")}if(f){e=f.match(/^\[([^\]]+)\](.*)$/);if(!e){throw ("400 Bad Request")}if(i[d]){if(i[d][i[d].length-1][e[1]]){i[d].push(c({},e[1]+e[2],g))}else{b.extend(true,i[d][i[d].length-1],c({},e[1]+e[2],g))}}else{i[d]=[c({},e[1]+e[2],g)]}}else{if(i[d]){i[d].push(a(g))}else{i[d]=[a(g)]}}}else{if(e=h.match(/^([^\[]+)\[([^\[]+)\](.*)$/)){d=e[1];f=e[2]+e[3];if(i[d]&&b.isArray(i[d])){throw ("400 Bad Request")}if(i[d]){b.extend(true,i[d],c(i[d],f,g))}else{i[d]=c({},f,g)}}}}return i}Sammy.NestedParams=function(d){d._parseParamPair=c}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.nested_params-latest.min.js b/javascript/sammy/min/plugins/sammy.nested_params-latest.min.js
new file mode 100644
index 00000000..c412f780
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.nested_params-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.nested_params.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:45 -0700 2010
+(function(b){Sammy=Sammy||{};function a(d){d=unescape(d);if(d==="true"){return true}else{if(d==="false"){return false}else{return d}}}function c(i,h,g){var e,d,f;if(h.match(/^[^\[]+$/)){i[h]=a(g)}else{if(e=h.match(/^([^\[]+)\[\](.*)$/)){d=e[1];f=e[2];if(i[d]&&!b.isArray(i[d])){throw ("400 Bad Request")}if(f){e=f.match(/^\[([^\]]+)\](.*)$/);if(!e){throw ("400 Bad Request")}if(i[d]){if(i[d][i[d].length-1][e[1]]){i[d].push(c({},e[1]+e[2],g))}else{b.extend(true,i[d][i[d].length-1],c({},e[1]+e[2],g))}}else{i[d]=[c({},e[1]+e[2],g)]}}else{if(i[d]){i[d].push(a(g))}else{i[d]=[a(g)]}}}else{if(e=h.match(/^([^\[]+)\[([^\[]+)\](.*)$/)){d=e[1];f=e[2]+e[3];if(i[d]&&b.isArray(i[d])){throw ("400 Bad Request")}if(i[d]){b.extend(true,i[d],c(i[d],f,g))}else{i[d]=c({},f,g)}}}}return i}Sammy.NestedParams=function(d){d._parseParamPair=c}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.path_location_proxy-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.path_location_proxy-0.6.2.min.js
new file mode 100644
index 00000000..476489c5
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.path_location_proxy-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.path_location_proxy.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:46 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.PathLocationProxy=function(b){this.app=b};Sammy.PathLocationProxy.prototype={bind:function(){},unbind:function(){},getLocation:function(){return[window.location.pathname,window.location.search].join("")},setLocation:function(b){return window.location=b}}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.path_location_proxy-latest.min.js b/javascript/sammy/min/plugins/sammy.path_location_proxy-latest.min.js
new file mode 100644
index 00000000..476489c5
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.path_location_proxy-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.path_location_proxy.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:46 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.PathLocationProxy=function(b){this.app=b};Sammy.PathLocationProxy.prototype={bind:function(){},unbind:function(){},getLocation:function(){return[window.location.pathname,window.location.search].join("")},setLocation:function(b){return window.location=b}}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.pure-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.pure-0.6.2.min.js
new file mode 100644
index 00000000..1f68eb54
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.pure-0.6.2.min.js
@@ -0,0 +1,17 @@
+// -- Sammy -- /plugins/sammy.pure.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:47 -0700 2010
+(function(c){
+/*
+  PURE Unobtrusive Rendering Engine for HTML
+
+  Licensed under the MIT licenses.
+  More information at: http://www.opensource.org
+
+  Copyright (c) 2010 Michael Cvilic - BeeBole.com
+
+  Thanks to Rog Peppe for the functional JS jump
+  revision: 2.47
+*/
+var a,b=a=function(){var e=arguments[0],d=false;if(typeof e==="string"){d=arguments[1]||false}return a.core(e,d)};a.core=function(y,k,x){var x=m(),j=[];switch(typeof y){case"string":j=x.find(k||document,y);if(j.length===0){z('The template "'+y+'" was not found')}break;case"undefined":z("The template root is undefined, check your selector");break;default:j=[y]}for(var A=0,s=j.length;A<s;A++){x[A]=j[A]}x.length=s;var r="_s"+Math.floor(Math.random()*1000000)+"_",g="_a"+Math.floor(Math.random()*1000000)+"_",f=/^(\+)?([^\@\+]+)?\@?([^\+]+)?(\+)?$/,t={IMG:"src",INPUT:"value"},q=Array.isArray?function(i){return Array.isArray(i)}:function(i){return Object.prototype.toString.call(i)==="[object Array]"};return x;function z(i){if(typeof console!=="undefined"){console.log(i)}else{alert(i)}throw ("pure error: "+i)}function m(){var i=a.plugins,H=function(){};H.prototype=i;H.prototype.compile=i.compile||o;H.prototype.render=i.render||F;H.prototype.autoRender=i.autoRender||h;H.prototype.find=i.find||w;H.prototype._compiler=u;H.prototype._error=z;return new H()}function n(i){return i.outerHTML||(function(J){var I=document.createElement("div"),H;I.appendChild(J.cloneNode(true));H=I.innerHTML;I=null;return H})(i)}function d(i,H){return function(I){return i(""+H.call(I.context,I))}}function w(H,i){if(typeof H==="string"){i=H;H=false}if(typeof document.querySelectorAll!=="undefined"){return(H||document).querySelectorAll(i)}else{z("You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine")}}function e(H,i){return function(I){var M=[H[0]],P=H.length,J,O,L,N;for(var K=1;K<P;K++){J=i[K](I);O=H[K];if(J===""){L=M[M.length-1];if((N=L.search(/[\w]+=\"?$/))>-1){M[M.length-1]=L.substring(0,N);O=O.substr(1)}}M[M.length]=J;M[M.length]=O}return M.join("")}}function p(H){var i=H.match(/^(\w+)\s*<-\s*(\S+)?$/);if(i===null){z('bad loop spec: "'+H+'"')}if(i[1]==="item"){z('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.')}if(!i[2]||(i[2]&&(/context/i).test(i[2]))){i[2]=function(I){return I.context}}return{name:i[1],sel:i[2]}}function B(L){if(typeof(L)==="function"){return L}var H=L.match(/^[a-zA-Z\$_\@][\w\$:-]*(\.[\w\$:-]*[^\.])*$/);if(H===null){var K=false,J=L,N=[],O=[],I=0,M;if(/\'|\"/.test(J.charAt(0))){if(/\'|\"/.test(J.charAt(J.length-1))){M=J.substring(1,J.length-1);return function(){return M}}}else{while((H=J.match(/#\{([^{}]+)\}/))!==null){K=true;N[I++]=J.slice(0,H.index);O[I]=B(H[1]);J=J.slice(H.index+H[0].length,J.length)}}if(!K){z("bad data selector syntax: "+L)}N[I]=J;return e(N,O)}H=L.split(".");return function(P){var S=P.context;if(!S){return""}var Q=P[H[0]],R=0;if(Q&&Q.item){S=Q.item;R+=1}var T=H.length;for(;R<T;R++){if(!S){break}S=S[H[R]]}return(!S&&S!==0)?"":S}}function G(O,H,T){var K,W,N,S,I,R=[];if(typeof H==="string"){K=H;var M=H.match(f);if(!M){z("bad selector syntax: "+H)}W=M[1];N=M[2];S=M[3];I=M[4];if(N==="."||(!N&&S)){R[0]=O}else{R=x.find(O,N)}if(!R||R.length===0){return z('The node "'+H+'" was not found in the template')}}else{W=H.prepend;S=H.attr;I=H.append;R=[O]}if(W||I){if(W&&I){z("append/prepend cannot take place at the same time")}else{if(T){z("no append/prepend/replace modifiers allowed for loop target")}else{if(I&&T){z("cannot append with loop (sel: "+K+")")}}}}var U,L,i,V,J,P,Q;if(S){V=(/^style$/i).test(S);J=(/^class$/i).test(S);P=J?"className":S;U=function(Y,X){Y.setAttribute(g+S,X);if(P in Y&&!V){Y[P]=""}if(Y.nodeType===1){Y.removeAttribute(S);J&&Y.removeAttribute(P)}};if(V||J){if(V){L=function(X){return X.style.cssText}}else{L=function(X){return X.className}}i=function(X){return X.replace(/\"/g,"&quot;")}}else{L=function(X){return X.getAttribute(S)};i=function(X){return X.replace(/\"/g,"&quot;").replace(/\s/g,"&nbsp;")}}if(W){Q=function(Y,X){U(Y,X+L(Y))}}else{if(I){Q=function(Y,X){U(Y,L(Y)+X)}}else{Q=function(Y,X){U(Y,X)}}}}else{if(T){Q=function(Z,Y){var X=Z.parentNode;if(X){X.insertBefore(document.createTextNode(Y),Z.nextSibling);X.removeChild(Z)}}}else{if(W){Q=function(Y,X){Y.insertBefore(document.createTextNode(X),Y.firstChild)}}else{if(I){Q=function(Y,X){Y.appendChild(document.createTextNode(X))}}else{Q=function(Y,X){while(Y.firstChild){Y.removeChild(Y.firstChild)}Y.appendChild(document.createTextNode(X))}}}}i=function(X){return X}}return{attr:S,nodes:R,set:Q,sel:K,quotefn:i}}function D(I,K){var J=r+K+":";for(var H=0;H<I.nodes.length;H++){I.set(I.nodes[H],J)}}function v(H,J,i,K,I){return function(R){var T=J(R),N=R[H],V={items:T},Q=0,M,P=[],S=function(X,Y,Z,W){R.pos=Y.pos=X;R.item=Y.item=T[X];R.items=T;typeof W!=="undefined"&&(R.length=W);if(typeof Z==="function"&&Z(R)===false){Q++;return}P.push(i.call(Y,R))};R[H]=V;if(q(T)){M=T.length||0;if(typeof K==="function"){T.sort(K)}for(var O=0,U=M;O<U;O++){S(O,V,I,M-Q)}}else{if(T&&typeof K!=="undefined"){z("sort is only available on arrays, not objects")}for(var L in T){T.hasOwnProperty(L)&&S(L,V,I)}}typeof N!=="undefined"?R[H]=N:delete R[H];return P.join("")}}function l(N,L,P,T){var Q=false,O,V,K,J;for(J in P){if(P.hasOwnProperty(J)){if(J==="sort"){V=P.sort;continue}else{if(J==="filter"){K=P.filter;continue}}if(Q){z("cannot have more than one loop on a target")}O=J;Q=true}}if(!O){z("Error in the selector: "+L+"\nA directive action must be a string, a function or a loop(<-)")}var I=P[O];if(typeof(I)==="string"||typeof(I)==="function"){P={};P[O]={root:I};return l(N,L,P,T)}var S=p(O),H=B(S.sel),R=G(N,L,true),i=R.nodes;for(A=0;A<i.length;A++){var M=i[A],U=u(M,I);T[T.length]=d(R.quotefn,v(S.name,H,U,V,K));R.nodes=[M];D(R,T.length-1)}}function C(J,N){var S=J.getElementsByTagName("*"),P=[],V={a:[],l:{}},K,L,O,T,M,Q,I,R,U;for(O=-1,T=S.length;O<T;O++){I=O>-1?S[O]:J;if(I.nodeType===1&&I.className!==""){R=I.className.split(" ");for(M=0,Q=R.length;M<Q;M++){U=R[M];K=H(U,I.tagName);if(K!==false){L=(/nodevalue/i).test(K.attr);if(K.sel.indexOf("@")>-1||L){I.className=I.className.replace("@"+K.attr,"");if(L){K.attr=false}}P.push({n:I,cspec:K})}}}}return P;function H(ae,X){var aa=ae.match(f),ac=aa[3]||t[X],Z={prepend:!!aa[1],prop:aa[2],attr:ac,append:!!aa[4],sel:ae},ab,af,Y,ad,W;for(ab=V.a.length-1;ab>=0;ab--){Y=V.a[ab];ad=Y.l[0];W=ad&&ad[Z.prop];if(typeof W!=="undefined"){Z.prop=Y.p+"."+Z.prop;if(V.l[Z.prop]===true){W=W[0]}break}}if(typeof W==="undefined"){W=q(N)?N[0][Z.prop]:N[Z.prop];if(typeof W==="undefined"){return false}}if(q(W)){V.a.push({l:W,p:Z.prop});V.l[Z.prop]=true;Z.t="loop"}else{Z.t="str"}return Z}}function u(W,K,aa,O){var I=[];O=O||aa&&C(W,aa);if(aa){var T,X,J,P,Z,Q,ab,R,Y;while(O.length>0){J=O[0].cspec;P=O[0].n;O.splice(0,1);if(J.t==="str"){Z=G(P,J,false);D(Z,I.length);I[I.length]=d(Z.quotefn,B(J.prop))}else{ab=B(J.sel);Z=G(P,J,true);Q=Z.nodes;for(T=0,X=Q.length;T<X;T++){R=Q[T];Y=u(R,false,aa,O);I[I.length]=d(Z.quotefn,v(J.sel,ab,Y));Z.nodes=[R];D(Z,I.length-1)}}}}var Z,L;for(var S in K){if(K.hasOwnProperty(S)){L=K[S];if(typeof(L)==="function"||typeof(L)==="string"){Z=G(W,S,false);D(Z,I.length);I[I.length]=d(Z.quotefn,B(L))}else{l(W,S,L,I)}}}var V=n(W),H=[];V=V.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig,"<$1 $3>");V=V.split(g).join("");var N=V.split(r),M;for(var U=1;U<N.length;U++){M=N[U];H[U]=I[parseInt(M,10)];N[U]=M.substring(M.indexOf(":")+1)}return e(N,H)}function o(J,H,I){var i=u((I||this[0]).cloneNode(true),J,H);return function(K){return i({context:K})}}function F(H,L){var K=typeof L==="function"?L:x.compile(L,false,this[0]);for(var I=0,J=this.length;I<J;I++){this[I]=E(this[I],K(H,false))}context=null;return this}function h(H,L){var K=x.compile(L,H,this[0]);for(var I=0,J=this.length;I<J;I++){this[I]=E(this[I],K(H,false))}context=null;return this}function E(K,H){var I,i=K.parentNode,J=0;switch(K.tagName){case"TBODY":case"THEAD":case"TFOOT":H="<TABLE>"+H+"</TABLE>";J=1;break;case"TR":H="<TABLE><TBODY>"+H+"</TBODY></TABLE>";J=2;break;case"TD":case"TH":H="<TABLE><TBODY><TR>"+H+"</TR></TBODY></TABLE>";J=3;break}tmp=document.createElement("SPAN");tmp.style.display="none";document.body.appendChild(tmp);tmp.innerHTML=H;I=tmp.firstChild;while(J--){I=I.firstChild}i.insertBefore(I,K);i.removeChild(K);document.body.removeChild(tmp);K=I;I=i=null;return K}};a.plugins={};a.libs={dojo:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return dojo.query(d,e)}}},domassistant:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return c(e).cssSelect(d)}}DOMAssistant.attach({publicMethods:["compile","render","autoRender"],compile:function(e,d){return a(this).compile(e,d)},render:function(d,e){return c(a(this).render(d,e))[0]},autoRender:function(d,e){return c(a(this).autoRender(d,e))[0]}})},jquery:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return jQuery(e).find(d)}}jQuery.fn.extend({compile:function(e,d){return a(this[0]).compile(e,d)},render:function(d,e){return jQuery(a(this[0]).render(d,e))},autoRender:function(d,e){return jQuery(a(this[0]).autoRender(d,e))}})},mootools:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return c(e).getElements(d)}}Element.implement({compile:function(e,d){return a(this).compile(e,d)},render:function(d,e){return a(this).render(d,e)},autoRender:function(d,e){return a(this).autoRender(d,e)}})},prototype:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){e=e===document?e.body:e;return typeof e==="string"?$$(e):c(e).select(d)}}Element.addMethods({compile:function(e,f,d){return a(e).compile(f,d)},render:function(e,d,f){return a(e).render(d,f)},autoRender:function(e,d,f){return a(e).autoRender(d,f)}})},sizzle:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return Sizzle(d,e)}}},sly:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return Sly(d,e)}}}};(function(){var d=typeof dojo!=="undefined"&&"dojo"||typeof DOMAssistant!=="undefined"&&"domassistant"||typeof jQuery!=="undefined"&&"jquery"||typeof MooTools!=="undefined"&&"mootools"||typeof Prototype!=="undefined"&&"prototype"||typeof Sizzle!=="undefined"&&"sizzle"||typeof Sly!=="undefined"&&"sly";d&&a.libs[d]()})();Sammy=Sammy||{};Sammy.Pure=function(f,d){var e=function(g,h,i){return c(g).autoRender(h,i)};if(!d){d="pure"}f.helper(d,e)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.pure-latest.min.js b/javascript/sammy/min/plugins/sammy.pure-latest.min.js
new file mode 100644
index 00000000..1f68eb54
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.pure-latest.min.js
@@ -0,0 +1,17 @@
+// -- Sammy -- /plugins/sammy.pure.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:47 -0700 2010
+(function(c){
+/*
+  PURE Unobtrusive Rendering Engine for HTML
+
+  Licensed under the MIT licenses.
+  More information at: http://www.opensource.org
+
+  Copyright (c) 2010 Michael Cvilic - BeeBole.com
+
+  Thanks to Rog Peppe for the functional JS jump
+  revision: 2.47
+*/
+var a,b=a=function(){var e=arguments[0],d=false;if(typeof e==="string"){d=arguments[1]||false}return a.core(e,d)};a.core=function(y,k,x){var x=m(),j=[];switch(typeof y){case"string":j=x.find(k||document,y);if(j.length===0){z('The template "'+y+'" was not found')}break;case"undefined":z("The template root is undefined, check your selector");break;default:j=[y]}for(var A=0,s=j.length;A<s;A++){x[A]=j[A]}x.length=s;var r="_s"+Math.floor(Math.random()*1000000)+"_",g="_a"+Math.floor(Math.random()*1000000)+"_",f=/^(\+)?([^\@\+]+)?\@?([^\+]+)?(\+)?$/,t={IMG:"src",INPUT:"value"},q=Array.isArray?function(i){return Array.isArray(i)}:function(i){return Object.prototype.toString.call(i)==="[object Array]"};return x;function z(i){if(typeof console!=="undefined"){console.log(i)}else{alert(i)}throw ("pure error: "+i)}function m(){var i=a.plugins,H=function(){};H.prototype=i;H.prototype.compile=i.compile||o;H.prototype.render=i.render||F;H.prototype.autoRender=i.autoRender||h;H.prototype.find=i.find||w;H.prototype._compiler=u;H.prototype._error=z;return new H()}function n(i){return i.outerHTML||(function(J){var I=document.createElement("div"),H;I.appendChild(J.cloneNode(true));H=I.innerHTML;I=null;return H})(i)}function d(i,H){return function(I){return i(""+H.call(I.context,I))}}function w(H,i){if(typeof H==="string"){i=H;H=false}if(typeof document.querySelectorAll!=="undefined"){return(H||document).querySelectorAll(i)}else{z("You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine")}}function e(H,i){return function(I){var M=[H[0]],P=H.length,J,O,L,N;for(var K=1;K<P;K++){J=i[K](I);O=H[K];if(J===""){L=M[M.length-1];if((N=L.search(/[\w]+=\"?$/))>-1){M[M.length-1]=L.substring(0,N);O=O.substr(1)}}M[M.length]=J;M[M.length]=O}return M.join("")}}function p(H){var i=H.match(/^(\w+)\s*<-\s*(\S+)?$/);if(i===null){z('bad loop spec: "'+H+'"')}if(i[1]==="item"){z('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.')}if(!i[2]||(i[2]&&(/context/i).test(i[2]))){i[2]=function(I){return I.context}}return{name:i[1],sel:i[2]}}function B(L){if(typeof(L)==="function"){return L}var H=L.match(/^[a-zA-Z\$_\@][\w\$:-]*(\.[\w\$:-]*[^\.])*$/);if(H===null){var K=false,J=L,N=[],O=[],I=0,M;if(/\'|\"/.test(J.charAt(0))){if(/\'|\"/.test(J.charAt(J.length-1))){M=J.substring(1,J.length-1);return function(){return M}}}else{while((H=J.match(/#\{([^{}]+)\}/))!==null){K=true;N[I++]=J.slice(0,H.index);O[I]=B(H[1]);J=J.slice(H.index+H[0].length,J.length)}}if(!K){z("bad data selector syntax: "+L)}N[I]=J;return e(N,O)}H=L.split(".");return function(P){var S=P.context;if(!S){return""}var Q=P[H[0]],R=0;if(Q&&Q.item){S=Q.item;R+=1}var T=H.length;for(;R<T;R++){if(!S){break}S=S[H[R]]}return(!S&&S!==0)?"":S}}function G(O,H,T){var K,W,N,S,I,R=[];if(typeof H==="string"){K=H;var M=H.match(f);if(!M){z("bad selector syntax: "+H)}W=M[1];N=M[2];S=M[3];I=M[4];if(N==="."||(!N&&S)){R[0]=O}else{R=x.find(O,N)}if(!R||R.length===0){return z('The node "'+H+'" was not found in the template')}}else{W=H.prepend;S=H.attr;I=H.append;R=[O]}if(W||I){if(W&&I){z("append/prepend cannot take place at the same time")}else{if(T){z("no append/prepend/replace modifiers allowed for loop target")}else{if(I&&T){z("cannot append with loop (sel: "+K+")")}}}}var U,L,i,V,J,P,Q;if(S){V=(/^style$/i).test(S);J=(/^class$/i).test(S);P=J?"className":S;U=function(Y,X){Y.setAttribute(g+S,X);if(P in Y&&!V){Y[P]=""}if(Y.nodeType===1){Y.removeAttribute(S);J&&Y.removeAttribute(P)}};if(V||J){if(V){L=function(X){return X.style.cssText}}else{L=function(X){return X.className}}i=function(X){return X.replace(/\"/g,"&quot;")}}else{L=function(X){return X.getAttribute(S)};i=function(X){return X.replace(/\"/g,"&quot;").replace(/\s/g,"&nbsp;")}}if(W){Q=function(Y,X){U(Y,X+L(Y))}}else{if(I){Q=function(Y,X){U(Y,L(Y)+X)}}else{Q=function(Y,X){U(Y,X)}}}}else{if(T){Q=function(Z,Y){var X=Z.parentNode;if(X){X.insertBefore(document.createTextNode(Y),Z.nextSibling);X.removeChild(Z)}}}else{if(W){Q=function(Y,X){Y.insertBefore(document.createTextNode(X),Y.firstChild)}}else{if(I){Q=function(Y,X){Y.appendChild(document.createTextNode(X))}}else{Q=function(Y,X){while(Y.firstChild){Y.removeChild(Y.firstChild)}Y.appendChild(document.createTextNode(X))}}}}i=function(X){return X}}return{attr:S,nodes:R,set:Q,sel:K,quotefn:i}}function D(I,K){var J=r+K+":";for(var H=0;H<I.nodes.length;H++){I.set(I.nodes[H],J)}}function v(H,J,i,K,I){return function(R){var T=J(R),N=R[H],V={items:T},Q=0,M,P=[],S=function(X,Y,Z,W){R.pos=Y.pos=X;R.item=Y.item=T[X];R.items=T;typeof W!=="undefined"&&(R.length=W);if(typeof Z==="function"&&Z(R)===false){Q++;return}P.push(i.call(Y,R))};R[H]=V;if(q(T)){M=T.length||0;if(typeof K==="function"){T.sort(K)}for(var O=0,U=M;O<U;O++){S(O,V,I,M-Q)}}else{if(T&&typeof K!=="undefined"){z("sort is only available on arrays, not objects")}for(var L in T){T.hasOwnProperty(L)&&S(L,V,I)}}typeof N!=="undefined"?R[H]=N:delete R[H];return P.join("")}}function l(N,L,P,T){var Q=false,O,V,K,J;for(J in P){if(P.hasOwnProperty(J)){if(J==="sort"){V=P.sort;continue}else{if(J==="filter"){K=P.filter;continue}}if(Q){z("cannot have more than one loop on a target")}O=J;Q=true}}if(!O){z("Error in the selector: "+L+"\nA directive action must be a string, a function or a loop(<-)")}var I=P[O];if(typeof(I)==="string"||typeof(I)==="function"){P={};P[O]={root:I};return l(N,L,P,T)}var S=p(O),H=B(S.sel),R=G(N,L,true),i=R.nodes;for(A=0;A<i.length;A++){var M=i[A],U=u(M,I);T[T.length]=d(R.quotefn,v(S.name,H,U,V,K));R.nodes=[M];D(R,T.length-1)}}function C(J,N){var S=J.getElementsByTagName("*"),P=[],V={a:[],l:{}},K,L,O,T,M,Q,I,R,U;for(O=-1,T=S.length;O<T;O++){I=O>-1?S[O]:J;if(I.nodeType===1&&I.className!==""){R=I.className.split(" ");for(M=0,Q=R.length;M<Q;M++){U=R[M];K=H(U,I.tagName);if(K!==false){L=(/nodevalue/i).test(K.attr);if(K.sel.indexOf("@")>-1||L){I.className=I.className.replace("@"+K.attr,"");if(L){K.attr=false}}P.push({n:I,cspec:K})}}}}return P;function H(ae,X){var aa=ae.match(f),ac=aa[3]||t[X],Z={prepend:!!aa[1],prop:aa[2],attr:ac,append:!!aa[4],sel:ae},ab,af,Y,ad,W;for(ab=V.a.length-1;ab>=0;ab--){Y=V.a[ab];ad=Y.l[0];W=ad&&ad[Z.prop];if(typeof W!=="undefined"){Z.prop=Y.p+"."+Z.prop;if(V.l[Z.prop]===true){W=W[0]}break}}if(typeof W==="undefined"){W=q(N)?N[0][Z.prop]:N[Z.prop];if(typeof W==="undefined"){return false}}if(q(W)){V.a.push({l:W,p:Z.prop});V.l[Z.prop]=true;Z.t="loop"}else{Z.t="str"}return Z}}function u(W,K,aa,O){var I=[];O=O||aa&&C(W,aa);if(aa){var T,X,J,P,Z,Q,ab,R,Y;while(O.length>0){J=O[0].cspec;P=O[0].n;O.splice(0,1);if(J.t==="str"){Z=G(P,J,false);D(Z,I.length);I[I.length]=d(Z.quotefn,B(J.prop))}else{ab=B(J.sel);Z=G(P,J,true);Q=Z.nodes;for(T=0,X=Q.length;T<X;T++){R=Q[T];Y=u(R,false,aa,O);I[I.length]=d(Z.quotefn,v(J.sel,ab,Y));Z.nodes=[R];D(Z,I.length-1)}}}}var Z,L;for(var S in K){if(K.hasOwnProperty(S)){L=K[S];if(typeof(L)==="function"||typeof(L)==="string"){Z=G(W,S,false);D(Z,I.length);I[I.length]=d(Z.quotefn,B(L))}else{l(W,S,L,I)}}}var V=n(W),H=[];V=V.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig,"<$1 $3>");V=V.split(g).join("");var N=V.split(r),M;for(var U=1;U<N.length;U++){M=N[U];H[U]=I[parseInt(M,10)];N[U]=M.substring(M.indexOf(":")+1)}return e(N,H)}function o(J,H,I){var i=u((I||this[0]).cloneNode(true),J,H);return function(K){return i({context:K})}}function F(H,L){var K=typeof L==="function"?L:x.compile(L,false,this[0]);for(var I=0,J=this.length;I<J;I++){this[I]=E(this[I],K(H,false))}context=null;return this}function h(H,L){var K=x.compile(L,H,this[0]);for(var I=0,J=this.length;I<J;I++){this[I]=E(this[I],K(H,false))}context=null;return this}function E(K,H){var I,i=K.parentNode,J=0;switch(K.tagName){case"TBODY":case"THEAD":case"TFOOT":H="<TABLE>"+H+"</TABLE>";J=1;break;case"TR":H="<TABLE><TBODY>"+H+"</TBODY></TABLE>";J=2;break;case"TD":case"TH":H="<TABLE><TBODY><TR>"+H+"</TR></TBODY></TABLE>";J=3;break}tmp=document.createElement("SPAN");tmp.style.display="none";document.body.appendChild(tmp);tmp.innerHTML=H;I=tmp.firstChild;while(J--){I=I.firstChild}i.insertBefore(I,K);i.removeChild(K);document.body.removeChild(tmp);K=I;I=i=null;return K}};a.plugins={};a.libs={dojo:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return dojo.query(d,e)}}},domassistant:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return c(e).cssSelect(d)}}DOMAssistant.attach({publicMethods:["compile","render","autoRender"],compile:function(e,d){return a(this).compile(e,d)},render:function(d,e){return c(a(this).render(d,e))[0]},autoRender:function(d,e){return c(a(this).autoRender(d,e))[0]}})},jquery:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return jQuery(e).find(d)}}jQuery.fn.extend({compile:function(e,d){return a(this[0]).compile(e,d)},render:function(d,e){return jQuery(a(this[0]).render(d,e))},autoRender:function(d,e){return jQuery(a(this[0]).autoRender(d,e))}})},mootools:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return c(e).getElements(d)}}Element.implement({compile:function(e,d){return a(this).compile(e,d)},render:function(d,e){return a(this).render(d,e)},autoRender:function(d,e){return a(this).autoRender(d,e)}})},prototype:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){e=e===document?e.body:e;return typeof e==="string"?$$(e):c(e).select(d)}}Element.addMethods({compile:function(e,f,d){return a(e).compile(f,d)},render:function(e,d,f){return a(e).render(d,f)},autoRender:function(e,d,f){return a(e).autoRender(d,f)}})},sizzle:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return Sizzle(d,e)}}},sly:function(){if(typeof document.querySelector==="undefined"){a.plugins.find=function(e,d){return Sly(d,e)}}}};(function(){var d=typeof dojo!=="undefined"&&"dojo"||typeof DOMAssistant!=="undefined"&&"domassistant"||typeof jQuery!=="undefined"&&"jquery"||typeof MooTools!=="undefined"&&"mootools"||typeof Prototype!=="undefined"&&"prototype"||typeof Sizzle!=="undefined"&&"sizzle"||typeof Sly!=="undefined"&&"sly";d&&a.libs[d]()})();Sammy=Sammy||{};Sammy.Pure=function(f,d){var e=function(g,h,i){return c(g).autoRender(h,i)};if(!d){d="pure"}f.helper(d,e)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.storage-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.storage-0.6.2.min.js
new file mode 100644
index 00000000..6fb005d7
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.storage-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.storage.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:48 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.Store=function(c){var b=this;this.options=c||{};this.name=this.options.name||"store";this.element=this.options.element||"body";this.$element=a(this.element);if(a.isArray(this.options.type)){a.each(this.options.type,function(d,e){if(Sammy.Store.isAvailable(e)){b.type=e;return false}})}else{this.type=this.options.type||"memory"}this.meta_key=this.options.meta_key||"__keys__";this.storage=new Sammy.Store[Sammy.Store.stores[this.type]](this.name,this.element,this.options)};Sammy.Store.stores={memory:"Memory",data:"Data",local:"LocalStorage",session:"SessionStorage",cookie:"Cookie"};a.extend(Sammy.Store.prototype,{isAvailable:function(){if(a.isFunction(this.storage.isAvailable)){return this.storage.isAvailable()}else{true}},exists:function(b){return this.storage.exists(b)},set:function(c,d){var b=(typeof d=="string")?d:JSON.stringify(d);c=c.toString();this.storage.set(c,b);if(c!=this.meta_key){this._addKey(c);this.$element.trigger("set-"+this.name,{key:c,value:d});this.$element.trigger("set-"+this.name+"-"+c,{key:c,value:d})}return d},get:function(b){var c=this.storage.get(b);if(typeof c=="undefined"||c==null||c==""){return c}try{return JSON.parse(c)}catch(d){return c}},clear:function(b){this._removeKey(b);return this.storage.clear(b)},clearAll:function(){var b=this;this.each(function(c,d){b.clear(c)})},keys:function(){return this.get(this.meta_key)||[]},each:function(e){var b=0,d=this.keys(),c;for(b;b<d.length;b++){c=e(d[b],this.get(d[b]));if(c===false){return false}}},filter:function(c){var b=[];this.each(function(d,e){if(c(d,e)){b.push([d,e])}return true});return b},first:function(c){var b=false;this.each(function(d,e){if(c(d,e)){b=[d,e];return false}});return b},fetch:function(b,c){if(!this.exists(b)){return this.set(b,c.apply(this))}else{return this.get(b)}},load:function(b,d,e){var c=this;a.get(d,function(f){c.set(b,f);if(e){e.apply(this,[f])}})},_addKey:function(b){var c=this.keys();if(a.inArray(b,c)==-1){c.push(b)}this.set(this.meta_key,c)},_removeKey:function(c){var d=this.keys();var b=a.inArray(c,d);if(b!=-1){d.splice(b,1)}this.set(this.meta_key,d)}});Sammy.Store.isAvailable=function(b){try{return Sammy.Store[Sammy.Store.stores[b]].prototype.isAvailable()}catch(c){return false}};Sammy.Store.Memory=function(b,c){this.name=b;this.element=c;this.namespace=[this.element,this.name].join(".");Sammy.Store.Memory.store=Sammy.Store.Memory.store||{};Sammy.Store.Memory.store[this.namespace]=Sammy.Store.Memory.store[this.namespace]||{};this.store=Sammy.Store.Memory.store[this.namespace]};a.extend(Sammy.Store.Memory.prototype,{isAvailable:function(){return true},exists:function(b){return(typeof this.store[b]!="undefined")},set:function(b,c){return this.store[b]=c},get:function(b){return this.store[b]},clear:function(b){delete this.store[b]}});Sammy.Store.Data=function(b,c){this.name=b;this.element=c;this.$element=a(c)};a.extend(Sammy.Store.Data.prototype,{isAvailable:function(){return true},exists:function(b){return !!this.$element.data(this._key(b))},set:function(b,c){return this.$element.data(this._key(b),c)},get:function(b){return this.$element.data(this._key(b))},clear:function(b){this.$element.removeData(this._key(b))},_key:function(b){return["store",this.name,b].join(".")}});Sammy.Store.LocalStorage=function(b,c){this.name=b;this.element=c};a.extend(Sammy.Store.LocalStorage.prototype,{isAvailable:function(){return("localStorage" in window)&&(window.location.protocol!="file:")},exists:function(b){return(this.get(b)!=null)},set:function(b,c){return window.localStorage.setItem(this._key(b),c)},get:function(b){return window.localStorage.getItem(this._key(b))},clear:function(b){window.localStorage.removeItem(this._key(b))},_key:function(b){return["store",this.element,this.name,b].join(".")}});Sammy.Store.SessionStorage=function(b,c){this.name=b;this.element=c};a.extend(Sammy.Store.SessionStorage.prototype,{isAvailable:function(){return("sessionStorage" in window)&&(window.location.protocol!="file:")&&(a.isFunction(window.sessionStorage.setItem))},exists:function(b){return(this.get(b)!=null)},set:function(b,c){return window.sessionStorage.setItem(this._key(b),c)},get:function(b){var c=window.sessionStorage.getItem(this._key(b));if(c&&typeof c.value!="undefined"){c=c.value}return c},clear:function(b){window.sessionStorage.removeItem(this._key(b))},_key:function(b){return["store",this.element,this.name,b].join(".")}});Sammy.Store.Cookie=function(c,d,b){this.name=c;this.element=d;this.options=b||{};this.path=this.options.path||"/";this.expires_in=this.options.expires_in||(14*24*60*60)};a.extend(Sammy.Store.Cookie.prototype,{isAvailable:function(){return("cookie" in document)&&(window.location.protocol!="file:")},exists:function(b){return(this.get(b)!=null)},set:function(b,c){return this._setCookie(b,c)},get:function(b){return this._getCookie(b)},clear:function(b){this._setCookie(b,"",-1)},_key:function(b){return["store",this.element,this.name,b].join(".")},_getCookie:function(c){var d=this._key(c).replace(/(\.|\*|\(|\)|\[|\])/g,"\\$1");var b=document.cookie.match("(^|;\\s)"+d+"=([^;]*)(;|$)");return(b?b[2]:null)},_setCookie:function(e,f,c){if(!c){c=(this.expires_in*1000)}var d=new Date();d.setTime(d.getTime()+c);var b=[this._key(e),"=",f,"; expires=",d.toGMTString(),"; path=",this.path].join("");document.cookie=b}});Sammy.Storage=function(b){this.use(Sammy.JSON);this.stores=this.stores||{};this.store=function(d,c){if(typeof this.stores[d]=="undefined"){var e="clear"+d.substr(0,1).toUpperCase()+d.substr(1);this.stores[d]=new Sammy.Store(a.extend({name:d,element:this.element_selector},c||{}));this[d]=function(f,g){if(typeof g=="undefined"){return this.stores[d].get(f)}else{if(a.isFunction(g)){return this.stores[d].fetch(f,g)}else{return this.stores[d].set(f,g)}}};this[e]=function(){return this.stores[d].clearAll()};this.helper(d,function(){return this.app[d].apply(this.app,arguments)});this.helper(e,function(){return this.app[e]()})}return this.stores[d]};this.helpers({store:function(){return this.app.store.apply(this.app,arguments)}})};Sammy.Session=function(c,b){this.use(Sammy.Storage);this.store("session",a.extend({type:["local","cookie","memory"]},b))};Sammy.Cache=function(c,b){this.use(Sammy.Storage);this.cache_partials=true;this.store("cache",a.extend({type:["local","session","memory"]},b))}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.storage-latest.min.js b/javascript/sammy/min/plugins/sammy.storage-latest.min.js
new file mode 100644
index 00000000..6fb005d7
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.storage-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.storage.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:48 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.Store=function(c){var b=this;this.options=c||{};this.name=this.options.name||"store";this.element=this.options.element||"body";this.$element=a(this.element);if(a.isArray(this.options.type)){a.each(this.options.type,function(d,e){if(Sammy.Store.isAvailable(e)){b.type=e;return false}})}else{this.type=this.options.type||"memory"}this.meta_key=this.options.meta_key||"__keys__";this.storage=new Sammy.Store[Sammy.Store.stores[this.type]](this.name,this.element,this.options)};Sammy.Store.stores={memory:"Memory",data:"Data",local:"LocalStorage",session:"SessionStorage",cookie:"Cookie"};a.extend(Sammy.Store.prototype,{isAvailable:function(){if(a.isFunction(this.storage.isAvailable)){return this.storage.isAvailable()}else{true}},exists:function(b){return this.storage.exists(b)},set:function(c,d){var b=(typeof d=="string")?d:JSON.stringify(d);c=c.toString();this.storage.set(c,b);if(c!=this.meta_key){this._addKey(c);this.$element.trigger("set-"+this.name,{key:c,value:d});this.$element.trigger("set-"+this.name+"-"+c,{key:c,value:d})}return d},get:function(b){var c=this.storage.get(b);if(typeof c=="undefined"||c==null||c==""){return c}try{return JSON.parse(c)}catch(d){return c}},clear:function(b){this._removeKey(b);return this.storage.clear(b)},clearAll:function(){var b=this;this.each(function(c,d){b.clear(c)})},keys:function(){return this.get(this.meta_key)||[]},each:function(e){var b=0,d=this.keys(),c;for(b;b<d.length;b++){c=e(d[b],this.get(d[b]));if(c===false){return false}}},filter:function(c){var b=[];this.each(function(d,e){if(c(d,e)){b.push([d,e])}return true});return b},first:function(c){var b=false;this.each(function(d,e){if(c(d,e)){b=[d,e];return false}});return b},fetch:function(b,c){if(!this.exists(b)){return this.set(b,c.apply(this))}else{return this.get(b)}},load:function(b,d,e){var c=this;a.get(d,function(f){c.set(b,f);if(e){e.apply(this,[f])}})},_addKey:function(b){var c=this.keys();if(a.inArray(b,c)==-1){c.push(b)}this.set(this.meta_key,c)},_removeKey:function(c){var d=this.keys();var b=a.inArray(c,d);if(b!=-1){d.splice(b,1)}this.set(this.meta_key,d)}});Sammy.Store.isAvailable=function(b){try{return Sammy.Store[Sammy.Store.stores[b]].prototype.isAvailable()}catch(c){return false}};Sammy.Store.Memory=function(b,c){this.name=b;this.element=c;this.namespace=[this.element,this.name].join(".");Sammy.Store.Memory.store=Sammy.Store.Memory.store||{};Sammy.Store.Memory.store[this.namespace]=Sammy.Store.Memory.store[this.namespace]||{};this.store=Sammy.Store.Memory.store[this.namespace]};a.extend(Sammy.Store.Memory.prototype,{isAvailable:function(){return true},exists:function(b){return(typeof this.store[b]!="undefined")},set:function(b,c){return this.store[b]=c},get:function(b){return this.store[b]},clear:function(b){delete this.store[b]}});Sammy.Store.Data=function(b,c){this.name=b;this.element=c;this.$element=a(c)};a.extend(Sammy.Store.Data.prototype,{isAvailable:function(){return true},exists:function(b){return !!this.$element.data(this._key(b))},set:function(b,c){return this.$element.data(this._key(b),c)},get:function(b){return this.$element.data(this._key(b))},clear:function(b){this.$element.removeData(this._key(b))},_key:function(b){return["store",this.name,b].join(".")}});Sammy.Store.LocalStorage=function(b,c){this.name=b;this.element=c};a.extend(Sammy.Store.LocalStorage.prototype,{isAvailable:function(){return("localStorage" in window)&&(window.location.protocol!="file:")},exists:function(b){return(this.get(b)!=null)},set:function(b,c){return window.localStorage.setItem(this._key(b),c)},get:function(b){return window.localStorage.getItem(this._key(b))},clear:function(b){window.localStorage.removeItem(this._key(b))},_key:function(b){return["store",this.element,this.name,b].join(".")}});Sammy.Store.SessionStorage=function(b,c){this.name=b;this.element=c};a.extend(Sammy.Store.SessionStorage.prototype,{isAvailable:function(){return("sessionStorage" in window)&&(window.location.protocol!="file:")&&(a.isFunction(window.sessionStorage.setItem))},exists:function(b){return(this.get(b)!=null)},set:function(b,c){return window.sessionStorage.setItem(this._key(b),c)},get:function(b){var c=window.sessionStorage.getItem(this._key(b));if(c&&typeof c.value!="undefined"){c=c.value}return c},clear:function(b){window.sessionStorage.removeItem(this._key(b))},_key:function(b){return["store",this.element,this.name,b].join(".")}});Sammy.Store.Cookie=function(c,d,b){this.name=c;this.element=d;this.options=b||{};this.path=this.options.path||"/";this.expires_in=this.options.expires_in||(14*24*60*60)};a.extend(Sammy.Store.Cookie.prototype,{isAvailable:function(){return("cookie" in document)&&(window.location.protocol!="file:")},exists:function(b){return(this.get(b)!=null)},set:function(b,c){return this._setCookie(b,c)},get:function(b){return this._getCookie(b)},clear:function(b){this._setCookie(b,"",-1)},_key:function(b){return["store",this.element,this.name,b].join(".")},_getCookie:function(c){var d=this._key(c).replace(/(\.|\*|\(|\)|\[|\])/g,"\\$1");var b=document.cookie.match("(^|;\\s)"+d+"=([^;]*)(;|$)");return(b?b[2]:null)},_setCookie:function(e,f,c){if(!c){c=(this.expires_in*1000)}var d=new Date();d.setTime(d.getTime()+c);var b=[this._key(e),"=",f,"; expires=",d.toGMTString(),"; path=",this.path].join("");document.cookie=b}});Sammy.Storage=function(b){this.use(Sammy.JSON);this.stores=this.stores||{};this.store=function(d,c){if(typeof this.stores[d]=="undefined"){var e="clear"+d.substr(0,1).toUpperCase()+d.substr(1);this.stores[d]=new Sammy.Store(a.extend({name:d,element:this.element_selector},c||{}));this[d]=function(f,g){if(typeof g=="undefined"){return this.stores[d].get(f)}else{if(a.isFunction(g)){return this.stores[d].fetch(f,g)}else{return this.stores[d].set(f,g)}}};this[e]=function(){return this.stores[d].clearAll()};this.helper(d,function(){return this.app[d].apply(this.app,arguments)});this.helper(e,function(){return this.app[e]()})}return this.stores[d]};this.helpers({store:function(){return this.app.store.apply(this.app,arguments)}})};Sammy.Session=function(c,b){this.use(Sammy.Storage);this.store("session",a.extend({type:["local","cookie","memory"]},b))};Sammy.Cache=function(c,b){this.use(Sammy.Storage);this.cache_partials=true;this.store("cache",a.extend({type:["local","session","memory"]},b))}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.template-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.template-0.6.2.min.js
new file mode 100644
index 00000000..47803655
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.template-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.template.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:49 -0700 2010
+(function(c){var a={};var b=function(d,e,f){if(a[d]){fn=a[d]}else{if(typeof e=="undefined"){return false}fn=a[d]=new Function("obj",'var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push("'+e.replace(/[\r\t\n]/g," ").replace(/\"/g,'\\"').split("<%").join("\t").replace(/((^|%>)[^\t]*)/g,"$1\r").replace(/\t=(.*?)%>/g,'",$1,"').split("\t").join('");').split("%>").join('p.push("').split("\r").join("")+"\");}return p.join('');")}if(typeof f!="undefined"){return fn(f)}else{return fn}};Sammy=Sammy||{};Sammy.Template=function(f,d){var e=function(h,i,g){if(typeof g=="undefined"){g=h}return b(g,h,c.extend({},this,i))};if(!d){d="template"}f.helper(d,e)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.template-latest.min.js b/javascript/sammy/min/plugins/sammy.template-latest.min.js
new file mode 100644
index 00000000..47803655
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.template-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.template.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:49 -0700 2010
+(function(c){var a={};var b=function(d,e,f){if(a[d]){fn=a[d]}else{if(typeof e=="undefined"){return false}fn=a[d]=new Function("obj",'var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push("'+e.replace(/[\r\t\n]/g," ").replace(/\"/g,'\\"').split("<%").join("\t").replace(/((^|%>)[^\t]*)/g,"$1\r").replace(/\t=(.*?)%>/g,'",$1,"').split("\t").join('");').split("%>").join('p.push("').split("\r").join("")+"\");}return p.join('');")}if(typeof f!="undefined"){return fn(f)}else{return fn}};Sammy=Sammy||{};Sammy.Template=function(f,d){var e=function(h,i,g){if(typeof g=="undefined"){g=h}return b(g,h,c.extend({},this,i))};if(!d){d="template"}f.helper(d,e)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.title-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.title-0.6.2.min.js
new file mode 100644
index 00000000..fc7265c9
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.title-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.title.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:49 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.Title=function(){this.setTitle=function(b){if(!a.isFunction(b)){this.title_function=function(c){return[b,c].join(" ")}}else{this.title_function=b}};this.helper("title",function(){var b=a.makeArray(arguments).join(" ");if(this.app.title_function){b=this.app.title_function(b)}document.title=b})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.title-latest.min.js b/javascript/sammy/min/plugins/sammy.title-latest.min.js
new file mode 100644
index 00000000..fc7265c9
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.title-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.title.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:49 -0700 2010
+(function(a){Sammy=Sammy||{};Sammy.Title=function(){this.setTitle=function(b){if(!a.isFunction(b)){this.title_function=function(c){return[b,c].join(" ")}}else{this.title_function=b}};this.helper("title",function(){var b=a.makeArray(arguments).join(" ");if(this.app.title_function){b=this.app.title_function(b)}document.title=b})}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.tmpl-0.6.2.min.js b/javascript/sammy/min/plugins/sammy.tmpl-0.6.2.min.js
new file mode 100644
index 00000000..aa1704e6
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.tmpl-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.tmpl.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:50 -0700 2010
+(function(i,f){var t=i.fn.domManip,h="_tmplitem",u=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,p={},e={},y,x={key:0,data:{}},w=0,q=0,g=[];function k(B,A,D,E){var C={data:E||(A?A.data:{}),_wrap:A?A._wrap:null,tmpl:null,parent:A||null,nodes:[],calls:c,nest:b,wrap:n,html:r,update:z};if(B){i.extend(C,B,{nodes:[],parent:A})}if(D){C.tmpl=D;C._ctnt=C._ctnt||C.tmpl(i,C);C.key=++w;(g.length?e:p)[w]=C}return C}i.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(A,B){i.fn[A]=function(C){var F=[],I=i(C),E,G,D,J,H=this.length===1&&this[0].parentNode;y=p||{};if(H&&H.nodeType===11&&H.childNodes.length===1&&I.length===1){I[B](this[0]);F=this}else{for(G=0,D=I.length;G<D;G++){q=G;E=(G>0?this.clone(true):this).get();i.fn[B].apply(i(I[G]),E);F=F.concat(E)}q=0;F=this.pushStack(F,A,I.selector)}J=y;y=null;i.tmpl.complete(J);return F}});i.fn.extend({tmpl:function(C,B,A){return i.tmpl(this[0],C,B,A)},tmplItem:function(){return i.tmplItem(this[0])},template:function(A){return i.template(A,this[0])},domManip:function(C,G,H,B){if(C[0]&&C[0].nodeType){var F=i.makeArray(arguments),E=C.length,D=0,A;while(D<E&&!(A=i.data(C[D++],"tmplItem"))){}if(E>1){F[0]=[i.makeArray(C)]}if(A&&q){F[2]=function(I){i.tmpl.afterManip(this,I,H)}}t.apply(this,F)}else{t.apply(this,arguments)}q=0;if(!y){i.tmpl.complete(p)}return this}});i.extend({tmpl:function(C,F,E,B){var D,A=!B;if(A){B=x;C=i.template[C]||i.template(null,C);e={}}else{if(!C){C=B.tmpl;p[B.key]=B;B.nodes=[];if(B.wrapped){s(B,B.wrapped)}return i(m(B,null,B.tmpl(i,B)))}}if(!C){return[]}if(typeof F==="function"){F=F.call(B||{})}if(E&&E.wrapped){s(E,E.wrapped)}D=i.isArray(F)?i.map(F,function(G){return G?k(E,B,C,G):null}):[k(E,B,C,F)];return A?i(m(B,null,D)):D},tmplItem:function(B){var A;if(B instanceof i){B=B[0]}while(B&&B.nodeType===1&&!(A=i.data(B,"tmplItem"))&&(B=B.parentNode)){}return A||x},template:function(B,A){if(A){if(typeof A==="string"){A=l(A)}else{if(A instanceof i){A=A[0]||{}}}if(A.nodeType){A=i.data(A,"tmpl")||i.data(A,"tmpl",l(A.innerHTML))}return typeof B==="string"?(i.template[B]=A):A}return B?(typeof B!=="string"?i.template(null,B):(i.template[B]||i.template(null,u.test(B)?B:i(B)))):null},encode:function(A){return(""+A).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;")}});i.extend(i.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){_=_.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(_,$1,$2);_=[];",close:"call=$item.calls();_=call._.concat($item.wrap(call,_));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){_.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){_.push($.encode($1a));}"},"!":{open:""}},complete:function(A){p={}},afterManip:function v(C,A,D){var B=A.nodeType===11?i.makeArray(A.childNodes):A.nodeType===1?[A]:[];D.call(C,A);o(B);q++}});function m(A,E,C){var D,B=C?i.map(C,function(F){return(typeof F==="string")?(A.key?F.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+h+'="'+A.key+'" $2'):F):m(F,A,F._ctnt)}):A;if(E){return B}B=B.join("");B.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(G,H,F,I){D=i(F).get();o(D);if(H){D=a(H).concat(D)}if(I){D=D.concat(a(I))}});return D?D:a(B)}function a(B){var A=document.createElement("div");A.innerHTML=B;return i.makeArray(A.childNodes)}function l(A){return new Function("jQuery","$item","var $=jQuery,call,_=[],$data=$item.data;with($data){_.push('"+i.trim(A).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(I,C,G,D,E,J,F){var L=i.tmpl.tag[G],B,H,K;if(!L){throw"Template command not found: "+G}B=L._default||[];if(J&&!/\w$/.test(E)){E+=J;J=""}if(E){E=j(E);F=F?(","+j(F)+")"):(J?")":"");H=J?(E.indexOf(".")>-1?E+J:("("+E+").call($item"+F)):E;K=J?H:"(typeof("+E+")==='function'?("+E+").call($item):("+E+"))"}else{K=H=B.$1||"null"}D=j(D);return"');"+L[C?"close":"open"].split("$notnull_1").join(E?"typeof("+E+")!=='undefined' && ("+E+")!=null":"true").split("$1a").join(K).split("$1").join(H).split("$2").join(D?D.replace(/\s*([^\(]+)\s*(\((.*?)\))?/g,function(N,M,O,P){P=P?(","+P+")"):(O?")":"");return P?("("+M+").call($item"+P):N}):(B.$2||""))+"_.push('"})+"');}return _;")}function s(B,A){B._wrap=m(B,true,i.isArray(A)?A:[u.test(A)?A:i(A).html()]).join("")}function j(A){return A?A.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function d(A){var B=document.createElement("div");B.appendChild(A.cloneNode(true));return B.innerHTML}function o(G){var I="_"+q,B,A,E={},F,D,C;for(F=0,D=G.length;F<D;F++){if((B=G[F]).nodeType!==1){continue}A=B.getElementsByTagName("*");for(C=A.length-1;C>=0;C--){H(A[C])}H(B)}function H(O){var L,N=O,M,J,K;if((K=O.getAttribute(h))){while(N.parentNode&&(N=N.parentNode).nodeType===1&&!(L=N.getAttribute(h))){}if(L!==K){N=N.parentNode?(N.nodeType===11?0:(N.getAttribute(h)||0)):0;if(!(J=p[K])){J=e[K];J=k(J,p[N]||e[N],null,true);J.key=++w;p[w]=J}if(q){P(K)}}O.removeAttribute(h)}else{if(q&&(J=i.data(O,"tmplItem"))){P(J.key);p[J.key]=J;N=i.data(O.parentNode,"tmplItem");N=N?N.key:0}}if(J){M=J;while(M&&M.key!=N){M.nodes.push(O);M=M.parent}delete J._ctnt;delete J._wrap;i.data(O,"tmplItem",J)}function P(Q){Q=Q+I;J=E[Q]=(E[Q]||k(J,p[J.parent.key+I]||J.parent,null,true))}}}function c(C,A,D,B){if(!C){return g.pop()}g.push({_:C,tmpl:A,item:this,data:D,options:B})}function b(A,C,B){return i.tmpl(i.template(A),C,B,this)}function n(C,A){var B=C.options||{};B.wrapped=A;return i.tmpl(i.template(C.tmpl),C.data,B,C.item)}function r(B,C){var A=this._wrap;return i.map(i(i.isArray(A)?A.join(""):A).filter(B||"*"),function(D){return C?D.innerText||D.textContent:D.outerHTML||d(D)})}function z(){var A=this.nodes;i.tmpl(null,null,null,this).insertBefore(A[0]);i(A).remove()}Sammy=Sammy||{};Sammy.Tmpl=function(C,A){var B=function(E,F,D){if(typeof D=="undefined"){D=E}if(!i.template[D]){i.template(D,E)}return i.tmpl(D,i.extend({},this,F))};if(!A){A="tmpl"}C.helper(A,B)}})(jQuery);
diff --git a/javascript/sammy/min/plugins/sammy.tmpl-latest.min.js b/javascript/sammy/min/plugins/sammy.tmpl-latest.min.js
new file mode 100644
index 00000000..aa1704e6
--- /dev/null
+++ b/javascript/sammy/min/plugins/sammy.tmpl-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /plugins/sammy.tmpl.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:50 -0700 2010
+(function(i,f){var t=i.fn.domManip,h="_tmplitem",u=/^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,p={},e={},y,x={key:0,data:{}},w=0,q=0,g=[];function k(B,A,D,E){var C={data:E||(A?A.data:{}),_wrap:A?A._wrap:null,tmpl:null,parent:A||null,nodes:[],calls:c,nest:b,wrap:n,html:r,update:z};if(B){i.extend(C,B,{nodes:[],parent:A})}if(D){C.tmpl=D;C._ctnt=C._ctnt||C.tmpl(i,C);C.key=++w;(g.length?e:p)[w]=C}return C}i.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(A,B){i.fn[A]=function(C){var F=[],I=i(C),E,G,D,J,H=this.length===1&&this[0].parentNode;y=p||{};if(H&&H.nodeType===11&&H.childNodes.length===1&&I.length===1){I[B](this[0]);F=this}else{for(G=0,D=I.length;G<D;G++){q=G;E=(G>0?this.clone(true):this).get();i.fn[B].apply(i(I[G]),E);F=F.concat(E)}q=0;F=this.pushStack(F,A,I.selector)}J=y;y=null;i.tmpl.complete(J);return F}});i.fn.extend({tmpl:function(C,B,A){return i.tmpl(this[0],C,B,A)},tmplItem:function(){return i.tmplItem(this[0])},template:function(A){return i.template(A,this[0])},domManip:function(C,G,H,B){if(C[0]&&C[0].nodeType){var F=i.makeArray(arguments),E=C.length,D=0,A;while(D<E&&!(A=i.data(C[D++],"tmplItem"))){}if(E>1){F[0]=[i.makeArray(C)]}if(A&&q){F[2]=function(I){i.tmpl.afterManip(this,I,H)}}t.apply(this,F)}else{t.apply(this,arguments)}q=0;if(!y){i.tmpl.complete(p)}return this}});i.extend({tmpl:function(C,F,E,B){var D,A=!B;if(A){B=x;C=i.template[C]||i.template(null,C);e={}}else{if(!C){C=B.tmpl;p[B.key]=B;B.nodes=[];if(B.wrapped){s(B,B.wrapped)}return i(m(B,null,B.tmpl(i,B)))}}if(!C){return[]}if(typeof F==="function"){F=F.call(B||{})}if(E&&E.wrapped){s(E,E.wrapped)}D=i.isArray(F)?i.map(F,function(G){return G?k(E,B,C,G):null}):[k(E,B,C,F)];return A?i(m(B,null,D)):D},tmplItem:function(B){var A;if(B instanceof i){B=B[0]}while(B&&B.nodeType===1&&!(A=i.data(B,"tmplItem"))&&(B=B.parentNode)){}return A||x},template:function(B,A){if(A){if(typeof A==="string"){A=l(A)}else{if(A instanceof i){A=A[0]||{}}}if(A.nodeType){A=i.data(A,"tmpl")||i.data(A,"tmpl",l(A.innerHTML))}return typeof B==="string"?(i.template[B]=A):A}return B?(typeof B!=="string"?i.template(null,B):(i.template[B]||i.template(null,u.test(B)?B:i(B)))):null},encode:function(A){return(""+A).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;")}});i.extend(i.tmpl,{tag:{tmpl:{_default:{$2:"null"},open:"if($notnull_1){_=_.concat($item.nest($1,$2));}"},wrap:{_default:{$2:"null"},open:"$item.calls(_,$1,$2);_=[];",close:"call=$item.calls();_=call._.concat($item.wrap(call,_));"},each:{_default:{$2:"$index, $value"},open:"if($notnull_1){$.each($1a,function($2){with(this){",close:"}});}"},"if":{open:"if(($notnull_1) && $1a){",close:"}"},"else":{_default:{$1:"true"},open:"}else if(($notnull_1) && $1a){"},html:{open:"if($notnull_1){_.push($1a);}"},"=":{_default:{$1:"$data"},open:"if($notnull_1){_.push($.encode($1a));}"},"!":{open:""}},complete:function(A){p={}},afterManip:function v(C,A,D){var B=A.nodeType===11?i.makeArray(A.childNodes):A.nodeType===1?[A]:[];D.call(C,A);o(B);q++}});function m(A,E,C){var D,B=C?i.map(C,function(F){return(typeof F==="string")?(A.key?F.replace(/(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g,"$1 "+h+'="'+A.key+'" $2'):F):m(F,A,F._ctnt)}):A;if(E){return B}B=B.join("");B.replace(/^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/,function(G,H,F,I){D=i(F).get();o(D);if(H){D=a(H).concat(D)}if(I){D=D.concat(a(I))}});return D?D:a(B)}function a(B){var A=document.createElement("div");A.innerHTML=B;return i.makeArray(A.childNodes)}function l(A){return new Function("jQuery","$item","var $=jQuery,call,_=[],$data=$item.data;with($data){_.push('"+i.trim(A).replace(/([\\'])/g,"\\$1").replace(/[\r\t\n]/g," ").replace(/\$\{([^\}]*)\}/g,"{{= $1}}").replace(/\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,function(I,C,G,D,E,J,F){var L=i.tmpl.tag[G],B,H,K;if(!L){throw"Template command not found: "+G}B=L._default||[];if(J&&!/\w$/.test(E)){E+=J;J=""}if(E){E=j(E);F=F?(","+j(F)+")"):(J?")":"");H=J?(E.indexOf(".")>-1?E+J:("("+E+").call($item"+F)):E;K=J?H:"(typeof("+E+")==='function'?("+E+").call($item):("+E+"))"}else{K=H=B.$1||"null"}D=j(D);return"');"+L[C?"close":"open"].split("$notnull_1").join(E?"typeof("+E+")!=='undefined' && ("+E+")!=null":"true").split("$1a").join(K).split("$1").join(H).split("$2").join(D?D.replace(/\s*([^\(]+)\s*(\((.*?)\))?/g,function(N,M,O,P){P=P?(","+P+")"):(O?")":"");return P?("("+M+").call($item"+P):N}):(B.$2||""))+"_.push('"})+"');}return _;")}function s(B,A){B._wrap=m(B,true,i.isArray(A)?A:[u.test(A)?A:i(A).html()]).join("")}function j(A){return A?A.replace(/\\'/g,"'").replace(/\\\\/g,"\\"):null}function d(A){var B=document.createElement("div");B.appendChild(A.cloneNode(true));return B.innerHTML}function o(G){var I="_"+q,B,A,E={},F,D,C;for(F=0,D=G.length;F<D;F++){if((B=G[F]).nodeType!==1){continue}A=B.getElementsByTagName("*");for(C=A.length-1;C>=0;C--){H(A[C])}H(B)}function H(O){var L,N=O,M,J,K;if((K=O.getAttribute(h))){while(N.parentNode&&(N=N.parentNode).nodeType===1&&!(L=N.getAttribute(h))){}if(L!==K){N=N.parentNode?(N.nodeType===11?0:(N.getAttribute(h)||0)):0;if(!(J=p[K])){J=e[K];J=k(J,p[N]||e[N],null,true);J.key=++w;p[w]=J}if(q){P(K)}}O.removeAttribute(h)}else{if(q&&(J=i.data(O,"tmplItem"))){P(J.key);p[J.key]=J;N=i.data(O.parentNode,"tmplItem");N=N?N.key:0}}if(J){M=J;while(M&&M.key!=N){M.nodes.push(O);M=M.parent}delete J._ctnt;delete J._wrap;i.data(O,"tmplItem",J)}function P(Q){Q=Q+I;J=E[Q]=(E[Q]||k(J,p[J.parent.key+I]||J.parent,null,true))}}}function c(C,A,D,B){if(!C){return g.pop()}g.push({_:C,tmpl:A,item:this,data:D,options:B})}function b(A,C,B){return i.tmpl(i.template(A),C,B,this)}function n(C,A){var B=C.options||{};B.wrapped=A;return i.tmpl(i.template(C.tmpl),C.data,B,C.item)}function r(B,C){var A=this._wrap;return i.map(i(i.isArray(A)?A.join(""):A).filter(B||"*"),function(D){return C?D.innerText||D.textContent:D.outerHTML||d(D)})}function z(){var A=this.nodes;i.tmpl(null,null,null,this).insertBefore(A[0]);i(A).remove()}Sammy=Sammy||{};Sammy.Tmpl=function(C,A){var B=function(E,F,D){if(typeof D=="undefined"){D=E}if(!i.template[D]){i.template(D,E)}return i.tmpl(D,i.extend({},this,F))};if(!A){A="tmpl"}C.helper(A,B)}})(jQuery);
diff --git a/javascript/sammy/min/sammy-0.6.2.min.js b/javascript/sammy/min/sammy-0.6.2.min.js
new file mode 100644
index 00000000..ae03d902
--- /dev/null
+++ b/javascript/sammy/min/sammy-0.6.2.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /sammy.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:51 -0700 2010
+(function(g,i){var n,f="([^/]+)",j=/:([\w\d]+)/g,k=/\?([^#]*)$/,b=function(o){return Array.prototype.slice.call(o)},c=function(o){return Object.prototype.toString.call(o)==="[object Function]"},l=function(o){return Object.prototype.toString.call(o)==="[object Array]"},h=decodeURIComponent,e=function(o){return o.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")},m=function(o){return function(p,q){return this.route.apply(this,[o,p,q])}},a={},d=[];n=function(){var p=b(arguments),q,o;n.apps=n.apps||{};if(p.length===0||p[0]&&c(p[0])){return n.apply(n,["body"].concat(p))}else{if(typeof(o=p.shift())=="string"){q=n.apps[o]||new n.Application();q.element_selector=o;if(p.length>0){g.each(p,function(r,s){q.use(s)})}if(q.element_selector!=o){delete n.apps[o]}n.apps[q.element_selector]=q;return q}}};n.VERSION="0.6.2";n.addLogger=function(o){d.push(o)};n.log=function(){var o=b(arguments);o.unshift("["+Date()+"]");g.each(d,function(q,p){p.apply(n,o)})};if(typeof i.console!="undefined"){if(c(console.log.apply)){n.addLogger(function(){i.console.log.apply(console,arguments)})}else{n.addLogger(function(){i.console.log(arguments)})}}else{if(typeof console!="undefined"){n.addLogger(function(){console.log.apply(console,arguments)})}}g.extend(n,{makeArray:b,isFunction:c,isArray:l});n.Object=function(o){return g.extend(this,o||{})};g.extend(n.Object.prototype,{escapeHTML:e,h:e,toHash:function(){var o={};g.each(this,function(q,p){if(!c(p)){o[q]=p}});return o},toHTML:function(){var o="";g.each(this,function(q,p){if(!c(p)){o+="<strong>"+q+"</strong> "+p+"<br />"}});return o},keys:function(o){var p=[];for(var q in this){if(!c(this[q])||!o){p.push(q)}}return p},has:function(o){return this[o]&&g.trim(this[o].toString())!=""},join:function(){var p=b(arguments);var o=p.shift();return p.join(o)},log:function(){n.log.apply(n,arguments)},toString:function(o){var p=[];g.each(this,function(r,q){if(!c(q)||o){p.push('"'+r+'": '+q.toString())}});return"Sammy.Object: {"+p.join(",")+"}"}});n.HashLocationProxy=function(p,o){this.app=p;this.is_native=false;this._startPolling(o)};n.HashLocationProxy.prototype={bind:function(){var o=this,p=this.app;g(i).bind("hashchange."+this.app.eventNamespace(),function(r,q){if(o.is_native===false&&!q){n.log("native hash change exists, using");o.is_native=true;i.clearInterval(n.HashLocationProxy._interval)}p.trigger("location-changed")});if(!n.HashLocationProxy._bindings){n.HashLocationProxy._bindings=0}n.HashLocationProxy._bindings++},unbind:function(){g(i).unbind("hashchange."+this.app.eventNamespace());n.HashLocationProxy._bindings--;if(n.HashLocationProxy._bindings<=0){i.clearInterval(n.HashLocationProxy._interval)}},getLocation:function(){var o=i.location.toString().match(/^[^#]*(#.+)$/);return o?o[1]:""},setLocation:function(o){return(i.location=o)},_startPolling:function(q){var p=this;if(!n.HashLocationProxy._interval){if(!q){q=10}var o=function(){var r=p.getLocation();if(!n.HashLocationProxy._last_location||r!=n.HashLocationProxy._last_location){i.setTimeout(function(){g(i).trigger("hashchange",[true])},13)}n.HashLocationProxy._last_location=r};o();n.HashLocationProxy._interval=i.setInterval(o,q)}}};n.Application=function(o){var p=this;this.routes={};this.listeners=new n.Object({});this.arounds=[];this.befores=[];this.namespace=(new Date()).getTime()+"-"+parseInt(Math.random()*1000,10);this.context_prototype=function(){n.EventContext.apply(this,arguments)};this.context_prototype.prototype=new n.EventContext();if(c(o)){o.apply(this,[this])}if(!this._location_proxy){this.setLocationProxy(new n.HashLocationProxy(this,this.run_interval_every))}if(this.debug){this.bindToAllEvents(function(r,q){p.log(p.toString(),r.cleaned_type,q||{})})}};n.Application.prototype=g.extend({},n.Object.prototype,{ROUTE_VERBS:["get","post","put","delete"],APP_EVENTS:["run","unload","lookup-route","run-route","route-found","event-context-before","event-context-after","changed","error","check-form-submission","redirect","location-changed"],_last_route:null,_location_proxy:null,_running:false,element_selector:"body",debug:false,raise_errors:false,run_interval_every:50,template_engine:null,toString:function(){return"Sammy.Application:"+this.element_selector},$element:function(){return g(this.element_selector)},use:function(){var o=b(arguments),q=o.shift(),p=q||"";try{o.unshift(this);if(typeof q=="string"){p="Sammy."+q;q=n[q]}q.apply(this,o)}catch(r){if(typeof q==="undefined"){this.error("Plugin Error: called use() but plugin ("+p.toString()+") is not defined",r)}else{if(!c(q)){this.error("Plugin Error: called use() but '"+p.toString()+"' is not a function",r)}else{this.error("Plugin Error",r)}}}return this},setLocationProxy:function(o){var p=this._location_proxy;this._location_proxy=o;if(this.isRunning()){if(p){p.unbind()}this._location_proxy.bind()}},route:function(s,p,u){var r=this,t=[],o,q;if(!u&&c(p)){p=s;u=p;s="any"}s=s.toLowerCase();if(p.constructor==String){j.lastIndex=0;while((q=j.exec(p))!==null){t.push(q[1])}p=new RegExp("^"+p.replace(j,f)+"$")}if(typeof u=="string"){u=r[u]}o=function(v){var w={verb:v,path:p,callback:u,param_names:t};r.routes[v]=r.routes[v]||[];r.routes[v].push(w)};if(s==="any"){g.each(this.ROUTE_VERBS,function(x,w){o(w)})}else{o(s)}return this},get:m("get"),post:m("post"),put:m("put"),del:m("delete"),any:m("any"),mapRoutes:function(p){var o=this;g.each(p,function(q,r){o.route.apply(o,r)});return this},eventNamespace:function(){return["sammy-app",this.namespace].join("-")},bind:function(o,q,s){var r=this;if(typeof s=="undefined"){s=q}var p=function(){var v,t,u;v=arguments[0];u=arguments[1];if(u&&u.context){t=u.context;delete u.context}else{t=new r.context_prototype(r,"bind",v.type,u,v.target)}v.cleaned_type=v.type.replace(r.eventNamespace(),"");s.apply(t,[v,u])};if(!this.listeners[o]){this.listeners[o]=[]}this.listeners[o].push(p);if(this.isRunning()){this._listen(o,p)}return this},trigger:function(o,p){this.$element().trigger([o,this.eventNamespace()].join("."),[p]);return this},refresh:function(){this.last_location=null;this.trigger("location-changed");return this},before:function(o,p){if(c(o)){p=o;o={}}this.befores.push([o,p]);return this},after:function(o){return this.bind("event-context-after",o)},around:function(o){this.arounds.push(o);return this},isRunning:function(){return this._running},helpers:function(o){g.extend(this.context_prototype.prototype,o);return this},helper:function(o,p){this.context_prototype.prototype[o]=p;return this},run:function(o){if(this.isRunning()){return false}var p=this;g.each(this.listeners.toHash(),function(q,r){g.each(r,function(t,s){p._listen(q,s)})});this.trigger("run",{start_url:o});this._running=true;this.last_location=null;if(this.getLocation()==""&&typeof o!="undefined"){this.setLocation(o)}this._checkLocation();this._location_proxy.bind();this.bind("location-changed",function(){p._checkLocation()});this.bind("submit",function(r){var q=p._checkFormSubmission(g(r.target).closest("form"));return(q===false)?r.preventDefault():false});g(i).bind("beforeunload",function(){p.unload()});return this.trigger("changed")},unload:function(){if(!this.isRunning()){return false}var o=this;this.trigger("unload");this._location_proxy.unbind();this.$element().unbind("submit").removeClass(o.eventNamespace());g.each(this.listeners.toHash(),function(p,q){g.each(q,function(s,r){o._unlisten(p,r)})});this._running=false;return this},bindToAllEvents:function(p){var o=this;g.each(this.APP_EVENTS,function(q,r){o.bind(r,p)});g.each(this.listeners.keys(true),function(r,q){if(o.APP_EVENTS.indexOf(q)==-1){o.bind(q,p)}});return this},routablePath:function(o){return o.replace(k,"")},lookupRoute:function(r,p){var q=this,o=false;this.trigger("lookup-route",{verb:r,path:p});if(typeof this.routes[r]!="undefined"){g.each(this.routes[r],function(t,s){if(q.routablePath(p).match(s.path)){o=s;return false}})}return o},runRoute:function(q,D,s,v){var r=this,B=this.lookupRoute(q,D),p,y,t,x,C,z,w,A,o;this.log("runRoute",[q,D].join(" "));this.trigger("run-route",{verb:q,path:D,params:s});if(typeof s=="undefined"){s={}}g.extend(s,this._parseQueryString(D));if(B){this.trigger("route-found",{route:B});if((A=B.path.exec(this.routablePath(D)))!==null){A.shift();g.each(A,function(E,F){if(B.param_names[E]){s[B.param_names[E]]=h(F)}else{if(!s.splat){s.splat=[]}s.splat.push(h(F))}})}p=new this.context_prototype(this,q,D,s,v);t=this.arounds.slice(0);C=this.befores.slice(0);w=[p].concat(s.splat);y=function(){var E;while(C.length>0){z=C.shift();if(r.contextMatchesOptions(p,z[0])){E=z[1].apply(p,[p]);if(E===false){return false}}}r.last_route=B;p.trigger("event-context-before",{context:p});E=B.callback.apply(p,w);p.trigger("event-context-after",{context:p});return E};g.each(t.reverse(),function(E,F){var G=y;y=function(){return F.apply(p,[G])}});try{o=y()}catch(u){this.error(["500 Error",q,D].join(" "),u)}return o}else{return this.notFound(q,D)}},contextMatchesOptions:function(r,t,p){var q=t;if(typeof q==="undefined"||q=={}){return true}if(typeof p==="undefined"){p=true}if(typeof q==="string"||c(q.test)){q={path:q}}if(q.only){return this.contextMatchesOptions(r,q.only,true)}else{if(q.except){return this.contextMatchesOptions(r,q.except,false)}}var o=true,s=true;if(q.path){if(c(q.path.test)){o=q.path.test(r.path)}else{o=(q.path.toString()===r.path)}}if(q.verb){s=q.verb===r.verb}return p?(s&&o):!(s&&o)},getLocation:function(){return this._location_proxy.getLocation()},setLocation:function(o){return this._location_proxy.setLocation(o)},swap:function(o){return this.$element().html(o)},templateCache:function(o,p){if(typeof p!="undefined"){return a[o]=p}else{return a[o]}},clearTemplateCache:function(){return a={}},notFound:function(q,p){var o=this.error(["404 Not Found",q,p].join(" "));return(q==="get")?o:true},error:function(p,o){if(!o){o=new Error()}o.message=[p,o.message].join(" ");this.trigger("error",{message:o.message,error:o});if(this.raise_errors){throw (o)}else{this.log(o.message,o)}},_checkLocation:function(){var o,p;o=this.getLocation();if(!this.last_location||this.last_location[0]!="get"||this.last_location[1]!=o){this.last_location=["get",o];p=this.runRoute("get",o)}return p},_getFormVerb:function(q){var p=g(q),r,o;o=p.find('input[name="_method"]');if(o.length>0){r=o.val()}if(!r){r=p[0].getAttribute("method")}return g.trim(r.toString().toLowerCase())},_checkFormSubmission:function(q){var o,r,t,s,p;this.trigger("check-form-submission",{form:q});o=g(q);r=o.attr("action");t=this._getFormVerb(o);if(!t||t==""){t="get"}this.log("_checkFormSubmission",o,r,t);if(t==="get"){this.setLocation(r+"?"+o.serialize());p=false}else{s=g.extend({},this._parseFormParams(o));p=this.runRoute(t,r,s,q.get(0))}return(typeof p=="undefined")?false:p},_parseFormParams:function(o){var r={},q=o.serializeArray(),p;for(p=0;p<q.length;p++){r=this._parseParamPair(r,q[p].name,q[p].value)}return r},_parseQueryString:function(r){var t={},q,p,s,o;q=r.match(k);if(q){p=q[1].split("&");for(o=0;o<p.length;o++){s=p[o].split("=");t=this._parseParamPair(t,h(s[0]),h(s[1]))}}return t},_parseParamPair:function(q,o,p){if(q[o]){if(l(q[o])){q[o].push(p)}else{q[o]=[q[o],p]}}else{q[o]=p}return q},_listen:function(o,p){return this.$element().bind([o,this.eventNamespace()].join("."),p)},_unlisten:function(o,p){return this.$element().unbind([o,this.eventNamespace()].join("."),p)}});n.RenderContext=function(o){this.event_context=o;this.callbacks=[];this.previous_content=null;this.content=null;this.next_engine=false;this.waiting=false};g.extend(n.RenderContext.prototype,{then:function(q){if(!c(q)){if(typeof q==="string"&&q in this.event_context){var p=this.event_context[q];q=function(r){return p.apply(this.event_context,[r])}}else{return this}}var o=this;if(this.waiting){this.callbacks.push(q)}else{this.wait();i.setTimeout(function(){var r=q.apply(o,[o.content,o.previous_content]);if(r!==false){o.next(r)}},13)}return this},wait:function(){this.waiting=true},next:function(o){this.waiting=false;if(typeof o!=="undefined"){this.previous_content=this.content;this.content=o}if(this.callbacks.length>0){this.then(this.callbacks.shift())}},load:function(o,p,r){var q=this;return this.then(function(){var s,t,v,u;if(c(p)){r=p;p={}}else{p=g.extend({},p)}if(r){this.then(r)}if(typeof o==="string"){v=(o.match(/\.json$/)||p.json);s=((v&&p.cache===true)||p.cache!==false);q.next_engine=q.event_context.engineFor(o);delete p.cache;delete p.json;if(p.engine){q.next_engine=p.engine;delete p.engine}if(s&&(t=this.event_context.app.templateCache(o))){return t}this.wait();g.ajax(g.extend({url:o,data:{},dataType:v?"json":null,type:"get",success:function(w){if(s){q.event_context.app.templateCache(o,w)}q.next(w)}},p));return false}else{if(o.nodeType){return o.innerHTML}if(o.selector){q.next_engine=o.attr("data-engine");if(p.clone===false){return o.remove()[0].innerHTML.toString()}else{return o[0].innerHTML.toString()}}}})},render:function(o,p,q){if(c(o)&&!p){return this.then(o)}else{if(!p&&this.content){p=this.content}return this.load(o).interpolate(p,o).then(q)}},partial:function(o,p){return this.render(o,p).swap()},send:function(){var q=this,p=b(arguments),o=p.shift();if(l(p[0])){p=p[0]}return this.then(function(r){p.push(function(s){q.next(s)});q.wait();o.apply(o,p);return false})},collect:function(s,r,o){var q=this;var p=function(){if(c(s)){r=s;s=this.content}var t=[],u=false;g.each(s,function(v,x){var w=r.apply(q,[v,x]);if(w.jquery&&w.length==1){w=w[0];u=true}t.push(w);return w});return u?t:t.join("")};return o?p():this.then(p)},renderEach:function(o,p,q,r){if(l(p)){r=q;q=p;p=null}return this.load(o).then(function(t){var s=this;if(!q){q=l(this.previous_content)?this.previous_content:[]}if(r){g.each(q,function(u,w){var x={},v=this.next_engine||o;p?(x[p]=w):(x=w);r(w,s.event_context.interpolate(t,x,v))})}else{return this.collect(q,function(u,w){var x={},v=this.next_engine||o;p?(x[p]=w):(x=w);return this.event_context.interpolate(t,x,v)},true)}})},interpolate:function(r,q,o){var p=this;return this.then(function(t,s){if(!r&&s){r=s}if(this.next_engine){q=this.next_engine;this.next_engine=false}var u=p.event_context.interpolate(t,r,q);return o?s+u:u})},swap:function(){return this.then(function(o){this.event_context.swap(o)}).trigger("changed",{})},appendTo:function(o){return this.then(function(p){g(o).append(p)}).trigger("changed",{})},prependTo:function(o){return this.then(function(p){g(o).prepend(p)}).trigger("changed",{})},replace:function(o){return this.then(function(p){g(o).html(p)}).trigger("changed",{})},trigger:function(o,p){return this.then(function(q){if(typeof p=="undefined"){p={content:q}}this.event_context.trigger(o,p)})}});n.EventContext=function(s,r,p,q,o){this.app=s;this.verb=r;this.path=p;this.params=new n.Object(q);this.target=o};n.EventContext.prototype=g.extend({},n.Object.prototype,{$element:function(){return this.app.$element()},engineFor:function(q){var p=this,o;if(c(q)){return q}q=q.toString();if((o=q.match(/\.([^\.]+)$/))){q=o[1]}if(q&&c(p[q])){return p[q]}if(p.app.template_engine){return this.engineFor(p.app.template_engine)}return function(r,s){return r}},interpolate:function(p,q,o){return this.engineFor(o).apply(this,[p,q])},render:function(o,p,q){return new n.RenderContext(this).render(o,p,q)},renderEach:function(o,p,q,r){return new n.RenderContext(this).renderEach(o,p,q,r)},load:function(o,p,q){return new n.RenderContext(this).load(o,p,q)},partial:function(o,p){return new n.RenderContext(this).partial(o,p)},send:function(){var o=new n.RenderContext(this);return o.send.apply(o,arguments)},redirect:function(){var q,p=b(arguments),o=this.app.getLocation();if(p.length>1){p.unshift("/");q=this.join.apply(this,p)}else{q=p[0]}this.trigger("redirect",{to:q});this.app.last_location=[this.verb,this.path];this.app.setLocation(q);if(o==q){this.app.trigger("location-changed")}},trigger:function(o,p){if(typeof p=="undefined"){p={}}if(!p.context){p.context=this}return this.app.trigger(o,p)},eventNamespace:function(){return this.app.eventNamespace()},swap:function(o){return this.app.swap(o)},notFound:function(){return this.app.notFound(this.verb,this.path)},json:function(o){return g.parseJSON(o)},toString:function(){return"Sammy.EventContext: "+[this.verb,this.path,this.params].join(" ")}});g.sammy=i.Sammy=n})(jQuery,window);
diff --git a/javascript/sammy/min/sammy-latest.min.js b/javascript/sammy/min/sammy-latest.min.js
new file mode 100644
index 00000000..ae03d902
--- /dev/null
+++ b/javascript/sammy/min/sammy-latest.min.js
@@ -0,0 +1,5 @@
+// -- Sammy -- /sammy.js
+// http://code.quirkey.com/sammy
+// Version: 0.6.2
+// Built: Mon Oct 11 12:41:51 -0700 2010
+(function(g,i){var n,f="([^/]+)",j=/:([\w\d]+)/g,k=/\?([^#]*)$/,b=function(o){return Array.prototype.slice.call(o)},c=function(o){return Object.prototype.toString.call(o)==="[object Function]"},l=function(o){return Object.prototype.toString.call(o)==="[object Array]"},h=decodeURIComponent,e=function(o){return o.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")},m=function(o){return function(p,q){return this.route.apply(this,[o,p,q])}},a={},d=[];n=function(){var p=b(arguments),q,o;n.apps=n.apps||{};if(p.length===0||p[0]&&c(p[0])){return n.apply(n,["body"].concat(p))}else{if(typeof(o=p.shift())=="string"){q=n.apps[o]||new n.Application();q.element_selector=o;if(p.length>0){g.each(p,function(r,s){q.use(s)})}if(q.element_selector!=o){delete n.apps[o]}n.apps[q.element_selector]=q;return q}}};n.VERSION="0.6.2";n.addLogger=function(o){d.push(o)};n.log=function(){var o=b(arguments);o.unshift("["+Date()+"]");g.each(d,function(q,p){p.apply(n,o)})};if(typeof i.console!="undefined"){if(c(console.log.apply)){n.addLogger(function(){i.console.log.apply(console,arguments)})}else{n.addLogger(function(){i.console.log(arguments)})}}else{if(typeof console!="undefined"){n.addLogger(function(){console.log.apply(console,arguments)})}}g.extend(n,{makeArray:b,isFunction:c,isArray:l});n.Object=function(o){return g.extend(this,o||{})};g.extend(n.Object.prototype,{escapeHTML:e,h:e,toHash:function(){var o={};g.each(this,function(q,p){if(!c(p)){o[q]=p}});return o},toHTML:function(){var o="";g.each(this,function(q,p){if(!c(p)){o+="<strong>"+q+"</strong> "+p+"<br />"}});return o},keys:function(o){var p=[];for(var q in this){if(!c(this[q])||!o){p.push(q)}}return p},has:function(o){return this[o]&&g.trim(this[o].toString())!=""},join:function(){var p=b(arguments);var o=p.shift();return p.join(o)},log:function(){n.log.apply(n,arguments)},toString:function(o){var p=[];g.each(this,function(r,q){if(!c(q)||o){p.push('"'+r+'": '+q.toString())}});return"Sammy.Object: {"+p.join(",")+"}"}});n.HashLocationProxy=function(p,o){this.app=p;this.is_native=false;this._startPolling(o)};n.HashLocationProxy.prototype={bind:function(){var o=this,p=this.app;g(i).bind("hashchange."+this.app.eventNamespace(),function(r,q){if(o.is_native===false&&!q){n.log("native hash change exists, using");o.is_native=true;i.clearInterval(n.HashLocationProxy._interval)}p.trigger("location-changed")});if(!n.HashLocationProxy._bindings){n.HashLocationProxy._bindings=0}n.HashLocationProxy._bindings++},unbind:function(){g(i).unbind("hashchange."+this.app.eventNamespace());n.HashLocationProxy._bindings--;if(n.HashLocationProxy._bindings<=0){i.clearInterval(n.HashLocationProxy._interval)}},getLocation:function(){var o=i.location.toString().match(/^[^#]*(#.+)$/);return o?o[1]:""},setLocation:function(o){return(i.location=o)},_startPolling:function(q){var p=this;if(!n.HashLocationProxy._interval){if(!q){q=10}var o=function(){var r=p.getLocation();if(!n.HashLocationProxy._last_location||r!=n.HashLocationProxy._last_location){i.setTimeout(function(){g(i).trigger("hashchange",[true])},13)}n.HashLocationProxy._last_location=r};o();n.HashLocationProxy._interval=i.setInterval(o,q)}}};n.Application=function(o){var p=this;this.routes={};this.listeners=new n.Object({});this.arounds=[];this.befores=[];this.namespace=(new Date()).getTime()+"-"+parseInt(Math.random()*1000,10);this.context_prototype=function(){n.EventContext.apply(this,arguments)};this.context_prototype.prototype=new n.EventContext();if(c(o)){o.apply(this,[this])}if(!this._location_proxy){this.setLocationProxy(new n.HashLocationProxy(this,this.run_interval_every))}if(this.debug){this.bindToAllEvents(function(r,q){p.log(p.toString(),r.cleaned_type,q||{})})}};n.Application.prototype=g.extend({},n.Object.prototype,{ROUTE_VERBS:["get","post","put","delete"],APP_EVENTS:["run","unload","lookup-route","run-route","route-found","event-context-before","event-context-after","changed","error","check-form-submission","redirect","location-changed"],_last_route:null,_location_proxy:null,_running:false,element_selector:"body",debug:false,raise_errors:false,run_interval_every:50,template_engine:null,toString:function(){return"Sammy.Application:"+this.element_selector},$element:function(){return g(this.element_selector)},use:function(){var o=b(arguments),q=o.shift(),p=q||"";try{o.unshift(this);if(typeof q=="string"){p="Sammy."+q;q=n[q]}q.apply(this,o)}catch(r){if(typeof q==="undefined"){this.error("Plugin Error: called use() but plugin ("+p.toString()+") is not defined",r)}else{if(!c(q)){this.error("Plugin Error: called use() but '"+p.toString()+"' is not a function",r)}else{this.error("Plugin Error",r)}}}return this},setLocationProxy:function(o){var p=this._location_proxy;this._location_proxy=o;if(this.isRunning()){if(p){p.unbind()}this._location_proxy.bind()}},route:function(s,p,u){var r=this,t=[],o,q;if(!u&&c(p)){p=s;u=p;s="any"}s=s.toLowerCase();if(p.constructor==String){j.lastIndex=0;while((q=j.exec(p))!==null){t.push(q[1])}p=new RegExp("^"+p.replace(j,f)+"$")}if(typeof u=="string"){u=r[u]}o=function(v){var w={verb:v,path:p,callback:u,param_names:t};r.routes[v]=r.routes[v]||[];r.routes[v].push(w)};if(s==="any"){g.each(this.ROUTE_VERBS,function(x,w){o(w)})}else{o(s)}return this},get:m("get"),post:m("post"),put:m("put"),del:m("delete"),any:m("any"),mapRoutes:function(p){var o=this;g.each(p,function(q,r){o.route.apply(o,r)});return this},eventNamespace:function(){return["sammy-app",this.namespace].join("-")},bind:function(o,q,s){var r=this;if(typeof s=="undefined"){s=q}var p=function(){var v,t,u;v=arguments[0];u=arguments[1];if(u&&u.context){t=u.context;delete u.context}else{t=new r.context_prototype(r,"bind",v.type,u,v.target)}v.cleaned_type=v.type.replace(r.eventNamespace(),"");s.apply(t,[v,u])};if(!this.listeners[o]){this.listeners[o]=[]}this.listeners[o].push(p);if(this.isRunning()){this._listen(o,p)}return this},trigger:function(o,p){this.$element().trigger([o,this.eventNamespace()].join("."),[p]);return this},refresh:function(){this.last_location=null;this.trigger("location-changed");return this},before:function(o,p){if(c(o)){p=o;o={}}this.befores.push([o,p]);return this},after:function(o){return this.bind("event-context-after",o)},around:function(o){this.arounds.push(o);return this},isRunning:function(){return this._running},helpers:function(o){g.extend(this.context_prototype.prototype,o);return this},helper:function(o,p){this.context_prototype.prototype[o]=p;return this},run:function(o){if(this.isRunning()){return false}var p=this;g.each(this.listeners.toHash(),function(q,r){g.each(r,function(t,s){p._listen(q,s)})});this.trigger("run",{start_url:o});this._running=true;this.last_location=null;if(this.getLocation()==""&&typeof o!="undefined"){this.setLocation(o)}this._checkLocation();this._location_proxy.bind();this.bind("location-changed",function(){p._checkLocation()});this.bind("submit",function(r){var q=p._checkFormSubmission(g(r.target).closest("form"));return(q===false)?r.preventDefault():false});g(i).bind("beforeunload",function(){p.unload()});return this.trigger("changed")},unload:function(){if(!this.isRunning()){return false}var o=this;this.trigger("unload");this._location_proxy.unbind();this.$element().unbind("submit").removeClass(o.eventNamespace());g.each(this.listeners.toHash(),function(p,q){g.each(q,function(s,r){o._unlisten(p,r)})});this._running=false;return this},bindToAllEvents:function(p){var o=this;g.each(this.APP_EVENTS,function(q,r){o.bind(r,p)});g.each(this.listeners.keys(true),function(r,q){if(o.APP_EVENTS.indexOf(q)==-1){o.bind(q,p)}});return this},routablePath:function(o){return o.replace(k,"")},lookupRoute:function(r,p){var q=this,o=false;this.trigger("lookup-route",{verb:r,path:p});if(typeof this.routes[r]!="undefined"){g.each(this.routes[r],function(t,s){if(q.routablePath(p).match(s.path)){o=s;return false}})}return o},runRoute:function(q,D,s,v){var r=this,B=this.lookupRoute(q,D),p,y,t,x,C,z,w,A,o;this.log("runRoute",[q,D].join(" "));this.trigger("run-route",{verb:q,path:D,params:s});if(typeof s=="undefined"){s={}}g.extend(s,this._parseQueryString(D));if(B){this.trigger("route-found",{route:B});if((A=B.path.exec(this.routablePath(D)))!==null){A.shift();g.each(A,function(E,F){if(B.param_names[E]){s[B.param_names[E]]=h(F)}else{if(!s.splat){s.splat=[]}s.splat.push(h(F))}})}p=new this.context_prototype(this,q,D,s,v);t=this.arounds.slice(0);C=this.befores.slice(0);w=[p].concat(s.splat);y=function(){var E;while(C.length>0){z=C.shift();if(r.contextMatchesOptions(p,z[0])){E=z[1].apply(p,[p]);if(E===false){return false}}}r.last_route=B;p.trigger("event-context-before",{context:p});E=B.callback.apply(p,w);p.trigger("event-context-after",{context:p});return E};g.each(t.reverse(),function(E,F){var G=y;y=function(){return F.apply(p,[G])}});try{o=y()}catch(u){this.error(["500 Error",q,D].join(" "),u)}return o}else{return this.notFound(q,D)}},contextMatchesOptions:function(r,t,p){var q=t;if(typeof q==="undefined"||q=={}){return true}if(typeof p==="undefined"){p=true}if(typeof q==="string"||c(q.test)){q={path:q}}if(q.only){return this.contextMatchesOptions(r,q.only,true)}else{if(q.except){return this.contextMatchesOptions(r,q.except,false)}}var o=true,s=true;if(q.path){if(c(q.path.test)){o=q.path.test(r.path)}else{o=(q.path.toString()===r.path)}}if(q.verb){s=q.verb===r.verb}return p?(s&&o):!(s&&o)},getLocation:function(){return this._location_proxy.getLocation()},setLocation:function(o){return this._location_proxy.setLocation(o)},swap:function(o){return this.$element().html(o)},templateCache:function(o,p){if(typeof p!="undefined"){return a[o]=p}else{return a[o]}},clearTemplateCache:function(){return a={}},notFound:function(q,p){var o=this.error(["404 Not Found",q,p].join(" "));return(q==="get")?o:true},error:function(p,o){if(!o){o=new Error()}o.message=[p,o.message].join(" ");this.trigger("error",{message:o.message,error:o});if(this.raise_errors){throw (o)}else{this.log(o.message,o)}},_checkLocation:function(){var o,p;o=this.getLocation();if(!this.last_location||this.last_location[0]!="get"||this.last_location[1]!=o){this.last_location=["get",o];p=this.runRoute("get",o)}return p},_getFormVerb:function(q){var p=g(q),r,o;o=p.find('input[name="_method"]');if(o.length>0){r=o.val()}if(!r){r=p[0].getAttribute("method")}return g.trim(r.toString().toLowerCase())},_checkFormSubmission:function(q){var o,r,t,s,p;this.trigger("check-form-submission",{form:q});o=g(q);r=o.attr("action");t=this._getFormVerb(o);if(!t||t==""){t="get"}this.log("_checkFormSubmission",o,r,t);if(t==="get"){this.setLocation(r+"?"+o.serialize());p=false}else{s=g.extend({},this._parseFormParams(o));p=this.runRoute(t,r,s,q.get(0))}return(typeof p=="undefined")?false:p},_parseFormParams:function(o){var r={},q=o.serializeArray(),p;for(p=0;p<q.length;p++){r=this._parseParamPair(r,q[p].name,q[p].value)}return r},_parseQueryString:function(r){var t={},q,p,s,o;q=r.match(k);if(q){p=q[1].split("&");for(o=0;o<p.length;o++){s=p[o].split("=");t=this._parseParamPair(t,h(s[0]),h(s[1]))}}return t},_parseParamPair:function(q,o,p){if(q[o]){if(l(q[o])){q[o].push(p)}else{q[o]=[q[o],p]}}else{q[o]=p}return q},_listen:function(o,p){return this.$element().bind([o,this.eventNamespace()].join("."),p)},_unlisten:function(o,p){return this.$element().unbind([o,this.eventNamespace()].join("."),p)}});n.RenderContext=function(o){this.event_context=o;this.callbacks=[];this.previous_content=null;this.content=null;this.next_engine=false;this.waiting=false};g.extend(n.RenderContext.prototype,{then:function(q){if(!c(q)){if(typeof q==="string"&&q in this.event_context){var p=this.event_context[q];q=function(r){return p.apply(this.event_context,[r])}}else{return this}}var o=this;if(this.waiting){this.callbacks.push(q)}else{this.wait();i.setTimeout(function(){var r=q.apply(o,[o.content,o.previous_content]);if(r!==false){o.next(r)}},13)}return this},wait:function(){this.waiting=true},next:function(o){this.waiting=false;if(typeof o!=="undefined"){this.previous_content=this.content;this.content=o}if(this.callbacks.length>0){this.then(this.callbacks.shift())}},load:function(o,p,r){var q=this;return this.then(function(){var s,t,v,u;if(c(p)){r=p;p={}}else{p=g.extend({},p)}if(r){this.then(r)}if(typeof o==="string"){v=(o.match(/\.json$/)||p.json);s=((v&&p.cache===true)||p.cache!==false);q.next_engine=q.event_context.engineFor(o);delete p.cache;delete p.json;if(p.engine){q.next_engine=p.engine;delete p.engine}if(s&&(t=this.event_context.app.templateCache(o))){return t}this.wait();g.ajax(g.extend({url:o,data:{},dataType:v?"json":null,type:"get",success:function(w){if(s){q.event_context.app.templateCache(o,w)}q.next(w)}},p));return false}else{if(o.nodeType){return o.innerHTML}if(o.selector){q.next_engine=o.attr("data-engine");if(p.clone===false){return o.remove()[0].innerHTML.toString()}else{return o[0].innerHTML.toString()}}}})},render:function(o,p,q){if(c(o)&&!p){return this.then(o)}else{if(!p&&this.content){p=this.content}return this.load(o).interpolate(p,o).then(q)}},partial:function(o,p){return this.render(o,p).swap()},send:function(){var q=this,p=b(arguments),o=p.shift();if(l(p[0])){p=p[0]}return this.then(function(r){p.push(function(s){q.next(s)});q.wait();o.apply(o,p);return false})},collect:function(s,r,o){var q=this;var p=function(){if(c(s)){r=s;s=this.content}var t=[],u=false;g.each(s,function(v,x){var w=r.apply(q,[v,x]);if(w.jquery&&w.length==1){w=w[0];u=true}t.push(w);return w});return u?t:t.join("")};return o?p():this.then(p)},renderEach:function(o,p,q,r){if(l(p)){r=q;q=p;p=null}return this.load(o).then(function(t){var s=this;if(!q){q=l(this.previous_content)?this.previous_content:[]}if(r){g.each(q,function(u,w){var x={},v=this.next_engine||o;p?(x[p]=w):(x=w);r(w,s.event_context.interpolate(t,x,v))})}else{return this.collect(q,function(u,w){var x={},v=this.next_engine||o;p?(x[p]=w):(x=w);return this.event_context.interpolate(t,x,v)},true)}})},interpolate:function(r,q,o){var p=this;return this.then(function(t,s){if(!r&&s){r=s}if(this.next_engine){q=this.next_engine;this.next_engine=false}var u=p.event_context.interpolate(t,r,q);return o?s+u:u})},swap:function(){return this.then(function(o){this.event_context.swap(o)}).trigger("changed",{})},appendTo:function(o){return this.then(function(p){g(o).append(p)}).trigger("changed",{})},prependTo:function(o){return this.then(function(p){g(o).prepend(p)}).trigger("changed",{})},replace:function(o){return this.then(function(p){g(o).html(p)}).trigger("changed",{})},trigger:function(o,p){return this.then(function(q){if(typeof p=="undefined"){p={content:q}}this.event_context.trigger(o,p)})}});n.EventContext=function(s,r,p,q,o){this.app=s;this.verb=r;this.path=p;this.params=new n.Object(q);this.target=o};n.EventContext.prototype=g.extend({},n.Object.prototype,{$element:function(){return this.app.$element()},engineFor:function(q){var p=this,o;if(c(q)){return q}q=q.toString();if((o=q.match(/\.([^\.]+)$/))){q=o[1]}if(q&&c(p[q])){return p[q]}if(p.app.template_engine){return this.engineFor(p.app.template_engine)}return function(r,s){return r}},interpolate:function(p,q,o){return this.engineFor(o).apply(this,[p,q])},render:function(o,p,q){return new n.RenderContext(this).render(o,p,q)},renderEach:function(o,p,q,r){return new n.RenderContext(this).renderEach(o,p,q,r)},load:function(o,p,q){return new n.RenderContext(this).load(o,p,q)},partial:function(o,p){return new n.RenderContext(this).partial(o,p)},send:function(){var o=new n.RenderContext(this);return o.send.apply(o,arguments)},redirect:function(){var q,p=b(arguments),o=this.app.getLocation();if(p.length>1){p.unshift("/");q=this.join.apply(this,p)}else{q=p[0]}this.trigger("redirect",{to:q});this.app.last_location=[this.verb,this.path];this.app.setLocation(q);if(o==q){this.app.trigger("location-changed")}},trigger:function(o,p){if(typeof p=="undefined"){p={}}if(!p.context){p.context=this}return this.app.trigger(o,p)},eventNamespace:function(){return this.app.eventNamespace()},swap:function(o){return this.app.swap(o)},notFound:function(){return this.app.notFound(this.verb,this.path)},json:function(o){return g.parseJSON(o)},toString:function(){return"Sammy.EventContext: "+[this.verb,this.path,this.params].join(" ")}});g.sammy=i.Sammy=n})(jQuery,window);
diff --git a/javascript/sammy/plugins/sammy.cache.js b/javascript/sammy/plugins/sammy.cache.js
new file mode 100644
index 00000000..b2622c87
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.cache.js
@@ -0,0 +1,115 @@
+// deprecated
+(function($) {
+
+  Sammy = Sammy || {};
+
+  // A simple cache strategy that stores key/values in memory.
+  Sammy.MemoryCacheProxy = function(initial) {
+    this._cache = initial || {};
+  };
+
+  $.extend(Sammy.MemoryCacheProxy.prototype, {
+    exists: function(name) {
+      return (typeof this._cache[name] != "undefined");
+    },
+    set: function(name, value) {
+      return this._cache[name] = value;
+    },
+    get: function(name) {
+      return this._cache[name];
+    },
+    clear: function(name) {
+      delete this._cache[name];
+    }
+  });
+
+  // A simple cache strategy that stores key/values <tt>$element.data()</tt> with a <tt>cache.</tt> prefix
+  Sammy.DataCacheProxy = function(initial, $element) {
+    initial = initial || {};
+    this.$element = $element;
+    $.each(initial, function(key, value) {
+      $element.data('cache.' + key, value);
+    });
+  };
+
+  $.extend(Sammy.DataCacheProxy.prototype, {
+    exists: function(name) {
+      return (typeof this.$element.data('cache.' + name) != "undefined");
+    },
+    set: function(name, value) {
+      return this.$element.data('cache.' + name, value);
+    },
+    get: function(name) {
+      return this.$element.data('cache.' + name);
+    },
+    clear: function(name) {
+      this.$element.removeData('cache.' + name);
+    }
+  });
+
+  // Sammy.Cache provides helpers for caching data within the lifecycle of a
+  // Sammy app. The plugin provides two main methods on <tt>Sammy.Application<tt>,
+  // <tt>cache</tt> and <tt>clearCache</tt>. Each app has its own cache store so that
+  // you dont have to worry about collisions. There are currently two different 'cache proxies'
+  // that share the same API but store the data in different ways.
+  //
+  // ### Arguments
+  //
+  // * `proxy` decides which caching proxy to use, either 'memory'(default) or 'data'
+  //
+  Sammy.Cache = function(app, proxy) {
+
+    app.log('**WARNING:** This version of Sammy.Cache has been deprecated in favor of using the version in Sammy.Storage and will be removed in 1.0')
+
+    if (proxy == 'data') {
+      this.cache_proxy = new Sammy.DataCacheProxy({}, this.$element());
+    } else {
+      this.cache_proxy = new Sammy.MemoryCacheProxy({});
+    }
+
+    app.cache_partials = true;
+
+    $.extend(app, {
+      // cache is the main method for interacting with the cache store. The same
+      // method is used for both setting and getting the value. The API is similar
+      // to jQuery.fn.attr()
+      //
+      // ### Examples
+      //
+      //      // setting a value
+      //      cache('key', 'value');
+      //
+      //      // getting a value
+      //      cache('key'); //=> 'value'
+      //
+      //      // setting a value with a callback
+      //      cache('key', function() {
+      //        // this is the app
+      //        return app.element_selector;
+      //      });
+      //
+      cache: function(name, value) {
+        if (typeof value == 'undefined') {
+          return this.cache_proxy.get(name);
+        } else if ($.isFunction(value) && !this.cache_proxy.exists(name)) {
+          return this.cache_proxy.set(name, value.apply(this));
+        } else {
+          return this.cache_proxy.set(name, value)
+        }
+      },
+
+      // clears the cached value for <tt>name</tt>
+      clearCache: function(name) {
+        return this.cache_proxy.clear(name);
+      }
+    });
+
+    app.helpers({
+      // a helper shortcut for use in <tt>Sammy.EventContext</tt>
+      cache: function(name, value) {
+        return this.app.cache(name, value);
+      }
+    });
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.data_location_proxy.js b/javascript/sammy/plugins/sammy.data_location_proxy.js
new file mode 100644
index 00000000..33140cd7
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.data_location_proxy.js
@@ -0,0 +1,78 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  // The DataLocationProxy is an optional location proxy prototype. As opposed to
+  // the `HashLocationProxy` it gets its location from a jQuery.data attribute
+  // tied to the application's element. You can set the name of the attribute by
+  // passing a string as the second argument to the constructor. The default attribute
+  // name is 'sammy-location'. To read more about location proxies, check out the
+  // documentation for `Sammy.HashLocationProxy`
+  //
+  // An optional `href_attribute` can be passed, which specifies a DOM element
+  // attribute that holds "links" to different locations in the app. When the
+  // proxy is bound, clicks to element that have this attribute activate a
+  // `setLocation()` using the contents of the `href_attribute`.
+  //
+  // ### Example
+  //
+  //      var app = $.sammy(function() {
+  //        // set up the location proxy
+  //        this.setLocationProxy(new Sammy.DataLocationProxy(this, 'location', 'rel'));
+  //
+  //        this.get('about', function() {
+  //          this.partial('about.html');
+  //        });
+  //
+  //      });
+  //
+  // In this scenario, if an element existed within the template:
+  //
+  //      <a href="/about" rel="about">About Us</a>
+  //
+  // Clicking on that link would not go to /about, but would set the apps location
+  // to 'about' and trigger the route.
+  Sammy.DataLocationProxy = function(app, data_name, href_attribute) {
+    this.app            = app;
+    this.data_name      = data_name || 'sammy-location';
+    this.href_attribute = href_attribute;
+  };
+
+  Sammy.DataLocationProxy.prototype = {
+    bind: function() {
+      var proxy = this;
+      this.app.$element().bind('setData', function(e, key, value) {
+        if (key == proxy.data_name) {
+          // jQuery unfortunately fires the event before it sets the value
+          // work around it, by setting the value ourselves
+          proxy.app.$element().each(function() {
+            $.data(this, proxy.data_name, value);
+          });
+          proxy.app.trigger('location-changed');
+        }
+      });
+      if (this.href_attribute) {
+        this.app.$element().delegate('[' + this.href_attribute + ']', 'click', function(e) {
+          e.preventDefault();
+          proxy.setLocation($(this).attr(proxy.href_attribute));
+        });
+      }
+    },
+
+    unbind: function() {
+      if (this.href_attribute) {
+        this.app.$element().undelegate('[' + this.href_attribute + ']', 'click');
+      }
+      this.app.$element().unbind('setData');
+    },
+
+    getLocation: function() {
+      return this.app.$element().data(this.data_name) || '';
+    },
+
+    setLocation: function(new_location) {
+      return this.app.$element().data(this.data_name, new_location);
+    }
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.ejs.js b/javascript/sammy/plugins/sammy.ejs.js
new file mode 100644
index 00000000..4fb74936
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.ejs.js
@@ -0,0 +1,700 @@
+(function($) {
+
+  // Embeddedjs is property of http://embeddedjs.com/
+  // Sammy ejs plugin written Codeofficer @ http://www.codeofficer.com/
+
+  var rsplit = function(string, regex) {
+    var result = regex.exec(string),
+        retArr = new Array(),
+        first_idx, last_idx, first_bit;
+    while (result != null) {
+      first_idx = result.index;
+      last_idx = regex.lastIndex;
+      if ((first_idx) != 0) {
+        first_bit = string.substring(0, first_idx);
+        retArr.push(string.substring(0, first_idx));
+        string = string.slice(first_idx);
+      };
+      retArr.push(result[0]);
+      string = string.slice(result[0].length);
+      result = regex.exec(string);
+    };
+    if (! string == '') {
+      retArr.push(string);
+    };
+    return retArr;
+  };
+
+  var chop = function(string) {
+    return string.substr(0, string.length - 1);
+  };
+
+  var extend = function(d, s) {
+    for (var n in s) {
+      if(s.hasOwnProperty(n))   d[n] = s[n];
+    };
+  };
+
+  /* @Constructor*/
+  EJS = function(options) {
+    options = (typeof(options) == "string") ? {view: options} : options;
+    this.set_options(options);
+    if (options.precompiled) {
+      this.template = {};
+      this.template.process = options.precompiled;
+      EJS.update(this.name, this);
+      return;
+    };
+    if (options.element) {
+      if (typeof(options.element) == 'string') {
+        var name = options.element;
+        options.element = document.getElementById(options.element);
+        if (options.element == null) throw name + 'does not exist!';
+      };
+      if (options.element.value) {
+        this.text = options.element.value;
+      } else {
+        this.text = options.element.innerHTML;
+      };
+      this.name = options.element.id;
+      this.type = '[';
+    } else if (options.url) {
+      options.url = EJS.endExt(options.url, this.extMatch);
+      this.name = this.name ? this.name : options.url;
+      var url = options.url;
+      //options.view = options.absolute_url || options.view || options.;
+      var template = EJS.get(this.name /*url*/, this.cache);
+      if (template) return template;
+      if (template == EJS.INVALID_PATH) return null;
+      try {
+        this.text = EJS.request( url + (this.cache ? '' : '?' + Math.random() ));
+      } catch(e) {};
+      if (this.text == null) {
+        throw( {type: 'EJS', message: 'There is no template at '+url}  );
+      };
+      //this.name = url;
+    };
+    var template = new EJS.Compiler(this.text, this.type);
+    template.compile(options, this.name);
+    EJS.update(this.name, this);
+    this.template = template;
+  };
+
+  /* @Prototype*/
+  EJS.prototype = {
+  /**
+   * Renders an object with extra view helpers attached to the view.
+   * @param {Object} object data to be rendered
+   * @param {Object} extra_helpers an object with additonal view helpers
+   * @return {String} returns the result of the string
+   */
+    render : function(object, extra_helpers) {
+      object = object || {};
+      this._extra_helpers = extra_helpers;
+      var v = new EJS.Helpers(object, extra_helpers || {});
+      return this.template.process.call(object, object,v);
+    },
+
+    update : function(element, options) {
+      if (typeof(element) == 'string') {
+        element = document.getElementById(element);
+      };
+      if (options == null) {
+        _template = this;
+        return function(object) {
+          EJS.prototype.update.call(_template, element, object);
+        };
+      };
+      if (typeof(options) == 'string') {
+        params = {};
+        params.url = options;
+        _template = this;
+        params.onComplete = function(request) {
+          var object = eval(request.responseText);
+          EJS.prototype.update.call(_template, element, object);
+        };
+        EJS.ajax_request(params);
+      } else {
+        element.innerHTML = this.render(options);
+      };
+    },
+
+    out : function() {
+      return this.template.out;
+    },
+
+    /**
+     * Sets options on this view to be rendered with.
+     * @param {Object} options
+     */
+    set_options : function(options){
+      this.type = options.type || EJS.type;
+      this.cache = (options.cache != null) ? options.cache : EJS.cache;
+      this.text = options.text || null;
+      this.name =   options.name || null;
+      this.ext = options.ext || EJS.ext;
+      this.extMatch = new RegExp(this.ext.replace(/\./, '\.'));
+    }
+  };
+
+  EJS.endExt = function(path, match) {
+    if (!path) return null;
+    match.lastIndex = 0;
+    return path + (match.test(path) ? '' : this.ext);
+  };
+
+  /* @Static*/
+  EJS.Scanner = function(source, left, right) {
+    extend(this, {
+      left_delimiter:  left +'%',
+      right_delimiter: '%'+right,
+      double_left: left+'%%',
+      double_right: '%%'+right,
+      left_equal: left+'%=',
+      left_comment: left+'%#'
+    });
+    this.SplitRegexp = (left == '[') ? /(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/ : new RegExp('('+this.double_left+')|(%%'+this.double_right+')|('+this.left_equal+')|('+this.left_comment+')|('+this.left_delimiter+')|('+this.right_delimiter+'\n)|('+this.right_delimiter+')|(\n)');
+    this.source = source;
+    this.stag = null;
+    this.lines = 0;
+  };
+
+  EJS.Scanner.to_text = function(input) {
+    if (input == null || input === undefined) return '';
+    if(input instanceof Date) return input.toDateString();
+    if(input.toString) return input.toString();
+    return '';
+  };
+
+  EJS.Scanner.prototype = {
+    scan: function(block) {
+      scanline = this.scanline;
+      regex = this.SplitRegexp;
+      if (! this.source == '') {
+        var source_split = rsplit(this.source, /\n/);
+        for (var i=0; i<source_split.length; i++) {
+          var item = source_split[i];
+          this.scanline(item, regex, block);
+        };
+      };
+    },
+
+    scanline: function(line, regex, block) {
+      this.lines++;
+      var line_split = rsplit(line, regex);
+      for (var i=0; i<line_split.length; i++) {
+        var token = line_split[i];
+        if (token != null) {
+          try {
+            block(token, this);
+          } catch(e) {
+            throw {type: 'EJS.Scanner', line: this.lines};
+          };
+        };
+      };
+    }
+  };
+
+  EJS.Buffer = function(pre_cmd, post_cmd) {
+    this.line = new Array();
+    this.script = "";
+    this.pre_cmd = pre_cmd;
+    this.post_cmd = post_cmd;
+    for (var i=0; i<this.pre_cmd.length; i++) {
+      this.push(pre_cmd[i]);
+    };
+  };
+
+  EJS.Buffer.prototype = {
+    push: function(cmd) {
+      this.line.push(cmd);
+    },
+
+    cr: function() {
+      this.script = this.script + this.line.join('; ');
+      this.line = new Array();
+      this.script = this.script + "\n";
+    },
+
+    close: function() {
+      if (this.line.length > 0) {
+        for (var i=0; i<this.post_cmd.length; i++){
+          this.push(pre_cmd[i]);
+        };
+        this.script = this.script + this.line.join('; ');
+        line = null;
+      };
+    }
+  };
+
+  EJS.Compiler = function(source, left) {
+    this.pre_cmd = ['var ___ViewO = [];'];
+    this.post_cmd = new Array();
+    this.source = ' ';
+    if (source != null) {
+      if (typeof(source) == 'string') {
+        source = source.replace(/\r\n/g, "\n");
+        source = source.replace(/\r/g,   "\n");
+        this.source = source;
+      } else if (source.innerHTML) {
+        this.source = source.innerHTML;
+      };
+      if (typeof this.source != 'string') {
+        this.source = "";
+      };
+    };
+    left = left || '<';
+    var right = '>';
+    switch(left) {
+      case '[':
+        right = ']';
+        break;
+      case '<':
+        break;
+      default:
+        throw left+' is not a supported deliminator';
+        break;
+    };
+    this.scanner = new EJS.Scanner(this.source, left, right);
+    this.out = '';
+  };
+
+  EJS.Compiler.prototype = {
+    compile: function(options, name) {
+      options = options || {};
+      this.out = '';
+      var put_cmd = "___ViewO.push(";
+      var insert_cmd = put_cmd;
+      var buff = new EJS.Buffer(this.pre_cmd, this.post_cmd);
+      var content = '';
+      var clean = function(content) {
+        content = content.replace(/\\/g, '\\\\');
+        content = content.replace(/\n/g, '\\n');
+        content = content.replace(/"/g,   '\\"');
+        return content;
+      };
+      this.scanner.scan(function(token, scanner) {
+        if (scanner.stag == null)  {
+          switch(token) {
+            case '\n':
+              content = content + "\n";
+              buff.push(put_cmd + '"' + clean(content) + '");');
+              buff.cr();
+              content = '';
+              break;
+            case scanner.left_delimiter:
+            case scanner.left_equal:
+            case scanner.left_comment:
+              scanner.stag = token;
+              if (content.length > 0) {
+                buff.push(put_cmd + '"' + clean(content) + '")');
+              };
+              content = '';
+              break;
+            case scanner.double_left:
+              content = content + scanner.left_delimiter;
+              break;
+            default:
+              content = content + token;
+              break;
+          };
+        } else {
+          switch(token) {
+            case scanner.right_delimiter:
+              switch(scanner.stag) {
+                case scanner.left_delimiter:
+                  if (content[content.length - 1] == '\n') {
+                    content = chop(content);
+                    buff.push(content);
+                    buff.cr();
+                  } else {
+                    buff.push(content);
+                  };
+                  break;
+                case scanner.left_equal:
+                  buff.push(insert_cmd + "(EJS.Scanner.to_text(" + content + ")))");
+                  break;
+              };
+              scanner.stag = null;
+              content = '';
+              break;
+            case scanner.double_right:
+              content = content + scanner.right_delimiter;
+              break;
+            default:
+              content = content + token;
+              break;
+          };
+        };
+      });
+      if (content.length > 0) {
+        // Chould be content.dump in Ruby
+        buff.push(put_cmd + '"' + clean(content) + '")');
+      };
+      buff.close();
+      this.out = buff.script + ";";
+      var to_be_evaled = '/*' + name + '*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {' + this.out + " return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";
+      try {
+        eval(to_be_evaled);
+      } catch(e) {
+        if (typeof JSLINT != 'undefined') {
+          JSLINT(this.out);
+          for (var i = 0; i < JSLINT.errors.length; i++) {
+            var error = JSLINT.errors[i];
+            if (error.reason != "Unnecessary semicolon.") {
+              error.line++;
+              var e = new Error();
+              e.lineNumber = error.line;
+              e.message = error.reason;
+              if (options.view) e.fileName = options.view;
+              throw e;
+            };
+          };
+        } else {
+          throw e;
+        };
+      };
+    }
+  };
+
+  //type, cache, folder
+  /**
+   * Sets default options for all views
+   * @param {Object} options Set view with the following options
+   * <table class="options">
+          <tbody><tr><th>Option</th><th>Default</th><th>Description</th></tr>
+          <tr>
+            <td>type</td>
+            <td>'<'</td>
+            <td>type of magic tags.   Options are '&lt;' or '['
+            </td>
+          </tr>
+          <tr>
+            <td>cache</td>
+            <td>true in production mode, false in other modes</td>
+            <td>true to cache template.
+            </td>
+          </tr>
+    </tbody></table>
+   *
+   */
+  EJS.config = function(options){
+    EJS.cache = (options.cache != null) ? options.cache : EJS.cache;
+    EJS.type = (options.type != null) ? options.type : EJS.type;
+    EJS.ext = (options.ext != null) ? options.ext : EJS.ext;
+    var templates_directory = EJS.templates_directory || {}; //nice and private container
+    EJS.templates_directory = templates_directory;
+    EJS.get = function(path, cache) {
+      if(cache == false) return null;
+      if(templates_directory[path]) return templates_directory[path];
+      return null;
+    };
+    EJS.update = function(path, template) {
+      if (path == null) return;
+      templates_directory[path] = template ;
+    };
+    EJS.INVALID_PATH =  -1;
+  };
+  EJS.config( {cache: true, type: '<', ext: '.ejs' } );
+
+  /**
+   * @constructor
+   * By adding functions to EJS.Helpers.prototype, those functions will be available in the
+   * views.
+   * @init Creates a view helper.   This function is called internally.  You should never call it.
+   * @param {Object} data The data passed to the view.  Helpers have access to it through this._data
+   */
+  EJS.Helpers = function(data, extras){
+    this._data = data;
+    this._extras = extras;
+    extend(this, extras);
+  };
+
+  /* @prototype*/
+  EJS.Helpers.prototype = {
+    /**
+     * Renders a new view.  If data is passed in, uses that to render the view.
+     * @param {Object} options standard options passed to a new view.
+     * @param {optional:Object} data
+     * @return {String}
+     */
+    view: function(options, data, helpers) {
+      if (!helpers) helpers = this._extras;
+      if (!data) data = this._data;
+      return new EJS(options).render(data, helpers);
+    },
+    /**
+     * For a given value, tries to create a human representation.
+     * @param {Object} input the value being converted.
+     * @param {Object} null_text what text should be present if input == null or undefined, defaults to ''
+     * @return {String}
+     */
+    to_text: function(input, null_text) {
+      if (input == null || input === undefined) return null_text || '';
+      if (input instanceof(Date)) return input.toDateString();
+      if (input.toString) return input.toString().replace(/\n/g, '<br />').replace(/''/g, "'");
+      return '';
+    }
+  };
+
+  EJS.newRequest = function() {
+    var factories = [function() { return new ActiveXObject("Msxml2.XMLHTTP"); },function() { return new XMLHttpRequest(); },function() { return new ActiveXObject("Microsoft.XMLHTTP"); }];
+    for(var i = 0; i < factories.length; i++) {
+      try {
+        var request = factories[i]();
+        if (request != null)  return request;
+      } catch(e) { continue; }
+    };
+  };
+
+  EJS.request = function(path) {
+    var request = new EJS.newRequest();
+    request.open("GET", path, false);
+    try {
+      request.send(null);
+    } catch(e){
+      return null;
+    };
+    if ( request.status == 404 || request.status == 2 ||(request.status == 0 && request.responseText == '') ) return null;
+    return request.responseText;
+  };
+
+  EJS.ajax_request = function(params) {
+    params.method = ( params.method ? params.method : 'GET');
+    var request = new EJS.newRequest();
+    request.onreadystatechange = function() {
+      if (request.readyState == 4) {
+        if (request.status == 200) {
+          params.onComplete(request);
+        } else {
+          params.onComplete(request);
+        };
+      };
+    };
+    request.open(params.method, params.url);
+    request.send(null);
+  };
+
+  EJS.Helpers.prototype.date_tag = function(name, value , html_options) {
+    if(!(value instanceof(Date))) value = new Date();
+    var month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
+    var years = [], months = [], days =[];
+    var year = value.getFullYear();
+    var month = value.getMonth();
+    var day = value.getDate();
+    for (var y = year - 15; y < year+15 ; y++) {
+      years.push({value: y, text: y});
+    };
+    for (var m = 0; m < 12; m++) {
+      months.push({value: (m), text: month_names[m]});
+    };
+    for (var d = 0; d < 31; d++) {
+      days.push({value: (d+1), text: (d+1)});
+    };
+    var year_select = this.select_tag(name+'[year]', year, years, {id: name+'[year]'} );
+    var month_select = this.select_tag(name+'[month]', month, months, {id: name+'[month]'});
+    var day_select = this.select_tag(name+'[day]', day, days, {id: name+'[day]'});
+    return year_select+month_select+day_select;
+  };
+
+  EJS.Helpers.prototype.form_tag = function(action, html_options) {
+    html_options = html_options || {};
+    html_options.action = action;
+    if(html_options.multipart == true) {
+      html_options.method = 'post';
+      html_options.enctype = 'multipart/form-data';
+    };
+    return this.start_tag_for('form', html_options);
+  };
+
+  EJS.Helpers.prototype.form_tag_end = function() {
+    return this.tag_end('form');
+  };
+
+  EJS.Helpers.prototype.hidden_field_tag   = function(name, value, html_options) {
+    return this.input_field_tag(name, value, 'hidden', html_options);
+  };
+
+  EJS.Helpers.prototype.input_field_tag = function(name, value , inputType, html_options) {
+    html_options = html_options || {};
+    html_options.id   = html_options.id || name;
+    html_options.value = value || '';
+    html_options.type = inputType || 'text';
+    html_options.name = name;
+    return this.single_tag_for('input', html_options);
+  };
+
+  EJS.Helpers.prototype.is_current_page = function(url) {
+    return (window.location.href == url || window.location.pathname == url ? true : false);
+  };
+
+  EJS.Helpers.prototype.link_to = function(name, url, html_options) {
+    if(!name) var name = 'null';
+    if(!html_options) var html_options = {};
+    if(html_options.confirm){
+      html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} ";
+      html_options.confirm = null;
+    };
+    html_options.href = url;
+    return this.start_tag_for('a', html_options)+name+ this.tag_end('a');
+  };
+
+  EJS.Helpers.prototype.submit_link_to = function(name, url, html_options) {
+    if(!name) var name = 'null';
+    if(!html_options) var html_options = {};
+    html_options.onclick = html_options.onclick || '';
+    if(html_options.confirm){
+      html_options.onclick = " var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} ";
+      html_options.confirm = null;
+    };
+    html_options.value = name;
+    html_options.type = 'submit';
+    html_options.onclick = html_options.onclick + (url ? this.url_for(url) : '')+'return false;';
+    return this.start_tag_for('input', html_options);
+  };
+
+  EJS.Helpers.prototype.link_to_if = function(condition, name, url, html_options, post, block) {
+    return this.link_to_unless((condition == false), name, url, html_options, post, block);
+  };
+
+  EJS.Helpers.prototype.link_to_unless = function(condition, name, url, html_options, block) {
+    html_options = html_options || {};
+    if(condition) {
+      if(block && typeof(block) == 'function') {
+        return block(name, url, html_options, block);
+      } else {
+        return name;
+      };
+    } else {
+      return this.link_to(name, url, html_options);
+    };
+  };
+
+  EJS.Helpers.prototype.link_to_unless_current = function(name, url, html_options, block) {
+    html_options = html_options || {};
+    return this.link_to_unless(this.is_current_page(url), name, url, html_options, block);
+  };
+
+
+  EJS.Helpers.prototype.password_field_tag = function(name, value, html_options) {
+    return this.input_field_tag(name, value, 'password', html_options);
+  };
+
+  EJS.Helpers.prototype.select_tag = function(name, value, choices, html_options) {
+    html_options = html_options || {};
+    html_options.id   = html_options.id  || name;
+    html_options.value = value;
+    html_options.name = name;
+    var txt = '';
+    txt += this.start_tag_for('select', html_options);
+    for(var i = 0; i < choices.length; i++) {
+      var choice = choices[i];
+      var optionOptions = {value: choice.value};
+      if(choice.value == value) optionOptions.selected ='selected';
+      txt += this.start_tag_for('option', optionOptions )+choice.text+this.tag_end('option');
+    };
+    txt += this.tag_end('select');
+    return txt;
+  };
+
+  EJS.Helpers.prototype.single_tag_for = function(tag, html_options) {
+    return this.tag(tag, html_options, '/>');
+  };
+
+  EJS.Helpers.prototype.start_tag_for = function(tag, html_options)   {
+    return this.tag(tag, html_options);
+  };
+
+  EJS.Helpers.prototype.submit_tag = function(name, html_options) {
+    html_options = html_options || {};
+    html_options.type = html_options.type   || 'submit';
+    html_options.value = name || 'Submit';
+    return this.single_tag_for('input', html_options);
+  };
+
+  EJS.Helpers.prototype.tag = function(tag, html_options, end) {
+    if(!end) var end = '>';
+    var txt = ' ';
+    for(var attr in html_options) {
+      if(html_options[attr] != null) {
+        var value = html_options[attr].toString();
+      } else {
+        var value='';
+      };
+      // special case because "class" is a reserved word in IE
+      if(attr == "Class") attr = "class";
+      if( value.indexOf("'") != -1 ) {
+        txt += attr+'=\"'+value+'\" ';
+      } else {
+        txt += attr+"='"+value+"' ";
+      };
+    };
+    return '<' + tag + txt + end;
+  };
+
+  EJS.Helpers.prototype.tag_end = function(tag) {
+    return '</'+tag+'>';
+  };
+
+  EJS.Helpers.prototype.text_area_tag = function(name, value, html_options) {
+    html_options = html_options || {};
+    html_options.id   = html_options.id  || name;
+    html_options.name   = html_options.name  || name;
+    value = value || '';
+    if(html_options.size) {
+      html_options.cols = html_options.size.split('x')[0];
+      html_options.rows = html_options.size.split('x')[1];
+      delete html_options.size;
+    };
+    html_options.cols = html_options.cols  || 50;
+    html_options.rows = html_options.rows  || 4;
+    return  this.start_tag_for('textarea', html_options)+value+this.tag_end('textarea');
+  };
+
+  EJS.Helpers.prototype.text_tag = EJS.Helpers.prototype.text_area_tag;
+
+  EJS.Helpers.prototype.text_field_tag = function(name, value, html_options) {
+    return this.input_field_tag(name, value, 'text', html_options);
+  };
+
+  EJS.Helpers.prototype.url_for = function(url) {
+    return 'window.location="' + url + '";';
+  };
+
+  EJS.Helpers.prototype.img_tag = function(image_location, alt, options){
+    options = options || {};
+    options.src = image_location;
+    options.alt = alt;
+    return this.single_tag_for('img', options);
+  };
+
+  // -------------------------------------------------------------
+
+  Sammy = Sammy || {};
+
+  Sammy.EJS = function(app, method_alias) {
+
+    // *Helper:* Uses simple templating to parse ERB like templates.
+    //
+    // ### Arguments
+    //
+    // * `template` A String template. '<% %>' tags are evaluated as Javascript and replaced with the elements in data.
+    // * `data` An Object containing the replacement values for the template.
+    //data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
+    // * `name` An optional String name to cache the template.
+    //
+    var template = function(template, data, name) {
+      // use name for caching
+      if (typeof name == 'undefined') name = template;
+      return new EJS({text: template, name: name}).render(data);
+    };
+
+    // set the default method name/extension
+    if (!method_alias) method_alias = 'ejs';
+
+    // create the helper at the method alias
+    app.helper(method_alias, template);
+
+   };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.form.js b/javascript/sammy/plugins/sammy.form.js
new file mode 100644
index 00000000..78edb876
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.form.js
@@ -0,0 +1,274 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  function getStringContent(object, content) {
+    if (typeof content === 'undefined') {
+      return '';
+    } else if ($.isFunction(content)) {
+      content = content.apply(object);
+    }
+    return content.toString();
+  };
+
+  function simple_element(tag, attributes, content) {
+    var html = "<";
+    html += tag;
+    if (typeof attributes != 'undefined') {
+      $.each(attributes, function(key, value) {
+        if (value != null) {
+          html += " " + key + "='";
+          html += getStringContent(attributes, value).replace(/\'/g, "\'");
+          html += "'";
+        }
+      });
+    }
+    if (content === false) {
+      html += ">";
+    } else if (typeof content != 'undefined') {
+      html += ">";
+      html += getStringContent(this, content);
+      html += "</" + tag + ">";
+    } else {
+      html += " />";
+    }
+    return html;
+  };
+
+  // Sammy.FormBuilder is based very closely on the Rails FormBuilder classes.
+  // Its goal is to make it easy to create HTML forms for creating and editing
+  // JavaScript objects. It eases the process by auto-populating existing values
+  // into form inputs and creating input names suitable for parsing by
+  // Sammy.NestedParams and other backend frameworks.
+  //
+  // You initialize a Sammy.FormBuilder by passing the 'name' of the object and
+  // the object itself. Once initialized you create form elements with the object's
+  // prototype methods. Each of these methods returns a string of HTML suitable for
+  // appending through a template or directly with jQuery.
+  //
+  // ### Example
+  //
+  //      var item = {
+  //        name: 'My Item',
+  //        price: '$25.50',
+  //        meta: {
+  //          id: '123'
+  //        }
+  //      };
+  //      var form = new Sammy.FormBuilder('item', item);
+  //      form.text('name');
+  //      //=> <input type='text' name='item[form]' value='My Item' />
+  //
+  // Nested attributes can be accessed/referred to by a 'keypath' which is
+  // basically a string representation of the dot notation.
+  //
+  //      form.hidden('meta.id');
+  //      //=> <input type='hidden' name='item[meta][id]' value='123' />
+  //
+  Sammy.FormBuilder = function(name, object) {
+    this.name   = name;
+    this.object = object;
+  };
+
+  $.extend(Sammy.FormBuilder.prototype, {
+
+    // creates the open form tag with the object attributes
+    open: function(attributes) {
+      return simple_element('form', $.extend({'method': 'post', 'action': '#/' + this.name + 's'}, attributes), false);
+    },
+
+    // closes the form
+    close: function() {
+      return '</form>';
+    },
+
+    // creates a label for `keypath` with the text `content
+    // with an optional `attributes` object
+    label: function(keypath, content, attributes) {
+      var attrs = {'for': this._attributesForKeyPath(keypath).name};
+      return simple_element('label', $.extend(attrs, attributes), content);
+    },
+
+    // creates a hidden input for `keypath` with an optional `attributes` object
+    hidden: function(keypath, attributes) {
+      attributes = $.extend({type: 'hidden'}, this._attributesForKeyPath(keypath), attributes);
+      return simple_element('input', attributes);
+    },
+
+    // creates a text input for `keypath` with an optional `attributes` object
+    text: function(keypath, attributes) {
+      attributes = $.extend({type: 'text'}, this._attributesForKeyPath(keypath), attributes);
+      return simple_element('input', attributes);
+    },
+
+    // creates a textarea for `keypath` with an optional `attributes` object
+    textarea: function(keypath, attributes) {
+      var current;
+      attributes = $.extend(this._attributesForKeyPath(keypath), attributes);
+      current = attributes.value;
+      delete attributes['value'];
+      return simple_element('textarea', attributes, current);
+    },
+
+    // creates a password input for `keypath` with an optional `attributes` object
+    password: function(keypath, attributes) {
+      return this.text(keypath, $.extend({type: 'password'}, attributes));
+    },
+
+    // creates a select element for `keypath` with the option elements
+    // specified by an array in `options`. If `options` is an array of arrays,
+    // the first element in each subarray becomes the text of the option and the
+    // second becomes the value.
+    //
+    // ### Example
+    //
+    //     var options = [
+    //       ['Small', 's'],
+    //       ['Medium', 'm'],
+    //       ['Large', 'l']
+    //     ];
+    //     form.select('size', options);
+    //     //=> <select name='item[size]'><option value='s'>Small</option> ...
+    //
+    //
+    select: function(keypath, options, attributes) {
+      var option_html = "", selected;
+      attributes = $.extend(this._attributesForKeyPath(keypath), attributes);
+      selected = attributes.value;
+      delete attributes['value'];
+      $.each(options, function(i, option) {
+        var value, text, option_attrs;
+        if ($.isArray(option)) {
+          value = option[1], text = option[0];
+        } else {
+          value = option, text = option;
+        }
+        option_attrs = {value: getStringContent(this.object, value)};
+        // select the correct option
+        if (value === selected) { option_attrs.selected = 'selected'; }
+        option_html += simple_element('option', option_attrs, text);
+      });
+      return simple_element('select', attributes, option_html);
+    },
+
+    // creates a radio input for keypath with the value `value`. Multiple
+    // radios can be created with different value, if `value` equals the
+    // current value of the key of the form builder's object the attribute
+    // checked='checked' will be added.
+    radio: function(keypath, value, attributes) {
+      var selected;
+      attributes = $.extend(this._attributesForKeyPath(keypath), attributes);
+      selected = attributes.value;
+      attributes.value = getStringContent(this.object, value);
+      if (selected == attributes.value) {
+        attributes.checked = 'checked';
+      }
+      return simple_element('input', $.extend({type:'radio'}, attributes));
+    },
+
+    // creates a checkbox input for keypath with the value `value`. Multiple
+    // checkboxes can be created with different value, if `value` equals the
+    // current value of the key of the form builder's object the attribute
+    // checked='checked' will be added.
+    //
+    // By default `checkbox()` also generates a hidden element whose value is
+    // the inverse of the value given. This is known hack to get around a common
+    // gotcha where browsers and jQuery itself does not include 'unchecked'
+    // elements in the list of submittable inputs. This ensures that a value
+    // should always be passed to Sammy and hence the server. You can disable
+    // the creation of the hidden element by setting the `hidden_element` attribute
+    // to `false`
+    checkbox: function(keypath, value, attributes) {
+      var content = "";
+      if (!attributes) { attributes = {}; }
+      if (attributes.hidden_element !== false) {
+        content += this.hidden(keypath, {'value': !value});
+      }
+      delete attributes['hidden_element'];
+      content += this.radio(keypath, value, $.extend({type: 'checkbox'}, attributes));
+      return content;
+    },
+
+    // creates a submit input for `keypath` with an optional `attributes` object
+    submit: function(attributes) {
+      return simple_element('input', $.extend({'type': 'submit'}, attributes));
+    },
+
+    _attributesForKeyPath: function(keypath) {
+      var builder    = this,
+          keys       = $.isArray(keypath) ? keypath : keypath.split(/\./),
+          name       = builder.name,
+          value      = builder.object,
+          class_name = builder.name;
+
+      $.each(keys, function(i, key) {
+        if ((typeof value === 'undefined') || value == '') {
+          value = ''
+        } else if (typeof key == 'number' || key.match(/^\d+$/)) {
+          value = value[parseInt(key, 10)];
+        } else {
+          value = value[key];
+        }
+        name += "[" + key + "]";
+        class_name += "-" + key;
+      });
+      return {'name': name,
+              'value': getStringContent(builder.object, value),
+              'class': class_name};
+    }
+  });
+
+  // Sammy.Form is a Sammy plugin that adds form building helpers to a
+  // Sammy.Application
+  Sammy.Form = function(app) {
+
+    app.helpers({
+      // simple_element is a simple helper for creating HTML tags.
+      //
+      // ### Arguments
+      //
+      // * `tag` the HTML tag to generate e.g. input, p, etc/
+      // * `attributes` an object representing the attributes of the element as
+      //   key value pairs. e.g. {class: 'element-class'}
+      // * `content` an optional string representing the content for the
+      //   the element. If ommited, the element becomes self closing
+      //
+      simple_element: simple_element,
+
+      // formFor creates a Sammy.Form builder object with the passed `name`
+      // and `object` and passes it as an argument to the `content_callback`.
+      // This is a shortcut for creating FormBuilder objects for use within
+      // templates.
+      //
+      // ### Example
+      //
+      //      // in item_form.template
+      //
+      //      <% formFor('item', item, function(f) { %>
+      //        <%= f.open({action: '#/items'}) %>
+      //        <p>
+      //          <%= f.label('name') %>
+      //          <%= f.text('name') %>
+      //        </p>
+      //        <p>
+      //          <%= f.submit() %>
+      //        </p>
+      //        <%= f.close() %>
+      //      <% }); %>
+      //
+      formFor: function(name, object, content_callback) {
+        var builder;
+        // define a form with just a name
+        if ($.isFunction(object)) {
+          content_callback = object;
+          object = this[name];
+        }
+        builder = new Sammy.FormBuilder(name, object),
+        content_callback.apply(this, [builder]);
+        return builder;
+      }
+    });
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.googleanalytics.js b/javascript/sammy/plugins/sammy.googleanalytics.js
new file mode 100644
index 00000000..4dce8abc
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.googleanalytics.js
@@ -0,0 +1,74 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  // A simple plugin that pings Google Analytics tracker
+  // every time a route is triggered. Originally by Brit Gardner (britg),
+  // with updates from Aaron Quint (quirkey).
+  //
+  // === Arguments
+  //
+  // +tracker+:: the Google Analytics pageTracker object.  Defaults to
+  // the default object defined by the GA snippet, or pass your own if you
+  // have a custom install
+  //
+  // === Example
+  //
+  // Install Google Analytics to your site as you normally would. Be sure that
+  // the 'pageTracker' global variable exists.
+  //
+  // Then, simply add the plugin to your Sammy App and it will automatically
+  // track all of your routes in Google Analytics.
+  // They will appear as page views to the route's path.
+  //
+  //      $.sammy(function() {
+  //        this.use('GoogleAnalytics');
+  //
+  //        ...
+  //      });
+  //
+  // If you have routes that you do not want to track, simply call `noTrack`
+  // within the route.
+  //
+  //      $.sammy(function() {
+  //        this.use('GoogleAnalytics')
+  //
+  //        get('#/dont/track/me', function() {
+  //          this.noTrack();  // This route will not be tracked
+  //        });
+  //      });
+  //
+  Sammy.GoogleAnalytics = function(app, tracker) {
+    var _tracker = tracker || window.pageTracker,
+      shouldTrack = true;
+
+    function disableTracking() {
+      shouldTrack = false;
+    }
+
+    function enableTracking() {
+      shouldTrack = true;
+    }
+
+    this.helpers({
+      // Disable tracking for the current route. Put at the begining of the
+      // route's callback
+      noTrack: function() {
+        disableTracking();
+      },
+      // send a page view to the tracker with `path`
+      track: function(path) {
+        if(typeof _tracker != 'undefined' && shouldTrack) {
+          this.log('tracking', path);
+          _tracker._trackPageview(path);
+        }
+      }
+    });
+
+    this.bind('event-context-after', function() {
+      this.track(this.path);
+      enableTracking();
+    });
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.haml.js b/javascript/sammy/plugins/sammy.haml.js
new file mode 100644
index 00000000..ad759ceb
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.haml.js
@@ -0,0 +1,576 @@
+(function($) {
+
+  /*
+    port of http://github.com/creationix/haml-js
+    version v0.2.6pre - 2010-10-01
+    by Tim Caswell <tim@creationix.com>
+  */
+
+  var matchers, self_close_tags, embedder, forceXML;
+
+  function html_escape(text) {
+    return (text + "").
+      replace(/&/g, "&amp;").
+      replace(/</g, "&lt;").
+      replace(/>/g, "&gt;").
+      replace(/\"/g, "&quot;");
+  }
+
+  function render_attribs(attribs) {
+    var key, value, result = [];
+    for (key in attribs) {
+      if (key !== '_content' && attribs.hasOwnProperty(key)) {
+        switch (attribs[key]) {
+        case 'undefined':
+        case 'false':
+        case 'null':
+        case '""':
+          break;
+        default:
+          try {
+            value = JSON.parse("[" + attribs[key] +"]")[0];
+            if (value === true) {
+              value = key;
+            } else if (typeof value === 'string' && embedder.test(value)) {
+              value = '" +\n' + parse_interpol(html_escape(value)) + ' +\n"';
+            } else {
+              value = html_escape(value);
+            }
+            result.push(" " + key + '=\\"' + value + '\\"');
+          } catch (e) {
+            result.push(" " + key + '=\\"" + html_escape(' + attribs[key] + ') + "\\"');
+          }
+        }
+      }
+    }
+    return result.join("");
+  }
+
+  // Parse the attribute block using a state machine
+  function parse_attribs(line) {
+    var attributes = {},
+        l = line.length,
+        i, c,
+        count = 1,
+        quote = false,
+        skip = false,
+        open, close, joiner, seperator,
+        pair = {
+          start: 1,
+          middle: null,
+          end: null
+        };
+
+    if (!(l > 0 && (line.charAt(0) === '{' || line.charAt(0) === '('))) {
+      return {
+        _content: line[0] === ' ' ? line.substr(1, l) : line
+      };
+    }
+    open = line.charAt(0);
+    close = (open === '{') ? '}' : ')';
+    joiner = (open === '{') ? ':' : '=';
+    seperator = (open === '{') ? ',' : ' ';
+
+    function process_pair() {
+      if (typeof pair.start === 'number' &&
+          typeof pair.middle === 'number' &&
+          typeof pair.end === 'number') {
+        var key = line.substr(pair.start, pair.middle - pair.start).trim(),
+            value = line.substr(pair.middle + 1, pair.end - pair.middle - 1).trim();
+        attributes[key] = value;
+      }
+      pair = {
+        start: null,
+        middle: null,
+        end: null
+      };
+    }
+
+    for (i = 1; count > 0; i += 1) {
+
+      // If we reach the end of the line, then there is a problem
+      if (i > l) {
+        throw "Malformed attribute block";
+      }
+
+      c = line.charAt(i);
+      if (skip) {
+        skip = false;
+      } else {
+        if (quote) {
+          if (c === '\\') {
+            skip = true;
+          }
+          if (c === quote) {
+            quote = false;
+          }
+        } else {
+          if (c === '"' || c === "'") {
+            quote = c;
+          }
+
+          if (count === 1) {
+            if (c === joiner) {
+              pair.middle = i;
+            }
+            if (c === seperator || c === close) {
+              pair.end = i;
+              process_pair();
+              if (c === seperator) {
+                pair.start = i + 1;
+              }
+            }
+          }
+
+          if (c === open || c === "(") {
+            count += 1;
+          }
+          if (c === close || (count > 1 && c === ")")) {
+            count -= 1;
+          }
+        }
+      }
+    }
+    attributes._content = line.substr(i, line.length);
+    return attributes;
+  }
+
+  // Split interpolated strings into an array of literals and code fragments.
+  function parse_interpol(value) {
+    var items = [],
+        pos = 0,
+        next = 0,
+        match;
+    while (true) {
+      // Match up to embedded string
+      next = value.substr(pos).search(embedder);
+      if (next < 0) {
+        if (pos < value.length) {
+          items.push(JSON.stringify(value.substr(pos)));
+        }
+        break;
+      }
+      items.push(JSON.stringify(value.substr(pos, next)));
+      pos += next;
+
+      // Match embedded string
+      match = value.substr(pos).match(embedder);
+      next = match[0].length;
+      if (next < 0) { break; }
+      items.push(match[1] || match[2]);
+      pos += next;
+    }
+    return items.filter(function (part) { return part && part.length > 0}).join(" +\n");
+  }
+
+  // Used to find embedded code in interpolated strings.
+  embedder = /\#\{([^}]*)\}/;
+
+  self_close_tags = ["meta", "img", "link", "br", "hr", "input", "area", "base"];
+
+  // All matchers' regexps should capture leading whitespace in first capture
+  // and trailing content in last capture
+  matchers = [
+    // html tags
+    {
+      regexp: /^(\s*)((?:[.#%][a-z_\-][a-z0-9_:\-]*)+)(.*)$/i,
+      process: function () {
+        var tag, classes, ids, attribs, content;
+        tag = this.matches[2];
+        classes = tag.match(/\.([a-z_\-][a-z0-9_\-]*)/gi);
+        ids = tag.match(/\#([a-z_\-][a-z0-9_\-]*)/gi);
+        tag = tag.match(/\%([a-z_\-][a-z0-9_:\-]*)/gi);
+
+        // Default to <div> tag
+        tag = tag ? tag[0].substr(1, tag[0].length) : 'div';
+
+        attribs = this.matches[3];
+        if (attribs) {
+          attribs = parse_attribs(attribs);
+          if (attribs._content) {
+            this.contents.unshift(attribs._content.trim());
+            delete(attribs._content);
+          }
+        } else {
+          attribs = {};
+        }
+
+        if (classes) {
+          classes = classes.map(function (klass) {
+            return klass.substr(1, klass.length);
+          }).join(' ');
+          if (attribs['class']) {
+            try {
+              attribs['class'] = JSON.stringify(classes + " " + JSON.parse(attribs['class']));
+            } catch (e) {
+              attribs['class'] = JSON.stringify(classes + " ") + " + " + attribs['class'];
+            }
+          } else {
+            attribs['class'] = JSON.stringify(classes);
+          }
+        }
+        if (ids) {
+          ids = ids.map(function (id) {
+            return id.substr(1, id.length);
+          }).join(' ');
+          if (attribs.id) {
+            attribs.id = JSON.stringify(ids + " ") + attribs.id;
+          } else {
+            attribs.id = JSON.stringify(ids);
+          }
+        }
+
+        attribs = render_attribs(attribs);
+
+        content = this.render_contents();
+        if (content === '""') {
+          content = '';
+        }
+
+        if (forceXML ? content.length > 0 : self_close_tags.indexOf(tag) == -1) {
+          return '"<' + tag + attribs + '>"' +
+            (content.length > 0 ? ' + \n' + content : "") +
+            ' + \n"</' + tag + '>"';
+        } else {
+          return '"<' + tag + attribs + ' />"';
+        }
+      }
+    },
+
+    // each loops
+    {
+      regexp: /^(\s*)(?::for|:each)\s+(?:([a-z_][a-z_\-]*),\s*)?([a-z_][a-z_\-]*)\s+in\s+(.*)(\s*)$/i,
+      process: function () {
+        var ivar = this.matches[2] || '__key__', // index
+            vvar = this.matches[3],              // value
+            avar = this.matches[4],              // array
+            rvar = '__result__';                 // results
+
+        if (this.matches[5]) {
+          this.contents.unshift(this.matches[5]);
+        }
+        return '(function () { ' +
+          'var ' + rvar + ' = [], ' + ivar + ', ' + vvar + '; ' +
+          'for (' + ivar + ' in ' + avar + ') { ' +
+          'if (' + avar + '.hasOwnProperty(' + ivar + ')) { ' +
+          vvar + ' = ' + avar + '[' + ivar + ']; ' +
+          rvar + '.push(\n' + (this.render_contents() || "''") + '\n); ' +
+          '} } return ' + rvar + '.join(""); }).call(this)';
+      }
+    },
+
+    // if statements
+    {
+      regexp: /^(\s*):if\s+(.*)\s*$/i,
+      process: function () {
+        var condition = this.matches[2];
+        return '(function () { ' +
+          'if (' + condition + ') { ' +
+          'return (\n' + (this.render_contents() || '') + '\n);' +
+          '} else { return ""; } }).call(this)';
+      }
+    },
+
+    // declarations
+    {
+      regexp: /^()!!!(?:\s*(.*))\s*$/,
+      process: function () {
+        var line = '';
+        switch ((this.matches[2] || '').toLowerCase()) {
+        case '':
+          // XHTML 1.0 Transitional
+          line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
+          break;
+        case 'strict':
+        case '1.0':
+          // XHTML 1.0 Strict
+          line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
+          break;
+        case 'frameset':
+          // XHTML 1.0 Frameset
+          line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';
+          break;
+        case '5':
+          // XHTML 5
+          line = '<!DOCTYPE html>';
+          break;
+        case '1.1':
+          // XHTML 1.1
+          line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
+          break;
+        case 'basic':
+          // XHTML Basic 1.1
+          line = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">';
+          break;
+        case 'mobile':
+          // XHTML Mobile 1.2
+          line = '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">';
+          break;
+        case 'xml':
+          // XML
+          line = "<?xml version='1.0' encoding='utf-8' ?>";
+          break;
+        case 'xml iso-8859-1':
+          // XML iso-8859-1
+          line = "<?xml version='1.0' encoding='iso-8859-1' ?>";
+          break;
+        }
+        return JSON.stringify(line + "\n");
+      }
+    },
+
+    // Embedded markdown. Needs to be added to exports externally.
+    {
+      regexp: /^(\s*):markdown\s*$/i,
+      process: function () {
+        return parse_interpol(exports.Markdown.encode(this.contents.join("\n")));
+      }
+    },
+
+    // script blocks
+    {
+      regexp: /^(\s*):(?:java)?script\s*$/,
+      process: function () {
+        return parse_interpol('\n<script type="text/javascript">\n' +
+          '//<![CDATA[\n' +
+          this.contents.join("\n") +
+          "\n//]]>\n</script>\n");
+      }
+    },
+
+    // css blocks
+    {
+      regexp: /^(\s*):css\s*$/,
+      process: function () {
+        return JSON.stringify('\n<style type="text/css">\n' +
+          this.contents.join("\n") +
+          "\n</style>\n");
+      }
+    },
+
+  ];
+
+  function compile(lines) {
+    var block = false,
+        output = [];
+
+    // If lines is a string, turn it into an array
+    if (typeof lines === 'string') {
+      lines = lines.trim().replace(/\n\r|\r/g, '\n').split('\n');
+    }
+
+    lines.forEach(function(line) {
+      var match, found = false;
+
+      // Collect all text as raw until outdent
+      if (block) {
+        match = block.check_indent(line);
+        if (match) {
+          block.contents.push(match[1] || "");
+          return;
+        } else {
+          output.push(block.process());
+          block = false;
+        }
+      }
+
+      matchers.forEach(function (matcher) {
+        if (!found) {
+          match = matcher.regexp(line);
+          if (match) {
+            block = {
+              contents: [],
+              matches: match,
+              check_indent: new RegExp("^(?:\\s*|" + match[1] + "  (.*))$"),
+              process: matcher.process,
+              render_contents: function () {
+                return compile(this. contents);
+              }
+            };
+            found = true;
+          }
+        }
+      });
+
+      // Match plain text
+      if (!found) {
+        output.push(function () {
+          // Escaped plain text
+          if (line[0] === '\\') {
+            return parse_interpol(line.substr(1, line.length));
+          }
+
+          // Plain variable data
+          if (line[0] === '=') {
+            line = line.substr(1, line.length).trim();
+            try {
+              return parse_interpol(JSON.parse(line));
+            } catch (e) {
+              return line;
+            }
+          }
+
+          // HTML escape variable data
+          if (line.substr(0, 2) === "&=") {
+            line = line.substr(2, line.length).trim();
+            try {
+              return JSON.stringify(html_escape(JSON.parse(line)));
+            } catch (e2) {
+              return 'html_escape(' + line + ')';
+            }
+          }
+
+          // Plain text
+          return parse_interpol(line);
+        }());
+      }
+
+    });
+    if (block) {
+      output.push(block.process());
+    }
+    return output.filter(function (part) { return part && part.length > 0}).join(" +\n");
+  };
+
+  function optimize(js) {
+    var new_js = [], buffer = [], part, end;
+
+    function flush() {
+      if (buffer.length > 0) {
+        new_js.push(JSON.stringify(buffer.join("")) + end);
+        buffer = [];
+      }
+    }
+    js.replace(/\n\r|\r/g, '\n').split('\n').forEach(function (line) {
+      part = line.match(/^(\".*\")(\s*\+\s*)?$/);
+      if (!part) {
+        flush();
+        new_js.push(line);
+        return;
+      }
+      end = part[2] || "";
+      part = part[1];
+      try {
+        buffer.push(JSON.parse(part));
+      } catch (e) {
+        flush();
+        new_js.push(line);
+      }
+    });
+    flush();
+    return new_js.join("\n");
+  };
+
+  function render(text, options) {
+    options = options || {};
+    text = text || "";
+    var js = compile(text);
+    if (options.optimize) {
+      js = Haml.optimize(js);
+    }
+    return execute(js, options.context || Haml, options.locals);
+  };
+
+  function execute(js, self, locals) {
+    return (function () {
+      with(locals || {}) {
+        try {
+          return eval("(" + js + ")");
+        } catch (e) {
+          return "\n<pre class='error'>" + html_escape(e.stack) + "</pre>\n";
+        }
+
+      }
+    }).call(self);
+  };
+
+  Haml = function Haml(haml, xml) {
+    forceXML = xml;
+    var js = optimize(compile(haml));
+    return new Function("locals",
+      html_escape + "\n" +
+      "with(locals || {}) {\n" +
+      "  try {\n" +
+      "    return (" + js + ");\n" +
+      "  } catch (e) {\n" +
+      "    return \"\\n<pre class='error'>\" + html_escape(e.stack) + \"</pre>\\n\";\n" +
+      "  }\n" +
+      "}");
+  }
+
+  Sammy = Sammy || {};
+
+  // <tt>Sammy.Haml</tt> provides a quick way of using haml style templates in your app.
+  // The plugin itself includes the haml-js library created by Tim Caswell at
+  // at http://github.com/creationix/haml-js
+  //
+  // Haml is an alternative HTML syntax that is really great for describing
+  // the structure of HTML documents.
+  //
+  // By default using Sammy.Haml in your app adds the <tt>haml()</tt> method to the EventContext
+  // prototype. However, just like <tt>Sammy.Template</tt> you can change the default name of the method
+  // by passing a second argument (e.g. you could use the hml() as the method alias so that all the template
+  // files could be in the form file.hml instead of file.haml)
+  //
+  // ### Example
+  //
+  // The template (mytemplate.haml):
+  //
+  //       %h1&= title
+  //
+  //       Hey, #{name}! Welcome to Haml!
+  //
+  // The app:
+  //
+  //       var $.app = $.sammy(function() {
+  //         // include the plugin
+  //         this.use(Sammy.Haml);
+  //
+  //         this.get('#/hello/:name', function() {
+  //           // set local vars
+  //           this.title = 'Hello!'
+  //           this.name = this.params.name;
+  //           // render the template and pass it through haml
+  //           this.partial('mytemplate.haml');
+  //         });
+  //
+  //       });
+  //
+  // If I go to #/hello/AQ in the browser, Sammy will render this to the <tt>body</tt>:
+  //
+  //       <h1>Hello!</h1>
+  //
+  //       Hey, AQ! Welcome to HAML!
+  //
+  // Note: You dont have to include the haml.js file on top of the plugin as the plugin
+  // includes the full source.
+  //
+  Sammy.Haml = function(app, method_alias) {
+    app.use(Sammy.JSON);
+
+    var haml_cache = {};
+    // *Helper* Uses haml-js to parse a template and interpolate and work with the passed data
+    //
+    // ### Arguments
+    //
+    // * `template` A String template.
+    // * `data` An Object containing the replacement values for the template.
+    //   data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
+    //
+    var haml = function(template, data, name) {
+      // use name for caching
+      if (typeof name == 'undefined') name = template;
+      var fn = haml_cache[name];
+      if (!fn) {
+        fn = haml_cache[name] = Haml(template);
+      }
+      return fn($.extend({}, this, data));
+    };
+
+    // set the default method name/extension
+    if (!method_alias) method_alias = 'haml';
+    app.helper(method_alias, haml);
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.handlebars.js b/javascript/sammy/plugins/sammy.handlebars.js
new file mode 100644
index 00000000..69f4493c
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.handlebars.js
@@ -0,0 +1,613 @@
+(function($) {
+    var Handlebars = {
+        compilerCache: {},
+
+        compile: function(string) {
+            if (Handlebars.compilerCache[string] == null) {
+                var fnBody = Handlebars.compileFunctionBody(string);
+                var fn = new Function("context", "fallback", "Handlebars", fnBody);
+                Handlebars.compilerCache[string] =
+                function(context, fallback) { return fn(context, fallback, Handlebars); };
+            }
+
+            return Handlebars.compilerCache[string];
+        },
+
+        compileToString: function(string) {
+            var fnBody = Handlebars.compileFunctionBody(string);
+            return "function(context, fallback) { " + fnBody + "}";
+        },
+
+        compileFunctionBody: function(string) {
+            var compiler = new Handlebars.Compiler(string);
+            compiler.compile();
+
+            return "fallback = fallback || {}; var stack = [];" + compiler.fn;
+        },
+
+        isFunction: function(fn) {
+            return Object.prototype.toString.call(fn) == "[object Function]";
+        },
+
+        trim: function(str) {
+            return str.replace(/^\s+|\s+$/g, '');
+        },
+
+        escapeText: function(string) {
+            string = string.replace(/'/g, "\\'");
+            string = string.replace(/\"/g, "\\\"");
+            return string;
+        },
+
+        escapeExpression: function(string) {
+            // don't escape SafeStrings, since they're already safe
+            if (string instanceof Handlebars.SafeString) {
+                return string.toString();
+            }
+            else if (string === null) {
+                string = "";
+            }
+
+            return string.toString().replace(/&(?!\w+;)|["\\<>]/g, function(str) {
+                switch(str) {
+                    case "&":
+                        return "&amp;";
+                        break;
+                    case '"':
+                        return "\"";
+                    case "\\":
+                        return "\\\\";
+                        break;
+                    case "<":
+                        return "&lt;";
+                        break;
+                    case ">":
+                        return "&gt;";
+                        break;
+                    default:
+                        return str;
+                }
+            });
+        },
+
+        compilePartial: function(partial) {
+            if (Handlebars.isFunction(partial)) {
+                compiled = partial;
+            } else {
+                compiled = Handlebars.compile(partial);
+            }
+
+            return compiled;
+        },
+
+        evalExpression: function(path, context, stack) {
+            var parsedPath = Handlebars.parsePath(path);
+            var depth = parsedPath[0];
+            var parts = parsedPath[1];
+            if (depth > stack.length) {
+                context = null;
+            } else if (depth > 0) {
+                context = stack[stack.length - depth];
+            }
+
+            for (var i = 0; i < parts.length && context !== undefined; i++) {
+                context = context[parts[i]];
+            }
+
+            return context;
+        },
+
+        buildContext: function(context, stack) {
+            var ContextWrapper = function(stack) {
+                this.__stack__ = stack.slice(0);
+                this.__get__ = function(path) {
+                    return Handlebars.evalExpression(path, this, this.__stack__);
+                };
+            };
+
+            ContextWrapper.prototype = context;
+            return new ContextWrapper(stack);
+        },
+
+        // spot to memoize paths to speed up loops and subsequent parses
+        pathPatterns: {},
+
+        // returns a two element array containing the numbers of contexts to back up the stack and
+        // the properties to dig into on the current context
+        //
+        // for example, if the path is "../../alan/name", the result will be [2, ["alan", "name"]].
+        parsePath: function(path) {
+            if (path == null) {
+                return [0, []];
+            } else if (Handlebars.pathPatterns[path] != null) {
+                return Handlebars.pathPatterns[path];
+            }
+
+            var parts = path.split("/");
+            var readDepth = false;
+            var depth = 0;
+            var dig = [];
+            for (var i = 0, j = parts.length; i < j; i++) {
+                switch(parts[i]) {
+                    case "..":
+                        if (readDepth) {
+                            throw new Handlebars.Exception("Cannot jump out of context after moving into a context.");
+                        } else {
+                            depth += 1;
+                        }
+                        break;
+                    case ".":
+                    // do nothing - using .'s is pretty dumb, but it's also basically free for us to support
+                    case "this":
+                        // if we do nothing you'll end up sticking in the same context
+                        break;
+                    default:
+                        readDepth = true;
+                        dig.push(parts[i]);
+                }
+            }
+
+            var ret = [depth, dig];
+            Handlebars.pathPatterns[path] = ret;
+            return ret;
+        },
+
+        isEmpty: function(value) {
+            if (typeof value === "undefined") {
+                return true;
+            } else if (!value) {
+                return true;
+            } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length == 0) {
+                return true;
+            } else {
+                return false;
+            }
+        },
+
+        // Escapes output and converts empty values to empty strings
+        filterOutput: function(value, escape) {
+
+            if (Handlebars.isEmpty(value)) {
+                return "";
+            } else if (escape) {
+                return Handlebars.escapeExpression(value);
+            } else {
+                return value;
+            }
+        },
+
+        handleBlock: function(lookup, context, arg, fn, notFn) {
+            var out = "";
+            if (Handlebars.isFunction(lookup)) {
+                out = out + lookup.call(context, arg, fn);
+                if (notFn != null && Handlebars.isFunction(lookup.not)) {
+                    out = out + lookup.not.call(context, arg, notFn);
+                }
+            }
+            else {
+                if (!Handlebars.isEmpty(lookup)) {
+                    out = out + Handlebars.helperMissing.call(arg, lookup, fn);
+                }
+
+                if (notFn != null) {
+                    out = out + Handlebars.helperMissing.not.call(arg, lookup, notFn);
+                }
+            }
+
+            return out;
+        },
+
+        handleExpression: function(lookup, context, arg, isEscaped) {
+            var out = "";
+
+            if (Handlebars.isFunction(lookup)) {
+                out = out + Handlebars.filterOutput(lookup.call(context, arg), isEscaped);
+            } else if(!Handlebars.isEmpty(lookup)) {
+                out = out + Handlebars.filterOutput(lookup, isEscaped);
+            }
+
+            return out;
+        },
+
+        handleInvertedSection: function(lookup, context, fn) {
+            var out = "";
+            if(Handlebars.isFunction(lookup) && Handlebars.isEmpty(lookup())) {
+                out = out + fn(context);
+            } else if (Handlebars.isEmpty(lookup)) {
+                out = out + fn(context);
+            }
+            return out;
+        }
+    }
+
+    Handlebars.Compiler = function(string) {
+        this.string = string;
+        this.pointer = -1;
+        this.mustache = false;
+        this.text = "";
+        this.fn = "var out = ''; var lookup; ";
+        this.newlines = "";
+        this.comment = false;
+        this.escaped = true;
+        this.partial = false;
+        this.inverted = false;
+        this.endCondition = null;
+        this.continueInverted = false;
+    };
+
+    Handlebars.Exception = function(message) {
+        this.message = message;
+    };
+
+// Build out our basic SafeString type
+    Handlebars.SafeString = function(string) {
+        this.string = string;
+    }
+    Handlebars.SafeString.prototype.toString = function() {
+        return this.string.toString();
+    }
+
+    Handlebars.helperMissing = function(object, fn) {
+        var ret = "";
+
+        if(object === true) {
+            return fn(this);
+        } else if(object === false) {
+            return "";
+        } else if(Object.prototype.toString.call(object) === "[object Array]") {
+            for(var i=0, j=object.length; i<j; i++) {
+                ret = ret + fn(object[i]);
+            }
+            return ret;
+        } else {
+            return fn(object);
+        }
+    };
+    Handlebars.helperMissing.not = function(context, fn) {
+        return fn(context);
+    }
+
+    Handlebars.Compiler.prototype = {
+        getChar: function(n) {
+            var ret = this.peek(n);
+            this.pointer = this.pointer + (n || 1);
+            return ret;
+        },
+
+        peek: function(n) {
+            n = n || 1;
+            var start = this.pointer + 1;
+            return this.string.slice(start, start + n);
+        },
+
+        compile: function(endCondition) {
+            // if we're at the end condition already then we don't have to do any work!
+            if (!endCondition || !endCondition(this)) {
+                var chr;
+                while(chr = this.getChar()) {
+                    if(chr === "{" && this.peek() === "{" && !this.mustache) {
+                        this.getChar();
+                        this.parseMustache();
+
+                    } else {
+                        if(chr === "\n") {
+                            this.newlines = this.newlines + "\n";
+                            chr = "\\n";
+                        } else if (chr === "\r") {
+                            this.newlines = this.newlines + "\r";
+                            chr = "\\r";
+                        } else if (chr === "\\") {
+                            chr = "\\\\";
+                        }
+                        this.text = this.text + chr;
+                    }
+
+                    if (endCondition && this.peek(5) == "{{^}}") {
+                        this.continueInverted = true;
+                        this.getChar(5);
+                        break;
+                    }
+                    else if(endCondition && endCondition(this)) { break };
+                }
+            }
+
+            this.addText();
+            this.fn += "return out;";
+
+            return;
+        },
+
+        addText: function() {
+            if(this.text) {
+                this.fn = this.fn + "out = out + \"" + Handlebars.escapeText(this.text) + "\"; ";
+                this.fn = this.fn + this.newlines;
+                this.newlines = "";
+                this.text = "";
+            }
+        },
+
+        addExpression: function(mustache, param) {
+            param = param || null;
+            var expr = this.lookupFor(mustache);
+            this.fn += "var proxy = Handlebars.buildContext(context, stack);"
+            this.fn += "out = out + Handlebars.handleExpression(" + expr + ", proxy, " + param + ", " + this.escaped + ");";
+        },
+
+        addInvertedSection: function(mustache) {
+            var compiler = this.compileToEndOfBlock(mustache);
+            var result = compiler.fn;
+
+            // each function made internally needs a unique IDs. These are locals, so they
+            // don't need to be globally unique, just per compiler
+            var fnId = "fn" + this.pointer.toString();
+            this.fn += "var " + fnId + " = function(context) {" + result + "}; ";
+            this.fn += "lookup = " + this.lookupFor(mustache) + "; ";
+            this.fn += "out = out + Handlebars.handleInvertedSection(lookup, context, " + fnId + ");"
+
+            this.openBlock = false;
+            this.inverted = false;
+        },
+
+        lookupFor: function(param) {
+            var parsed = Handlebars.parsePath(param);
+            var depth = parsed[0];
+            var parts = parsed[1];
+
+            if (depth > 0 || parts.length > 1) {
+                return "(Handlebars.evalExpression('" + param + "', context, stack))";
+            } else if (parts.length == 1) {
+                return "(context['" + parts[0] + "'] || fallback['" + parts[0] + "'])";
+            } else {
+                return "(context || fallback)";
+            }
+        },
+
+        compileToEndOfBlock: function(mustache) {
+            var compiler = new Handlebars.Compiler(this.string.slice(this.pointer + 1));
+
+            // sub-compile with a custom EOF instruction
+            compiler.compile(function(compiler) {
+                if (compiler.peek(3) === "{{/") {
+                    if(compiler.peek(mustache.length + 5) === "{{/" + mustache + "}}") {
+                        compiler.getChar(mustache.length + 5);
+                        return true;
+                    } else {
+                        throw new Handlebars.Exception("Mismatched block close: expected " + mustache + ".");
+                    }
+                }
+            });
+
+            // move the pointer forward the amount of characters handled by the sub-compiler
+            this.pointer += compiler.pointer + 1;
+
+            return compiler;
+        },
+
+        addBlock: function(mustache, param, parts) {
+            // set up the stack before the new compiler starts
+            //this.fn += "stack.push(context);";
+            var compiler = this.compileToEndOfBlock(mustache);
+            var result = compiler.fn;
+
+            // each function made internally needs a unique IDs. These are locals, so they
+            // don't need to be globally unique, just per compiler
+            var fnId = "fn" + this.pointer.toString();
+            this.fn += "var wrappedContext = Handlebars.buildContext(context, stack);";
+            this.fn += "stack.push(context);";
+            this.fn += "var " + fnId + " = function(context) {" + result + "}; ";
+            this.fn += "lookup = " + this.lookupFor(mustache) + "; ";
+
+            if (compiler.continueInverted) {
+                var invertedCompiler = this.compileToEndOfBlock(mustache);
+                this.fn += " var " + fnId + "Not = function(context) { " + invertedCompiler.fn + " };";
+            }
+            else {
+                this.fn += " var " + fnId + "Not = null;";
+            }
+            this.fn += "out = out + Handlebars.handleBlock(lookup, wrappedContext, " + param + ", " + fnId + ", " + fnId + "Not);"
+
+            this.fn += "stack.pop();";
+            this.openBlock = false;
+        },
+
+        addPartial: function(mustache, param) {
+            // either used a cached copy of the partial or compile a new one
+            this.fn += "if (typeof fallback['partials'] === 'undefined' || typeof fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');";
+            this.fn += "out = out + Handlebars.compilePartial(fallback['partials']['" + mustache + "'])(" + param + ", fallback);";
+        },
+
+        parseMustache: function() {
+            var chr, part, mustache, param;
+
+            var next = this.peek();
+
+            if(next === "!") {
+                this.comment = true;
+                this.getChar();
+            } else if(next === "#") {
+                this.openBlock = true;
+                this.getChar();
+            } else if (next === ">") {
+                this.partial = true;
+                this.getChar();
+            } else if (next === "^") {
+                this.inverted = true;
+                this.openBlock = true;
+                this.getChar();
+            } else if(next === "{" || next === "&") {
+                this.escaped = false;
+                this.getChar();
+            }
+
+            this.addText();
+            this.mustache = " ";
+
+            while(chr = this.getChar()) {
+                if(this.mustache && chr === "}" && this.peek() === "}") {
+                    var parts = Handlebars.trim(this.mustache).split(/\s+/);
+                    mustache = parts[0];
+                    param = this.lookupFor(parts[1]);
+
+                    this.mustache = false;
+
+                    // finish reading off the close of the handlebars
+                    this.getChar();
+                    // {{{expression}} is techically valid, but if we started with {{{ we'll try to read
+                    // }}} off of the close of the handlebars
+                    if (!this.escaped && this.peek() === "}") {
+                        this.getChar();
+                    }
+
+                    if(this.comment) {
+                        this.comment = false;
+                        return;
+                    } else if (this.partial) {
+                        this.addPartial(mustache, param)
+                        this.partial = false;
+                        return;
+                    } else if (this.inverted) {
+                        this.addInvertedSection(mustache);
+                        this.inverted = false;
+                        return;
+                    } else if(this.openBlock) {
+                        this.addBlock(mustache, param, parts)
+                        return;
+                    } else {
+                        return this.addExpression(mustache, param);
+                    }
+
+                    this.escaped = true;
+                } else if(this.comment) {
+                    ;
+                } else {
+                    this.mustache = this.mustache + chr;
+                }
+            }
+        }
+    };
+
+// CommonJS Exports
+    var exports = exports || {};
+    exports['compile'] = Handlebars.compile;
+    exports['compileToString'] = Handlebars.compileToString;
+
+    Sammy = Sammy || {};
+
+    // <tt>Sammy.Handlebars</tt> provides a quick way of using Handlebars templates in your app.
+    // The plugin itself includes the handlebars.js library created by Yehuda Katz at
+    // at http://github.com/wycats/handlebars.js
+    //
+    // Handlebars.js is an extension to the Mustache templating language  created by Chris Wanstrath. Handlebars.js
+    // and Mustache are both logicless templating languages that keep the view and the code separated like
+    // we all know they should be.
+    //
+    // By default using Sammy.Handlbars in your app adds the <tt>handlebars()</tt> method to the EventContext
+    // prototype. However, just like <tt>Sammy.Template</tt> you can change the default name of the method
+    // by passing a second argument (e.g. you could use the hbr() as the method alias so that all the template
+    // files could be in the form file.hbr instead of file.handlebars)
+    //
+    // ### Example #1
+    //
+    // The template (mytemplate.hb):
+    //
+    //       <h1>\{\{title\}\}<h1>
+    //
+    //       Hey, {{name}}! Welcome to Handlebars!
+    //
+    // The app:
+    //
+    //       var $.app = $.sammy(function() {
+    //         // include the plugin and alias handlebars() to hb()
+    //         this.use(Sammy.Handlebars, 'hb');
+    //
+    //         this.get('#/hello/:name', function() {
+    //           // set local vars
+    //           this.title = 'Hello!'
+    //           this.name = this.params.name;
+    //           // render the template and pass it through handlebars
+    //           this.partial('mytemplate.hb');
+    //         });
+    //
+    //       });
+    //
+    // If I go to #/hello/AQ in the browser, Sammy will render this to the <tt>body</tt>:
+    //
+    //       <h1>Hello!</h1>
+    //
+    //       Hey, AQ! Welcome to Handlebars!
+    //
+    //
+    // ### Example #2 - Handlebars partials
+    //
+    // The template (mytemplate.hb)
+    //
+    //       Hey, {{name}}! {{>hello_friend}}
+    //
+    //
+    // The partial (mypartial.hb)
+    //
+    //       Say hello to your friend {{friend}}!
+    //
+    // The app:
+    //
+    //       var $.app = $.sammy(function() {
+    //         // include the plugin and alias handlebars() to hb()
+    //         this.use(Sammy.Handlebars, 'hb');
+    //
+    //         this.get('#/hello/:name/to/:friend', function() {
+    //           var context = this;
+    //
+    //           // fetch handlebars-partial first
+    //           $.get('mypartial.hb', function(response){
+    //             context.partials = response;
+    //
+    //             // set local vars
+    //             context.name = this.params.name;
+    //             context.hello_friend = {name: this.params.friend};
+    //
+    //             // render the template and pass it through handlebars
+    //             context.partial('mytemplate.hb');
+    //           });
+    //         });
+    //
+    //       });
+    //
+    // If I go to #/hello/AQ/to/dP in the browser, Sammy will render this to the <tt>body</tt>:
+    //
+    //       Hey, AQ! Say hello to your friend dP!
+    //
+    // Note: You dont have to include the handlebars.js file on top of the plugin as the plugin
+    // includes the full source.
+    //
+    Sammy.Handlebars = function(app, method_alias) {
+
+        var handlebars_cache = {};
+        // *Helper* Uses handlebars.js to parse a template and interpolate and work with the passed data
+        //
+        // ### Arguments
+        //
+        // * `template` A String template.
+        // * `data` An Object containing the replacement values for the template.
+        //   data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
+        //
+        var handlebars = function(template, data, partials, name) {
+            // use name for caching
+            if (typeof name == 'undefined') name = template;
+            var fn = handlebars_cache[name];
+            if (!fn) {
+                fn = handlebars_cache[name] = Handlebars.compile(template);
+            }
+
+            data     = $.extend({}, this, data);
+            partials = $.extend({}, data.partials, partials);
+
+            return fn(data, {"partials":partials});
+        };
+
+        // set the default method name/extension
+        if (!method_alias) method_alias = 'handlebars';
+        app.helper(method_alias, handlebars);
+
+    };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.json.js b/javascript/sammy/plugins/sammy.json.js
new file mode 100644
index 00000000..83b9dd05
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.json.js
@@ -0,0 +1,362 @@
+(function($) {
+
+  // json2.js - only included if native json does not exist
+  // http://www.json.org/js.html
+  if (!window.JSON) {
+    window.JSON = {};
+  }
+  (function () {
+
+      function f(n) {
+          // Format integers to have at least two digits.
+          return n < 10 ? '0' + n : n;
+      }
+
+      if (typeof Date.prototype.toJSON !== 'function') {
+
+          Date.prototype.toJSON = function (key) {
+
+              return this.getUTCFullYear()   + '-' +
+                   f(this.getUTCMonth() + 1) + '-' +
+                   f(this.getUTCDate())      + 'T' +
+                   f(this.getUTCHours())     + ':' +
+                   f(this.getUTCMinutes())   + ':' +
+                   f(this.getUTCSeconds())   + 'Z';
+          };
+
+          String.prototype.toJSON =
+          Number.prototype.toJSON =
+          Boolean.prototype.toJSON = function (key) {
+              return this.valueOf();
+          };
+      }
+
+      var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+          escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+          gap,
+          indent,
+          meta = {    // table of character substitutions
+              '\b': '\\b',
+              '\t': '\\t',
+              '\n': '\\n',
+              '\f': '\\f',
+              '\r': '\\r',
+              '"' : '\\"',
+              '\\': '\\\\'
+          },
+          rep;
+
+
+      function quote(string) {
+
+  // If the string contains no control characters, no quote characters, and no
+  // backslash characters, then we can safely slap some quotes around it.
+  // Otherwise we must also replace the offending characters with safe escape
+  // sequences.
+
+          escapable.lastIndex = 0;
+          return escapable.test(string) ?
+              '"' + string.replace(escapable, function (a) {
+                  var c = meta[a];
+                  return typeof c === 'string' ? c :
+                      '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+              }) + '"' :
+              '"' + string + '"';
+      }
+
+
+      function str(key, holder) {
+
+  // Produce a string from holder[key].
+
+          var i,          // The loop counter.
+              k,          // The member key.
+              v,          // The member value.
+              length,
+              mind = gap,
+              partial,
+              value = holder[key];
+
+  // If the value has a toJSON method, call it to obtain a replacement value.
+
+          if (value && typeof value === 'object' &&
+                  typeof value.toJSON === 'function') {
+              value = value.toJSON(key);
+          }
+
+  // If we were called with a replacer function, then call the replacer to
+  // obtain a replacement value.
+
+          if (typeof rep === 'function') {
+              value = rep.call(holder, key, value);
+          }
+
+  // What happens next depends on the value's type.
+
+          switch (typeof value) {
+          case 'string':
+              return quote(value);
+
+          case 'number':
+
+  // JSON numbers must be finite. Encode non-finite numbers as null.
+
+              return isFinite(value) ? String(value) : 'null';
+
+          case 'boolean':
+          case 'null':
+
+  // If the value is a boolean or null, convert it to a string. Note:
+  // typeof null does not produce 'null'. The case is included here in
+  // the remote chance that this gets fixed someday.
+
+              return String(value);
+
+  // If the type is 'object', we might be dealing with an object or an array or
+  // null.
+
+          case 'object':
+
+  // Due to a specification blunder in ECMAScript, typeof null is 'object',
+  // so watch out for that case.
+
+              if (!value) {
+                  return 'null';
+              }
+
+  // Make an array to hold the partial results of stringifying this object value.
+
+              gap += indent;
+              partial = [];
+
+  // Is the value an array?
+
+              if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+  // The value is an array. Stringify every element. Use null as a placeholder
+  // for non-JSON values.
+
+                  length = value.length;
+                  for (i = 0; i < length; i += 1) {
+                      partial[i] = str(i, value) || 'null';
+                  }
+
+  // Join all of the elements together, separated with commas, and wrap them in
+  // brackets.
+
+                  v = partial.length === 0 ? '[]' :
+                      gap ? '[\n' + gap +
+                              partial.join(',\n' + gap) + '\n' +
+                                  mind + ']' :
+                            '[' + partial.join(',') + ']';
+                  gap = mind;
+                  return v;
+              }
+
+  // If the replacer is an array, use it to select the members to be stringified.
+
+              if (rep && typeof rep === 'object') {
+                  length = rep.length;
+                  for (i = 0; i < length; i += 1) {
+                      k = rep[i];
+                      if (typeof k === 'string') {
+                          v = str(k, value);
+                          if (v) {
+                              partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                          }
+                      }
+                  }
+              } else {
+
+  // Otherwise, iterate through all of the keys in the object.
+
+                  for (k in value) {
+                      if (Object.hasOwnProperty.call(value, k)) {
+                          v = str(k, value);
+                          if (v) {
+                              partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                          }
+                      }
+                  }
+              }
+
+  // Join all of the member texts together, separated with commas,
+  // and wrap them in braces.
+
+              v = partial.length === 0 ? '{}' :
+                  gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                          mind + '}' : '{' + partial.join(',') + '}';
+              gap = mind;
+              return v;
+          }
+      }
+
+  // If the JSON object does not yet have a stringify method, give it one.
+
+      if (typeof JSON.stringify !== 'function') {
+          JSON.stringify = function (value, replacer, space) {
+
+  // The stringify method takes a value and an optional replacer, and an optional
+  // space parameter, and returns a JSON text. The replacer can be a function
+  // that can replace values, or an array of strings that will select the keys.
+  // A default replacer method can be provided. Use of the space parameter can
+  // produce text that is more easily readable.
+
+              var i;
+              gap = '';
+              indent = '';
+
+  // If the space parameter is a number, make an indent string containing that
+  // many spaces.
+
+              if (typeof space === 'number') {
+                  for (i = 0; i < space; i += 1) {
+                      indent += ' ';
+                  }
+
+  // If the space parameter is a string, it will be used as the indent string.
+
+              } else if (typeof space === 'string') {
+                  indent = space;
+              }
+
+  // If there is a replacer, it must be a function or an array.
+  // Otherwise, throw an error.
+
+              rep = replacer;
+              if (replacer && typeof replacer !== 'function' &&
+                      (typeof replacer !== 'object' ||
+                       typeof replacer.length !== 'number')) {
+                  throw new Error('JSON.stringify');
+              }
+
+  // Make a fake root object containing our value under the key of ''.
+  // Return the result of stringifying the value.
+
+              return str('', {'': value});
+          };
+      }
+
+
+  // If the JSON object does not yet have a parse method, give it one.
+
+      if (typeof JSON.parse !== 'function') {
+          JSON.parse = function (text, reviver) {
+
+  // The parse method takes a text and an optional reviver function, and returns
+  // a JavaScript value if the text is a valid JSON text.
+
+              var j;
+
+              function walk(holder, key) {
+
+  // The walk method is used to recursively walk the resulting structure so
+  // that modifications can be made.
+
+                  var k, v, value = holder[key];
+                  if (value && typeof value === 'object') {
+                      for (k in value) {
+                          if (Object.hasOwnProperty.call(value, k)) {
+                              v = walk(value, k);
+                              if (v !== undefined) {
+                                  value[k] = v;
+                              } else {
+                                  delete value[k];
+                              }
+                          }
+                      }
+                  }
+                  return reviver.call(holder, key, value);
+              }
+
+
+  // Parsing happens in four stages. In the first stage, we replace certain
+  // Unicode characters with escape sequences. JavaScript handles many characters
+  // incorrectly, either silently deleting them, or treating them as line endings.
+
+              cx.lastIndex = 0;
+              if (cx.test(text)) {
+                  text = text.replace(cx, function (a) {
+                      return '\\u' +
+                          ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                  });
+              }
+
+  // In the second stage, we run the text against regular expressions that look
+  // for non-JSON patterns. We are especially concerned with '()' and 'new'
+  // because they can cause invocation, and '=' because it can cause mutation.
+  // But just to be safe, we want to reject all unexpected forms.
+
+  // We split the second stage into 4 regexp operations in order to work around
+  // crippling inefficiencies in IE's and Safari's regexp engines. First we
+  // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+  // replace all simple value tokens with ']' characters. Third, we delete all
+  // open brackets that follow a colon or comma or that begin the text. Finally,
+  // we look to see that the remaining characters are only whitespace or ']' or
+  // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+              if (/^[\],:{}\s]*$/.
+  test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+  replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+  replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+  // In the third stage we use the eval function to compile the text into a
+  // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+  // in JavaScript: it can begin a block or an object literal. We wrap the text
+  // in parens to eliminate the ambiguity.
+
+                  j = eval('(' + text + ')');
+
+  // In the optional fourth stage, we recursively walk the new structure, passing
+  // each name/value pair to a reviver function for possible transformation.
+
+                  return typeof reviver === 'function' ?
+                      walk({'': j}, '') : j;
+              }
+
+  // If the text is not JSON parseable, then a SyntaxError is thrown.
+
+              throw new SyntaxError('JSON.parse');
+          };
+      }
+  }());
+
+  Sammy = Sammy || {};
+
+  // Sammy.JSON is a simple wrapper around Douglas Crockford's ever-useful json2.js
+  // (http://www.json.org/js.html]) Sammy.JSON includes the top level JSON object if
+  // it doesn't already exist (a.k.a. does not override the native implementation that
+  // some browsers include). It also adds a <tt>json()</tt> helper to a Sammy app when
+  // included.
+  Sammy.JSON = function(app) {
+
+    app.helpers({
+      // json is a polymorphic function that translates objects aback and forth
+      // from JSON to JS. If given a string, it will parse into JS, if given a JS
+      // object it will stringify into JSON.
+      //
+      // ### Example
+      //
+      //      var app = $.sammy(function() {
+      //        this.use(Sammy.JSON);
+      //
+      //        this.get('#/', function() {
+      //          this.json({user_id: 123}); //=> "{\"user_id\":\"123\"}"
+      //          this.json("{\"user_id\":\"123\"}"); //=> [object Object]
+      //          this.json("{\"user_id\":\"123\"}").user_id; //=> "123"
+      //        });
+      //      })
+      //
+      //
+      json: function(object) {
+        if (typeof object == 'string') {
+          return JSON.parse(object);
+        } else {
+          return JSON.stringify(object);
+        }
+      }
+    });
+
+  }
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.meld.js b/javascript/sammy/plugins/sammy.meld.js
new file mode 100644
index 00000000..91a33c32
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.meld.js
@@ -0,0 +1,140 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  // `Sammy.Meld` is a simple templating engine that uses the power of jQuery's
+  // DOM manipulation to easily meld JSON data and HTML templates very quickly.
+  //
+  // The template can either be a string (i.e. loaded from a remote template)
+  // or a DOM Element/jQuery object. This allows you to have templates be DOM
+  // elements as the initial document load.
+  //
+  // ### Example
+  //
+  // The simplest case is a nested `<div>` whose class name is tied to a
+  // property of a JS object.
+  //
+  // Template:
+  //
+  //        <div class="post">
+  //          <div class="title"></div>
+  //          <div class="entry"></div>
+  //          <div class="author">
+  //            <span class="name"></span>
+  //          </div>
+  //        </div>
+  //
+  // Data:
+  //
+  //        {
+  //          "post": {
+  //            "title": "My Post",
+  //            "entry": "My Entry",
+  //            "author": {
+  //              "name": "@aq"
+  //            }
+  //          }
+  //        }
+  //
+  // Result:
+  //
+  //        <div class="post">
+  //          <div class="title">My Post</div>
+  //          <div class="entry">My Entry</div>
+  //          <div class="author">
+  //            <span class="name">@aq</span>
+  //          </div>
+  //        </div>
+  //
+  // Templates can be much more complex, and more deeply nested.
+  // More examples can be found in `test/fixtures/meld/`
+  //
+  // If you don't think the lookup by classes is semantic for you, you can easily
+  // switch the method of lookup by defining a selector function in the options
+  //
+  // For example:
+  //
+  //      meld($('.post'), post_data, {
+  //        selector: function(k) {
+  //          return '[data-key=' + k + ']';
+  //        }
+  //      });
+  //
+  // Would look for template nodes like `<div data-key='entry'>`
+  //
+  Sammy.Meld = function(app, method_alias) {
+    var default_options = {
+      selector: function(k) { return '.' + k; },
+      remove_false: true
+    };
+
+    var meld = function(template, data, options) {
+      var $template = $(template);
+
+      options = $.extend(default_options, options || {});
+
+      if (typeof data === 'string') {
+        $template.html(data);
+      } else {
+        $.each(data, function(key, value) {
+          var selector = options.selector(key),
+              $sub = $template.filter(selector),
+              $container,
+              $item,
+              is_list = false,
+              subindex = $template.index($sub);
+
+          if ($sub.length === 0) { $sub = $template.find(selector); }
+          if ($sub.length > 0) {
+            if ($.isArray(value)) {
+              $container = $('<div/>');
+              if ($sub.is('ol, ul')) {
+                is_list = true;
+                $item   = $sub.children('li:first');
+                if ($item.length == 0) { $item = $('<li/>'); }
+              } else if ($sub.children().length == 1) {
+                is_list = true;
+                $item = $sub.children(':first').clone();
+              } else {
+                $item = $sub.clone();
+              }
+              for (var i = 0; i < value.length; i++) {
+                $container.append(meld($item.clone(), value[i], options));
+              }
+              if (is_list) {
+                $sub.html($container.html());
+              } else if ($sub[0] == $template[0]) {
+                $template = $($container.html());
+              } else if (subindex >= 0) {
+                var args = [subindex, 1];
+                args = args.concat($container.children().get());
+                $template.splice.apply($template, args);
+              }
+            } else if (options.remove_false && value === false) {
+              $template.splice(subindex, 1);
+            } else if (typeof value === 'object') {
+              if ($sub.is(':empty')) {
+                $sub.attr(value, true);
+              } else {
+                $sub.html(meld($sub.html(), value, options));
+              }
+            } else {
+              $sub.html(value.toString());
+            }
+          } else {
+            $template.attr(key, value, true);
+          }
+        });
+      }
+      var dom = $template;
+      return dom;
+    };
+
+    // set the default method name/extension
+    if (!method_alias) method_alias = 'meld';
+    // create the helper at the method alias
+    app.helper(method_alias, meld);
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.mustache.js b/javascript/sammy/plugins/sammy.mustache.js
new file mode 100644
index 00000000..169f0c7b
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.mustache.js
@@ -0,0 +1,444 @@
+(function($) {
+
+if (!window.Mustache) {
+
+  /*
+    mustache.js — Logic-less templates in JavaScript
+
+    See http://mustache.github.com/ for more info.
+  */
+
+  var Mustache = function() {
+    var Renderer = function() {};
+
+    Renderer.prototype = {
+      otag: "{{",
+      ctag: "}}",
+      pragmas: {},
+      buffer: [],
+      pragmas_implemented: {
+        "IMPLICIT-ITERATOR": true
+      },
+      context: {},
+
+      render: function(template, context, partials, in_recursion) {
+        // reset buffer & set context
+        if(!in_recursion) {
+          this.context = context;
+          this.buffer = []; // TODO: make this non-lazy
+        }
+
+        // fail fast
+        if(!this.includes("", template)) {
+          if(in_recursion) {
+            return template;
+          } else {
+            this.send(template);
+            return;
+          }
+        }
+
+        template = this.render_pragmas(template);
+        var html = this.render_section(template, context, partials);
+        if(in_recursion) {
+          return this.render_tags(html, context, partials, in_recursion);
+        }
+
+        this.render_tags(html, context, partials, in_recursion);
+      },
+
+      /*
+        Sends parsed lines
+      */
+      send: function(line) {
+        if(line != "") {
+          this.buffer.push(line);
+        }
+      },
+
+      /*
+        Looks for %PRAGMAS
+      */
+      render_pragmas: function(template) {
+        // no pragmas
+        if(!this.includes("%", template)) {
+          return template;
+        }
+
+        var that = this;
+        var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
+              this.ctag);
+        return template.replace(regex, function(match, pragma, options) {
+          if(!that.pragmas_implemented[pragma]) {
+            throw({message:
+              "This implementation of mustache doesn't understand the '" +
+              pragma + "' pragma"});
+          }
+          that.pragmas[pragma] = {};
+          if(options) {
+            var opts = options.split("=");
+            that.pragmas[pragma][opts[0]] = opts[1];
+          }
+          return "";
+          // ignore unknown pragmas silently
+        });
+      },
+
+      /*
+        Tries to find a partial in the curent scope and render it
+      */
+      render_partial: function(name, context, partials) {
+        name = this.trim(name);
+        if(!partials || partials[name] === undefined) {
+          throw({message: "unknown_partial '" + name + "'"});
+        }
+        if(typeof(context[name]) != "object") {
+          return this.render(partials[name], context, partials, true);
+        }
+        return this.render(partials[name], context[name], partials, true);
+      },
+
+      /*
+        Renders inverted (^) and normal (#) sections
+      */
+      render_section: function(template, context, partials) {
+        if(!this.includes("#", template) && !this.includes("^", template)) {
+          return template;
+        }
+
+        var that = this;
+        // CSW - Added "+?" so it finds the tighest bound, not the widest
+        var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
+                "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
+                "\\s*", "mg");
+
+        // for each {{#foo}}{{/foo}} section do...
+        return template.replace(regex, function(match, type, name, content) {
+          var value = that.find(name, context);
+          if(type == "^") { // inverted section
+            if(!value || that.is_array(value) && value.length === 0) {
+              // false or empty list, render it
+              return that.render(content, context, partials, true);
+            } else {
+              return "";
+            }
+          } else if(type == "#") { // normal section
+            if(that.is_array(value)) { // Enumerable, Let's loop!
+              return that.map(value, function(row) {
+                return that.render(content, that.create_context(row),
+                  partials, true);
+              }).join("");
+            } else if(that.is_object(value)) { // Object, Use it as subcontext!
+              return that.render(content, that.create_context(value),
+                partials, true);
+            } else if(typeof value === "function") {
+              // higher order section
+              return value.call(context, content, function(text) {
+                return that.render(text, context, partials, true);
+              });
+            } else if(value) { // boolean section
+              return that.render(content, context, partials, true);
+            } else {
+              return "";
+            }
+          }
+        });
+      },
+
+      /*
+        Replace {{foo}} and friends with values from our view
+      */
+      render_tags: function(template, context, partials, in_recursion) {
+        // tit for tat
+        var that = this;
+
+        var new_regex = function() {
+          return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
+            that.ctag + "+", "g");
+        };
+
+        var regex = new_regex();
+        var tag_replace_callback = function(match, operator, name) {
+          switch(operator) {
+          case "!": // ignore comments
+            return "";
+          case "=": // set new delimiters, rebuild the replace regexp
+            that.set_delimiters(name);
+            regex = new_regex();
+            return "";
+          case ">": // render partial
+            return that.render_partial(name, context, partials);
+          case "{": // the triple mustache is unescaped
+            return that.find(name, context);
+          default: // escape the value
+            return that.escape(that.find(name, context));
+          }
+        };
+        var lines = template.split("\n");
+        for(var i = 0; i < lines.length; i++) {
+          lines[i] = lines[i].replace(regex, tag_replace_callback, this);
+          if(!in_recursion) {
+            this.send(lines[i]);
+          }
+        }
+
+        if(in_recursion) {
+          return lines.join("\n");
+        }
+      },
+
+      set_delimiters: function(delimiters) {
+        var dels = delimiters.split(" ");
+        this.otag = this.escape_regex(dels[0]);
+        this.ctag = this.escape_regex(dels[1]);
+      },
+
+      escape_regex: function(text) {
+        // thank you Simon Willison
+        if(!arguments.callee.sRE) {
+          var specials = [
+            '/', '.', '*', '+', '?', '|',
+            '(', ')', '[', ']', '{', '}', '\\'
+          ];
+          arguments.callee.sRE = new RegExp(
+            '(\\' + specials.join('|\\') + ')', 'g'
+          );
+        }
+        return text.replace(arguments.callee.sRE, '\\$1');
+      },
+
+      /*
+        find `name` in current `context`. That is find me a value
+        from the view object
+      */
+      find: function(name, context) {
+        name = this.trim(name);
+
+        // Checks whether a value is thruthy or false or 0
+        function is_kinda_truthy(bool) {
+          return bool === false || bool === 0 || bool;
+        }
+
+        var value;
+        if(is_kinda_truthy(context[name])) {
+          value = context[name];
+        } else if(is_kinda_truthy(this.context[name])) {
+          value = this.context[name];
+        }
+
+        if(typeof value === "function") {
+          return value.apply(context);
+        }
+        if(value !== undefined) {
+          return value;
+        }
+        // silently ignore unkown variables
+        return "";
+      },
+
+      // Utility methods
+
+      /* includes tag */
+      includes: function(needle, haystack) {
+        return haystack.indexOf(this.otag + needle) != -1;
+      },
+
+      /*
+        Does away with nasty characters
+      */
+      escape: function(s) {
+        s = String(s === null ? "" : s);
+        return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
+          switch(s) {
+          case "&": return "&amp;";
+          case "\\": return "\\\\";
+          case '"': return '\"';
+          case "<": return "&lt;";
+          case ">": return "&gt;";
+          default: return s;
+          }
+        });
+      },
+
+      // by @langalex, support for arrays of strings
+      create_context: function(_context) {
+        if(this.is_object(_context)) {
+          return _context;
+        } else {
+          var iterator = ".";
+          if(this.pragmas["IMPLICIT-ITERATOR"]) {
+            iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
+          }
+          var ctx = {};
+          ctx[iterator] = _context;
+          return ctx;
+        }
+      },
+
+      is_object: function(a) {
+        return a && typeof a == "object";
+      },
+
+      is_array: function(a) {
+        return Object.prototype.toString.call(a) === '[object Array]';
+      },
+
+      /*
+        Gets rid of leading and trailing whitespace
+      */
+      trim: function(s) {
+        return s.replace(/^\s*|\s*$/g, "");
+      },
+
+      /*
+        Why, why, why? Because IE. Cry, cry cry.
+      */
+      map: function(array, fn) {
+        if (typeof array.map == "function") {
+          return array.map(fn);
+        } else {
+          var r = [];
+          var l = array.length;
+          for(var i = 0; i < l; i++) {
+            r.push(fn(array[i]));
+          }
+          return r;
+        }
+      }
+    };
+
+    return({
+      name: "mustache.js",
+      version: "0.3.1-dev",
+
+      /*
+        Turns a template and view into HTML
+      */
+      to_html: function(template, view, partials, send_fun) {
+        var renderer = new Renderer();
+        if(send_fun) {
+          renderer.send = send_fun;
+        }
+        renderer.render(template, view, partials);
+        if(!send_fun) {
+          return renderer.buffer.join("\n");
+        }
+      }
+    });
+  }();
+
+} // Ensure Mustache
+
+  Sammy = Sammy || {};
+
+  // <tt>Sammy.Mustache</tt> provides a quick way of using mustache style templates in your app.
+  // The plugin itself includes the awesome mustache.js lib created and maintained by Jan Lehnardt
+  // at http://github.com/janl/mustache.js
+  //
+  // Mustache is a clever templating system that relys on double brackets {{}} for interpolation.
+  // For full details on syntax check out the original Ruby implementation created by Chris Wanstrath at
+  // http://github.com/defunkt/mustache
+  //
+  // By default using Sammy.Mustache in your app adds the <tt>mustache()</tt> method to the EventContext
+  // prototype. However, just like <tt>Sammy.Template</tt> you can change the default name of the method
+  // by passing a second argument (e.g. you could use the ms() as the method alias so that all the template
+  // files could be in the form file.ms instead of file.mustache)
+  //
+  // ### Example #1
+  //
+  // The template (mytemplate.ms):
+  //
+  //       <h1>\{\{title\}\}<h1>
+  //
+  //       Hey, {{name}}! Welcome to Mustache!
+  //
+  // The app:
+  //
+  //       var $.app = $.sammy(function() {
+  //         // include the plugin and alias mustache() to ms()
+  //         this.use(Sammy.Mustache, 'ms');
+  //
+  //         this.get('#/hello/:name', function() {
+  //           // set local vars
+  //           this.title = 'Hello!'
+  //           this.name = this.params.name;
+  //           // render the template and pass it through mustache
+  //           this.partial('mytemplate.ms');
+  //         });
+  //
+  //       });
+  //
+  // If I go to #/hello/AQ in the browser, Sammy will render this to the <tt>body</tt>:
+  //
+  //       <h1>Hello!</h1>
+  //
+  //       Hey, AQ! Welcome to Mustache!
+  //
+  //
+  // ### Example #2 - Mustache partials
+  //
+  // The template (mytemplate.ms)
+  //
+  //       Hey, {{name}}! {{>hello_friend}}
+  //
+  //
+  // The partial (mypartial.ms)
+  //
+  //       Say hello to your friend {{friend}}!
+  //
+  // The app:
+  //
+  //       var $.app = $.sammy(function() {
+  //         // include the plugin and alias mustache() to ms()
+  //         this.use(Sammy.Mustache, 'ms');
+  //
+  //         this.get('#/hello/:name/to/:friend', function() {
+  //           var context = this;
+  //
+  //           // fetch mustache-partial first
+  //           $.get('mypartial.ms', function(response){
+  //             context.partials = response;
+  //
+  //             // set local vars
+  //             context.name = this.params.name;
+  //             context.hello_friend = {name: this.params.friend};
+  //
+  //             // render the template and pass it through mustache
+  //             context.partial('mytemplate.ms');
+  //           });
+  //         });
+  //
+  //       });
+  //
+  // If I go to #/hello/AQ/to/dP in the browser, Sammy will render this to the <tt>body</tt>:
+  //
+  //       Hey, AQ! Say hello to your friend dP!
+  //
+  // Note: You dont have to include the mustache.js file on top of the plugin as the plugin
+  // includes the full source.
+  //
+  Sammy.Mustache = function(app, method_alias) {
+
+    // *Helper* Uses Mustache.js to parse a template and interpolate and work with the passed data
+    //
+    // ### Arguments
+    //
+    // * `template` A String template. {{}} Tags are evaluated and interpolated by Mustache.js
+    // * `data` An Object containing the replacement values for the template.
+    //   data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
+    // * `partials` An Object containing one or more partials (String templates
+    //   that are called from the main template).
+    //
+    var mustache = function(template, data, partials) {
+      data     = $.extend({}, this, data);
+      partials = $.extend({}, data.partials, partials);
+      return Mustache.to_html(template, data, partials);
+    };
+
+    // set the default method name/extension
+    if (!method_alias) method_alias = 'mustache';
+    app.helper(method_alias, mustache);
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.nested_params.js b/javascript/sammy/plugins/sammy.nested_params.js
new file mode 100644
index 00000000..645f8c9c
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.nested_params.js
@@ -0,0 +1,118 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  function parseValue(value) {
+    value = unescape(value);
+    if (value === "true") {
+      return true;
+    } else if (value === "false") {
+      return false;
+    } else {
+      return value;
+    }
+  };
+
+  function parseNestedParam(params, field_name, field_value) {
+    var match, name, rest;
+
+    if (field_name.match(/^[^\[]+$/)) {
+      // basic value
+      params[field_name] = parseValue(field_value);
+    } else if (match = field_name.match(/^([^\[]+)\[\](.*)$/)) {
+      // array
+      name = match[1];
+      rest = match[2];
+
+      if(params[name] && !$.isArray(params[name])) { throw('400 Bad Request'); }
+
+      if (rest) {
+        // array is not at the end of the parameter string
+        match = rest.match(/^\[([^\]]+)\](.*)$/);
+        if(!match) { throw('400 Bad Request'); }
+
+        if (params[name]) {
+          if(params[name][params[name].length - 1][match[1]]) {
+            params[name].push(parseNestedParam({}, match[1] + match[2], field_value));
+          } else {
+            $.extend(true, params[name][params[name].length - 1], parseNestedParam({}, match[1] + match[2], field_value));
+          }
+        } else {
+          params[name] = [parseNestedParam({}, match[1] + match[2], field_value)];
+        }
+      } else {
+        // array is at the end of the parameter string
+        if (params[name]) {
+          params[name].push(parseValue(field_value));
+        } else {
+          params[name] = [parseValue(field_value)];
+        }
+      }
+    } else if (match = field_name.match(/^([^\[]+)\[([^\[]+)\](.*)$/)) {
+      // hash
+      name = match[1];
+      rest = match[2] + match[3];
+
+      if (params[name] && $.isArray(params[name])) { throw('400 Bad Request'); }
+
+      if (params[name]) {
+        $.extend(true, params[name], parseNestedParam(params[name], rest, field_value));
+      } else {
+        params[name] = parseNestedParam({}, rest, field_value);
+      }
+    }
+    return params;
+  };
+
+  // <tt>Sammy.NestedParams</tt> overrides the default form parsing behavior to provide
+  // extended functionality for parsing Rack/Rails style form name/value pairs into JS
+  // Objects. In fact it passes the same suite of tests as Rack's nested query parsing.
+  // The code and tests were ported to JavaScript/Sammy by http://github.com/endor
+  //
+  // This allows you to translate a form with properly named inputs into a JSON object.
+  //
+  // ### Example
+  //
+  // Given an HTML form like so:
+  //
+  //     <form action="#/parse_me" method="post">
+  //       <input type="text" name="obj[first]" />
+  //       <input type="text" name="obj[second]" />
+  //       <input type="text" name="obj[hash][first]" />
+  //       <input type="text" name="obj[hash][second]" />
+  //     </form>
+  //
+  // And a Sammy app like:
+  //
+  //     var app = $.sammy(function(app) {
+  //       this.use(Sammy.NestedParams);
+  //
+  //       this.post('#/parse_me', function(context) {
+  //         $.log(this.params);
+  //       });
+  //     });
+  //
+  // If you filled out the form with some values and submitted it, you would see something
+  // like this in your log:
+  //
+  //     {
+  //       'obj': {
+  //         'first': 'value',
+  //         'second': 'value',
+  //         'hash': {
+  //           'first': 'value',
+  //           'second': 'value'
+  //         }
+  //       }
+  //     }
+  //
+  // It supports creating arrays with [] and other niceities. Check out the tests for
+  // full specs.
+  //
+  Sammy.NestedParams = function(app) {
+
+    app._parseParamPair = parseNestedParam;
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.path_location_proxy.js b/javascript/sammy/plugins/sammy.path_location_proxy.js
new file mode 100644
index 00000000..13f9014b
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.path_location_proxy.js
@@ -0,0 +1,29 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  // `Sammy.PathLocationProxy` is a simple Location Proxy that just
+  // gets and sets window.location. This allows you to use
+  // Sammy to route on the full URL path instead of just the hash. It
+  // will take a full refresh to get the app to change state.
+  //
+  // To read more about location proxies, check out the
+  // documentation for `Sammy.HashLocationProxy`
+  Sammy.PathLocationProxy = function(app) {
+    this.app = app;
+  };
+
+  Sammy.PathLocationProxy.prototype = {
+    bind: function() {},
+    unbind: function() {},
+
+    getLocation: function() {
+      return [window.location.pathname, window.location.search].join('');
+    },
+
+    setLocation: function(new_location) {
+      return window.location = new_location;
+    }
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.pure.js b/javascript/sammy/plugins/sammy.pure.js
new file mode 100644
index 00000000..16fe3df2
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.pure.js
@@ -0,0 +1,756 @@
+(function($) {
+
+/*!
+  PURE Unobtrusive Rendering Engine for HTML
+
+  Licensed under the MIT licenses.
+  More information at: http://www.opensource.org
+
+  Copyright (c) 2010 Michael Cvilic - BeeBole.com
+
+  Thanks to Rog Peppe for the functional JS jump
+  revision: 2.47
+*/
+
+var $p, pure = $p = function(){
+  var sel = arguments[0],
+    ctxt = false;
+
+  if(typeof sel === 'string'){
+    ctxt = arguments[1] || false;
+  }
+  return $p.core(sel, ctxt);
+};
+
+$p.core = function(sel, ctxt, plugins){
+  //get an instance of the plugins
+  var plugins = getPlugins(),
+    templates = [];
+
+  //search for the template node(s)
+  switch(typeof sel){
+    case 'string':
+      templates = plugins.find(ctxt || document, sel);
+      if(templates.length === 0) {
+        error('The template "' + sel + '" was not found');
+      }
+    break;
+    case 'undefined':
+      error('The template root is undefined, check your selector');
+    break;
+    default:
+      templates = [sel];
+  }
+
+  for(var i = 0, ii = templates.length; i < ii; i++){
+    plugins[i] = templates[i];
+  }
+  plugins.length = ii;
+
+  // set the signature string that will be replaced at render time
+  var Sig = '_s' + Math.floor( Math.random() * 1000000 ) + '_',
+    // another signature to prepend to attributes and avoid checks: style, height, on[events]...
+    attPfx = '_a' + Math.floor( Math.random() * 1000000 ) + '_',
+    // rx to parse selectors, e.g. "+tr.foo[class]"
+    selRx = /^(\+)?([^\@\+]+)?\@?([^\+]+)?(\+)?$/,
+    // set automatically attributes for some tags
+    autoAttr = {
+      IMG:'src',
+      INPUT:'value'
+    },
+    // check if the argument is an array - thanks salty-horse (Ori Avtalion)
+    isArray = Array.isArray ?
+      function(o) {
+        return Array.isArray(o);
+      } :
+      function(o) {
+        return Object.prototype.toString.call(o) === "[object Array]";
+      };
+
+  return plugins;
+
+
+  /* * * * * * * * * * * * * * * * * * * * * * * * * *
+    core functions
+   * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+  // error utility
+  function error(e){
+    if(typeof console !== 'undefined'){
+      console.log(e);
+    }else{ alert(e); }
+    throw('pure error: ' + e);
+  }
+
+  //return a new instance of plugins
+  function getPlugins(){
+    var plugins = $p.plugins,
+      f = function(){};
+    f.prototype = plugins;
+
+    // do not overwrite functions if external definition
+    f.prototype.compile    = plugins.compile || compile;
+    f.prototype.render     = plugins.render || render;
+    f.prototype.autoRender = plugins.autoRender || autoRender;
+    f.prototype.find       = plugins.find || find;
+
+    // give the compiler and the error handling to the plugin context
+    f.prototype._compiler  = compiler;
+    f.prototype._error     = error;
+
+    return new f();
+  }
+
+  // returns the outer HTML of a node
+  function outerHTML(node){
+    // if IE take the internal method otherwise build one
+    return node.outerHTML || (
+      function(n){
+            var div = document.createElement('div'), h;
+            div.appendChild( n.cloneNode(true) );
+        h = div.innerHTML;
+        div = null;
+        return h;
+      })(node);
+  }
+
+  // returns the string generator function
+  function wrapquote(qfn, f){
+    return function(ctxt){
+      return qfn('' + f.call(ctxt.context, ctxt));
+    };
+  }
+
+  // default find using querySelector when available on the browser
+  function find(n, sel){
+    if(typeof n === 'string'){
+      sel = n;
+      n = false;
+    }
+    if(typeof document.querySelectorAll !== 'undefined'){
+      return (n||document).querySelectorAll( sel );
+    }else{
+      error('You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine');
+    }
+  }
+
+  // create a function that concatenates constant string
+  // sections (given in parts) and the results of called
+  // functions to fill in the gaps between parts (fns).
+  // fns[n] fills in the gap between parts[n-1] and parts[n];
+  // fns[0] is unused.
+  // this is the inner template evaluation loop.
+  function concatenator(parts, fns){
+    return function(ctxt){
+      var strs = [ parts[ 0 ] ],
+        n = parts.length,
+        fnVal, pVal, attLine, pos;
+
+      for(var i = 1; i < n; i++){
+        fnVal = fns[i]( ctxt );
+        pVal = parts[i];
+
+        // if the value is empty and attribute, remove it
+        if(fnVal === ''){
+          attLine = strs[ strs.length - 1 ];
+          if( ( pos = attLine.search( /[\w]+=\"?$/ ) ) > -1){
+            strs[ strs.length - 1 ] = attLine.substring( 0, pos );
+            pVal = pVal.substr( 1 );
+          }
+        }
+
+        strs[ strs.length ] = fnVal;
+        strs[ strs.length ] = pVal;
+      }
+      return strs.join('');
+    };
+  }
+
+  // parse and check the loop directive
+  function parseloopspec(p){
+    var m = p.match( /^(\w+)\s*<-\s*(\S+)?$/ );
+    if(m === null){
+      error('bad loop spec: "' + p + '"');
+    }
+    if(m[1] === 'item'){
+      error('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.');
+    }
+    if( !m[2] || (m[2] && (/context/i).test(m[2]))){ //undefined or space(IE)
+      m[2] = function(ctxt){return ctxt.context;};
+    }
+    return {name: m[1], sel: m[2]};
+  }
+
+  // parse a data selector and return a function that
+  // can traverse the data accordingly, given a context.
+  function dataselectfn(sel){
+    if(typeof(sel) === 'function'){
+      return sel;
+    }
+    //check for a valid js variable name with hyphen(for properties only), $, _ and :
+    var m = sel.match(/^[a-zA-Z\$_\@][\w\$:-]*(\.[\w\$:-]*[^\.])*$/);
+    if(m === null){
+      var found = false, s = sel, parts = [], pfns = [], i = 0, retStr;
+      // check if literal
+      if(/\'|\"/.test( s.charAt(0) )){
+        if(/\'|\"/.test( s.charAt(s.length-1) )){
+          retStr = s.substring(1, s.length-1);
+          return function(){ return retStr; };
+        }
+      }else{
+        // check if literal + #{var}
+        while((m = s.match(/#\{([^{}]+)\}/)) !== null){
+          found = true;
+          parts[i++] = s.slice(0, m.index);
+          pfns[i] = dataselectfn(m[1]);
+          s = s.slice(m.index + m[0].length, s.length);
+        }
+      }
+      if(!found){
+        error('bad data selector syntax: ' + sel);
+      }
+      parts[i] = s;
+      return concatenator(parts, pfns);
+    }
+    m = sel.split('.');
+    return function(ctxt){
+      var data = ctxt.context;
+      if(!data){
+        return '';
+      }
+      var  v = ctxt[m[0]],
+        i = 0;
+      if(v && v.item){
+        data = v.item;
+        i += 1;
+      }
+      var n = m.length;
+      for(; i < n; i++){
+        if(!data){break;}
+        data = data[m[i]];
+      }
+      return (!data && data !== 0) ? '':data;
+    };
+  }
+
+  // wrap in an object the target node/attr and their properties
+  function gettarget(dom, sel, isloop){
+    var osel, prepend, selector, attr, append, target = [];
+    if( typeof sel === 'string' ){
+      osel = sel;
+      var m = sel.match(selRx);
+      if( !m ){
+        error( 'bad selector syntax: ' + sel );
+      }
+
+      prepend = m[1];
+      selector = m[2];
+      attr = m[3];
+      append = m[4];
+
+      if(selector === '.' || ( !selector && attr ) ){
+        target[0] = dom;
+      }else{
+        target = plugins.find(dom, selector);
+      }
+      if(!target || target.length === 0){
+        return error('The node "' + sel + '" was not found in the template');
+      }
+    }else{
+      // autoRender node
+      prepend = sel.prepend;
+      attr = sel.attr;
+      append = sel.append;
+      target = [dom];
+    }
+
+    if( prepend || append ){
+      if( prepend && append ){
+        error('append/prepend cannot take place at the same time');
+      }else if( isloop ){
+        error('no append/prepend/replace modifiers allowed for loop target');
+      }else if( append && isloop ){
+        error('cannot append with loop (sel: ' + osel + ')');
+      }
+    }
+    var setstr, getstr, quotefn, isStyle, isClass, attName, setfn;
+    if(attr){
+      isStyle = (/^style$/i).test(attr);
+      isClass = (/^class$/i).test(attr);
+      attName = isClass ? 'className' : attr;
+      setstr = function(node, s) {
+        node.setAttribute(attPfx + attr, s);
+        if (attName in node && !isStyle) {
+          node[attName] = '';
+        }
+        if (node.nodeType === 1) {
+          node.removeAttribute(attr);
+          isClass && node.removeAttribute(attName);
+        }
+      };
+      if (isStyle || isClass) {//IE no quotes special care
+        if(isStyle){
+          getstr = function(n){ return n.style.cssText; };
+        }else{
+          getstr = function(n){ return n.className;  };
+        }
+        quotefn = function(s){ return s.replace(/\"/g, '&quot;'); };
+      }else {
+        getstr = function(n){ return n.getAttribute(attr); };
+        quotefn = function(s){ return s.replace(/\"/g, '&quot;').replace(/\s/g, '&nbsp;'); };
+      }
+      if(prepend){
+        setfn = function(node, s){ setstr( node, s + getstr( node )); };
+      }else if(append){
+        setfn = function(node, s){ setstr( node, getstr( node ) + s); };
+      }else{
+        setfn = function(node, s){ setstr( node, s ); };
+      }
+    }else{
+      if (isloop) {
+        setfn = function(node, s) {
+          var pn = node.parentNode;
+          if (pn) {
+            //replace node with s
+            pn.insertBefore(document.createTextNode(s), node.nextSibling);
+            pn.removeChild(node);
+          }
+        };
+      } else {
+        if (prepend) {
+          setfn = function(node, s) { node.insertBefore(document.createTextNode(s), node.firstChild);  };
+        } else if (append) {
+          setfn = function(node, s) { node.appendChild(document.createTextNode(s));};
+        } else {
+          setfn = function(node, s) {
+            while (node.firstChild) { node.removeChild(node.firstChild); }
+            node.appendChild(document.createTextNode(s));
+          };
+        }
+      }
+      quotefn = function(s) { return s; };
+    }
+    return { attr: attr, nodes: target, set: setfn, sel: osel, quotefn: quotefn };
+  }
+
+  function setsig(target, n){
+    var sig = Sig + n + ':';
+    for(var i = 0; i < target.nodes.length; i++){
+      // could check for overlapping targets here.
+      target.set( target.nodes[i], sig );
+    }
+  }
+
+  // read de loop data, and pass it to the inner rendering function
+  function loopfn(name, dselect, inner, sorter, filter){
+    return function(ctxt){
+      var a = dselect(ctxt),
+        old = ctxt[name],
+        temp = { items : a },
+        filtered = 0,
+        length,
+        strs = [],
+        buildArg = function(idx, temp, ftr, len){
+          ctxt.pos = temp.pos = idx;
+          ctxt.item = temp.item = a[ idx ];
+          ctxt.items = a;
+          //if array, set a length property - filtered items
+          typeof len !== 'undefined' &&  (ctxt.length = len);
+          //if filter directive
+          if(typeof ftr === 'function' && ftr(ctxt) === false){
+            filtered++;
+            return;
+          }
+          strs.push( inner.call(temp, ctxt ) );
+        };
+      ctxt[name] = temp;
+      if( isArray(a) ){
+        length = a.length || 0;
+        // if sort directive
+        if(typeof sorter === 'function'){
+          a.sort(sorter);
+        }
+        //loop on array
+        for(var i = 0, ii = length; i < ii; i++){
+          buildArg(i, temp, filter, length - filtered);
+        }
+      }else{
+        if(a && typeof sorter !== 'undefined'){
+          error('sort is only available on arrays, not objects');
+        }
+        //loop on collections
+        for(var prop in a){
+          a.hasOwnProperty( prop ) && buildArg(prop, temp, filter);
+        }
+      }
+
+      typeof old !== 'undefined' ? ctxt[name] = old : delete ctxt[name];
+      return strs.join('');
+    };
+  }
+  // generate the template for a loop node
+  function loopgen(dom, sel, loop, fns){
+    var already = false, ls, sorter, filter, prop;
+    for(prop in loop){
+      if(loop.hasOwnProperty(prop)){
+        if(prop === 'sort'){
+          sorter = loop.sort;
+          continue;
+        }else if(prop === 'filter'){
+          filter = loop.filter;
+          continue;
+        }
+        if(already){
+          error('cannot have more than one loop on a target');
+        }
+        ls = prop;
+        already = true;
+      }
+    }
+    if(!ls){
+      error('Error in the selector: ' + sel + '\nA directive action must be a string, a function or a loop(<-)');
+    }
+    var dsel = loop[ls];
+    // if it's a simple data selector then we default to contents, not replacement.
+    if(typeof(dsel) === 'string' || typeof(dsel) === 'function'){
+      loop = {};
+      loop[ls] = {root: dsel};
+      return loopgen(dom, sel, loop, fns);
+    }
+    var spec = parseloopspec(ls),
+      itersel = dataselectfn(spec.sel),
+      target = gettarget(dom, sel, true),
+      nodes = target.nodes;
+
+    for(i = 0; i < nodes.length; i++){
+      var node = nodes[i],
+        inner = compiler(node, dsel);
+      fns[fns.length] = wrapquote(target.quotefn, loopfn(spec.name, itersel, inner, sorter, filter));
+      target.nodes = [node];    // N.B. side effect on target.
+      setsig(target, fns.length - 1);
+    }
+  }
+
+  function getAutoNodes(n, data){
+    var ns = n.getElementsByTagName('*'),
+      an = [],
+      openLoops = {a:[],l:{}},
+      cspec,
+      isNodeValue,
+      i, ii, j, jj, ni, cs, cj;
+    //for each node found in the template
+    for(i = -1, ii = ns.length; i < ii; i++){
+      ni = i > -1 ?ns[i]:n;
+      if(ni.nodeType === 1 && ni.className !== ''){
+        //when a className is found
+        cs = ni.className.split(' ');
+        // for each className
+        for(j = 0, jj=cs.length;j<jj;j++){
+          cj = cs[j];
+          // check if it is related to a context property
+          cspec = checkClass(cj, ni.tagName);
+          // if so, store the node, plus the type of data
+          if(cspec !== false){
+            isNodeValue = (/nodevalue/i).test(cspec.attr);
+            if(cspec.sel.indexOf('@') > -1 || isNodeValue){
+              ni.className = ni.className.replace('@'+cspec.attr, '');
+              if(isNodeValue){
+                cspec.attr = false;
+              }
+            }
+            an.push({n:ni, cspec:cspec});
+          }
+        }
+      }
+    }
+    return an;
+
+    function checkClass(c, tagName){
+      // read the class
+      var ca = c.match(selRx),
+        attr = ca[3] || autoAttr[tagName],
+        cspec = {prepend:!!ca[1], prop:ca[2], attr:attr, append:!!ca[4], sel:c},
+        i, ii, loopi, loopil, val;
+      // check in existing open loops
+      for(i = openLoops.a.length-1; i >= 0; i--){
+        loopi = openLoops.a[i];
+        loopil = loopi.l[0];
+        val = loopil && loopil[cspec.prop];
+        if(typeof val !== 'undefined'){
+          cspec.prop = loopi.p + '.' + cspec.prop;
+          if(openLoops.l[cspec.prop] === true){
+            val = val[0];
+          }
+          break;
+        }
+      }
+      // not found check first level of data
+      if(typeof val === 'undefined'){
+        val = isArray(data) ? data[0][cspec.prop] : data[cspec.prop];
+        // nothing found return
+        if(typeof val === 'undefined'){
+          return false;
+        }
+      }
+      // set the spec for autoNode
+      if(isArray(val)){
+        openLoops.a.push( {l:val, p:cspec.prop} );
+        openLoops.l[cspec.prop] = true;
+        cspec.t = 'loop';
+      }else{
+        cspec.t = 'str';
+      }
+      return cspec;
+    }
+  }
+
+  // returns a function that, given a context argument,
+  // will render the template defined by dom and directive.
+  function compiler(dom, directive, data, ans){
+    var fns = [];
+    // autoRendering nodes parsing -> auto-nodes
+    ans = ans || data && getAutoNodes(dom, data);
+    if(data){
+      var j, jj, cspec, n, target, nodes, itersel, node, inner;
+      // for each auto-nodes
+      while(ans.length > 0){
+        cspec = ans[0].cspec;
+        n = ans[0].n;
+        ans.splice(0, 1);
+        if(cspec.t === 'str'){
+          // if the target is a value
+          target = gettarget(n, cspec, false);
+          setsig(target, fns.length);
+          fns[fns.length] = wrapquote(target.quotefn, dataselectfn(cspec.prop));
+        }else{
+          // if the target is a loop
+          itersel = dataselectfn(cspec.sel);
+          target = gettarget(n, cspec, true);
+          nodes = target.nodes;
+          for(j = 0, jj = nodes.length; j < jj; j++){
+            node = nodes[j];
+            inner = compiler(node, false, data, ans);
+            fns[fns.length] = wrapquote(target.quotefn, loopfn(cspec.sel, itersel, inner));
+            target.nodes = [node];
+            setsig(target, fns.length - 1);
+          }
+        }
+      }
+    }
+    // read directives
+    var target, dsel;
+    for(var sel in directive){
+      if(directive.hasOwnProperty(sel)){
+        dsel = directive[sel];
+        if(typeof(dsel) === 'function' || typeof(dsel) === 'string'){
+          // set the value for the node/attr
+          target = gettarget(dom, sel, false);
+          setsig(target, fns.length);
+          fns[fns.length] = wrapquote(target.quotefn, dataselectfn(dsel));
+        }else{
+          // loop on node
+          loopgen(dom, sel, dsel, fns);
+        }
+      }
+    }
+        // convert node to a string
+        var h = outerHTML(dom), pfns = [];
+    // IE adds an unremovable "selected, value" attribute
+    // hard replace while waiting for a better solution
+        h = h.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig, "<$1 $3>");
+
+        // remove attribute prefix
+        h = h.split(attPfx).join('');
+
+    // slice the html string at "Sig"
+    var parts = h.split( Sig ), p;
+    // for each slice add the return string of
+    for(var i = 1; i < parts.length; i++){
+      p = parts[i];
+      // part is of the form "fn-number:..." as placed there by setsig.
+      pfns[i] = fns[ parseInt(p, 10) ];
+      parts[i] = p.substring( p.indexOf(':') + 1 );
+    }
+    return concatenator(parts, pfns);
+  }
+  // compile the template with directive
+  // if a context is passed, the autoRendering is triggered automatically
+  // return a function waiting the data as argument
+  function compile(directive, ctxt, template){
+    var rfn = compiler( ( template || this[0] ).cloneNode(true), directive, ctxt);
+    return function(context){
+      return rfn({context:context});
+    };
+  }
+  //compile with the directive as argument
+  // run the template function on the context argument
+  // return an HTML string
+  // should replace the template and return this
+  function render(ctxt, directive){
+    var fn = typeof directive === 'function' ? directive : plugins.compile( directive, false, this[0] );
+    for(var i = 0, ii = this.length; i < ii; i++){
+      this[i] = replaceWith( this[i], fn( ctxt, false ));
+    }
+    context = null;
+    return this;
+  }
+
+  // compile the template with autoRender
+  // run the template function on the context argument
+  // return an HTML string
+  function autoRender(ctxt, directive){
+    var fn = plugins.compile( directive, ctxt, this[0] );
+    for(var i = 0, ii = this.length; i < ii; i++){
+      this[i] = replaceWith( this[i], fn( ctxt, false));
+    }
+    context = null;
+    return this;
+  }
+
+  function replaceWith(elm, html) {
+    var ne,
+      ep = elm.parentNode,
+      depth = 0;
+    switch (elm.tagName) {
+      case 'TBODY': case 'THEAD': case 'TFOOT':
+        html = '<TABLE>' + html + '</TABLE>';
+        depth = 1;
+      break;
+      case 'TR':
+        html = '<TABLE><TBODY>' + html + '</TBODY></TABLE>';
+        depth = 2;
+      break;
+      case 'TD': case 'TH':
+        html = '<TABLE><TBODY><TR>' + html + '</TR></TBODY></TABLE>';
+        depth = 3;
+      break;
+    }
+    tmp = document.createElement('SPAN');
+    tmp.style.display = 'none';
+    document.body.appendChild(tmp);
+    tmp.innerHTML = html;
+    ne = tmp.firstChild;
+    while (depth--) {
+      ne = ne.firstChild;
+    }
+    ep.insertBefore(ne, elm);
+    ep.removeChild(elm);
+    document.body.removeChild(tmp);
+    elm = ne;
+
+    ne = ep = null;
+    return elm;
+  }
+};
+
+$p.plugins = {};
+
+$p.libs = {
+  dojo:function(){
+    if(typeof document.querySelector === 'undefined'){
+      $p.plugins.find = function(n, sel){
+        return dojo.query(sel, n);
+      };
+    }
+  },
+  domassistant:function(){
+    if(typeof document.querySelector === 'undefined'){
+      $p.plugins.find = function(n, sel){
+        return $(n).cssSelect(sel);
+      };
+    }
+    DOMAssistant.attach({
+      publicMethods : [ 'compile', 'render', 'autoRender'],
+      compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); },
+      render:function(ctxt, directive){ return $( $p(this).render(ctxt, directive) )[0]; },
+      autoRender:function(ctxt, directive){ return $( $p(this).autoRender(ctxt, directive) )[0]; }
+    });
+  },
+  jquery:function(){
+    if(typeof document.querySelector === 'undefined'){
+      $p.plugins.find = function(n, sel){
+        return jQuery(n).find(sel);
+      };
+    }
+    jQuery.fn.extend({
+      compile:function(directive, ctxt){ return $p(this[0]).compile(directive, ctxt); },
+      render:function(ctxt, directive){ return jQuery( $p( this[0] ).render( ctxt, directive ) ); },
+      autoRender:function(ctxt, directive){ return jQuery( $p( this[0] ).autoRender( ctxt, directive ) ); }
+    });
+  },
+  mootools:function(){
+    if(typeof document.querySelector === 'undefined'){
+      $p.plugins.find = function(n, sel){
+        return $(n).getElements(sel);
+      };
+    }
+    Element.implement({
+      compile:function(directive, ctxt){ return $p(this).compile(directive, ctxt); },
+      render:function(ctxt, directive){ return $p(this).render(ctxt, directive); },
+      autoRender:function(ctxt, directive){ return $p(this).autoRender(ctxt, directive); }
+    });
+  },
+  prototype:function(){
+    if(typeof document.querySelector === 'undefined'){
+      $p.plugins.find = function(n, sel){
+        n = n === document ? n.body : n;
+        return typeof n === 'string' ? $$(n) : $(n).select(sel);
+      };
+    }
+    Element.addMethods({
+      compile:function(element, directive, ctxt){ return $p(element).compile(directive, ctxt); },
+      render:function(element, ctxt, directive){ return $p(element).render(ctxt, directive); },
+      autoRender:function(element, ctxt, directive){ return $p(element).autoRender(ctxt, directive); }
+    });
+  },
+  sizzle:function(){
+    if(typeof document.querySelector === 'undefined'){
+      $p.plugins.find = function(n, sel){
+        return Sizzle(sel, n);
+      };
+    }
+  },
+  sly:function(){
+    if(typeof document.querySelector === 'undefined'){
+      $p.plugins.find = function(n, sel){
+        return Sly(sel, n);
+      };
+    }
+  }
+};
+
+// get lib specifics if available
+(function(){
+  var libkey =
+    typeof dojo         !== 'undefined' && 'dojo' ||
+    typeof DOMAssistant !== 'undefined' && 'domassistant' ||
+    typeof jQuery       !== 'undefined' && 'jquery' ||
+    typeof MooTools     !== 'undefined' && 'mootools' ||
+    typeof Prototype    !== 'undefined' && 'prototype' ||
+    typeof Sizzle       !== 'undefined' && 'sizzle' ||
+    typeof Sly          !== 'undefined' && 'sly';
+
+  libkey && $p.libs[libkey]();
+})();
+
+
+  Sammy = Sammy || {};
+
+  // `Sammy.Pure` is a simple wrapper around the pure.js templating engine for
+  // use in Sammy apps.
+  //
+  // See http://beebole.com/pure/ for detailed documentation.
+  Sammy.Pure = function(app, method_alias) {
+
+    var pure = function(template, data, directives) {
+      return $(template).autoRender(data, directives);
+    };
+
+    // set the default method name/extension
+    if (!method_alias) method_alias = 'pure';
+    app.helper(method_alias, pure);
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.storage.js b/javascript/sammy/plugins/sammy.storage.js
new file mode 100644
index 00000000..60207f72
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.storage.js
@@ -0,0 +1,577 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  // Sammy.Store is an abstract adapter class that wraps the multitude of in
+  // browser data storage into a single common set of methods for storing and
+  // retreiving data. The JSON library is used (through the inclusion of the
+  // Sammy.JSON) plugin, to automatically convert objects back and forth from
+  // stored strings.
+  //
+  // Sammy.Store can be used directly, but within a Sammy.Application it is much
+  // easier to use the `Sammy.Storage` plugin and its helper methods.
+  //
+  // Sammy.Store also supports the KVO pattern, by firing DOM/jQuery Events when
+  // a key is set.
+  //
+  // ### Example
+  //
+  //      // create a new store named 'mystore', tied to the #main element, using HTML5 localStorage
+  //      // Note: localStorage only works on browsers that support it
+  //      var store = new Sammy.Store({name: 'mystore', element: '#element', type: 'local'});
+  //      store.set('foo', 'bar');
+  //      store.get('foo'); //=> 'bar'
+  //      store.set('json', {obj: 'this is an obj'});
+  //      store.get('json'); //=> {obj: 'this is an obj'}
+  //      store.keys(); //=> ['foo','json']
+  //      store.clear('foo');
+  //      store.keys(); //=> ['json']
+  //      store.clearAll();
+  //      store.keys(); //=> []
+  //
+  // ### Arguments
+  //
+  // The constructor takes a single argument which is a Object containing these possible options.
+  //
+  // * `name` The name/namespace of this store. Stores are unique by name/type. (default 'store')
+  // * `element` A selector for the element that the store is bound to. (default 'body')
+  // * `type` The type of storage/proxy to use (default 'memory')
+  //
+  // Extra options are passed to the storage constructor.
+  // Sammy.Store supports the following methods of storage:
+  //
+  // * `memory` Basic object storage
+  // * `data` jQuery.data DOM Storage
+  // * `cookie` Access to document.cookie. Limited to 2K
+  // * `local` HTML5 DOM localStorage, browswer support is currently limited.
+  // * `session` HTML5 DOM sessionStorage, browswer support is currently limited.
+  //
+  Sammy.Store = function(options) {
+    var store = this;
+    this.options  = options || {};
+    this.name     = this.options.name || 'store';
+    this.element  = this.options.element || 'body';
+    this.$element = $(this.element);
+    if ($.isArray(this.options.type)) {
+      $.each(this.options.type, function(i, type) {
+        if (Sammy.Store.isAvailable(type)) {
+          store.type = type;
+          return false;
+        }
+      });
+    } else {
+      this.type = this.options.type || 'memory';
+    }
+    this.meta_key = this.options.meta_key || '__keys__';
+    this.storage  = new Sammy.Store[Sammy.Store.stores[this.type]](this.name, this.element, this.options);
+  };
+
+  Sammy.Store.stores = {
+    'memory': 'Memory',
+    'data': 'Data',
+    'local': 'LocalStorage',
+    'session': 'SessionStorage',
+    'cookie': 'Cookie'
+  };
+
+  $.extend(Sammy.Store.prototype, {
+    // Checks for the availability of the current storage type in the current browser/config.
+    isAvailable: function() {
+      if ($.isFunction(this.storage.isAvailable)) {
+        return this.storage.isAvailable();
+      } else {
+        true;
+      }
+    },
+    // Checks for the existance of `key` in the current store. Returns a boolean.
+    exists: function(key) {
+      return this.storage.exists(key);
+    },
+    // Sets the value of `key` with `value`. If `value` is an
+    // object, it is turned to and stored as a string with `JSON.stringify`.
+    // It also tries to conform to the KVO pattern triggering jQuery events on the
+    // element that the store is bound to.
+    //
+    // ### Example
+    //
+    //      var store = new Sammy.Store({name: 'kvo'});
+    //      $('body').bind('set-kvo-foo', function(e, data) {
+    //        Sammy.log(data.key + ' changed to ' + data.value);
+    //      });
+    //      store.set('foo', 'bar'); // logged: foo changed to bar
+    //
+    set: function(key, value) {
+      var string_value = (typeof value == 'string') ? value : JSON.stringify(value);
+      key = key.toString();
+      this.storage.set(key, string_value);
+      if (key != this.meta_key) {
+        this._addKey(key);
+        this.$element.trigger('set-' + this.name, {key: key, value: value});
+        this.$element.trigger('set-' + this.name + '-' + key, {key: key, value: value});
+      };
+      // always return the original value
+      return value;
+    },
+    // Returns the set value at `key`, parsing with `JSON.parse` and
+    // turning into an object if possible
+    get: function(key) {
+      var value = this.storage.get(key);
+      if (typeof value == 'undefined' || value == null || value == '') {
+        return value;
+      }
+      try {
+        return JSON.parse(value);
+      } catch(e) {
+        return value;
+      }
+    },
+    // Removes the value at `key` from the current store
+    clear: function(key) {
+      this._removeKey(key);
+      return this.storage.clear(key);
+    },
+    // Clears all the values for the current store.
+    clearAll: function() {
+      var self = this;
+      this.each(function(key, value) {
+        self.clear(key);
+      });
+    },
+    // Returns the all the keys set for the current store as an array.
+    // Internally Sammy.Store keeps this array in a 'meta_key' for easy access.
+    keys: function() {
+      return this.get(this.meta_key) || [];
+    },
+    // Iterates over each key value pair passing them to the `callback` function
+    //
+    // ### Example
+    //
+    //      store.each(function(key, value) {
+    //        Sammy.log('key', key, 'value', value);
+    //      });
+    //
+    each: function(callback) {
+      var i = 0,
+          keys = this.keys(),
+          returned;
+
+      for (i; i < keys.length; i++) {
+        returned = callback(keys[i], this.get(keys[i]));
+        if (returned === false) { return false; }
+      };
+    },
+    // Filters the store by a filter function that takes a key value.
+    // Returns an array of arrays where the first element of each array
+    // is the key and the second is the value of that key.
+    //
+    // ### Example
+    //
+    //      var store = new Sammy.Store;
+    //      store.set('one', 'two');
+    //      store.set('two', 'three');
+    //      store.set('1', 'two');
+    //      var returned = store.filter(function(key, value) {
+    //        // only return
+    //        return value === 'two';
+    //      });
+    //      // returned => [['one', 'two'], ['1', 'two']];
+    //
+    filter: function(callback) {
+      var found = [];
+      this.each(function(key, value) {
+        if (callback(key, value)) {
+          found.push([key, value]);
+        }
+        return true;
+      });
+      return found;
+    },
+    // Works exactly like filter except only returns the first matching key
+    // value pair instead of all of them
+    first: function(callback) {
+      var found = false;
+      this.each(function(key, value) {
+        if (callback(key, value)) {
+          found = [key, value];
+          return false;
+        }
+      });
+      return found;
+    },
+    // Returns the value at `key` if set, otherwise, runs the callback
+    // and sets the value to the value returned in the callback.
+    //
+    // ### Example
+    //
+    //    var store = new Sammy.Store;
+    //    store.exists('foo'); //=> false
+    //    store.fetch('foo', function() {
+    //      return 'bar!';
+    //    }); //=> 'bar!'
+    //    store.get('foo') //=> 'bar!'
+    //    store.fetch('foo', function() {
+    //      return 'baz!';
+    //    }); //=> 'bar!
+    //
+    fetch: function(key, callback) {
+      if (!this.exists(key)) {
+        return this.set(key, callback.apply(this));
+      } else {
+        return this.get(key);
+      }
+    },
+    // loads the response of a request to `path` into `key`.
+    //
+    // ### Example
+    //
+    // In /mytemplate.tpl:
+    //
+    //    My Template
+    //
+    // In app.js:
+    //
+    //    var store = new Sammy.Store;
+    //    store.load('mytemplate', '/mytemplate.tpl', function() {
+    //      s.get('mytemplate') //=> My Template
+    //    });
+    //
+    load: function(key, path, callback) {
+      var s = this;
+      $.get(path, function(response) {
+        s.set(key, response);
+        if (callback) { callback.apply(this, [response]); }
+      });
+    },
+
+    _addKey: function(key) {
+      var keys = this.keys();
+      if ($.inArray(key, keys) == -1) { keys.push(key); }
+      this.set(this.meta_key, keys);
+    },
+    _removeKey: function(key) {
+      var keys = this.keys();
+      var index = $.inArray(key, keys);
+      if (index != -1) { keys.splice(index, 1); }
+      this.set(this.meta_key, keys);
+    }
+  });
+
+  // Tests if the type of storage is available/works in the current browser/config.
+  // Especially useful for testing the availability of the awesome, but not widely
+  // supported HTML5 DOM storage
+  Sammy.Store.isAvailable = function(type) {
+    try {
+      return Sammy.Store[Sammy.Store.stores[type]].prototype.isAvailable();
+    } catch(e) {
+      return false;
+    }
+  };
+
+  // Memory ('memory') is the basic/default store. It stores data in a global
+  // JS object. Data is lost on refresh.
+  Sammy.Store.Memory = function(name, element) {
+    this.name  = name;
+    this.element = element;
+    this.namespace = [this.element, this.name].join('.');
+    Sammy.Store.Memory.store = Sammy.Store.Memory.store || {};
+    Sammy.Store.Memory.store[this.namespace] = Sammy.Store.Memory.store[this.namespace] || {};
+    this.store = Sammy.Store.Memory.store[this.namespace];
+  };
+  $.extend(Sammy.Store.Memory.prototype, {
+    isAvailable: function() { return true; },
+    exists: function(key) {
+      return (typeof this.store[key] != "undefined");
+    },
+    set: function(key, value) {
+      return this.store[key] = value;
+    },
+    get: function(key) {
+      return this.store[key];
+    },
+    clear: function(key) {
+      delete this.store[key];
+    }
+  });
+
+  // Data ('data') stores objects using the jQuery.data() methods. This has the advantadge
+  // of scoping the data to the specific element. Like the 'memory' store its data
+  // will only last for the length of the current request (data is lost on refresh/etc).
+  Sammy.Store.Data = function(name, element) {
+    this.name = name;
+    this.element = element;
+    this.$element = $(element);
+  };
+  $.extend(Sammy.Store.Data.prototype, {
+    isAvailable: function() { return true; },
+    exists: function(key) {
+      return !!this.$element.data(this._key(key));
+    },
+    set: function(key, value) {
+      return this.$element.data(this._key(key), value);
+    },
+    get: function(key) {
+      return this.$element.data(this._key(key));
+    },
+    clear: function(key) {
+      this.$element.removeData(this._key(key));
+    },
+    _key: function(key) {
+      return ['store', this.name, key].join('.');
+    }
+  });
+
+  // LocalStorage ('local') makes use of HTML5 DOM Storage, and the window.localStorage
+  // object. The great advantage of this method is that data will persist beyond
+  // the current request. It can be considered a pretty awesome replacement for
+  // cookies accessed via JS. The great disadvantage, though, is its only available
+  // on the latest and greatest browsers.
+  //
+  // For more info on DOM Storage:
+  // [https://developer.mozilla.org/en/DOM/Storage]
+  // [http://www.w3.org/TR/2009/WD-webstorage-20091222/]
+  //
+  Sammy.Store.LocalStorage = function(name, element) {
+    this.name = name;
+    this.element = element;
+  };
+  $.extend(Sammy.Store.LocalStorage.prototype, {
+    isAvailable: function() {
+      return ('localStorage' in window) && (window.location.protocol != 'file:');
+    },
+    exists: function(key) {
+      return (this.get(key) != null);
+    },
+    set: function(key, value) {
+      return window.localStorage.setItem(this._key(key), value);
+    },
+    get: function(key) {
+      return window.localStorage.getItem(this._key(key));
+    },
+    clear: function(key) {
+      window.localStorage.removeItem(this._key(key));;
+    },
+    _key: function(key) {
+      return ['store', this.element, this.name, key].join('.');
+    }
+  });
+
+  // .SessionStorage ('session') is similar to LocalStorage (part of the same API)
+  // and shares similar browser support/availability. The difference is that
+  // SessionStorage is only persistant through the current 'session' which is defined
+  // as the length that the current window is open. This means that data will survive
+  // refreshes but not close/open or multiple windows/tabs. For more info, check out
+  // the `LocalStorage` documentation and links.
+  Sammy.Store.SessionStorage = function(name, element) {
+    this.name = name;
+    this.element = element;
+  };
+  $.extend(Sammy.Store.SessionStorage.prototype, {
+    isAvailable: function() {
+      return ('sessionStorage' in window) &&
+      (window.location.protocol != 'file:') &&
+      ($.isFunction(window.sessionStorage.setItem));
+    },
+    exists: function(key) {
+      return (this.get(key) != null);
+    },
+    set: function(key, value) {
+      return window.sessionStorage.setItem(this._key(key), value);
+    },
+    get: function(key) {
+      var value = window.sessionStorage.getItem(this._key(key));
+      if (value && typeof value.value != "undefined") { value = value.value }
+      return value;
+    },
+    clear: function(key) {
+      window.sessionStorage.removeItem(this._key(key));;
+    },
+    _key: function(key) {
+      return ['store', this.element, this.name, key].join('.');
+    }
+  });
+
+  // .Cookie ('cookie') storage uses browser cookies to store data. JavaScript
+  // has access to a single document.cookie variable, which is limited to 2Kb in
+  // size. Cookies are also considered 'unsecure' as the data can be read easily
+  // by other sites/JS. Cookies do have the advantage, though, of being widely
+  // supported and persistent through refresh and close/open. Where available,
+  // HTML5 DOM Storage like LocalStorage and SessionStorage should be used.
+  //
+  // .Cookie can also take additional options:
+  //
+  // * `expires_in` Number of seconds to keep the cookie alive (default 2 weeks).
+  // * `path` The path to activate the current cookie for (default '/').
+  //
+  // For more information about document.cookie, check out the pre-eminint article
+  // by ppk: [http://www.quirksmode.org/js/cookies.html]
+  //
+  Sammy.Store.Cookie = function(name, element, options) {
+    this.name = name;
+    this.element = element;
+    this.options = options || {};
+    this.path = this.options.path || '/';
+    // set the expires in seconds or default 14 days
+    this.expires_in = this.options.expires_in || (14 * 24 * 60 * 60);
+  };
+  $.extend(Sammy.Store.Cookie.prototype, {
+    isAvailable: function() {
+      return ('cookie' in document) && (window.location.protocol != 'file:');
+    },
+    exists: function(key) {
+      return (this.get(key) != null);
+    },
+    set: function(key, value) {
+      return this._setCookie(key, value);
+    },
+    get: function(key) {
+      return this._getCookie(key);
+    },
+    clear: function(key) {
+      this._setCookie(key, "", -1);
+    },
+    _key: function(key) {
+      return ['store', this.element, this.name, key].join('.');
+    },
+    _getCookie: function(key) {
+      var escaped = this._key(key).replace(/(\.|\*|\(|\)|\[|\])/g, '\\$1');
+      var match = document.cookie.match("(^|;\\s)" + escaped + "=([^;]*)(;|$)")
+      return (match ? match[2] : null);
+    },
+    _setCookie: function(key, value, expires) {
+      if (!expires) { expires = (this.expires_in * 1000) }
+      var date = new Date();
+      date.setTime(date.getTime() + expires);
+      var set_cookie = [
+        this._key(key), "=", value,
+        "; expires=", date.toGMTString(),
+        "; path=", this.path
+      ].join('');
+      document.cookie = set_cookie;
+    }
+  });
+
+  // Sammy.Storage is a plugin that provides shortcuts for creating and using
+  // Sammy.Store objects. Once included it provides the `store()` app level
+  // and helper methods. Depends on Sammy.JSON (or json2.js).
+  Sammy.Storage = function(app) {
+    this.use(Sammy.JSON);
+
+    this.stores = this.stores || {};
+
+    // `store()` creates and looks up existing `Sammy.Store` objects
+    // for the current application. The first time used for a given `'name'`
+    // initializes a `Sammy.Store` and also creates a helper under the store's
+    // name.
+    //
+    // ### Example
+    //
+    //      var app = $.sammy(function() {
+    //        this.use(Sammy.Storage);
+    //
+    //        // initializes the store on app creation.
+    //        this.store('mystore', {type: 'cookie'});
+    //
+    //        this.get('#/', function() {
+    //          // returns the Sammy.Store object
+    //          this.store('mystore');
+    //          // sets 'foo' to 'bar' using the shortcut/helper
+    //          // equivilent to this.store('mystore').set('foo', 'bar');
+    //          this.mystore('foo', 'bar');
+    //          // returns 'bar'
+    //          // equivilent to this.store('mystore').get('foo');
+    //          this.mystore('foo');
+    //          // returns 'baz!'
+    //          // equivilent to:
+    //          // this.store('mystore').fetch('foo!', function() {
+    //          //   return 'baz!';
+    //          // })
+    //          this.mystore('foo!', function() {
+    //            return 'baz!';
+    //          });
+    //
+    //          this.clearMystore();
+    //          // equivilent to:
+    //          // this.store('mystore').clearAll()
+    //        });
+    //
+    //      });
+    //
+    // ### Arguments
+    //
+    // * `name` The name of the store and helper. the name must be unique per application.
+    // * `options` A JS object of options that can be passed to the Store constuctor on initialization.
+    //
+    this.store = function(name, options) {
+      // if the store has not been initialized
+      if (typeof this.stores[name] == 'undefined') {
+        // create initialize the store
+        var clear_method_name = "clear" + name.substr(0,1).toUpperCase() + name.substr(1);
+        this.stores[name] = new Sammy.Store($.extend({
+          name: name,
+          element: this.element_selector
+        }, options || {}));
+        // app.name()
+        this[name] = function(key, value) {
+          if (typeof value == 'undefined') {
+            return this.stores[name].get(key);
+          } else if ($.isFunction(value)) {
+            return this.stores[name].fetch(key, value);
+          } else {
+            return this.stores[name].set(key, value)
+          }
+        };
+        // app.clearName();
+        this[clear_method_name] = function() {
+          return this.stores[name].clearAll();
+        }
+        // context.name()
+        this.helper(name, function() {
+          return this.app[name].apply(this.app, arguments);
+        });
+        // context.clearName();
+        this.helper(clear_method_name, function() {
+          return this.app[clear_method_name]();
+        });
+      }
+      return this.stores[name];
+    };
+
+    this.helpers({
+      store: function() {
+        return this.app.store.apply(this.app, arguments);
+      }
+    });
+  };
+
+  // Sammy.Session is an additional plugin for creating a common 'session' store
+  // for the given app. It is a very simple wrapper around `Sammy.Storage`
+  // that provides a simple fallback mechanism for trying to provide the best
+  // possible storage type for the session. This means, `LocalStorage`
+  // if available, otherwise `Cookie`, otherwise `Memory`.
+  // It provides the `session()` helper through `Sammy.Storage#store()`.
+  //
+  // See the `Sammy.Storage` plugin for full documentation.
+  //
+  Sammy.Session = function(app, options) {
+    this.use(Sammy.Storage);
+    // check for local storage, then cookie storage, then just use memory
+    this.store('session', $.extend({type: ['local', 'cookie', 'memory']}, options));
+  };
+
+  // Sammy.Cache provides helpers for caching data within the lifecycle of a
+  // Sammy app. The plugin provides two main methods on `Sammy.Application`,
+  // `cache` and `clearCache`. Each app has its own cache store so that
+  // you dont have to worry about collisions. As of 0.5 the original Sammy.Cache module
+  // has been deprecated in favor of this one based on Sammy.Storage. The exposed
+  // API is almost identical, but Sammy.Storage provides additional backends including
+  // HTML5 Storage. `Sammy.Cache` will try to use these backends when available
+  // (in this order) `LocalStorage`, `SessionStorage`, and `Memory`
+  Sammy.Cache = function(app, options) {
+    this.use(Sammy.Storage);
+    // set cache_partials to true
+    this.cache_partials = true;
+    // check for local storage, then session storage, then just use memory
+    this.store('cache', $.extend({type: ['local', 'session', 'memory']}, options));
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.template.js b/javascript/sammy/plugins/sammy.template.js
new file mode 100644
index 00000000..f24c23e9
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.template.js
@@ -0,0 +1,117 @@
+(function($) {
+
+  // Simple JavaScript Templating
+  // John Resig - http://ejohn.org/ - MIT Licensed
+  // adapted from: http://ejohn.org/blog/javascript-micro-templating/
+  // originally $.srender by Greg Borenstein http://ideasfordozens.com in Feb 2009
+  // modified for Sammy by Aaron Quint for caching templates by name
+  var srender_cache = {};
+  var srender = function(name, template, data) {
+    // target is an optional element; if provided, the result will be inserted into it
+    // otherwise the result will simply be returned to the caller
+    if (srender_cache[name]) {
+      fn = srender_cache[name];
+    } else {
+      if (typeof template == 'undefined') {
+        // was a cache check, return false
+        return false;
+      }
+      // Generate a reusable function that will serve as a template
+      // generator (and which will be cached).
+      fn = srender_cache[name] = new Function("obj",
+      "var p=[],print=function(){p.push.apply(p,arguments);};" +
+
+      // Introduce the data as local variables using with(){}
+      "with(obj){p.push(\"" +
+
+      // Convert the template into pure JavaScript
+      template
+        .replace(/[\r\t\n]/g, " ")
+        .replace(/\"/g, '\\"')
+        .split("<%").join("\t")
+        .replace(/((^|%>)[^\t]*)/g, "$1\r")
+        .replace(/\t=(.*?)%>/g, "\",$1,\"")
+        .split("\t").join("\");")
+        .split("%>").join("p.push(\"")
+        .split("\r").join("")
+        + "\");}return p.join('');");
+    }
+
+    if (typeof data != 'undefined') {
+      return fn(data);
+    } else {
+      return fn;
+    }
+  };
+
+  Sammy = Sammy || {};
+
+  // <tt>Sammy.Template</tt> is a simple plugin that provides a way to create
+  // and render client side templates. The rendering code is based on John Resig's
+  // quick templates and Greg Borenstien's srender plugin.
+  // This is also a great template/boilerplate for Sammy plugins.
+  //
+  // Templates use <% %> tags to denote embedded javascript.
+  //
+  // ### Examples
+  //
+  // Here is an example template (user.template):
+  //
+  //      <div class="user">
+  //        <div class="user-name"><%= user.name %></div>
+  //        <% if (user.photo_url) { %>
+  //          <div class="photo"><img src="<%= user.photo_url %>" /></div>
+  //        <% } %>
+  //      </div>
+  //
+  // Given that is a publicly accesible file, you would render it like:
+  //
+  //       $.sammy(function() {
+  //         // include the plugin
+  //         this.use(Sammy.Template);
+  //
+  //         this.get('#/', function() {
+  //           // the template is rendered in the current context.
+  //           this.user = {name: 'Aaron Quint'};
+  //           // partial calls template() because of the file extension
+  //           this.partial('user.template');
+  //         })
+  //       });
+  //
+  // You can also pass a second argument to use() that will alias the template
+  // method and therefore allow you to use a different extension for template files
+  // in <tt>partial()</tt>
+  //
+  //      // alias to 'tpl'
+  //      this.use(Sammy.Template, 'tpl');
+  //
+  //      // now .tpl files will be run through srender
+  //      this.get('#/', function() {
+  //        this.partial('myfile.tpl');
+  //      });
+  //
+  Sammy.Template = function(app, method_alias) {
+
+    // *Helper:* Uses simple templating to parse ERB like templates.
+    //
+    // ### Arguments
+    //
+    // * `template` A String template. '<% %>' tags are evaluated as Javascript and replaced with the elements in data.
+    // * `data` An Object containing the replacement values for the template.
+    //   data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
+    // * `name` An optional String name to cache the template.
+    //
+    var template = function(template, data, name) {
+      // use name for caching
+      if (typeof name == 'undefined') name = template;
+      return srender(name, template, $.extend({}, this, data));
+    };
+
+    // set the default method name/extension
+    if (!method_alias) method_alias = 'template';
+    // create the helper at the method alias
+    app.helper(method_alias, template);
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.title.js b/javascript/sammy/plugins/sammy.title.js
new file mode 100644
index 00000000..9874a1ef
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.title.js
@@ -0,0 +1,59 @@
+(function($) {
+
+  Sammy = Sammy || {};
+
+  // Sammy.Title is a very simple plugin to easily set the document's title.
+  // It supplies a helper for setting the title (`title()`) within routes,
+  // and an app level method for setting the global title (`setTitle()`)
+  Sammy.Title = function() {
+
+    // setTitle allows setting a global title or a function that modifies the
+    // title for each route/page.
+    //
+    // ### Example
+    //
+    //    // setting a title prefix
+    //    $.sammy(function() {
+    //
+    //      this.setTitle('My App -');
+    //
+    //      this.get('#/', function() {
+    //        this.title('Home'); // document's title == "My App - Home"
+    //      });
+    //    });
+    //
+    //    // setting a title with a function
+    //    $.sammy(function() {
+    //
+    //      this.setTitle(function(title) {
+    //        return [title, " /// My App"].join('');
+    //      });
+    //
+    //      this.get('#/', function() {
+    //        this.title('Home'); // document's title == "Home /// My App";
+    //      });
+    //    });
+    //
+    this.setTitle = function(title) {
+      if (!$.isFunction(title)) {
+        this.title_function = function(additional_title) {
+          return [title, additional_title].join(' ');
+        }
+      } else {
+        this.title_function = title;
+      }
+    };
+
+    // *Helper* title() sets the document title, passing it through the function
+    // defined by setTitle() if set.
+    this.helper('title', function() {
+      var new_title = $.makeArray(arguments).join(' ');
+      if (this.app.title_function) {
+        new_title = this.app.title_function(new_title);
+      }
+      document.title = new_title;
+    });
+
+  };
+
+})(jQuery);
diff --git a/javascript/sammy/plugins/sammy.tmpl.js b/javascript/sammy/plugins/sammy.tmpl.js
new file mode 100644
index 00000000..96b2fe5b
--- /dev/null
+++ b/javascript/sammy/plugins/sammy.tmpl.js
@@ -0,0 +1,520 @@
+(function( jQuery, undefined ){
+
+  /*
+   * a port of the...
+   * jQuery Templating Plugin
+   * Copyright 2010, John Resig
+   * Dual licensed under the MIT or GPL Version 2 licenses.
+   */
+
+  var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
+    newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
+
+  function newTmplItem( options, parentItem, fn, data ) {
+    // Returns a template item data structure for a new rendered instance of a template (a 'template item').
+    // The content field is a hierarchical array of strings and nested items (to be
+    // removed and replaced by nodes field of dom elements, once inserted in DOM).
+    var newItem = {
+      data: data || (parentItem ? parentItem.data : {}),
+      _wrap: parentItem ? parentItem._wrap : null,
+      tmpl: null,
+      parent: parentItem || null,
+      nodes: [],
+      calls: tiCalls,
+      nest: tiNest,
+      wrap: tiWrap,
+      html: tiHtml,
+      update: tiUpdate
+    };
+    if ( options ) {
+      jQuery.extend( newItem, options, { nodes: [], parent: parentItem } );
+    }
+    if ( fn ) {
+      // Build the hierarchical content to be used during insertion into DOM
+      newItem.tmpl = fn;
+      newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
+      newItem.key = ++itemKey;
+      // Keep track of new template item, until it is stored as jQuery Data on DOM element
+      (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
+    }
+    return newItem;
+  }
+
+  // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
+  jQuery.each({
+    appendTo: "append",
+    prependTo: "prepend",
+    insertBefore: "before",
+    insertAfter: "after",
+    replaceAll: "replaceWith"
+  }, function( name, original ) {
+    jQuery.fn[ name ] = function( selector ) {
+      var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems,
+        parent = this.length === 1 && this[0].parentNode;
+
+      appendToTmplItems = newTmplItems || {};
+      if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+        insert[ original ]( this[0] );
+        ret = this;
+      } else {
+        for ( i = 0, l = insert.length; i < l; i++ ) {
+          cloneIndex = i;
+          elems = (i > 0 ? this.clone(true) : this).get();
+          jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+          ret = ret.concat( elems );
+        }
+        cloneIndex = 0;
+        ret = this.pushStack( ret, name, insert.selector );
+      }
+      tmplItems = appendToTmplItems;
+      appendToTmplItems = null;
+      jQuery.tmpl.complete( tmplItems );
+      return ret;
+    };
+  });
+
+  jQuery.fn.extend({
+    // Use first wrapped element as template markup.
+    // Return wrapped set of template items, obtained by rendering template against data.
+    tmpl: function( data, options, parentItem ) {
+      return jQuery.tmpl( this[0], data, options, parentItem );
+    },
+
+    // Find which rendered template item the first wrapped DOM element belongs to
+    tmplItem: function() {
+      return jQuery.tmplItem( this[0] );
+    },
+
+    // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
+    template: function( name ) {
+      return jQuery.template( name, this[0] );
+    },
+
+    domManip: function( args, table, callback, options ) {
+      // This appears to be a bug in the appendTo, etc. implementation
+      // it should be doing .call() instead of .apply(). See #6227
+      if ( args[0] && args[0].nodeType ) {
+        var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem;
+        while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {}
+        if ( argsLength > 1 ) {
+          dmArgs[0] = [jQuery.makeArray( args )];
+        }
+        if ( tmplItem && cloneIndex ) {
+          dmArgs[2] = function( fragClone ) {
+            // Handler called by oldManip when rendered template has been inserted into DOM.
+            jQuery.tmpl.afterManip( this, fragClone, callback );
+          };
+        }
+        oldManip.apply( this, dmArgs );
+      } else {
+        oldManip.apply( this, arguments );
+      }
+      cloneIndex = 0;
+      if ( !appendToTmplItems ) {
+        jQuery.tmpl.complete( newTmplItems );
+      }
+      return this;
+    }
+  });
+
+  jQuery.extend({
+    // Return wrapped set of template items, obtained by rendering template against data.
+    tmpl: function( tmpl, data, options, parentItem ) {
+      var ret, topLevel = !parentItem;
+      if ( topLevel ) {
+        // This is a top-level tmpl call (not from a nested template using {{tmpl}})
+        parentItem = topTmplItem;
+        tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl );
+        wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level
+      } else if ( !tmpl ) {
+        // The template item is already associated with DOM - this is a refresh.
+        // Re-evaluate rendered template for the parentItem
+        tmpl = parentItem.tmpl;
+        newTmplItems[parentItem.key] = parentItem;
+        parentItem.nodes = [];
+        if ( parentItem.wrapped ) {
+          updateWrapped( parentItem, parentItem.wrapped );
+        }
+        // Rebuild, without creating a new template item
+        return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
+      }
+      if ( !tmpl ) {
+        return []; // Could throw...
+      }
+      if ( typeof data === "function" ) {
+        data = data.call( parentItem || {} );
+      }
+      if ( options && options.wrapped ) {
+        updateWrapped( options, options.wrapped );
+      }
+      ret = jQuery.isArray( data ) ?
+        jQuery.map( data, function( dataItem ) {
+          return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
+        }) :
+        [ newTmplItem( options, parentItem, tmpl, data ) ];
+      return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
+    },
+
+    // Return rendered template item for an element.
+    tmplItem: function( elem ) {
+      var tmplItem;
+      if ( elem instanceof jQuery ) {
+        elem = elem[0];
+      }
+      while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
+      return tmplItem || topTmplItem;
+    },
+
+    // Set:
+    // Use $.template( name, tmpl ) to cache a named template,
+    // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
+    // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration.
+
+    // Get:
+    // Use $.template( name ) to access a cached template.
+    // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString )
+    // will return the compiled template, without adding a name reference.
+    // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent
+    // to $.template( null, templateString )
+    template: function( name, tmpl ) {
+      if (tmpl) {
+        // Compile template and associate with name
+        if ( typeof tmpl === "string" ) {
+          // This is an HTML string being passed directly in.
+          tmpl = buildTmplFn( tmpl )
+        } else if ( tmpl instanceof jQuery ) {
+          tmpl = tmpl[0] || {};
+        }
+        if ( tmpl.nodeType ) {
+          // If this is a template block, use cached copy, or generate tmpl function and cache.
+          tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
+        }
+        return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
+      }
+      // Return named compiled template
+      return name ? (typeof name !== "string" ? jQuery.template( null, name ):
+        (jQuery.template[name] ||
+          // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec)
+          jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null;
+    },
+
+    encode: function( text ) {
+      // Do HTML encoding replacing < > & and ' and " by corresponding entities.
+      return ("" + text).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
+    }
+  });
+
+  jQuery.extend( jQuery.tmpl, {
+    tag: {
+      "tmpl": {
+        _default: { $2: "null" },
+        open: "if($notnull_1){_=_.concat($item.nest($1,$2));}"
+        // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
+        // This means that {{tmpl foo}} treats foo as a template (which IS a function).
+        // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
+      },
+      "wrap": {
+        _default: { $2: "null" },
+        open: "$item.calls(_,$1,$2);_=[];",
+        close: "call=$item.calls();_=call._.concat($item.wrap(call,_));"
+      },
+      "each": {
+        _default: { $2: "$index, $value" },
+        open: "if($notnull_1){$.each($1a,function($2){with(this){",
+        close: "}});}"
+      },
+      "if": {
+        open: "if(($notnull_1) && $1a){",
+        close: "}"
+      },
+      "else": {
+        _default: { $1: "true" },
+        open: "}else if(($notnull_1) && $1a){"
+      },
+      "html": {
+        // Unecoded expression evaluation.
+        open: "if($notnull_1){_.push($1a);}"
+      },
+      "=": {
+        // Encoded expression evaluation. Abbreviated form is ${}.
+        _default: { $1: "$data" },
+        open: "if($notnull_1){_.push($.encode($1a));}"
+      },
+      "!": {
+        // Comment tag. Skipped by parser
+        open: ""
+      }
+    },
+
+    // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
+    complete: function( items ) {
+      newTmplItems = {};
+    },
+
+    // Call this from code which overrides domManip, or equivalent
+    // Manage cloning/storing template items etc.
+    afterManip: function afterManip( elem, fragClone, callback ) {
+      // Provides cloned fragment ready for fixup prior to and after insertion into DOM
+      var content = fragClone.nodeType === 11 ?
+        jQuery.makeArray(fragClone.childNodes) :
+        fragClone.nodeType === 1 ? [fragClone] : [];
+
+      // Return fragment to original caller (e.g. append) for DOM insertion
+      callback.call( elem, fragClone );
+
+      // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
+      storeTmplItems( content );
+      cloneIndex++;
+    }
+  });
+
+  //========================== Private helper functions, used by code above ==========================
+
+  function build( tmplItem, nested, content ) {
+    // Convert hierarchical content into flat string array
+    // and finally return array of fragments ready for DOM insertion
+    var frag, ret = content ? jQuery.map( content, function( item ) {
+      return (typeof item === "string") ?
+        // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
+        (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
+        // This is a child template item. Build nested template.
+        build( item, tmplItem, item._ctnt );
+    }) :
+    // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}.
+    tmplItem;
+    if ( nested ) {
+      return ret;
+    }
+
+    // top-level template
+    ret = ret.join("");
+
+    // Support templates which have initial or final text nodes, or consist only of text
+    // Also support HTML entities within the HTML markup.
+    ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
+      frag = jQuery( middle ).get();
+
+      storeTmplItems( frag );
+      if ( before ) {
+        frag = unencode( before ).concat(frag);
+      }
+      if ( after ) {
+        frag = frag.concat(unencode( after ));
+      }
+    });
+    return frag ? frag : unencode( ret );
+  }
+
+  function unencode( text ) {
+    // Use createElement, since createTextNode will not render HTML entities correctly
+    var el = document.createElement( "div" );
+    el.innerHTML = text;
+    return jQuery.makeArray(el.childNodes);
+  }
+
+  // Generate a reusable function that will serve to render a template against data
+  function buildTmplFn( markup ) {
+    return new Function("jQuery","$item",
+      "var $=jQuery,call,_=[],$data=$item.data;" +
+
+      // Introduce the data as local variables using with(){}
+      "with($data){_.push('" +
+
+      // Convert the template into pure JavaScript
+      jQuery.trim(markup)
+        .replace( /([\\'])/g, "\\$1" )
+        .replace( /[\r\t\n]/g, " " )
+        .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" )
+        .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,
+        function( all, slash, type, fnargs, target, parens, args ) {
+          var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
+          if ( !tag ) {
+            throw "Template command not found: " + type;
+          }
+          def = tag._default || [];
+          if ( parens && !/\w$/.test(target)) {
+            target += parens;
+            parens = "";
+          }
+          if ( target ) {
+            target = unescape( target );
+            args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
+            // Support for target being things like a.toLowerCase();
+            // In that case don't call with template item as 'this' pointer. Just evaluate...
+            expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target;
+            exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
+          } else {
+            exprAutoFnDetect = expr = def.$1 || "null";
+          }
+          fnargs = unescape( fnargs );
+          return "');" +
+            tag[ slash ? "close" : "open" ]
+              .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
+              .split( "$1a" ).join( exprAutoFnDetect )
+              .split( "$1" ).join( expr )
+              .split( "$2" ).join( fnargs ?
+                fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) {
+                  params = params ? ("," + params + ")") : (parens ? ")" : "");
+                  return params ? ("(" + name + ").call($item" + params) : all;
+                })
+                : (def.$2||"")
+              ) +
+            "_.push('";
+        }) +
+      "');}return _;"
+    );
+  }
+  function updateWrapped( options, wrapped ) {
+    // Build the wrapped content.
+    options._wrap = build( options, true,
+      // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
+      jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
+    ).join("");
+  }
+
+  function unescape( args ) {
+    return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
+  }
+  function outerHtml( elem ) {
+    var div = document.createElement("div");
+    div.appendChild( elem.cloneNode(true) );
+    return div.innerHTML;
+  }
+
+  // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
+  function storeTmplItems( content ) {
+    var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m;
+    for ( i = 0, l = content.length; i < l; i++ ) {
+      if ( (elem = content[i]).nodeType !== 1 ) {
+        continue;
+      }
+      elems = elem.getElementsByTagName("*");
+      for ( m = elems.length - 1; m >= 0; m-- ) {
+        processItemKey( elems[m] );
+      }
+      processItemKey( elem );
+    }
+    function processItemKey( el ) {
+      var pntKey, pntNode = el, pntItem, tmplItem, key;
+      // Ensure that each rendered template inserted into the DOM has its own template item,
+      if ( (key = el.getAttribute( tmplItmAtt ))) {
+        while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
+        if ( pntKey !== key ) {
+          // The next ancestor with a _tmplitem expando is on a different key than this one.
+          // So this is a top-level element within this template item
+          // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment.
+          pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0;
+          if ( !(tmplItem = newTmplItems[key]) ) {
+            // The item is for wrapped content, and was copied from the temporary parent wrappedItem.
+            tmplItem = wrappedItems[key];
+            tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true );
+            tmplItem.key = ++itemKey;
+            newTmplItems[itemKey] = tmplItem;
+          }
+          if ( cloneIndex ) {
+            cloneTmplItem( key );
+          }
+        }
+        el.removeAttribute( tmplItmAtt );
+      } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
+        // This was a rendered element, cloned during append or appendTo etc.
+        // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
+        cloneTmplItem( tmplItem.key );
+        newTmplItems[tmplItem.key] = tmplItem;
+        pntNode = jQuery.data( el.parentNode, "tmplItem" );
+        pntNode = pntNode ? pntNode.key : 0;
+      }
+      if ( tmplItem ) {
+        pntItem = tmplItem;
+        // Find the template item of the parent element.
+        // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
+        while ( pntItem && pntItem.key != pntNode ) {
+          // Add this element as a top-level node for this rendered template item, as well as for any
+          // ancestor items between this item and the item of its parent element
+          pntItem.nodes.push( el );
+          pntItem = pntItem.parent;
+        }
+        // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
+        delete tmplItem._ctnt;
+        delete tmplItem._wrap;
+        // Store template item as jQuery data on the element
+        jQuery.data( el, "tmplItem", tmplItem );
+      }
+      function cloneTmplItem( key ) {
+        key = key + keySuffix;
+        tmplItem = newClonedItems[key] =
+          (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true ));
+      }
+    }
+  }
+
+  //---- Helper functions for template item ----
+
+  function tiCalls( content, tmpl, data, options ) {
+    if ( !content ) {
+      return stack.pop();
+    }
+    stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options });
+  }
+
+  function tiNest( tmpl, data, options ) {
+    // nested template, using {{tmpl}} tag
+    return jQuery.tmpl( jQuery.template( tmpl ), data, options, this );
+  }
+
+  function tiWrap( call, wrapped ) {
+    // nested template, using {{wrap}} tag
+    var options = call.options || {};
+    options.wrapped = wrapped;
+    // Apply the template, which may incorporate wrapped content,
+    return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
+  }
+
+  function tiHtml( filter, textOnly ) {
+    var wrapped = this._wrap;
+    return jQuery.map(
+      jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
+      function(e) {
+        return textOnly ?
+          e.innerText || e.textContent :
+          e.outerHTML || outerHtml(e);
+      });
+  }
+
+  function tiUpdate() {
+    var coll = this.nodes;
+    jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
+    jQuery( coll ).remove();
+  }
+
+  Sammy = Sammy || {};
+
+  Sammy.Tmpl = function(app, method_alias) {
+
+    // *Helper:* Uses jQuery-tmpl to parse a template and interpolate and work with the passed data
+    //
+    // ### Arguments
+    //
+    // * `template` A String template. '${ }' tags are evaluated as Javascript and replaced with the elements in data.
+    // * `data` An Object containing the replacement values for the template.
+    //   data is extended with the <tt>EventContext</tt> allowing you to call its methods within the template.
+    // * `name` An optional String name to cache the template.
+    //
+    var template = function(template, data, name) {
+      // use name for caching
+      if (typeof name == 'undefined') name = template;
+
+      // check the cache
+      if (!jQuery.template[name]) jQuery.template(name, template);
+
+      // we could also pass along jQuery-tmpl options as a last param?
+      return jQuery.tmpl(name, jQuery.extend({}, this, data));
+    };
+
+    // set the default method name/extension
+    if (!method_alias) method_alias = 'tmpl';
+    // create the helper at the method alias
+    app.helper(method_alias, template);
+
+  };
+})( jQuery );
diff --git a/javascript/sammy/sammy.js b/javascript/sammy/sammy.js
new file mode 100644
index 00000000..93c97da8
--- /dev/null
+++ b/javascript/sammy/sammy.js
@@ -0,0 +1,1838 @@
+// name: sammy
+// version: 0.6.2
+
+(function($, window) {
+
+  var Sammy,
+      PATH_REPLACER = "([^\/]+)",
+      PATH_NAME_MATCHER = /:([\w\d]+)/g,
+      QUERY_STRING_MATCHER = /\?([^#]*)$/,
+      // mainly for making `arguments` an Array
+      _makeArray = function(nonarray) { return Array.prototype.slice.call(nonarray); },
+      // borrowed from jQuery
+      _isFunction = function( obj ) { return Object.prototype.toString.call(obj) === "[object Function]"; },
+      _isArray = function( obj ) { return Object.prototype.toString.call(obj) === "[object Array]"; },
+      _decode = decodeURIComponent,
+      _encode = encodeURIComponent,
+      _escapeHTML = function(s) {
+        return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+      },
+      _routeWrapper = function(verb) {
+        return function(path, callback) { return this.route.apply(this, [verb, path, callback]); };
+      },
+      _template_cache = {},
+      loggers = [];
+
+
+  // `Sammy` (also aliased as $.sammy) is not only the namespace for a
+  // number of prototypes, its also a top level method that allows for easy
+  // creation/management of `Sammy.Application` instances. There are a
+  // number of different forms for `Sammy()` but each returns an instance
+  // of `Sammy.Application`. When a new instance is created using
+  // `Sammy` it is added to an Object called `Sammy.apps`. This
+  // provides for an easy way to get at existing Sammy applications. Only one
+  // instance is allowed per `element_selector` so when calling
+  // `Sammy('selector')` multiple times, the first time will create
+  // the application and the following times will extend the application
+  // already added to that selector.
+  //
+  // ### Example
+  //
+  //      // returns the app at #main or a new app
+  //      Sammy('#main')
+  //
+  //      // equivilent to "new Sammy.Application", except appends to apps
+  //      Sammy();
+  //      Sammy(function() { ... });
+  //
+  //      // extends the app at '#main' with function.
+  //      Sammy('#main', function() { ... });
+  //
+  Sammy = function() {
+    var args = _makeArray(arguments),
+        app, selector;
+    Sammy.apps = Sammy.apps || {};
+    if (args.length === 0 || args[0] && _isFunction(args[0])) { // Sammy()
+      return Sammy.apply(Sammy, ['body'].concat(args));
+    } else if (typeof (selector = args.shift()) == 'string') { // Sammy('#main')
+      app = Sammy.apps[selector] || new Sammy.Application();
+      app.element_selector = selector;
+      if (args.length > 0) {
+        $.each(args, function(i, plugin) {
+          app.use(plugin);
+        });
+      }
+      // if the selector changes make sure the refrence in Sammy.apps changes
+      if (app.element_selector != selector) {
+        delete Sammy.apps[selector];
+      }
+      Sammy.apps[app.element_selector] = app;
+      return app;
+    }
+  };
+
+  Sammy.VERSION = '0.6.2';
+
+  // Add to the global logger pool. Takes a function that accepts an
+  // unknown number of arguments and should print them or send them somewhere
+  // The first argument is always a timestamp.
+  Sammy.addLogger = function(logger) {
+    loggers.push(logger);
+  };
+
+  // Sends a log message to each logger listed in the global
+  // loggers pool. Can take any number of arguments.
+  // Also prefixes the arguments with a timestamp.
+  Sammy.log = function()  {
+    var args = _makeArray(arguments);
+    args.unshift("[" + Date() + "]");
+    $.each(loggers, function(i, logger) {
+      logger.apply(Sammy, args);
+    });
+  };
+
+  if (typeof window.console != 'undefined') {
+    if (_isFunction(window.console.log.apply)) {
+      Sammy.addLogger(function() {
+        window.console.log.apply(window.console, arguments);
+      });
+    } else {
+      Sammy.addLogger(function() {
+        window.console.log(arguments);
+      });
+    }
+  } else if (typeof console != 'undefined') {
+    Sammy.addLogger(function() {
+      console.log.apply(console, arguments);
+    });
+  }
+
+  $.extend(Sammy, {
+    makeArray: _makeArray,
+    isFunction: _isFunction,
+    isArray: _isArray
+  })
+
+  // Sammy.Object is the base for all other Sammy classes. It provides some useful
+  // functionality, including cloning, iterating, etc.
+  Sammy.Object = function(obj) { // constructor
+    return $.extend(this, obj || {});
+  };
+
+  $.extend(Sammy.Object.prototype, {
+
+    // Escape HTML in string, use in templates to prevent script injection.
+    // Also aliased as `h()`
+    escapeHTML: _escapeHTML,
+    h: _escapeHTML,
+
+    // Returns a copy of the object with Functions removed.
+    toHash: function() {
+      var json = {};
+      $.each(this, function(k,v) {
+        if (!_isFunction(v)) {
+          json[k] = v;
+        }
+      });
+      return json;
+    },
+
+    // Renders a simple HTML version of this Objects attributes.
+    // Does not render functions.
+    // For example. Given this Sammy.Object:
+    //
+    //    var s = new Sammy.Object({first_name: 'Sammy', last_name: 'Davis Jr.'});
+    //    s.toHTML() //=> '<strong>first_name</strong> Sammy<br /><strong>last_name</strong> Davis Jr.<br />'
+    //
+    toHTML: function() {
+      var display = "";
+      $.each(this, function(k, v) {
+        if (!_isFunction(v)) {
+          display += "<strong>" + k + "</strong> " + v + "<br />";
+        }
+      });
+      return display;
+    },
+
+    // Returns an array of keys for this object. If `attributes_only`
+    // is true will not return keys that map to a `function()`
+    keys: function(attributes_only) {
+      var keys = [];
+      for (var property in this) {
+        if (!_isFunction(this[property]) || !attributes_only) {
+          keys.push(property);
+        }
+      }
+      return keys;
+    },
+
+    // Checks if the object has a value at `key` and that the value is not empty
+    has: function(key) {
+      return this[key] && $.trim(this[key].toString()) != '';
+    },
+
+    // convenience method to join as many arguments as you want
+    // by the first argument - useful for making paths
+    join: function() {
+      var args = _makeArray(arguments);
+      var delimiter = args.shift();
+      return args.join(delimiter);
+    },
+
+    // Shortcut to Sammy.log
+    log: function() {
+      Sammy.log.apply(Sammy, arguments);
+    },
+
+    // Returns a string representation of this object.
+    // if `include_functions` is true, it will also toString() the
+    // methods of this object. By default only prints the attributes.
+    toString: function(include_functions) {
+      var s = [];
+      $.each(this, function(k, v) {
+        if (!_isFunction(v) || include_functions) {
+          s.push('"' + k + '": ' + v.toString());
+        }
+      });
+      return "Sammy.Object: {" + s.join(',') + "}";
+    }
+  });
+
+  // The HashLocationProxy is the default location proxy for all Sammy applications.
+  // A location proxy is a prototype that conforms to a simple interface. The purpose
+  // of a location proxy is to notify the Sammy.Application its bound to when the location
+  // or 'external state' changes. The HashLocationProxy considers the state to be
+  // changed when the 'hash' (window.location.hash / '#') changes. It does this in two
+  // different ways depending on what browser you are using. The newest browsers
+  // (IE, Safari > 4, FF >= 3.6) support a 'onhashchange' DOM event, thats fired whenever
+  // the location.hash changes. In this situation the HashLocationProxy just binds
+  // to this event and delegates it to the application. In the case of older browsers
+  // a poller is set up to track changes to the hash. Unlike Sammy 0.3 or earlier,
+  // the HashLocationProxy allows the poller to be a global object, eliminating the
+  // need for multiple pollers even when thier are multiple apps on the page.
+  Sammy.HashLocationProxy = function(app, run_interval_every) {
+    this.app = app;
+    // set is native to false and start the poller immediately
+    this.is_native = false;
+    this._startPolling(run_interval_every);
+  };
+
+  Sammy.HashLocationProxy.prototype = {
+
+    // bind the proxy events to the current app.
+    bind: function() {
+      var proxy = this, app = this.app;
+      $(window).bind('hashchange.' + this.app.eventNamespace(), function(e, non_native) {
+        // if we receive a native hash change event, set the proxy accordingly
+        // and stop polling
+        if (proxy.is_native === false && !non_native) {
+          Sammy.log('native hash change exists, using');
+          proxy.is_native = true;
+          window.clearInterval(Sammy.HashLocationProxy._interval);
+        }
+        app.trigger('location-changed');
+      });
+      if (!Sammy.HashLocationProxy._bindings) {
+        Sammy.HashLocationProxy._bindings = 0;
+      }
+      Sammy.HashLocationProxy._bindings++;
+    },
+
+    // unbind the proxy events from the current app
+    unbind: function() {
+      $(window).unbind('hashchange.' + this.app.eventNamespace());
+      Sammy.HashLocationProxy._bindings--;
+      if (Sammy.HashLocationProxy._bindings <= 0) {
+        window.clearInterval(Sammy.HashLocationProxy._interval);
+      }
+    },
+
+    // get the current location from the hash.
+    getLocation: function() {
+     // Bypass the `window.location.hash` attribute.  If a question mark
+      // appears in the hash IE6 will strip it and all of the following
+      // characters from `window.location.hash`.
+      var matches = window.location.toString().match(/^[^#]*(#.+)$/);
+      return matches ? matches[1] : '';
+    },
+
+    // set the current location to `new_location`
+    setLocation: function(new_location) {
+      return (window.location = new_location);
+    },
+
+    _startPolling: function(every) {
+      // set up interval
+      var proxy = this;
+      if (!Sammy.HashLocationProxy._interval) {
+        if (!every) { every = 10; }
+        var hashCheck = function() {
+          var current_location = proxy.getLocation();
+          if (!Sammy.HashLocationProxy._last_location ||
+            current_location != Sammy.HashLocationProxy._last_location) {
+            window.setTimeout(function() {
+              $(window).trigger('hashchange', [true]);
+            }, 13);
+          }
+          Sammy.HashLocationProxy._last_location = current_location;
+        };
+        hashCheck();
+        Sammy.HashLocationProxy._interval = window.setInterval(hashCheck, every);
+      }
+    }
+  };
+
+
+  // Sammy.Application is the Base prototype for defining 'applications'.
+  // An 'application' is a collection of 'routes' and bound events that is
+  // attached to an element when `run()` is called.
+  // The only argument an 'app_function' is evaluated within the context of the application.
+  Sammy.Application = function(app_function) {
+    var app = this;
+    this.routes            = {};
+    this.listeners         = new Sammy.Object({});
+    this.arounds           = [];
+    this.befores           = [];
+    // generate a unique namespace
+    this.namespace         = (new Date()).getTime() + '-' + parseInt(Math.random() * 1000, 10);
+    this.context_prototype = function() { Sammy.EventContext.apply(this, arguments); };
+    this.context_prototype.prototype = new Sammy.EventContext();
+
+    if (_isFunction(app_function)) {
+      app_function.apply(this, [this]);
+    }
+    // set the location proxy if not defined to the default (HashLocationProxy)
+    if (!this._location_proxy) {
+      this.setLocationProxy(new Sammy.HashLocationProxy(this, this.run_interval_every));
+    }
+    if (this.debug) {
+      this.bindToAllEvents(function(e, data) {
+        app.log(app.toString(), e.cleaned_type, data || {});
+      });
+    }
+  };
+
+  Sammy.Application.prototype = $.extend({}, Sammy.Object.prototype, {
+
+    // the four route verbs
+    ROUTE_VERBS: ['get','post','put','delete'],
+
+    // An array of the default events triggered by the
+    // application during its lifecycle
+    APP_EVENTS: ['run',
+                 'unload',
+                 'lookup-route',
+                 'run-route',
+                 'route-found',
+                 'event-context-before',
+                 'event-context-after',
+                 'changed',
+                 'error',
+                 'check-form-submission',
+                 'redirect',
+                 'location-changed'],
+
+    _last_route: null,
+    _location_proxy: null,
+    _running: false,
+
+    // Defines what element the application is bound to. Provide a selector
+    // (parseable by `jQuery()`) and this will be used by `$element()`
+    element_selector: 'body',
+
+    // When set to true, logs all of the default events using `log()`
+    debug: false,
+
+    // When set to true, and the error() handler is not overriden, will actually
+    // raise JS errors in routes (500) and when routes can't be found (404)
+    raise_errors: false,
+
+    // The time in milliseconds that the URL is queried for changes
+    run_interval_every: 50,
+
+    // The default template engine to use when using `partial()` in an
+    // `EventContext`. `template_engine` can either be a string that
+    // corresponds to the name of a method/helper on EventContext or it can be a function
+    // that takes two arguments, the content of the unrendered partial and an optional
+    // JS object that contains interpolation data. Template engine is only called/refered
+    // to if the extension of the partial is null or unknown. See `partial()`
+    // for more information
+    template_engine: null,
+
+    // //=> Sammy.Application: body
+    toString: function() {
+      return 'Sammy.Application:' + this.element_selector;
+    },
+
+    // returns a jQuery object of the Applications bound element.
+    $element: function(selector) {
+      return selector ? $(this.element_selector).find(selector) : $(this.element_selector);
+    },
+
+    // `use()` is the entry point for including Sammy plugins.
+    // The first argument to use should be a function() that is evaluated
+    // in the context of the current application, just like the `app_function`
+    // argument to the `Sammy.Application` constructor.
+    //
+    // Any additional arguments are passed to the app function sequentially.
+    //
+    // For much more detail about plugins, check out:
+    // http://code.quirkey.com/sammy/doc/plugins.html
+    //
+    // ### Example
+    //
+    //      var MyPlugin = function(app, prepend) {
+    //
+    //        this.helpers({
+    //          myhelper: function(text) {
+    //            alert(prepend + " " + text);
+    //          }
+    //        });
+    //
+    //      };
+    //
+    //      var app = $.sammy(function() {
+    //
+    //        this.use(MyPlugin, 'This is my plugin');
+    //
+    //        this.get('#/', function() {
+    //          this.myhelper('and dont you forget it!');
+    //          //=> Alerts: This is my plugin and dont you forget it!
+    //        });
+    //
+    //      });
+    //
+    // If plugin is passed as a string it assumes your are trying to load
+    // Sammy."Plugin". This is the prefered way of loading core Sammy plugins
+    // as it allows for better error-messaging.
+    //
+    // ### Example
+    //
+    //      $.sammy(function() {
+    //        this.use('Mustache'); //=> Sammy.Mustache
+    //        this.use('Storage'); //=> Sammy.Storage
+    //      });
+    //
+    use: function() {
+      // flatten the arguments
+      var args = _makeArray(arguments),
+          plugin = args.shift(),
+          plugin_name = plugin || '';
+      try {
+        args.unshift(this);
+        if (typeof plugin == 'string') {
+          plugin_name = 'Sammy.' + plugin;
+          plugin = Sammy[plugin];
+        }
+        plugin.apply(this, args);
+      } catch(e) {
+        if (typeof plugin === 'undefined') {
+          this.error("Plugin Error: called use() but plugin (" + plugin_name.toString() + ") is not defined", e);
+        } else if (!_isFunction(plugin)) {
+          this.error("Plugin Error: called use() but '" + plugin_name.toString() + "' is not a function", e);
+        } else {
+          this.error("Plugin Error", e);
+        }
+      }
+      return this;
+    },
+
+    // Sets the location proxy for the current app. By default this is set to
+    // a new `Sammy.HashLocationProxy` on initialization. However, you can set
+    // the location_proxy inside you're app function to give your app a custom
+    // location mechanism. See `Sammy.HashLocationProxy` and `Sammy.DataLocationProxy`
+    // for examples.
+    //
+    // `setLocationProxy()` takes an initialized location proxy.
+    //
+    // ### Example
+    //
+    //        // to bind to data instead of the default hash;
+    //        var app = $.sammy(function() {
+    //          this.setLocationProxy(new Sammy.DataLocationProxy(this));
+    //        });
+    //
+    setLocationProxy: function(new_proxy) {
+      var original_proxy = this._location_proxy;
+      this._location_proxy = new_proxy;
+      if (this.isRunning()) {
+        if (original_proxy) {
+          // if there is already a location proxy, unbind it.
+          original_proxy.unbind();
+        }
+        this._location_proxy.bind();
+      }
+    },
+
+    // `route()` is the main method for defining routes within an application.
+    // For great detail on routes, check out: http://code.quirkey.com/sammy/doc/routes.html
+    //
+    // This method also has aliases for each of the different verbs (eg. `get()`, `post()`, etc.)
+    //
+    // ### Arguments
+    //
+    // * `verb` A String in the set of ROUTE_VERBS or 'any'. 'any' will add routes for each
+    //    of the ROUTE_VERBS. If only two arguments are passed,
+    //    the first argument is the path, the second is the callback and the verb
+    //    is assumed to be 'any'.
+    // * `path` A Regexp or a String representing the path to match to invoke this verb.
+    // * `callback` A Function that is called/evaluated whent the route is run see: `runRoute()`.
+    //    It is also possible to pass a string as the callback, which is looked up as the name
+    //    of a method on the application.
+    //
+    route: function(verb, path, callback) {
+      var app = this, param_names = [], add_route, path_match;
+
+      // if the method signature is just (path, callback)
+      // assume the verb is 'any'
+      if (!callback && _isFunction(path)) {
+        path = verb;
+        callback = path;
+        verb = 'any';
+      }
+
+      verb = verb.toLowerCase(); // ensure verb is lower case
+
+      // if path is a string turn it into a regex
+      if (path.constructor == String) {
+
+        // Needs to be explicitly set because IE will maintain the index unless NULL is returned,
+        // which means that with two consecutive routes that contain params, the second set of params will not be found and end up in splat instead of params
+        // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/lastIndex
+        PATH_NAME_MATCHER.lastIndex = 0;
+
+        // find the names
+        while ((path_match = PATH_NAME_MATCHER.exec(path)) !== null) {
+          param_names.push(path_match[1]);
+        }
+        // replace with the path replacement
+        path = new RegExp("^" + path.replace(PATH_NAME_MATCHER, PATH_REPLACER) + "$");
+      }
+      // lookup callback
+      if (typeof callback == 'string') {
+        callback = app[callback];
+      }
+
+      add_route = function(with_verb) {
+        var r = {verb: with_verb, path: path, callback: callback, param_names: param_names};
+        // add route to routes array
+        app.routes[with_verb] = app.routes[with_verb] || [];
+        // place routes in order of definition
+        app.routes[with_verb].push(r);
+      };
+
+      if (verb === 'any') {
+        $.each(this.ROUTE_VERBS, function(i, v) { add_route(v); });
+      } else {
+        add_route(verb);
+      }
+
+      // return the app
+      return this;
+    },
+
+    // Alias for route('get', ...)
+    get: _routeWrapper('get'),
+
+    // Alias for route('post', ...)
+    post: _routeWrapper('post'),
+
+    // Alias for route('put', ...)
+    put: _routeWrapper('put'),
+
+    // Alias for route('delete', ...)
+    del: _routeWrapper('delete'),
+
+    // Alias for route('any', ...)
+    any: _routeWrapper('any'),
+
+    // `mapRoutes` takes an array of arrays, each array being passed to route()
+    // as arguments, this allows for mass definition of routes. Another benefit is
+    // this makes it possible/easier to load routes via remote JSON.
+    //
+    // ### Example
+    //
+    //    var app = $.sammy(function() {
+    //
+    //      this.mapRoutes([
+    //          ['get', '#/', function() { this.log('index'); }],
+    //          // strings in callbacks are looked up as methods on the app
+    //          ['post', '#/create', 'addUser'],
+    //          // No verb assumes 'any' as the verb
+    //          [/dowhatever/, function() { this.log(this.verb, this.path)}];
+    //        ]);
+    //    })
+    //
+    mapRoutes: function(route_array) {
+      var app = this;
+      $.each(route_array, function(i, route_args) {
+        app.route.apply(app, route_args);
+      });
+      return this;
+    },
+
+    // A unique event namespace defined per application.
+    // All events bound with `bind()` are automatically bound within this space.
+    eventNamespace: function() {
+      return ['sammy-app', this.namespace].join('-');
+    },
+
+    // Works just like `jQuery.fn.bind()` with a couple noteable differences.
+    //
+    // * It binds all events to the application element
+    // * All events are bound within the `eventNamespace()`
+    // * Events are not actually bound until the application is started with `run()`
+    // * callbacks are evaluated within the context of a Sammy.EventContext
+    //
+    // See http://code.quirkey.com/sammy/docs/events.html for more info.
+    //
+    bind: function(name, data, callback) {
+      var app = this;
+      // build the callback
+      // if the arity is 2, callback is the second argument
+      if (typeof callback == 'undefined') { callback = data; }
+      var listener_callback =  function() {
+        // pull off the context from the arguments to the callback
+        var e, context, data;
+        e       = arguments[0];
+        data    = arguments[1];
+        if (data && data.context) {
+          context = data.context;
+          delete data.context;
+        } else {
+          context = new app.context_prototype(app, 'bind', e.type, data, e.target);
+        }
+        e.cleaned_type = e.type.replace(app.eventNamespace(), '');
+        callback.apply(context, [e, data]);
+      };
+
+      // it could be that the app element doesnt exist yet
+      // so attach to the listeners array and then run()
+      // will actually bind the event.
+      if (!this.listeners[name]) { this.listeners[name] = []; }
+      this.listeners[name].push(listener_callback);
+      if (this.isRunning()) {
+        // if the app is running
+        // *actually* bind the event to the app element
+        this._listen(name, listener_callback);
+      }
+      return this;
+    },
+
+    // Triggers custom events defined with `bind()`
+    //
+    // ### Arguments
+    //
+    // * `name` The name of the event. Automatically prefixed with the `eventNamespace()`
+    // * `data` An optional Object that can be passed to the bound callback.
+    // * `context` An optional context/Object in which to execute the bound callback.
+    //   If no context is supplied a the context is a new `Sammy.EventContext`
+    //
+    trigger: function(name, data) {
+      this.$element().trigger([name, this.eventNamespace()].join('.'), [data]);
+      return this;
+    },
+
+    // Reruns the current route
+    refresh: function() {
+      this.last_location = null;
+      this.trigger('location-changed');
+      return this;
+    },
+
+    // Takes a single callback that is pushed on to a stack.
+    // Before any route is run, the callbacks are evaluated in order within
+    // the current `Sammy.EventContext`
+    //
+    // If any of the callbacks explicitly return false, execution of any
+    // further callbacks and the route itself is halted.
+    //
+    // You can also provide a set of options that will define when to run this
+    // before based on the route it proceeds.
+    //
+    // ### Example
+    //
+    //      var app = $.sammy(function() {
+    //
+    //        // will run at #/route but not at #/
+    //        this.before('#/route', function() {
+    //          //...
+    //        });
+    //
+    //        // will run at #/ but not at #/route
+    //        this.before({except: {path: '#/route'}}, function() {
+    //          this.log('not before #/route');
+    //        });
+    //
+    //        this.get('#/', function() {});
+    //
+    //        this.get('#/route', function() {});
+    //
+    //      });
+    //
+    // See `contextMatchesOptions()` for a full list of supported options
+    //
+    before: function(options, callback) {
+      if (_isFunction(options)) {
+        callback = options;
+        options = {};
+      }
+      this.befores.push([options, callback]);
+      return this;
+    },
+
+    // A shortcut for binding a callback to be run after a route is executed.
+    // After callbacks have no guarunteed order.
+    after: function(callback) {
+      return this.bind('event-context-after', callback);
+    },
+
+
+    // Adds an around filter to the application. around filters are functions
+    // that take a single argument `callback` which is the entire route
+    // execution path wrapped up in a closure. This means you can decide whether
+    // or not to proceed with execution by not invoking `callback` or,
+    // more usefuly wrapping callback inside the result of an asynchronous execution.
+    //
+    // ### Example
+    //
+    // The most common use case for around() is calling a _possibly_ async function
+    // and executing the route within the functions callback:
+    //
+    //      var app = $.sammy(function() {
+    //
+    //        var current_user = false;
+    //
+    //        function checkLoggedIn(callback) {
+    //          // /session returns a JSON representation of the logged in user
+    //          // or an empty object
+    //          if (!current_user) {
+    //            $.getJSON('/session', function(json) {
+    //              if (json.login) {
+    //                // show the user as logged in
+    //                current_user = json;
+    //                // execute the route path
+    //                callback();
+    //              } else {
+    //                // show the user as not logged in
+    //                current_user = false;
+    //                // the context of aroundFilters is an EventContext
+    //                this.redirect('#/login');
+    //              }
+    //            });
+    //          } else {
+    //            // execute the route path
+    //            callback();
+    //          }
+    //        };
+    //
+    //        this.around(checkLoggedIn);
+    //
+    //      });
+    //
+    around: function(callback) {
+      this.arounds.push(callback);
+      return this;
+    },
+
+    // Returns `true` if the current application is running.
+    isRunning: function() {
+      return this._running;
+    },
+
+    // Helpers extends the EventContext prototype specific to this app.
+    // This allows you to define app specific helper functions that can be used
+    // whenever you're inside of an event context (templates, routes, bind).
+    //
+    // ### Example
+    //
+    //    var app = $.sammy(function() {
+    //
+    //      helpers({
+    //        upcase: function(text) {
+    //         return text.toString().toUpperCase();
+    //        }
+    //      });
+    //
+    //      get('#/', function() { with(this) {
+    //        // inside of this context I can use the helpers
+    //        $('#main').html(upcase($('#main').text());
+    //      }});
+    //
+    //    });
+    //
+    //
+    // ### Arguments
+    //
+    // * `extensions` An object collection of functions to extend the context.
+    //
+    helpers: function(extensions) {
+      $.extend(this.context_prototype.prototype, extensions);
+      return this;
+    },
+
+    // Helper extends the event context just like `helpers()` but does it
+    // a single method at a time. This is especially useful for dynamically named
+    // helpers
+    //
+    // ### Example
+    //
+    //     // Trivial example that adds 3 helper methods to the context dynamically
+    //     var app = $.sammy(function(app) {
+    //
+    //       $.each([1,2,3], function(i, num) {
+    //         app.helper('helper' + num, function() {
+    //           this.log("I'm helper number " + num);
+    //         });
+    //       });
+    //
+    //       this.get('#/', function() {
+    //         this.helper2(); //=> I'm helper number 2
+    //       });
+    //     });
+    //
+    // ### Arguments
+    //
+    // * `name` The name of the method
+    // * `method` The function to be added to the prototype at `name`
+    //
+    helper: function(name, method) {
+      this.context_prototype.prototype[name] = method;
+      return this;
+    },
+
+    // Actually starts the application's lifecycle. `run()` should be invoked
+    // within a document.ready block to ensure the DOM exists before binding events, etc.
+    //
+    // ### Example
+    //
+    //    var app = $.sammy(function() { ... }); // your application
+    //    $(function() { // document.ready
+    //        app.run();
+    //     });
+    //
+    // ### Arguments
+    //
+    // * `start_url` Optionally, a String can be passed which the App will redirect to
+    //   after the events/routes have been bound.
+    run: function(start_url) {
+      if (this.isRunning()) { return false; }
+      var app = this;
+
+      // actually bind all the listeners
+      $.each(this.listeners.toHash(), function(name, callbacks) {
+        $.each(callbacks, function(i, listener_callback) {
+          app._listen(name, listener_callback);
+        });
+      });
+
+      this.trigger('run', {start_url: start_url});
+      this._running = true;
+      // set last location
+      this.last_location = null;
+      if (this.getLocation() == '' && typeof start_url != 'undefined') {
+        this.setLocation(start_url);
+      }
+      // check url
+      this._checkLocation();
+      this._location_proxy.bind();
+      this.bind('location-changed', function() {
+        app._checkLocation();
+      });
+
+      // bind to submit to capture post/put/delete routes
+      this.bind('submit', function(e) {
+        var returned = app._checkFormSubmission($(e.target).closest('form'));
+        return (returned === false) ? e.preventDefault() : false;
+      });
+
+      // bind unload to body unload
+      $(window).bind('beforeunload', function() {
+        app.unload();
+      });
+
+      // trigger html changed
+      return this.trigger('changed');
+    },
+
+    // The opposite of `run()`, un-binds all event listeners and intervals
+    // `run()` Automaticaly binds a `onunload` event to run this when
+    // the document is closed.
+    unload: function() {
+      if (!this.isRunning()) { return false; }
+      var app = this;
+      this.trigger('unload');
+      // clear interval
+      this._location_proxy.unbind();
+      // unbind form submits
+      this.$element().unbind('submit').removeClass(app.eventNamespace());
+      // unbind all events
+      $.each(this.listeners.toHash() , function(name, listeners) {
+        $.each(listeners, function(i, listener_callback) {
+          app._unlisten(name, listener_callback);
+        });
+      });
+      this._running = false;
+      return this;
+    },
+
+    // Will bind a single callback function to every event that is already
+    // being listened to in the app. This includes all the `APP_EVENTS`
+    // as well as any custom events defined with `bind()`.
+    //
+    // Used internally for debug logging.
+    bindToAllEvents: function(callback) {
+      var app = this;
+      // bind to the APP_EVENTS first
+      $.each(this.APP_EVENTS, function(i, e) {
+        app.bind(e, callback);
+      });
+      // next, bind to listener names (only if they dont exist in APP_EVENTS)
+      $.each(this.listeners.keys(true), function(i, name) {
+        if (app.APP_EVENTS.indexOf(name) == -1) {
+          app.bind(name, callback);
+        }
+      });
+      return this;
+    },
+
+    // Returns a copy of the given path with any query string after the hash
+    // removed.
+    routablePath: function(path) {
+      return path.replace(QUERY_STRING_MATCHER, '');
+    },
+
+    // Given a verb and a String path, will return either a route object or false
+    // if a matching route can be found within the current defined set.
+    lookupRoute: function(verb, path) {
+      var app = this, routed = false;
+      this.trigger('lookup-route', {verb: verb, path: path});
+      if (typeof this.routes[verb] != 'undefined') {
+        $.each(this.routes[verb], function(i, route) {
+          if (app.routablePath(path).match(route.path)) {
+            routed = route;
+            return false;
+          }
+        });
+      }
+      return routed;
+    },
+
+    // First, invokes `lookupRoute()` and if a route is found, parses the
+    // possible URL params and then invokes the route's callback within a new
+    // `Sammy.EventContext`. If the route can not be found, it calls
+    // `notFound()`. If `raise_errors` is set to `true` and
+    // the `error()` has not been overriden, it will throw an actual JS
+    // error.
+    //
+    // You probably will never have to call this directly.
+    //
+    // ### Arguments
+    //
+    // * `verb` A String for the verb.
+    // * `path` A String path to lookup.
+    // * `params` An Object of Params pulled from the URI or passed directly.
+    //
+    // ### Returns
+    //
+    // Either returns the value returned by the route callback or raises a 404 Not Found error.
+    //
+    runRoute: function(verb, path, params, target) {
+      var app = this,
+          route = this.lookupRoute(verb, path),
+          context,
+          wrapped_route,
+          arounds,
+          around,
+          befores,
+          before,
+          callback_args,
+          path_params,
+          final_returned;
+
+      this.log('runRoute', [verb, path].join(' '));
+      this.trigger('run-route', {verb: verb, path: path, params: params});
+      if (typeof params == 'undefined') { params = {}; }
+
+      $.extend(params, this._parseQueryString(path));
+
+      if (route) {
+        this.trigger('route-found', {route: route});
+        // pull out the params from the path
+        if ((path_params = route.path.exec(this.routablePath(path))) !== null) {
+          // first match is the full path
+          path_params.shift();
+          // for each of the matches
+          $.each(path_params, function(i, param) {
+            // if theres a matching param name
+            if (route.param_names[i]) {
+              // set the name to the match
+              params[route.param_names[i]] = _decode(param);
+            } else {
+              // initialize 'splat'
+              if (!params.splat) { params.splat = []; }
+              params.splat.push(_decode(param));
+            }
+          });
+        }
+
+        // set event context
+        context  = new this.context_prototype(this, verb, path, params, target);
+        // ensure arrays
+        arounds = this.arounds.slice(0);
+        befores = this.befores.slice(0);
+        // set the callback args to the context + contents of the splat
+        callback_args = [context].concat(params.splat);
+        // wrap the route up with the before filters
+        wrapped_route = function() {
+          var returned;
+          while (befores.length > 0) {
+            before = befores.shift();
+            // check the options
+            if (app.contextMatchesOptions(context, before[0])) {
+              returned = before[1].apply(context, [context]);
+              if (returned === false) { return false; }
+            }
+          }
+          app.last_route = route;
+          context.trigger('event-context-before', {context: context});
+          returned = route.callback.apply(context, callback_args);
+          context.trigger('event-context-after', {context: context});
+          return returned;
+        };
+        $.each(arounds.reverse(), function(i, around) {
+          var last_wrapped_route = wrapped_route;
+          wrapped_route = function() { return around.apply(context, [last_wrapped_route]); };
+        });
+        try {
+          final_returned = wrapped_route();
+        } catch(e) {
+          this.error(['500 Error', verb, path].join(' '), e);
+        }
+        return final_returned;
+      } else {
+        return this.notFound(verb, path);
+      }
+    },
+
+    // Matches an object of options against an `EventContext` like object that
+    // contains `path` and `verb` attributes. Internally Sammy uses this
+    // for matching `before()` filters against specific options. You can set the
+    // object to _only_ match certain paths or verbs, or match all paths or verbs _except_
+    // those that match the options.
+    //
+    // ### Example
+    //
+    //     var app = $.sammy(),
+    //         context = {verb: 'get', path: '#/mypath'};
+    //
+    //     // match against a path string
+    //     app.contextMatchesOptions(context, '#/mypath'); //=> true
+    //     app.contextMatchesOptions(context, '#/otherpath'); //=> false
+    //     // equivilent to
+    //     app.contextMatchesOptions(context, {only: {path:'#/mypath'}}); //=> true
+    //     app.contextMatchesOptions(context, {only: {path:'#/otherpath'}}); //=> false
+    //     // match against a path regexp
+    //     app.contextMatchesOptions(context, /path/); //=> true
+    //     app.contextMatchesOptions(context, /^path/); //=> false
+    //     // match only a verb
+    //     app.contextMatchesOptions(context, {only: {verb:'get'}}); //=> true
+    //     app.contextMatchesOptions(context, {only: {verb:'post'}}); //=> false
+    //     // match all except a verb
+    //     app.contextMatchesOptions(context, {except: {verb:'post'}}); //=> true
+    //     app.contextMatchesOptions(context, {except: {verb:'get'}}); //=> false
+    //     // match all except a path
+    //     app.contextMatchesOptions(context, {except: {path:'#/otherpath'}}); //=> true
+    //     app.contextMatchesOptions(context, {except: {path:'#/mypath'}}); //=> false
+    //
+    contextMatchesOptions: function(context, match_options, positive) {
+      // empty options always match
+      var options = match_options;
+      if (typeof options === 'undefined' || options == {}) {
+        return true;
+      }
+      if (typeof positive === 'undefined') {
+        positive = true;
+      }
+      // normalize options
+      if (typeof options === 'string' || _isFunction(options.test)) {
+        options = {path: options};
+      }
+      if (options.only) {
+        return this.contextMatchesOptions(context, options.only, true);
+      } else if (options.except) {
+        return this.contextMatchesOptions(context, options.except, false);
+      }
+      var path_matched = true, verb_matched = true;
+      if (options.path) {
+        // wierd regexp test
+        if (_isFunction(options.path.test)) {
+          path_matched = options.path.test(context.path);
+        } else {
+          path_matched = (options.path.toString() === context.path);
+        }
+      }
+      if (options.verb) {
+        verb_matched = options.verb === context.verb;
+      }
+      return positive ? (verb_matched && path_matched) : !(verb_matched && path_matched);
+    },
+
+
+    // Delegates to the `location_proxy` to get the current location.
+    // See `Sammy.HashLocationProxy` for more info on location proxies.
+    getLocation: function() {
+      return this._location_proxy.getLocation();
+    },
+
+    // Delegates to the `location_proxy` to set the current location.
+    // See `Sammy.HashLocationProxy` for more info on location proxies.
+    //
+    // ### Arguments
+    //
+    // * `new_location` A new location string (e.g. '#/')
+    //
+    setLocation: function(new_location) {
+      return this._location_proxy.setLocation(new_location);
+    },
+
+    // Swaps the content of `$element()` with `content`
+    // You can override this method to provide an alternate swap behavior
+    // for `EventContext.partial()`.
+    //
+    // ### Example
+    //
+    //    var app = $.sammy(function() {
+    //
+    //      // implements a 'fade out'/'fade in'
+    //      this.swap = function(content) {
+    //        this.$element().hide('slow').html(content).show('slow');
+    //      }
+    //
+    //      get('#/', function() {
+    //        this.partial('index.html.erb') // will fade out and in
+    //      });
+    //
+    //    });
+    //
+    swap: function(content) {
+      return this.$element().html(content);
+    },
+
+    // a simple global cache for templates. Uses the same semantics as
+    // `Sammy.Cache` and `Sammy.Storage` so can easily be replaced with
+    // a persistant storage that lasts beyond the current request.
+    templateCache: function(key, value) {
+      if (typeof value != 'undefined') {
+        return _template_cache[key] = value;
+      } else {
+        return _template_cache[key];
+      }
+    },
+
+    // clear the templateCache
+    clearTemplateCache: function() {
+      return _template_cache = {};
+    },
+
+    // This thows a '404 Not Found' error by invoking `error()`.
+    // Override this method or `error()` to provide custom
+    // 404 behavior (i.e redirecting to / or showing a warning)
+    notFound: function(verb, path) {
+      var ret = this.error(['404 Not Found', verb, path].join(' '));
+      return (verb === 'get') ? ret : true;
+    },
+
+    // The base error handler takes a string `message` and an `Error`
+    // object. If `raise_errors` is set to `true` on the app level,
+    // this will re-throw the error to the browser. Otherwise it will send the error
+    // to `log()`. Override this method to provide custom error handling
+    // e.g logging to a server side component or displaying some feedback to the
+    // user.
+    error: function(message, original_error) {
+      if (!original_error) { original_error = new Error(); }
+      original_error.message = [message, original_error.message].join(' ');
+      this.trigger('error', {message: original_error.message, error: original_error});
+      if (this.raise_errors) {
+        throw(original_error);
+      } else {
+        this.log(original_error.message, original_error);
+      }
+    },
+
+    _checkLocation: function() {
+      var location, returned;
+      // get current location
+      location = this.getLocation();
+      // compare to see if hash has changed
+      if (!this.last_location || this.last_location[0] != 'get' || this.last_location[1] != location) {
+        // reset last location
+        this.last_location = ['get', location];
+        // lookup route for current hash
+        returned = this.runRoute('get', location);
+      }
+      return returned;
+    },
+
+    _getFormVerb: function(form) {
+      var $form = $(form), verb, $_method;
+      $_method = $form.find('input[name="_method"]');
+      if ($_method.length > 0) { verb = $_method.val(); }
+      if (!verb) { verb = $form[0].getAttribute('method'); }
+      return $.trim(verb.toString().toLowerCase());
+    },
+
+    _checkFormSubmission: function(form) {
+      var $form, path, verb, params, returned;
+      this.trigger('check-form-submission', {form: form});
+      $form = $(form);
+      path  = $form.attr('action');
+      verb  = this._getFormVerb($form);
+      if (!verb || verb == '') { verb = 'get'; }
+      this.log('_checkFormSubmission', $form, path, verb);
+      if (verb === 'get') {
+        this.setLocation(path + '?' + this._serializeFormParams($form));
+        returned = false;
+      } else {
+        params = $.extend({}, this._parseFormParams($form));
+        returned = this.runRoute(verb, path, params, form.get(0));
+      };
+      return (typeof returned == 'undefined') ? false : returned;
+    },
+
+    _serializeFormParams: function($form) {
+       var queryString = "",
+         fields = $form.serializeArray(),
+         i;
+       if (fields.length > 0) {
+         queryString = this._encodeFormPair(fields[0].name, fields[0].value);
+         for (i = 1; i < fields.length; i++) {
+           queryString = queryString + "&" + this._encodeFormPair(fields[i].name, fields[i].value);
+         }
+       }
+       return queryString;
+    },
+
+    _encodeFormPair: function(name, value){
+      return _encode(name) + "=" + _encode(value);
+    },
+
+    _parseFormParams: function($form) {
+      var params = {},
+          form_fields = $form.serializeArray(),
+          i;
+      for (i = 0; i < form_fields.length; i++) {
+        params = this._parseParamPair(params, form_fields[i].name, form_fields[i].value);
+      }
+      return params;
+    },
+
+    _parseQueryString: function(path) {
+      var params = {}, parts, pairs, pair, i;
+
+      parts = path.match(QUERY_STRING_MATCHER);
+      if (parts) {
+        pairs = parts[1].split('&');
+        for (i = 0; i < pairs.length; i++) {
+          pair = pairs[i].split('=');
+          params = this._parseParamPair(params, _decode(pair[0]), _decode(pair[1]));
+        }
+      }
+      return params;
+    },
+
+    _parseParamPair: function(params, key, value) {
+      if (params[key]) {
+        if (_isArray(params[key])) {
+          params[key].push(value);
+        } else {
+          params[key] = [params[key], value];
+        }
+      } else {
+        params[key] = value;
+      }
+      return params;
+    },
+
+    _listen: function(name, callback) {
+      return this.$element().bind([name, this.eventNamespace()].join('.'), callback);
+    },
+
+    _unlisten: function(name, callback) {
+      return this.$element().unbind([name, this.eventNamespace()].join('.'), callback);
+    }
+
+  });
+
+  // `Sammy.RenderContext` is an object that makes sequential template loading,
+  // rendering and interpolation seamless even when dealing with asyncronous
+  // operations.
+  //
+  // `RenderContext` objects are not usually created directly, rather they are
+  // instatiated from an `Sammy.EventContext` by using `render()`, `load()` or
+  // `partial()` which all return `RenderContext` objects.
+  //
+  // `RenderContext` methods always returns a modified `RenderContext`
+  // for chaining (like jQuery itself).
+  //
+  // The core magic is in the `then()` method which puts the callback passed as
+  // an argument into a queue to be executed once the previous callback is complete.
+  // All the methods of `RenderContext` are wrapped in `then()` which allows you
+  // to queue up methods by chaining, but maintaing a guarunteed execution order
+  // even with remote calls to fetch templates.
+  //
+  Sammy.RenderContext = function(event_context) {
+    this.event_context    = event_context;
+    this.callbacks        = [];
+    this.previous_content = null;
+    this.content          = null;
+    this.next_engine      = false;
+    this.waiting          = false;
+  };
+
+  Sammy.RenderContext.prototype = $.extend({}, Sammy.Object.prototype, {
+
+    // The "core" of the `RenderContext` object, adds the `callback` to the
+    // queue. If the context is `waiting` (meaning an async operation is happening)
+    // then the callback will be executed in order, once the other operations are
+    // complete. If there is no currently executing operation, the `callback`
+    // is executed immediately.
+    //
+    // The value returned from the callback is stored in `content` for the
+    // subsiquent operation. If you return `false`, the queue will pause, and
+    // the next callback in the queue will not be executed until `next()` is
+    // called. This allows for the guarunteed order of execution while working
+    // with async operations.
+    //
+    // If then() is passed a string instead of a function, the string is looked
+    // up as a helper method on the event context.
+    //
+    // ### Example
+    //
+    //      this.get('#/', function() {
+    //        // initialize the RenderContext
+    //        // Even though `load()` executes async, the next `then()`
+    //        // wont execute until the load finishes
+    //        this.load('myfile.txt')
+    //            .then(function(content) {
+    //              // the first argument to then is the content of the
+    //              // prev operation
+    //              $('#main').html(content);
+    //            });
+    //      });
+    //
+    then: function(callback) {
+      if (!_isFunction(callback)) {
+        // if a string is passed to then, assume we want to call
+        // a helper on the event context in its context
+        if (typeof callback === 'string' && callback in this.event_context) {
+          var helper = this.event_context[callback];
+          callback = function(content) {
+            return helper.apply(this.event_context, [content]);
+          };
+        } else {
+          return this;
+        }
+      }
+      var context = this;
+      if (this.waiting) {
+        this.callbacks.push(callback);
+      } else {
+        this.wait();
+        window.setTimeout(function() {
+          var returned = callback.apply(context, [context.content, context.previous_content]);
+          if (returned !== false) {
+            context.next(returned);
+          }
+        }, 13);
+      }
+      return this;
+    },
+
+    // Pause the `RenderContext` queue. Combined with `next()` allows for async
+    // operations.
+    //
+    // ### Example
+    //
+    //        this.get('#/', function() {
+    //          this.load('mytext.json')
+    //              .then(function(content) {
+    //                var context = this,
+    //                    data    = JSON.parse(content);
+    //                // pause execution
+    //                context.wait();
+    //                // post to a url
+    //                $.post(data.url, {}, function(response) {
+    //                  context.next(JSON.parse(response));
+    //                });
+    //              })
+    //              .then(function(data) {
+    //                // data is json from the previous post
+    //                $('#message').text(data.status);
+    //              });
+    //        });
+    wait: function() {
+      this.waiting = true;
+    },
+
+    // Resume the queue, setting `content` to be used in the next operation.
+    // See `wait()` for an example.
+    next: function(content) {
+      this.waiting = false;
+      if (typeof content !== 'undefined') {
+        this.previous_content = this.content;
+        this.content = content;
+      }
+      if (this.callbacks.length > 0) {
+        this.then(this.callbacks.shift());
+      }
+    },
+
+    // Load a template into the context.
+    // The `location` can either be a string specifiying the remote path to the
+    // file, a jQuery object, or a DOM element.
+    //
+    // No interpolation happens by default, the content is stored in
+    // `content`.
+    //
+    // In the case of a path, unless the option `{cache: false}` is passed the
+    // data is stored in the app's `templateCache()`.
+    //
+    // If a jQuery or DOM object is passed the `innerHTML` of the node is pulled in.
+    // This is useful for nesting templates as part of the initial page load wrapped
+    // in invisible elements or `<script>` tags. With template paths, the template
+    // engine is looked up by the extension. For DOM/jQuery embedded templates,
+    // this isnt possible, so there are a couple of options:
+    //
+    //  * pass an `{engine:}` option.
+    //  * define the engine in the `data-engine` attribute of the passed node.
+    //  * just store the raw template data and use `interpolate()` manually
+    //
+    // If a `callback` is passed it is executed after the template load.
+    load: function(location, options, callback) {
+      var context = this;
+      return this.then(function() {
+        var should_cache, cached, is_json, location_array;
+        if (_isFunction(options)) {
+          callback = options;
+          options = {};
+        } else {
+          options = $.extend({}, options);
+        }
+        if (callback) { this.then(callback); }
+        if (typeof location === 'string') {
+          // its a path
+          is_json      = (location.match(/\.json$/) || options.json);
+          should_cache = ((is_json && options.cache === true) || options.cache !== false);
+          context.next_engine = context.event_context.engineFor(location);
+          delete options.cache;
+          delete options.json;
+          if (options.engine) {
+            context.next_engine = options.engine;
+            delete options.engine;
+          }
+          if (should_cache && (cached = this.event_context.app.templateCache(location))) {
+            return cached;
+          }
+          this.wait();
+          $.ajax($.extend({
+            url: location,
+            data: {},
+            dataType: is_json ? 'json' : null,
+            type: 'get',
+            success: function(data) {
+              if (should_cache) {
+                context.event_context.app.templateCache(location, data);
+              }
+              context.next(data);
+            }
+          }, options));
+          return false;
+        } else {
+          // its a dom/jQuery
+          if (location.nodeType) {
+            return location.innerHTML;
+          }
+          if (location.selector) {
+            // its a jQuery
+            context.next_engine = location.attr('data-engine');
+            if (options.clone === false) {
+              return location.remove()[0].innerHTML.toString();
+            } else {
+              return location[0].innerHTML.toString();
+            }
+          }
+        }
+      });
+    },
+
+    // `load()` a template and then `interpolate()` it with data.
+    //
+    // ### Example
+    //
+    //      this.get('#/', function() {
+    //        this.render('mytemplate.template', {name: 'test'});
+    //      });
+    //
+    render: function(location, data, callback) {
+      if (_isFunction(location) && !data) {
+        return this.then(location);
+      } else {
+        if (!data && this.content) { data = this.content; }
+        return this.load(location)
+                   .interpolate(data, location)
+                   .then(callback);
+      }
+    },
+
+    // `render()` the the `location` with `data` and then `swap()` the
+    // app's `$element` with the rendered content.
+    partial: function(location, data) {
+      return this.render(location, data).swap();
+    },
+
+    // defers the call of function to occur in order of the render queue.
+    // The function can accept any number of arguments as long as the last
+    // argument is a callback function. This is useful for putting arbitrary
+    // asynchronous functions into the queue. The content passed to the
+    // callback is passed as `content` to the next item in the queue.
+    //
+    // === Example
+    //
+    //        this.send($.getJSON, '/app.json')
+    //            .then(function(json) {
+    //              $('#message).text(json['message']);
+    //            });
+    //
+    //
+    send: function() {
+      var context = this,
+          args = _makeArray(arguments),
+          fun  = args.shift();
+
+      if (_isArray(args[0])) { args = args[0]; }
+
+      return this.then(function(content) {
+        args.push(function(response) { context.next(response); });
+        context.wait();
+        fun.apply(fun, args);
+        return false;
+      });
+    },
+
+    // itterates over an array, applying the callback for each item item. the
+    // callback takes the same style of arguments as `jQuery.each()` (index, item).
+    // The return value of each callback is collected as a single string and stored
+    // as `content` to be used in the next iteration of the `RenderContext`.
+    collect: function(array, callback, now) {
+      var context = this;
+      var coll = function() {
+        if (_isFunction(array)) {
+          callback = array;
+          array = this.content;
+        }
+        var contents = [], doms = false;
+        $.each(array, function(i, item) {
+          var returned = callback.apply(context, [i, item]);
+          if (returned.jquery && returned.length == 1) {
+            returned = returned[0];
+            doms = true;
+          }
+          contents.push(returned);
+          return returned;
+        });
+        return doms ? contents : contents.join('');
+      };
+      return now ? coll() : this.then(coll);
+    },
+
+    // loads a template, and then interpolates it for each item in the `data`
+    // array. If a callback is passed, it will call the callback with each
+    // item in the array _after_ interpolation
+    renderEach: function(location, name, data, callback) {
+      if (_isArray(name)) {
+        callback = data;
+        data = name;
+        name = null;
+      }
+      return this.load(location).then(function(content) {
+          var rctx = this;
+          if (!data) {
+            data = _isArray(this.previous_content) ? this.previous_content : [];
+          }
+          if (callback) {
+            $.each(data, function(i, value) {
+              var idata = {}, engine = this.next_engine || location;
+              name ? (idata[name] = value) : (idata = value);
+              callback(value, rctx.event_context.interpolate(content, idata, engine));
+            });
+          } else {
+            return this.collect(data, function(i, value) {
+              var idata = {}, engine = this.next_engine || location;
+              name ? (idata[name] = value) : (idata = value);
+              return this.event_context.interpolate(content, idata, engine);
+            }, true);
+          }
+      });
+    },
+
+    // uses the previous loaded `content` and the `data` object to interpolate
+    // a template. `engine` defines the templating/interpolation method/engine
+    // that should be used. If `engine` is not passed, the `next_engine` is
+    // used. If `retain` is `true`, the final interpolated data is appended to
+    // the `previous_content` instead of just replacing it.
+    interpolate: function(data, engine, retain) {
+      var context = this;
+      return this.then(function(content, prev) {
+        if (!data && prev) { data = prev; }
+        if (this.next_engine) {
+          engine = this.next_engine;
+          this.next_engine = false;
+        }
+        var rendered = context.event_context.interpolate(content, data, engine);
+        return retain ? prev + rendered : rendered;
+      });
+    },
+
+    // executes `EventContext#swap()` with the `content`
+    swap: function() {
+      return this.then(function(content) {
+        this.event_context.swap(content);
+      }).trigger('changed', {});
+    },
+
+    // Same usage as `jQuery.fn.appendTo()` but uses `then()` to ensure order
+    appendTo: function(selector) {
+      return this.then(function(content) {
+        $(selector).append(content);
+      }).trigger('changed', {});
+    },
+
+    // Same usage as `jQuery.fn.prependTo()` but uses `then()` to ensure order
+    prependTo: function(selector) {
+      return this.then(function(content) {
+        $(selector).prepend(content);
+      }).trigger('changed', {});
+    },
+
+    // Replaces the `$(selector)` using `html()` with the previously loaded
+    // `content`
+    replace: function(selector) {
+      return this.then(function(content) {
+        $(selector).html(content);
+      }).trigger('changed', {});
+    },
+
+    // trigger the event in the order of the event context. Same semantics
+    // as `Sammy.EventContext#trigger()`. If data is ommitted, `content`
+    // is sent as `{content: content}`
+    trigger: function(name, data) {
+      return this.then(function(content) {
+        if (typeof data == 'undefined') { data = {content: content}; }
+        this.event_context.trigger(name, data);
+      });
+    }
+
+  });
+
+  // `Sammy.EventContext` objects are created every time a route is run or a
+  // bound event is triggered. The callbacks for these events are evaluated within a `Sammy.EventContext`
+  // This within these callbacks the special methods of `EventContext` are available.
+  //
+  // ### Example
+  //
+  //  $.sammy(function() {
+  //    // The context here is this Sammy.Application
+  //    this.get('#/:name', function() {
+  //      // The context here is a new Sammy.EventContext
+  //      if (this.params['name'] == 'sammy') {
+  //        this.partial('name.html.erb', {name: 'Sammy'});
+  //      } else {
+  //        this.redirect('#/somewhere-else')
+  //      }
+  //    });
+  //  });
+  //
+  // Initialize a new EventContext
+  //
+  // ### Arguments
+  //
+  // * `app` The `Sammy.Application` this event is called within.
+  // * `verb` The verb invoked to run this context/route.
+  // * `path` The string path invoked to run this context/route.
+  // * `params` An Object of optional params to pass to the context. Is converted
+  //   to a `Sammy.Object`.
+  // * `target` a DOM element that the event that holds this context originates
+  //   from. For post, put and del routes, this is the form element that triggered
+  //   the route.
+  //
+  Sammy.EventContext = function(app, verb, path, params, target) {
+    this.app    = app;
+    this.verb   = verb;
+    this.path   = path;
+    this.params = new Sammy.Object(params);
+    this.target = target;
+  };
+
+  Sammy.EventContext.prototype = $.extend({}, Sammy.Object.prototype, {
+
+    // A shortcut to the app's `$element()`
+    $element: function() {
+      return this.app.$element(_makeArray(arguments).shift());
+    },
+
+    // Look up a templating engine within the current app and context.
+    // `engine` can be one of the following:
+    //
+    // * a function: should conform to `function(content, data) { return interploated; }`
+    // * a template path: 'template.ejs', looks up the extension to match to
+    //   the `ejs()` helper
+    // * a string referering to the helper: "mustache" => `mustache()`
+    //
+    // If no engine is found, use the app's default `template_engine`
+    //
+    engineFor: function(engine) {
+      var context = this, engine_match;
+      // if path is actually an engine function just return it
+      if (_isFunction(engine)) { return engine; }
+      // lookup engine name by path extension
+      engine = engine.toString();
+      if ((engine_match = engine.match(/\.([^\.]+)$/))) {
+        engine = engine_match[1];
+      }
+      // set the engine to the default template engine if no match is found
+      if (engine && _isFunction(context[engine])) {
+        return context[engine];
+      }
+
+      if (context.app.template_engine) {
+        return this.engineFor(context.app.template_engine);
+      }
+      return function(content, data) { return content; };
+    },
+
+    // using the template `engine` found with `engineFor()`, interpolate the
+    // `data` into `content`
+    interpolate: function(content, data, engine) {
+      return this.engineFor(engine).apply(this, [content, data]);
+    },
+
+    // Create and return a `Sammy.RenderContext` calling `render()` on it.
+    // Loads the template and interpolate the data, however does not actual
+    // place it in the DOM.
+    //
+    // ### Example
+    //
+    //      // mytemplate.mustache <div class="name">{{name}}</div>
+    //      render('mytemplate.mustache', {name: 'quirkey'});
+    //      // sets the `content` to <div class="name">quirkey</div>
+    //      render('mytemplate.mustache', {name: 'quirkey'})
+    //        .appendTo('ul');
+    //      // appends the rendered content to $('ul')
+    //
+    render: function(location, data, callback) {
+      return new Sammy.RenderContext(this).render(location, data, callback);
+    },
+
+    // Create and return a `Sammy.RenderContext` calling `renderEach()` on it.
+    // Loads the template and interpolates the data for each item,
+    // however does not actual place it in the DOM.
+    //
+    // ### Example
+    //
+    //      // mytemplate.mustache <div class="name">{{name}}</div>
+    //      renderEach('mytemplate.mustache', [{name: 'quirkey'}, {name: 'endor'}])
+    //      // sets the `content` to <div class="name">quirkey</div><div class="name">endor</div>
+    //      renderEach('mytemplate.mustache', [{name: 'quirkey'}, {name: 'endor'}]).appendTo('ul');
+    //      // appends the rendered content to $('ul')
+    //
+    renderEach: function(location, name, data, callback) {
+      return new Sammy.RenderContext(this).renderEach(location, name, data, callback);
+    },
+
+    // create a new `Sammy.RenderContext` calling `load()` with `location` and
+    // `options`. Called without interpolation or placement, this allows for
+    // preloading/caching the templates.
+    load: function(location, options, callback) {
+      return new Sammy.RenderContext(this).load(location, options, callback);
+    },
+
+    // `render()` the the `location` with `data` and then `swap()` the
+    // app's `$element` with the rendered content.
+    partial: function(location, data) {
+      return new Sammy.RenderContext(this).partial(location, data);
+    },
+
+    // create a new `Sammy.RenderContext` calling `send()` with an arbitrary
+    // function
+    send: function() {
+      var rctx = new Sammy.RenderContext(this);
+      return rctx.send.apply(rctx, arguments);
+    },
+
+    // Changes the location of the current window. If `to` begins with
+    // '#' it only changes the document's hash. If passed more than 1 argument
+    // redirect will join them together with forward slashes.
+    //
+    // ### Example
+    //
+    //      redirect('#/other/route');
+    //      // equivilent to
+    //      redirect('#', 'other', 'route');
+    //
+    redirect: function() {
+      var to, args = _makeArray(arguments),
+          current_location = this.app.getLocation();
+      if (args.length > 1) {
+        args.unshift('/');
+        to = this.join.apply(this, args);
+      } else {
+        to = args[0];
+      }
+      this.trigger('redirect', {to: to});
+      this.app.last_location = [this.verb, this.path];
+      this.app.setLocation(to);
+      if (current_location == to) {
+        this.app.trigger('location-changed');
+      }
+    },
+
+    // Triggers events on `app` within the current context.
+    trigger: function(name, data) {
+      if (typeof data == 'undefined') { data = {}; }
+      if (!data.context) { data.context = this; }
+      return this.app.trigger(name, data);
+    },
+
+    // A shortcut to app's `eventNamespace()`
+    eventNamespace: function() {
+      return this.app.eventNamespace();
+    },
+
+    // A shortcut to app's `swap()`
+    swap: function(contents) {
+      return this.app.swap(contents);
+    },
+
+    // Raises a possible `notFound()` error for the current path.
+    notFound: function() {
+      return this.app.notFound(this.verb, this.path);
+    },
+
+    // Default JSON parsing uses jQuery's `parseJSON()`. Include `Sammy.JSON`
+    // plugin for the more conformant "crockford special".
+    json: function(string) {
+      return $.parseJSON(string);
+    },
+
+    // //=> Sammy.EventContext: get #/ {}
+    toString: function() {
+      return "Sammy.EventContext: " + [this.verb, this.path, this.params].join(' ');
+    }
+
+  });
+
+  // An alias to Sammy
+  $.sammy = window.Sammy = Sammy;
+
+})(jQuery, window);
diff --git a/javascript/validate.min.js b/javascript/validate.min.js
new file mode 100644
index 00000000..7453885e
--- /dev/null
+++ b/javascript/validate.min.js
@@ -0,0 +1,9 @@
+/*!
+ * Validator v0.11.9 for Bootstrap 3, by @1000hz
+ * Copyright 2017 Cina Saffary
+ * Licensed under http://opensource.org/licenses/MIT
+ *
+ * https://github.com/1000hz/bootstrap-validator
+ */
+
++function(a){"use strict";function b(b){return b.is('[type="checkbox"]')?b.prop("checked"):b.is('[type="radio"]')?!!a('[name="'+b.attr("name")+'"]:checked').length:b.is("select[multiple]")?(b.val()||[]).length:b.val()}function c(b){return this.each(function(){var c=a(this),e=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b),f=c.data("bs.validator");(f||"destroy"!=b)&&(f||c.data("bs.validator",f=new d(this,e)),"string"==typeof b&&f[b]())})}var d=function(c,e){this.options=e,this.validators=a.extend({},d.VALIDATORS,e.custom),this.$element=a(c),this.$btn=a('button[type="submit"], input[type="submit"]').filter('[form="'+this.$element.attr("id")+'"]').add(this.$element.find('input[type="submit"], button[type="submit"]')),this.update(),this.$element.on("input.bs.validator change.bs.validator focusout.bs.validator",a.proxy(this.onInput,this)),this.$element.on("submit.bs.validator",a.proxy(this.onSubmit,this)),this.$element.on("reset.bs.validator",a.proxy(this.reset,this)),this.$element.find("[data-match]").each(function(){var c=a(this),d=c.attr("data-match");a(d).on("input.bs.validator",function(){b(c)&&c.trigger("input.bs.validator")})}),this.$inputs.filter(function(){return b(a(this))&&!a(this).closest(".has-error").length}).trigger("focusout"),this.$element.attr("novalidate",!0)};d.VERSION="0.11.9",d.INPUT_SELECTOR=':input:not([type="hidden"], [type="submit"], [type="reset"], button)',d.FOCUS_OFFSET=20,d.DEFAULTS={delay:500,html:!1,disable:!0,focus:!0,custom:{},errors:{match:"Does not match",minlength:"Not long enough"},feedback:{success:"glyphicon-ok",error:"glyphicon-remove"}},d.VALIDATORS={"native":function(a){var b=a[0];return b.checkValidity?!b.checkValidity()&&!b.validity.valid&&(b.validationMessage||"error!"):void 0},match:function(b){var c=b.attr("data-match");return b.val()!==a(c).val()&&d.DEFAULTS.errors.match},minlength:function(a){var b=a.attr("data-minlength");return a.val().length<b&&d.DEFAULTS.errors.minlength}},d.prototype.update=function(){var b=this;return this.$inputs=this.$element.find(d.INPUT_SELECTOR).add(this.$element.find('[data-validate="true"]')).not(this.$element.find('[data-validate="false"]').each(function(){b.clearErrors(a(this))})),this.toggleSubmit(),this},d.prototype.onInput=function(b){var c=this,d=a(b.target),e="focusout"!==b.type;this.$inputs.is(d)&&this.validateInput(d,e).done(function(){c.toggleSubmit()})},d.prototype.validateInput=function(c,d){var e=(b(c),c.data("bs.validator.errors"));c.is('[type="radio"]')&&(c=this.$element.find('input[name="'+c.attr("name")+'"]'));var f=a.Event("validate.bs.validator",{relatedTarget:c[0]});if(this.$element.trigger(f),!f.isDefaultPrevented()){var g=this;return this.runValidators(c).done(function(b){c.data("bs.validator.errors",b),b.length?d?g.defer(c,g.showErrors):g.showErrors(c):g.clearErrors(c),e&&b.toString()===e.toString()||(f=b.length?a.Event("invalid.bs.validator",{relatedTarget:c[0],detail:b}):a.Event("valid.bs.validator",{relatedTarget:c[0],detail:e}),g.$element.trigger(f)),g.toggleSubmit(),g.$element.trigger(a.Event("validated.bs.validator",{relatedTarget:c[0]}))})}},d.prototype.runValidators=function(c){function d(a){return c.attr("data-"+a+"-error")}function e(){var a=c[0].validity;return a.typeMismatch?c.attr("data-type-error"):a.patternMismatch?c.attr("data-pattern-error"):a.stepMismatch?c.attr("data-step-error"):a.rangeOverflow?c.attr("data-max-error"):a.rangeUnderflow?c.attr("data-min-error"):a.valueMissing?c.attr("data-required-error"):null}function f(){return c.attr("data-error")}function g(a){return d(a)||e()||f()}var h=[],i=a.Deferred();return c.data("bs.validator.deferred")&&c.data("bs.validator.deferred").reject(),c.data("bs.validator.deferred",i),a.each(this.validators,a.proxy(function(a,d){var e=null;!b(c)&&!c.attr("required")||void 0===c.attr("data-"+a)&&"native"!=a||!(e=d.call(this,c))||(e=g(a)||e,!~h.indexOf(e)&&h.push(e))},this)),!h.length&&b(c)&&c.attr("data-remote")?this.defer(c,function(){var d={};d[c.attr("name")]=b(c),a.get(c.attr("data-remote"),d).fail(function(a,b,c){h.push(g("remote")||c)}).always(function(){i.resolve(h)})}):i.resolve(h),i.promise()},d.prototype.validate=function(){var b=this;return a.when(this.$inputs.map(function(){return b.validateInput(a(this),!1)})).then(function(){b.toggleSubmit(),b.focusError()}),this},d.prototype.focusError=function(){if(this.options.focus){var b=this.$element.find(".has-error :input:first");0!==b.length&&(a("html, body").animate({scrollTop:b.offset().top-d.FOCUS_OFFSET},250),b.focus())}},d.prototype.showErrors=function(b){var c=this.options.html?"html":"text",d=b.data("bs.validator.errors"),e=b.closest(".form-group"),f=e.find(".help-block.with-errors"),g=e.find(".form-control-feedback");d.length&&(d=a("<ul/>").addClass("list-unstyled").append(a.map(d,function(b){return a("<li/>")[c](b)})),void 0===f.data("bs.validator.originalContent")&&f.data("bs.validator.originalContent",f.html()),f.empty().append(d),e.addClass("has-error has-danger"),e.hasClass("has-feedback")&&g.removeClass(this.options.feedback.success)&&g.addClass(this.options.feedback.error)&&e.removeClass("has-success"))},d.prototype.clearErrors=function(a){var c=a.closest(".form-group"),d=c.find(".help-block.with-errors"),e=c.find(".form-control-feedback");d.html(d.data("bs.validator.originalContent")),c.removeClass("has-error has-danger has-success"),c.hasClass("has-feedback")&&e.removeClass(this.options.feedback.error)&&e.removeClass(this.options.feedback.success)&&b(a)&&e.addClass(this.options.feedback.success)&&c.addClass("has-success")},d.prototype.hasErrors=function(){function b(){return!!(a(this).data("bs.validator.errors")||[]).length}return!!this.$inputs.filter(b).length},d.prototype.isIncomplete=function(){function c(){var c=b(a(this));return!("string"==typeof c?a.trim(c):c)}return!!this.$inputs.filter("[required]").filter(c).length},d.prototype.onSubmit=function(a){this.validate(),(this.isIncomplete()||this.hasErrors())&&a.preventDefault()},d.prototype.toggleSubmit=function(){this.options.disable&&this.$btn.toggleClass("disabled",this.isIncomplete()||this.hasErrors())},d.prototype.defer=function(b,c){return c=a.proxy(c,this,b),this.options.delay?(window.clearTimeout(b.data("bs.validator.timeout")),void b.data("bs.validator.timeout",window.setTimeout(c,this.options.delay))):c()},d.prototype.reset=function(){return this.$element.find(".form-control-feedback").removeClass(this.options.feedback.error).removeClass(this.options.feedback.success),this.$inputs.removeData(["bs.validator.errors","bs.validator.deferred"]).each(function(){var b=a(this),c=b.data("bs.validator.timeout");window.clearTimeout(c)&&b.removeData("bs.validator.timeout")}),this.$element.find(".help-block.with-errors").each(function(){var b=a(this),c=b.data("bs.validator.originalContent");b.removeData("bs.validator.originalContent").html(c)}),this.$btn.removeClass("disabled"),this.$element.find(".has-error, .has-danger, .has-success").removeClass("has-error has-danger has-success"),this},d.prototype.destroy=function(){return this.reset(),this.$element.removeAttr("novalidate").removeData("bs.validator").off(".bs.validator"),this.$inputs.off(".bs.validator"),this.options=null,this.validators=null,this.$element=null,this.$btn=null,this.$inputs=null,this};var e=a.fn.validator;a.fn.validator=c,a.fn.validator.Constructor=d,a.fn.validator.noConflict=function(){return a.fn.validator=e,this},a(window).on("load",function(){a('form[data-toggle="validator"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery);
diff --git a/logo.png b/logo.png
new file mode 100644
index 00000000..9c1fb0c0
Binary files /dev/null and b/logo.png differ
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..7bd033a6
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,396 @@
+{
+  "name": "ecommercespa",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "ajv": {
+      "version": "5.5.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+      "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+      "requires": {
+        "co": "4.6.0",
+        "fast-deep-equal": "1.0.0",
+        "fast-json-stable-stringify": "2.0.0",
+        "json-schema-traverse": "0.3.1"
+      }
+    },
+    "asn1": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+    },
+    "aws-sign2": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+    },
+    "aws4": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
+      "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
+      "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+      "optional": true,
+      "requires": {
+        "tweetnacl": "0.14.5"
+      }
+    },
+    "boom": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
+      "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
+      "requires": {
+        "hoek": "4.2.1"
+      }
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+    },
+    "co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+    },
+    "combined-stream": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
+      "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+      "requires": {
+        "delayed-stream": "1.0.0"
+      }
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "cryptiles": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
+      "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
+      "requires": {
+        "boom": "5.2.0"
+      },
+      "dependencies": {
+        "boom": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
+          "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
+          "requires": {
+            "hoek": "4.2.1"
+          }
+        }
+      }
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "requires": {
+        "assert-plus": "1.0.0"
+      }
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+    },
+    "ecc-jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+      "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+      "optional": true,
+      "requires": {
+        "jsbn": "0.1.1"
+      }
+    },
+    "extend": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+      "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+    },
+    "fast-deep-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
+      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+    },
+    "fi-khipu": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/fi-khipu/-/fi-khipu-0.1.0.tgz",
+      "integrity": "sha1-Dt+RAz87bmvROonPbEuLpF1/YwU=",
+      "requires": {
+        "is_js": "0.7.6",
+        "qs": "6.5.1",
+        "request": "2.83.0"
+      }
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+    },
+    "form-data": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
+      "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+      "requires": {
+        "asynckit": "0.4.0",
+        "combined-stream": "1.0.6",
+        "mime-types": "2.1.18"
+      }
+    },
+    "getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "requires": {
+        "assert-plus": "1.0.0"
+      }
+    },
+    "har-schema": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+    },
+    "har-validator": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
+      "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+      "requires": {
+        "ajv": "5.5.2",
+        "har-schema": "2.0.0"
+      }
+    },
+    "hawk": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
+      "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
+      "requires": {
+        "boom": "4.3.1",
+        "cryptiles": "3.1.2",
+        "hoek": "4.2.1",
+        "sntp": "2.1.0"
+      }
+    },
+    "hoek": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
+      "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
+    },
+    "http-signature": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "requires": {
+        "assert-plus": "1.0.0",
+        "jsprim": "1.4.1",
+        "sshpk": "1.13.1"
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+    },
+    "is_js": {
+      "version": "0.7.6",
+      "resolved": "https://registry.npmjs.org/is_js/-/is_js-0.7.6.tgz",
+      "integrity": "sha1-XUGq4K61gnqbjuFcqsSnCgWBETo="
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+    },
+    "jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "optional": true
+    },
+    "json-schema": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+    },
+    "json-schema-traverse": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+    },
+    "json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+    },
+    "jsprim": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "requires": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.2.3",
+        "verror": "1.10.0"
+      }
+    },
+    "mime-db": {
+      "version": "1.33.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+      "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
+    },
+    "mime-types": {
+      "version": "2.1.18",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+      "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+      "requires": {
+        "mime-db": "1.33.0"
+      }
+    },
+    "oauth-sign": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+      "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+    },
+    "punycode": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+    },
+    "qs": {
+      "version": "6.5.1",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
+    },
+    "request": {
+      "version": "2.83.0",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
+      "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
+      "requires": {
+        "aws-sign2": "0.7.0",
+        "aws4": "1.6.0",
+        "caseless": "0.12.0",
+        "combined-stream": "1.0.6",
+        "extend": "3.0.1",
+        "forever-agent": "0.6.1",
+        "form-data": "2.3.2",
+        "har-validator": "5.0.3",
+        "hawk": "6.0.2",
+        "http-signature": "1.2.0",
+        "is-typedarray": "1.0.0",
+        "isstream": "0.1.2",
+        "json-stringify-safe": "5.0.1",
+        "mime-types": "2.1.18",
+        "oauth-sign": "0.8.2",
+        "performance-now": "2.1.0",
+        "qs": "6.5.1",
+        "safe-buffer": "5.1.1",
+        "stringstream": "0.0.5",
+        "tough-cookie": "2.3.3",
+        "tunnel-agent": "0.6.0",
+        "uuid": "3.2.1"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+    },
+    "sntp": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
+      "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
+      "requires": {
+        "hoek": "4.2.1"
+      }
+    },
+    "sshpk": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
+      "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
+      "requires": {
+        "asn1": "0.2.3",
+        "assert-plus": "1.0.0",
+        "bcrypt-pbkdf": "1.0.1",
+        "dashdash": "1.14.1",
+        "ecc-jsbn": "0.1.1",
+        "getpass": "0.1.7",
+        "jsbn": "0.1.1",
+        "tweetnacl": "0.14.5"
+      }
+    },
+    "stringstream": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+      "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
+    },
+    "tough-cookie": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
+      "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
+      "requires": {
+        "punycode": "1.4.1"
+      }
+    },
+    "tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "requires": {
+        "safe-buffer": "5.1.1"
+      }
+    },
+    "tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "optional": true
+    },
+    "uuid": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
+      "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
+    },
+    "verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "requires": {
+        "assert-plus": "1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "1.3.0"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..89c9c8b9
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+  "name": "ecommercespa",
+  "version": "1.0.0",
+  "description": "",
+  "main": "assets/js/json_store.js",
+  "dependencies": {
+    "fi-khipu": "^0.1.0"
+  },
+  "devDependencies": {},
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/mthompsonc/eCommerceSpa.git"
+  },
+  "author": "Paulina Baeza, Camila Cornejos, María Paz Thompson y Sabrina Villalobos",
+  "license": "ISC",
+  "bugs": {
+    "url": "https://github.com/mthompsonc/eCommerceSpa/issues"
+  },
+  "homepage": "https://github.com/mthompsonc/eCommerceSpa#readme"
+}
diff --git a/stripe-php/charge.php b/stripe-php/charge.php
new file mode 100644
index 00000000..0ee24978
--- /dev/null
+++ b/stripe-php/charge.php
@@ -0,0 +1,19 @@
+<?php
+  require_once('./config.php');
+
+  $token  = $_POST['stripeToken'];
+  $email  = $_POST['stripeEmail'];
+
+  $customer = \Stripe\Customer::create(array(
+      'email' => $email,
+      'source'  => $token
+  ));
+
+  $charge = \Stripe\Charge::create(array(
+      'customer' => $customer->id,
+      'amount'   => 5000,
+      'currency' => 'usd'
+  ));
+
+  echo '<h1>Successfully charged $50.00!</h1>';
+?>
\ No newline at end of file
diff --git a/stripe-php/composer.json b/stripe-php/composer.json
new file mode 100644
index 00000000..c13195eb
--- /dev/null
+++ b/stripe-php/composer.json
@@ -0,0 +1,5 @@
+{
+    "require": {
+        "stripe/stripe-php": "^6.3"
+    }
+}
diff --git a/stripe-php/composer.lock b/stripe-php/composer.lock
new file mode 100644
index 00000000..556fc05f
--- /dev/null
+++ b/stripe-php/composer.lock
@@ -0,0 +1,116 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "be68cab3ad7365b9c6dbed9f4e3b94d8",
+    "packages": [
+        {
+            "name": "monolog/monolog",
+            "version": "1.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/monolog.git",
+                "reference": "b704c49a3051536f67f2d39f13568f74615b9922"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b704c49a3051536f67f2d39f13568f74615b9922",
+                "reference": "b704c49a3051536f67f2d39f13568f74615b9922",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Monolog": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Logging for PHP 5.3",
+            "homepage": "http://github.com/Seldaek/monolog",
+            "keywords": [
+                "log",
+                "logging"
+            ],
+            "time": "2011-10-24T09:39:02+00:00"
+        },
+        {
+            "name": "stripe/stripe-php",
+            "version": "v6.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/stripe/stripe-php.git",
+                "reference": "8322135c4cfbbf46b4cd66dfc8d9ad63241730b3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/8322135c4cfbbf46b4cd66dfc8d9ad63241730b3",
+                "reference": "8322135c4cfbbf46b4cd66dfc8d9ad63241730b3",
+                "shasum": ""
+            },
+            "require": {
+                "ext-curl": "*",
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0",
+                "satooshi/php-coveralls": "~0.6.1",
+                "squizlabs/php_codesniffer": "~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Stripe\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Stripe and contributors",
+                    "homepage": "https://github.com/stripe/stripe-php/contributors"
+                }
+            ],
+            "description": "Stripe PHP Library",
+            "homepage": "https://stripe.com/",
+            "keywords": [
+                "api",
+                "payment processing",
+                "stripe"
+            ],
+            "time": "2018-02-23T17:34:01+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}
diff --git a/stripe-php/config.php b/stripe-php/config.php
new file mode 100644
index 00000000..d38eab1e
--- /dev/null
+++ b/stripe-php/config.php
@@ -0,0 +1,10 @@
+<?php
+require_once('vendor/autoload.php');
+
+$stripe = array(
+  "secret_key"      => "sk_test_BQokikJOvBiI2HlWgH4olfQ2",
+  "publishable_key" => "pk_test_6pRNASCoBOKtIshFeQd4XMUh"
+);
+
+\Stripe\Stripe::setApiKey('sk_test_BQokikJOvBiI2HlWgH4olfQ2');
+?>
\ No newline at end of file
diff --git a/stripe-php/index.php b/stripe-php/index.php
new file mode 100644
index 00000000..1abe9e5a
--- /dev/null
+++ b/stripe-php/index.php
@@ -0,0 +1,9 @@
+<?php require_once('./config.php'); ?>
+
+<form action="charge.php" method="post">
+  <script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
+          data-key="<?php echo $stripe['publishable_key']; ?>"
+          data-description="Access for a year"
+          data-amount="5000"
+          data-locale="auto"></script>
+</form>
\ No newline at end of file
diff --git a/stripe-php/vendor/autoload.php b/stripe-php/vendor/autoload.php
new file mode 100644
index 00000000..b5d574ff
--- /dev/null
+++ b/stripe-php/vendor/autoload.php
@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit8e2e40f60cd8a6bb9903fd24739963de::getLoader();
diff --git a/stripe-php/vendor/composer/ClassLoader.php b/stripe-php/vendor/composer/ClassLoader.php
new file mode 100644
index 00000000..dc02dfb1
--- /dev/null
+++ b/stripe-php/vendor/composer/ClassLoader.php
@@ -0,0 +1,445 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    http://www.php-fig.org/psr/psr-0/
+ * @see    http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+    private $classMapAuthoritative = false;
+    private $missingClasses = array();
+    private $apcuPrefix;
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', $this->prefixesPsr0);
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-4 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath.'\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}
diff --git a/stripe-php/vendor/composer/LICENSE b/stripe-php/vendor/composer/LICENSE
new file mode 100644
index 00000000..f27399a0
--- /dev/null
+++ b/stripe-php/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/stripe-php/vendor/composer/autoload_classmap.php b/stripe-php/vendor/composer/autoload_classmap.php
new file mode 100644
index 00000000..7a91153b
--- /dev/null
+++ b/stripe-php/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/stripe-php/vendor/composer/autoload_namespaces.php b/stripe-php/vendor/composer/autoload_namespaces.php
new file mode 100644
index 00000000..0067c55f
--- /dev/null
+++ b/stripe-php/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Monolog' => array($vendorDir . '/monolog/monolog/src'),
+);
diff --git a/stripe-php/vendor/composer/autoload_psr4.php b/stripe-php/vendor/composer/autoload_psr4.php
new file mode 100644
index 00000000..b7971032
--- /dev/null
+++ b/stripe-php/vendor/composer/autoload_psr4.php
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Stripe\\' => array($vendorDir . '/stripe/stripe-php/lib'),
+);
diff --git a/stripe-php/vendor/composer/autoload_real.php b/stripe-php/vendor/composer/autoload_real.php
new file mode 100644
index 00000000..f7d0b316
--- /dev/null
+++ b/stripe-php/vendor/composer/autoload_real.php
@@ -0,0 +1,52 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit8e2e40f60cd8a6bb9903fd24739963de
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        spl_autoload_register(array('ComposerAutoloaderInit8e2e40f60cd8a6bb9903fd24739963de', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+        spl_autoload_unregister(array('ComposerAutoloaderInit8e2e40f60cd8a6bb9903fd24739963de', 'loadClassLoader'));
+
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+        if ($useStaticLoader) {
+            require_once __DIR__ . '/autoload_static.php';
+
+            call_user_func(\Composer\Autoload\ComposerStaticInit8e2e40f60cd8a6bb9903fd24739963de::getInitializer($loader));
+        } else {
+            $map = require __DIR__ . '/autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                $loader->set($namespace, $path);
+            }
+
+            $map = require __DIR__ . '/autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                $loader->setPsr4($namespace, $path);
+            }
+
+            $classMap = require __DIR__ . '/autoload_classmap.php';
+            if ($classMap) {
+                $loader->addClassMap($classMap);
+            }
+        }
+
+        $loader->register(true);
+
+        return $loader;
+    }
+}
diff --git a/stripe-php/vendor/composer/autoload_static.php b/stripe-php/vendor/composer/autoload_static.php
new file mode 100644
index 00000000..b66c00e7
--- /dev/null
+++ b/stripe-php/vendor/composer/autoload_static.php
@@ -0,0 +1,42 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit8e2e40f60cd8a6bb9903fd24739963de
+{
+    public static $prefixLengthsPsr4 = array (
+        'S' => 
+        array (
+            'Stripe\\' => 7,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'Stripe\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/stripe/stripe-php/lib',
+        ),
+    );
+
+    public static $prefixesPsr0 = array (
+        'M' => 
+        array (
+            'Monolog' => 
+            array (
+                0 => __DIR__ . '/..' . '/monolog/monolog/src',
+            ),
+        ),
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInit8e2e40f60cd8a6bb9903fd24739963de::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit8e2e40f60cd8a6bb9903fd24739963de::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInit8e2e40f60cd8a6bb9903fd24739963de::$prefixesPsr0;
+
+        }, null, ClassLoader::class);
+    }
+}
diff --git a/stripe-php/vendor/composer/installed.json b/stripe-php/vendor/composer/installed.json
new file mode 100644
index 00000000..45b0bc89
--- /dev/null
+++ b/stripe-php/vendor/composer/installed.json
@@ -0,0 +1,104 @@
+[
+    {
+        "name": "monolog/monolog",
+        "version": "1.0.2",
+        "version_normalized": "1.0.2.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/Seldaek/monolog.git",
+            "reference": "b704c49a3051536f67f2d39f13568f74615b9922"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b704c49a3051536f67f2d39f13568f74615b9922",
+            "reference": "b704c49a3051536f67f2d39f13568f74615b9922",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2011-10-24T09:39:02+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-0": {
+                "Monolog": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Jordi Boggiano",
+                "email": "j.boggiano@seld.be",
+                "homepage": "http://seld.be",
+                "role": "Developer"
+            }
+        ],
+        "description": "Logging for PHP 5.3",
+        "homepage": "http://github.com/Seldaek/monolog",
+        "keywords": [
+            "log",
+            "logging"
+        ]
+    },
+    {
+        "name": "stripe/stripe-php",
+        "version": "v6.3.0",
+        "version_normalized": "6.3.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/stripe/stripe-php.git",
+            "reference": "8322135c4cfbbf46b4cd66dfc8d9ad63241730b3"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/stripe/stripe-php/zipball/8322135c4cfbbf46b4cd66dfc8d9ad63241730b3",
+            "reference": "8322135c4cfbbf46b4cd66dfc8d9ad63241730b3",
+            "shasum": ""
+        },
+        "require": {
+            "ext-curl": "*",
+            "ext-json": "*",
+            "ext-mbstring": "*",
+            "php": ">=5.4.0"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "~4.0",
+            "satooshi/php-coveralls": "~0.6.1",
+            "squizlabs/php_codesniffer": "~2.0"
+        },
+        "time": "2018-02-23T17:34:01+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.0-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Stripe\\": "lib/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Stripe and contributors",
+                "homepage": "https://github.com/stripe/stripe-php/contributors"
+            }
+        ],
+        "description": "Stripe PHP Library",
+        "homepage": "https://stripe.com/",
+        "keywords": [
+            "api",
+            "payment processing",
+            "stripe"
+        ]
+    }
+]
diff --git a/stripe-php/vendor/stripe/stripe-php/.coveralls.yml b/stripe-php/vendor/stripe/stripe-php/.coveralls.yml
new file mode 100644
index 00000000..a343c9a7
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/.coveralls.yml
@@ -0,0 +1,4 @@
+service_name: travis-ci
+src_dir: .
+coverage_clover: clover.xml
+json_path: coveralls-upload.json
diff --git a/stripe-php/vendor/stripe/stripe-php/.github/ISSUE_TEMPLATE.md b/stripe-php/vendor/stripe/stripe-php/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..c200db60
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,5 @@
+Please only file issues here that you believe represent actual bugs or feature requests for the Stripe PHP library.
+
+If you're having general trouble with your Stripe integration, please reach out to support using the form at https://support.stripe.com/ (preferred) or via email to support@stripe.com.
+
+If you are reporting a bug, please include your PHP version and the version of the Stripe PHP library you're using, as well as any other details that may be helpful in reproducing the problem.
diff --git a/stripe-php/vendor/stripe/stripe-php/.gitignore b/stripe-php/vendor/stripe/stripe-php/.gitignore
new file mode 100644
index 00000000..17b94183
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/.gitignore
@@ -0,0 +1,17 @@
+# Mac OS X dumps these all over the place.
+.DS_Store
+
+# Ignore the SimpleTest library if it is installed to /test/.
+/test/simpletest/
+
+# Ignore the /vendor/ directory for people using composer
+/vendor/
+
+# If the vendor directory isn't being commited the composer.lock file should also be ignored
+composer.lock
+
+# Ignore PHPUnit coverage file
+clover.xml
+
+# Ignore IDE's configuration files
+.idea
diff --git a/stripe-php/vendor/stripe/stripe-php/.travis.yml b/stripe-php/vendor/stripe/stripe-php/.travis.yml
new file mode 100644
index 00000000..d575ed51
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/.travis.yml
@@ -0,0 +1,40 @@
+sudo: false
+
+language: php
+
+php:
+  - 5.4
+  - 5.5
+  - 5.6
+  - 7.0
+  - 7.1
+  - 7.2
+  - hhvm
+
+env:
+  global:
+    - STRIPE_MOCK_VERSION=0.8.0
+  matrix:
+    - AUTOLOAD=1
+    - AUTOLOAD=0
+
+cache:
+  directories:
+    - $HOME/.composer/cache/files
+    - stripe-mock
+
+before_install:
+  # Unpack and start stripe-mock so that the test suite can talk to it
+  - |
+    if [ ! -d "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}" ]; then
+      mkdir -p stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/
+      curl -L "https://github.com/stripe/stripe-mock/releases/download/v${STRIPE_MOCK_VERSION}/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -o "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz"
+      tar -zxf "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -C "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/"
+    fi
+  - |
+    stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/stripe-mock > /dev/null &
+    STRIPE_MOCK_PID=$!
+
+script: ./build.php ${AUTOLOAD}
+
+after_script: ./vendor/bin/coveralls -v
diff --git a/stripe-php/vendor/stripe/stripe-php/CHANGELOG.md b/stripe-php/vendor/stripe/stripe-php/CHANGELOG.md
new file mode 100644
index 00000000..8479c679
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/CHANGELOG.md
@@ -0,0 +1,437 @@
+# Changelog
+
+## 6.3.0 - 2018-02-23
+* [#450](https://github.com/stripe/stripe-php/pull/450) Add support for `code` attribute on all Stripe exceptions
+
+## 6.2.0 - 2018-02-21
+* [#440](https://github.com/stripe/stripe-php/pull/440) Add support for topups
+* [#442](https://github.com/stripe/stripe-php/pull/442) Fix PHPDoc for `\Stripe\Error\SignatureVerification`
+
+## 6.1.0 - 2018-02-12
+* [#435](https://github.com/stripe/stripe-php/pull/435) Fix header persistence on `Collection` objects
+* [#436](https://github.com/stripe/stripe-php/pull/436) Introduce new `Idempotency` error class
+
+## 6.0.0 - 2018-02-07
+Major version release. List of backwards incompatible changes to watch out for:
++ The minimum PHP version is now 5.4.0. If you're using PHP 5.3 or older, consider upgrading to a more recent version.
+* `\Stripe\AttachedObject` no longer exists. Attributes that used to be instances of `\Stripe\AttachedObject` (such as `metadata`) are now instances of `\Stripe\StripeObject`.
++ Attributes that used to be PHP arrays (such as `legal_entity->additional_owners` on `\Stripe\Account` instances) are now instances of `\Stripe\StripeObject`, except when they are empty. `\Stripe\StripeObject` has array semantics so this should not be an issue unless you are actively checking types.
+* `\Stripe\Collection` now derives from `\Stripe\StripeObject` rather than from `\Stripe\ApiResource`.
+
+Pull requests included in this release:
+* [#410](https://github.com/stripe/stripe-php/pull/410) Drop support for PHP 5.3
+* [#411](https://github.com/stripe/stripe-php/pull/411) Use traits for common API operations
+* [#414](https://github.com/stripe/stripe-php/pull/414) Use short array syntax
+* [#404](https://github.com/stripe/stripe-php/pull/404) Fix serialization logic
+* [#417](https://github.com/stripe/stripe-php/pull/417) Remove `ExternalAccount` class
+* [#418](https://github.com/stripe/stripe-php/pull/418) Increase test coverage
+* [#421](https://github.com/stripe/stripe-php/pull/421) Update CA bundle and add script for future updates
+* [#422](https://github.com/stripe/stripe-php/pull/422) Use vendored CA bundle for all requests
+* [#428](https://github.com/stripe/stripe-php/pull/428) Support for automatic request retries
+
+## 5.9.2 - 2018-02-07
+* [#431](https://github.com/stripe/stripe-php/pull/431) Update PHPDoc @property tags for latest API version
+
+## 5.9.1 - 2018-02-06
+* [#427](https://github.com/stripe/stripe-php/pull/427) Add and update PHPDoc @property tags on all API resources
+
+## 5.9.0 - 2018-01-17
+* [#421](https://github.com/stripe/stripe-php/pull/421) Updated bundled CA certificates
+* [#423](https://github.com/stripe/stripe-php/pull/423) Escape unsanitized input in OAuth example
+
+## 5.8.0 - 2017-12-20
+* [#403](https://github.com/stripe/stripe-php/pull/403) Add `__debugInfo()` magic method to `StripeObject`
+
+## 5.7.0 - 2017-11-28
+* [#390](https://github.com/stripe/stripe-php/pull/390) Remove some unsupported API methods
+* [#391](https://github.com/stripe/stripe-php/pull/391) Alphabetize the list of API resources in `Util::convertToStripeObject()` and add missing resources
+* [#393](https://github.com/stripe/stripe-php/pull/393) Fix expiry date update for card sources
+
+## 5.6.0 - 2017-10-31
+* [#386](https://github.com/stripe/stripe-php/pull/386) Support for exchange rates APIs
+
+## 5.5.1 - 2017-10-30
+* [#387](https://github.com/stripe/stripe-php/pull/387) Allow `personal_address_kana` and `personal_address_kanji` to be updated on an account
+
+## 5.5.0 - 2017-10-27
+* [#385](https://github.com/stripe/stripe-php/pull/385) Support for listing source transactions
+
+## 5.4.0 - 2017-10-24
+* [#383](https://github.com/stripe/stripe-php/pull/383) Add static methods to manipulate resources from parent
+    * `Account` gains methods for external accounts and login links (e.g. `createExternalAccount`, `createLoginLink`)
+    * `ApplicationFee` gains methods for refunds
+    * `Customer` gains methods for sources
+    * `Transfer` gains methods for reversals
+
+## 5.3.0 - 2017-10-11
+* [#378](https://github.com/stripe/stripe-php/pull/378) Rename source `delete` to `detach` (and deprecate the former)
+
+## 5.2.3 - 2017-09-27
+* Add PHPDoc for `Card`
+
+## 5.2.2 - 2017-09-20
+* Fix deserialization mapping of `FileUpload` objects
+
+## 5.2.1 - 2017-09-14
+* Serialized `shipping` nested attribute
+
+## 5.2.0 - 2017-08-29
+* Add support for `InvalidClient` OAuth error
+
+## 5.1.3 - 2017-08-14
+* Allow `address_kana` and `address_kanji` to be updated for custom accounts
+
+## 5.1.2 - 2017-08-01
+* Fix documented return type of `autoPagingIterator()` (was missing namespace)
+
+## 5.1.1 - 2017-07-03
+* Fix order returns to use the right URL `/v1/order_returns`
+
+## 5.1.0 - 2017-06-30
+* Add support for OAuth
+
+## 5.0.0 - 2017-06-27
+* `pay` on invoice now takes params as well as opts
+
+## 4.13.0 - 2017-06-19
+* Add support for ephemeral keys
+
+## 4.12.0 - 2017-06-05
+* Clients can implement `getUserAgentInfo()` to add additional user agent information
+
+## 4.11.0 - 2017-06-05
+* Implement `Countable` for `AttachedObject` (`metadata` and `additional_owners`)
+
+## 4.10.0 - 2017-05-25
+* Add support for login links
+
+## 4.9.1 - 2017-05-10
+* Fix docs to include arrays on `$id` parameter for retrieve methods
+
+## 4.9.0 - 2017-04-28
+* Support for checking webhook signatures
+
+## 4.8.1 - 2017-04-24
+* Allow nested field `payout_schedule` to be updated
+
+## 4.8.0 - 2017-04-20
+* Add `\Stripe\Stripe::setLogger()` to support an external PSR-3 compatible logger
+
+## 4.7.0 - 2017-04-10
+* Add support for payouts and recipient transfers
+
+## 4.6.0 - 2017-04-06
+* Please see 4.7.0 instead (no-op release)	
+
+## 4.5.1 - 2017-03-22
+* Remove hard dependency on cURL
+
+## 4.5.0 - 2017-03-20
+* Support for detaching sources from customers
+
+## 4.4.2 - 2017-02-27
+* Correct handling of `owner` parameter when updating sources
+
+## 4.4.1 - 2017-02-24
+* Correct the error check on a bad JSON decoding
+
+## 4.4.0 - 2017-01-18
+* Add support for updating sources
+
+## 4.3.0 - 2016-11-30
+* Add support for verifying sources
+
+## 4.2.0 - 2016-11-21
+* Add retrieve method for 3-D Secure resources
+
+## 4.1.1 - 2016-10-21
+* Add docblock with model properties for `Plan`
+
+## 4.1.0 - 2016-10-18
+* Support for 403 status codes (permission denied)
+
+## 4.0.1 - 2016-10-17
+* Fix transfer reversal materialization
+* Fixes for some property definitions in docblocks
+
+## 4.0.0 - 2016-09-28
+* Support for subscription items
+* Drop attempt to force TLS 1.2: please note that this could be breaking if you're using old OS distributions or packages and upgraded recently (so please make sure to test your integration!)
+
+## 3.23.0 - 2016-09-15
+* Add support for Apple Pay domains
+
+## 3.22.0 - 2016-09-13
+* Add `Stripe::setAppInfo` to allow plugins to register user agent information
+
+## 3.21.0 - 2016-08-25
+* Add `Source` model for generic payment sources
+
+## 3.20.0 - 2016-08-08
+* Add `getDeclineCode` to card errors
+
+## 3.19.0 - 2016-07-29
+* Opt requests directly into TLS 1.2 where OpenSSL >= 1.0.1 (see #277 for context)
+
+## 3.18.0 - 2016-07-28
+* Add new `STATUS_` constants for subscriptions
+
+## 3.17.1 - 2016-07-28
+* Fix auto-paging iterator so that it plays nicely with `iterator_to_array`
+
+## 3.17.0 - 2016-07-14
+* Add field annotations to model classes for better editor hinting
+
+## 3.16.0 - 2016-07-12
+* Add `ThreeDSecure` model for 3-D secure payments
+
+## 3.15.0 - 2016-06-29
+* Add static `update` method to all resources that can be changed.
+
+## 3.14.3 - 2016-06-20
+* Make sure that cURL never sends `Expects: 100-continue`, even on large request bodies
+
+## 3.14.2 - 2016-06-03
+* Add `inventory` under `SKU` to list of keys that have nested data and can be updated
+
+## 3.14.1 - 2016-05-27
+* Fix some inconsistencies in PHPDoc
+
+## 3.14.0 - 2016-05-25
+* Add support for returning Relay orders
+
+## 3.13.0 - 2016-05-04
+* Add `list`, `create`, `update`, `retrieve`, and `delete` methods to the Subscription class
+
+## 3.12.1 - 2016-04-07
+* Additional check on value arrays for some extra safety
+
+## 3.12.0 - 2016-03-31
+* Fix bug `refreshFrom` on `StripeObject` would not take an `$opts` array
+* Fix bug where `$opts` not passed to parent `save` method in `Account`
+* Fix bug where non-existent variable was referenced in `reverse` in `Transfer`
+* Update CA cert bundle for compatibility with OpenSSL versions below 1.0.1
+
+## 3.11.0 - 2016-03-22
+* Allow `CurlClient` to be initialized with default `CURLOPT_*` options
+
+## 3.10.1 - 2016-03-22
+* Fix bug where request params and options were ignored in `ApplicationFee`'s `refund.`
+
+## 3.10.0 - 2016-03-15
+* Add `reject` on `Account` to support the new API feature
+
+## 3.9.2 - 2016-03-04
+* Fix error when an object's metadata is set more than once
+
+## 3.9.1 - 2016-02-24
+* Fix encoding behavior of nested arrays for requests (see #227)
+
+## 3.9.0 - 2016-02-09
+* Add automatic pagination mechanism with `autoPagingIterator()`
+* Allow global account ID to be set with `Stripe::setAccountId()`
+
+## 3.8.0 - 2016-02-08
+* Add `CountrySpec` model for looking up country payment information
+
+## 3.7.1 - 2016-02-01
+* Update bundled CA certs
+
+## 3.7.0 - 2016-01-27
+* Support deleting Relay products and SKUs
+
+## 3.6.0 - 2016-01-05
+* Allow configuration of HTTP client timeouts
+
+## 3.5.0 - 2015-12-01
+* Add a verification routine for external accounts
+
+## 3.4.0 - 2015-09-14
+* Products, SKUs, and Orders -- https://stripe.com/relay
+
+## 3.3.0 - 2015-09-11
+* Add support for 429 Rate Limit response
+
+## 3.2.0 - 2015-08-17
+* Add refund listing and retrieval without an associated charge
+
+## 3.1.0 - 2015-08-03
+* Add dispute listing and retrieval
+* Add support for manage account deletion
+
+## 3.0.0 - 2015-07-28
+* Rename `\Stripe\Object` to `\Stripe\StripeObject` (PHP 7 compatibility)
+* Rename `getCode` and `getParam` in exceptions to `getStripeCode` and `getStripeParam`
+* Add support for calling `json_encode` on Stripe objects in PHP 5.4+
+* Start supporting/testing PHP 7
+
+## 2.3.0 - 2015-07-06
+* Add request ID to all Stripe exceptions
+
+## 2.2.0 - 2015-06-01
+* Add support for Alipay accounts as sources
+* Add support for bank accounts as sources (private beta)
+* Add support for bank accounts and cards as external_accounts on Account objects
+
+## 2.1.4 - 2015-05-13
+* Fix CA certificate file path (thanks @lphilps & @matthewarkin)
+
+## 2.1.3 - 2015-05-12
+* Fix to account updating to permit `tos_acceptance` and `personal_address` to be set properly
+* Fix to Transfer reversal creation (thanks @neatness!)
+* Network requests are now done through a swappable class for easier mocking
+
+## 2.1.2 - 2015-04-10
+* Remove SSL cert revokation checking (all pre-Heartbleed certs have expired)
+* Bug fixes to account updating
+
+## 2.1.1 - 2015-02-27
+* Support transfer reversals
+
+## 2.1.0 - 2015-02-19
+* Support new API version (2015-02-18)
+* Added Bitcoin Receiever update and delete actions
+* Edited tests to prefer "source" over "card" as per new API version
+
+## 2.0.1 - 2015-02-16
+* Fix to fetching endpoints that use a non-default baseUrl (`FileUpload`)
+
+## 2.0.0 - 2015-02-14
+* Bumped minimum version to 5.3.3
+* Switched to Stripe namespace instead of Stripe_ class name prefiexes (thanks @chadicus!)
+* Switched tests to PHPUnit (thanks @chadicus!)
+* Switched style guide to PSR2 (thanks @chadicus!)
+* Added $opts hash to the end of most methods: this permits passing 'idempotency_key', 'stripe_account', or 'stripe_version'. The last 2 will persist across multiple object loads.
+* Added support for retrieving Account by ID
+
+## 1.18.0 - 2015-01-21
+* Support making bitcoin charges through BitcoinReceiver source object
+
+## 1.17.5 - 2014-12-23
+* Adding support for creating file uploads.
+
+## 1.17.4 - 2014-12-15
+* Saving objects fetched with a custom key now works (thanks @JustinHook & @jpasilan)
+* Added methods for reporting charges as safe or fraudulent and for specifying the reason for refunds
+
+## 1.17.3 - 2014-11-06
+* Better handling of HHVM support for SSL certificate blacklist checking.
+
+## 1.17.2 - 2014-09-23
+* Coupons now are backed by a `Stripe_Coupon` instead of `Stripe_Object`, and support updating metadata
+* Running operations (`create`, `retrieve`, `all`) on upcoming invoice items now works
+
+## 1.17.1 - 2014-07-31
+* Requests now send Content-Type header
+
+## 1.17.0 - 2014-07-29
+* Application Fee refunds now a list instead of array
+* HHVM now works
+* Small bug fixes (thanks @bencromwell & @fastest963)
+* `__toString` now returns the name of the object in addition to its JSON representation
+
+## 1.16.0 - 2014-06-17
+* Add metadata for refunds and disputes
+
+## 1.15.0 - 2014-05-28
+* Support canceling transfers
+
+## 1.14.1 - 2014-05-21
+* Support cards for recipients.
+
+## 1.13.1 - 2014-05-15
+* Fix bug in account resource where `id` wasn't in the result
+
+## 1.13.0 - 2014-04-10
+* Add support for certificate blacklisting
+* Update ca bundle
+* Drop support for HHVM (Temporarily)
+
+## 1.12.0 - 2014-04-01
+* Add Stripe_RateLimitError for catching rate limit errors.
+* Update to Zend coding style (thanks,  @jpiasetz)
+
+## 1.11.0 - 2014-01-29
+* Add support for multiple subscriptions per customer
+
+## 1.10.1 - 2013-12-02
+* Add new ApplicationFee
+
+## 1.9.1 - 2013-11-08
+* Fix a bug where a null nestable object causes warnings to fire.
+
+## 1.9.0 - 2013-10-16
+* Add support for metadata API.
+
+## 1.8.4 - 2013-09-18
+* Add support for closing disputes.
+
+## 1.8.3 - 2013-08-13
+* Add new Balance and BalanceTransaction
+
+## 1.8.2 - 2013-08-12
+* Add support for unsetting attributes by updating to NULL. Setting properties to a blank string is now an error.
+
+## 1.8.1 - 2013-07-12
+* Add support for multiple cards API (Stripe API version 2013-07-12: https://stripe.com/docs/upgrades#2013-07-05)
+
+## 1.8.0 - 2013-04-11
+* Allow Transfers to be creatable
+* Add new Recipient resource
+
+## 1.7.15 - 2013-02-21
+* Add 'id' to the list of permanent object attributes
+
+## 1.7.14 - 2013-02-20
+
+* Don't re-encode strings that are already encoded in UTF-8. If you were previously using plan or coupon objects with UTF-8 IDs, they may have been treated as ISO-8859-1 (Latin-1) and encoded to UTF-8 a 2nd time. You may now need to pass the IDs to utf8_encode before passing them to Stripe_Plan::retrieve or Stripe_Coupon::retrieve.
+* Ensure that all input is encoded in UTF-8 before submitting it to Stripe's servers. (github issue #27)
+
+## 1.7.13 - 2013-02-01
+* Add support for passing options when retrieving Stripe objects e.g., Stripe_Charge::retrieve(array("id"=>"foo", "expand" => array("customer"))); Stripe_Charge::retrieve("foo") will continue to work
+
+## 1.7.12 - 2013-01-15
+* Add support for setting a Stripe API version override
+
+## 1.7.11 - 2012-12-30
+* Version bump to cleanup constants and such (fix issue #26)
+
+## 1.7.10 - 2012-11-08
+* Add support for updating charge disputes.
+* Fix bug preventing retrieval of null attributes
+
+## 1.7.9 - 2012-11-08
+* Fix usage under autoloaders such as the one generated by composer (fix issue #22)
+
+## 1.7.8 - 2012-10-30
+* Add support for creating invoices.
+* Add support for new invoice lines return format
+* Add support for new list objects
+
+## 1.7.7 - 2012-09-14
+* Get all of the various version numbers in the repo in sync (no other changes)
+
+## 1.7.6 - 2012-08-31
+* Add update and pay methods to Invoice resource
+
+## 1.7.5 - 2012-08-23
+* Change internal function names so that Stripe_SingletonApiRequest is E_STRICT-clean (github issue #16)
+
+## 1.7.4 - 2012-08-21
+* Bugfix so that Stripe objects (e.g. Customer, Charge objects) used in API calls are transparently converted to their object IDs
+
+## 1.7.3 - 2012-08-15
+* Add new Account resource
+
+## 1.7.2 - 2012-06-26
+* Make clearer that you should be including lib/Stripe.php, not test/Stripe.php (github issue #14)
+
+## 1.7.1 - 2012-05-24
+* Add missing argument to Stripe_InvalidRequestError constructor in Stripe_ApiResource::instanceUrl. Fixes a warning when Stripe_ApiResource::instanceUrl is called on a resource with no ID (fix issue #12)
+
+## 1.7.0 - 2012-05-17
+* Support Composer and Packagist (github issue #9)
+* Add new deleteDiscount method to Stripe_Customer
+* Add new Transfer resource
+* Switch from using HTTP Basic auth to Bearer auth. (Note: Stripe will support Basic auth for the indefinite future, but recommends Bearer auth when possible going forward)
+* Numerous test suite improvements
diff --git a/stripe-php/vendor/stripe/stripe-php/LICENSE b/stripe-php/vendor/stripe/stripe-php/LICENSE
new file mode 100644
index 00000000..a21757e4
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2010-2015 Stripe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/stripe-php/vendor/stripe/stripe-php/README.md b/stripe-php/vendor/stripe/stripe-php/README.md
new file mode 100644
index 00000000..fa492c45
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/README.md
@@ -0,0 +1,233 @@
+# Stripe PHP bindings
+
+[![Build Status](https://travis-ci.org/stripe/stripe-php.svg?branch=master)](https://travis-ci.org/stripe/stripe-php)
+[![Latest Stable Version](https://poser.pugx.org/stripe/stripe-php/v/stable.svg)](https://packagist.org/packages/stripe/stripe-php)
+[![Total Downloads](https://poser.pugx.org/stripe/stripe-php/downloads.svg)](https://packagist.org/packages/stripe/stripe-php)
+[![License](https://poser.pugx.org/stripe/stripe-php/license.svg)](https://packagist.org/packages/stripe/stripe-php)
+[![Code Coverage](https://coveralls.io/repos/stripe/stripe-php/badge.svg?branch=master)](https://coveralls.io/r/stripe/stripe-php?branch=master)
+
+You can sign up for a Stripe account at https://stripe.com.
+
+## Requirements
+
+PHP 5.4.0 and later.
+
+## Composer
+
+You can install the bindings via [Composer](http://getcomposer.org/). Run the following command:
+
+```bash
+composer require stripe/stripe-php
+```
+
+To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/01-basic-usage.md#autoloading):
+
+```php
+require_once('vendor/autoload.php');
+```
+
+## Manual Installation
+
+If you do not wish to use Composer, you can download the [latest release](https://github.com/stripe/stripe-php/releases). Then, to use the bindings, include the `init.php` file.
+
+```php
+require_once('/path/to/stripe-php/init.php');
+```
+
+## Dependencies
+
+The bindings require the following extensions in order to work properly:
+
+- [`curl`](https://secure.php.net/manual/en/book.curl.php), although you can use your own non-cURL client if you prefer
+- [`json`](https://secure.php.net/manual/en/book.json.php)
+- [`mbstring`](https://secure.php.net/manual/en/book.mbstring.php) (Multibyte String)
+
+If you use Composer, these dependencies should be handled automatically. If you install manually, you'll want to make sure that these extensions are available.
+
+## Getting Started
+
+Simple usage looks like:
+
+```php
+\Stripe\Stripe::setApiKey('sk_test_BQokikJOvBiI2HlWgH4olfQ2');
+$charge = \Stripe\Charge::create(['amount' => 2000, 'currency' => 'usd', 'source' => 'tok_189fqt2eZvKYlo2CTGBeg6Uq']);
+echo $charge;
+```
+
+## Documentation
+
+Please see https://stripe.com/docs/api for up-to-date documentation.
+
+## Legacy Version Support
+
+### PHP 5.3
+
+If you are using PHP 5.3, you can download v5.8.0 ([zip](https://github.com/stripe/stripe-php/archive/v5.8.0.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.8.0.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
+
+### PHP 5.2
+
+If you are using PHP 5.2, you can download v1.18.0 ([zip](https://github.com/stripe/stripe-php/archive/v1.18.0.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v1.18.0.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
+
+This legacy version may be included via `require_once("/path/to/stripe-php/lib/Stripe.php");`, and used like:
+
+```php
+Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249');
+$charge = Stripe_Charge::create(array('source' => 'tok_XXXXXXXX', 'amount' => 2000, 'currency' => 'usd'));
+echo $charge;
+```
+
+## Custom Request Timeouts
+
+*NOTE:* We do not recommend decreasing the timeout for non-read-only calls (e.g. charge creation), since even if you locally timeout, the request on Stripe's side can still complete. If you are decreasing timeouts on these calls, make sure to use [idempotency tokens](https://stripe.com/docs/api/php#idempotent_requests) to avoid executing the same transaction twice as a result of timeout retry logic.
+
+To modify request timeouts (connect or total, in seconds) you'll need to tell the API client to use a CurlClient other than its default. You'll set the timeouts in that CurlClient.
+
+```php
+// set up your tweaked Curl client
+$curl = new \Stripe\HttpClient\CurlClient();
+$curl->setTimeout(10); // default is \Stripe\HttpClient\CurlClient::DEFAULT_TIMEOUT
+$curl->setConnectTimeout(5); // default is \Stripe\HttpClient\CurlClient::DEFAULT_CONNECT_TIMEOUT
+
+echo $curl->getTimeout(); // 10
+echo $curl->getConnectTimeout(); // 5
+
+// tell Stripe to use the tweaked client
+\Stripe\ApiRequestor::setHttpClient($curl);
+
+// use the Stripe API client as you normally would
+```
+
+## Custom cURL Options (e.g. proxies)
+
+Need to set a proxy for your requests? Pass in the requisite `CURLOPT_*` array to the CurlClient constructor, using the same syntax as `curl_stopt_array()`. This will set the default cURL options for each HTTP request made by the SDK, though many more common options (e.g. timeouts; see above on how to set those) will be overridden by the client even if set here.
+
+```php
+// set up your tweaked Curl client
+$curl = new \Stripe\HttpClient\CurlClient([CURLOPT_PROXY => 'proxy.local:80']);
+// tell Stripe to use the tweaked client
+\Stripe\ApiRequestor::setHttpClient($curl);
+```
+
+Alternately, a callable can be passed to the CurlClient constructor that returns the above array based on request inputs. See `testDefaultOptions()` in `tests/CurlClientTest.php` for an example of this behavior. Note that the callable is called at the beginning of every API request, before the request is sent.
+
+### Configuring a Logger
+
+The library does minimal logging, but it can be configured
+with a [`PSR-3` compatible logger][psr3] so that messages
+end up there instead of `error_log`:
+
+```php
+\Stripe\Stripe::setLogger($logger);
+```
+
+### Accessing response data
+
+You can access the data from the last API response on any object via `getLastResponse()`.
+
+```php
+$charge = \Stripe\Charge::create(['amount' => 2000, 'currency' => 'usd', 'source' => 'tok_visa']);
+echo $charge->getLastResponse()->headers['Request-Id'];
+```
+
+### SSL / TLS compatibility issues
+
+Stripe's API now requires that [all connections use TLS 1.2](https://stripe.com/blog/upgrading-tls). Some systems (most notably some older CentOS and RHEL versions) are capable of using TLS 1.2 but will use TLS 1.0 or 1.1 by default. In this case, you'd get an `invalid_request_error` with the following error message: "Stripe no longer supports API requests made with TLS 1.0. Please initiate HTTPS connections with TLS 1.2 or later. You can learn more about this at [https://stripe.com/blog/upgrading-tls](https://stripe.com/blog/upgrading-tls).".
+
+The recommended course of action is to [upgrade your cURL and OpenSSL packages](https://support.stripe.com/questions/how-do-i-upgrade-my-stripe-integration-from-tls-1-0-to-tls-1-2#php) so that TLS 1.2 is used by default, but if that is not possible, you might be able to solve the issue by setting the `CURLOPT_SSLVERSION` option to either `CURL_SSLVERSION_TLSv1` or `CURL_SSLVERSION_TLSv1_2`:
+
+```php
+$curl = new \Stripe\HttpClient\CurlClient([CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1]);
+\Stripe\ApiRequestor::setHttpClient($curl);
+```
+
+### Per-request Configuration
+
+For apps that need to use multiple keys during the lifetime of a process, like
+one that uses [Stripe Connect][connect], it's also possible to set a
+per-request key and/or account:
+
+```php
+\Stripe\Charge::all([], [
+    'api_key' => 'sk_test_...',
+    'stripe_account' => 'acct_...'
+]);
+
+\Stripe\Charge::retrieve("ch_18atAXCdGbJFKhCuBAa4532Z", [
+    'api_key' => 'sk_test_...',
+    'stripe_account' => 'acct_...'
+]);
+```
+
+### Configuring CA Bundles
+
+By default, the library will use its own internal bundle of known CA
+certificates, but it's possible to configure your own:
+
+```php
+\Stripe\Stripe::setCABundlePath("path/to/ca/bundle");
+```
+
+### Configuring Automatic Retries
+
+The library can be configured to automatically retry requests that fail due to
+an intermittent network problem:
+
+```php
+\Stripe\Stripe::setMaxNetworkRetries(2);
+```
+
+[Idempotency keys][idempotency-keys] are added to requests to guarantee that
+retries are safe.
+
+## Development
+
+Install dependencies:
+
+``` bash
+composer install
+```
+
+The test suite depends on [stripe-mock], so make sure to fetch and run it from a
+background terminal ([stripe-mock's README][stripe-mock] also contains
+instructions for installing via Homebrew and other methods):
+
+    go get -u github.com/stripe/stripe-mock
+    stripe-mock
+
+Install dependencies as mentioned above (which will resolve [PHPUnit](http://packagist.org/packages/phpunit/phpunit)), then you can run the test suite:
+
+```bash
+./vendor/bin/phpunit
+```
+
+Or to run an individual test file:
+
+```bash
+./vendor/bin/phpunit tests/UtilTest.php
+```
+
+Update bundled CA certificates from the [Mozilla cURL release][curl]:
+
+```bash
+./update_certs.php
+```
+
+## Attention plugin developers
+
+Are you writing a plugin that integrates Stripe and embeds our library? Then please use the `setAppInfo` function to identify your plugin. For example:
+
+```php
+\Stripe\Stripe::setAppInfo("MyAwesomePlugin", "1.2.34", "https://myawesomeplugin.info");
+```
+
+The method should be called once, before any request is sent to the API. The second and third parameters are optional.
+
+### SSL / TLS configuration option
+
+See the "SSL / TLS compatibility issues" paragraph above for full context. If you want to ensure that your plugin can be used on all systems, you should add a configuration option to let your users choose between different values for `CURLOPT_SSLVERSION`: none (default), `CURL_SSLVERSION_TLSv1` and `CURL_SSLVERSION_TLSv1_2`.
+
+[connect]: https://stripe.com/connect
+[curl]: http://curl.haxx.se/docs/caextract.html
+[psr3]: http://www.php-fig.org/psr/psr-3/
+[idempotency-keys]: https://stripe.com/docs/api/php#idempotent_requests
+[stripe-mock]: https://github.com/stripe/stripe-mock
diff --git a/stripe-php/vendor/stripe/stripe-php/VERSION b/stripe-php/vendor/stripe/stripe-php/VERSION
new file mode 100644
index 00000000..798e3899
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/VERSION
@@ -0,0 +1 @@
+6.3.0
diff --git a/stripe-php/vendor/stripe/stripe-php/build.php b/stripe-php/vendor/stripe/stripe-php/build.php
new file mode 100644
index 00000000..cd053e2b
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/build.php
@@ -0,0 +1,36 @@
+#!/usr/bin/env php
+<?php
+chdir(dirname(__FILE__));
+
+$autoload = (int)$argv[1];
+$returnStatus = null;
+
+if (!$autoload) {
+    // Modify composer to not autoload Stripe
+    $composer = json_decode(file_get_contents('composer.json'), true);
+    unset($composer['autoload']);
+    unset($composer['require-dev']['squizlabs/php_codesniffer']);
+    file_put_contents('composer.json', json_encode($composer, JSON_PRETTY_PRINT));
+}
+
+passthru('composer install', $returnStatus);
+if ($returnStatus !== 0) {
+    exit(1);
+}
+
+if ($autoload) {
+    // Only run CS on 1 of the 2 environments
+    passthru(
+        './vendor/bin/phpcs --standard=PSR2 -n lib tests *.php',
+        $returnStatus
+    );
+    if ($returnStatus !== 0) {
+        exit(1);
+    }
+}
+
+$config = $autoload ? 'phpunit.xml' : 'phpunit.no_autoload.xml';
+passthru("./vendor/bin/phpunit -c $config", $returnStatus);
+if ($returnStatus !== 0) {
+    exit(1);
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/composer.json b/stripe-php/vendor/stripe/stripe-php/composer.json
new file mode 100644
index 00000000..67473fca
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/composer.json
@@ -0,0 +1,36 @@
+{
+  "name": "stripe/stripe-php",
+  "description": "Stripe PHP Library",
+  "keywords": [
+    "stripe",
+    "payment processing",
+    "api"
+  ],
+  "homepage": "https://stripe.com/",
+  "license": "MIT",
+  "authors": [
+    {
+      "name": "Stripe and contributors",
+      "homepage": "https://github.com/stripe/stripe-php/contributors"
+    }
+  ],
+  "require": {
+    "php": ">=5.4.0",
+    "ext-curl": "*",
+    "ext-json": "*",
+    "ext-mbstring": "*"
+  },
+  "require-dev": {
+    "phpunit/phpunit": "~4.0",
+    "satooshi/php-coveralls": "~0.6.1",
+    "squizlabs/php_codesniffer": "~2.0"
+  },
+  "autoload": {
+    "psr-4": { "Stripe\\" : "lib/" }
+  },
+  "extra": {
+    "branch-alias": {
+      "dev-master": "2.0-dev"
+    }
+  }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/data/ca-certificates.crt b/stripe-php/vendor/stripe/stripe-php/data/ca-certificates.crt
new file mode 100644
index 00000000..39ba3368
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/data/ca-certificates.crt
@@ -0,0 +1,3646 @@
+##
+## Bundle of CA Root Certificates
+##
+## Certificate data from Mozilla as of: Wed Sep 20 03:12:05 2017 GMT
+##
+## This is a bundle of X.509 certificates of public Certificate Authorities
+## (CA). These were automatically extracted from Mozilla's root certificates
+## file (certdata.txt).  This file can be found in the mozilla source tree:
+## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
+##
+## It contains the certificates in PEM format and therefore
+## can be directly used with curl / libcurl / php_curl, or with
+## an Apache+mod_ssl webserver for SSL client authentication.
+## Just configure this file as the SSLCACertificateFile.
+##
+## Conversion done with mk-ca-bundle.pl version 1.27.
+## SHA256: 2b2dbe5244e0047e088c597998883a913f6c5fffd1cb5c0fe5a368c8466cb2ec
+##
+
+
+GlobalSign Root CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
+GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
+b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
+BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
+VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
+DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
+THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
+Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
+c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
+gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
+HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
+AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
+Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
+j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
+hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
+X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6
+ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp
+s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN
+S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL
+TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C
+ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i
+YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN
+BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp
+9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu
+01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7
+9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----
+
+Verisign Class 3 Public Primary Certification Authority - G3
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1
+EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc
+cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw
+EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj
+055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
+ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
+j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
+/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0
+xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa
+t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+
+Entrust.net Premium 2048 Secure Server CA
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u
+ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp
+bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV
+BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx
+NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3
+d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u
+ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
+Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr
+hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW
+nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi
+VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ
+KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy
+T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
+zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT
+J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e
+nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=
+-----END CERTIFICATE-----
+
+Baltimore CyberTrust Root
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE
+ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li
+ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC
+SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs
+dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME
+uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB
+UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C
+G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9
+XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr
+l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI
+VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh
+cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5
+hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa
+Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
+RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+
+AddTrust External Root
+======================
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
+QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD
+VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw
+NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU
+cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg
+Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821
++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw
+Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo
+aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy
+2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7
+7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P
+BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL
+VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk
+VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
+j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355
+e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u
+G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV
+BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw
+b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG
+A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0
+MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu
+MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu
+Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v
+dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz
+A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww
+Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68
+j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN
+rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1
+MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH
+hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
+A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM
+Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa
+v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS
+W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
+tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
+-----END CERTIFICATE-----
+
+GeoTrust Global CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
+Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw
+MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
+LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo
+BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet
+8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc
+T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU
+vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk
+DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q
+zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4
+d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2
+mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p
+XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
+Mw==
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1
+MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu
+Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t
+JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e
+RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs
+7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d
+8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V
+qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga
+Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB
+Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu
+KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08
+ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0
+XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB
+hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
+aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2
+qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL
+oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK
+xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF
+KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2
+DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK
+xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU
+p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI
+P/rmMuGNG2+k5o7Y+SlIis5z/iw=
+-----END CERTIFICATE-----
+
+GeoTrust Universal CA 2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
+R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0
+MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg
+SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0
+DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17
+j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q
+JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a
+QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2
+WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP
+20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn
+ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC
+SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG
+8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2
++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E
+BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
+dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ
+4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+
+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq
+A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg
+Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP
+pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d
+FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp
+gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm
+X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
+-----END CERTIFICATE-----
+
+Visa eCommerce Root
+===================
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG
+EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug
+QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2
+WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm
+VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
+bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL
+F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b
+RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0
+TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI
+/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs
+GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
+MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc
+CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW
+YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz
+zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
+YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
+398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+
+Certum Root CA
+==============
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK
+ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla
+Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u
+by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x
+wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL
+kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ
+89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K
+Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P
+NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+
+GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg
+GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/
+0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS
+qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw==
+-----END CERTIFICATE-----
+
+Comodo AAA Services root
+========================
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
+R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
+TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw
+MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl
+c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV
+BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG
+C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs
+i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW
+Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH
+Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK
+Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f
+BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl
+cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz
+LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
+7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
+Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
+8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C
+12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
+-----END CERTIFICATE-----
+
+QuoVadis Root CA
+================
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE
+ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz
+MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp
+cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD
+EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk
+J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL
+F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL
+YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen
+AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w
+PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y
+ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7
+MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj
+YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs
+ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW
+Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu
+BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw
+FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6
+tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo
+fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul
+LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x
+gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi
+5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi
+5nrQNiOKSnQ2+Q==
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 2
+==================
+-----BEGIN CERTIFICATE-----
+MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx
+ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6
+XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk
+lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB
+lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy
+lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt
+66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn
+wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh
+D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy
+BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie
+J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud
+DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU
+a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
+ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv
+Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3
+UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm
+VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK
++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW
+IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1
+WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X
+f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II
+4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8
+VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 3
+==================
+-----BEGIN CERTIFICATE-----
+MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT
+EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx
+OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
+aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg
+DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij
+KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K
+DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv
+BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp
+p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8
+nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX
+MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM
+Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz
+uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT
+BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj
+YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
+aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB
+BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD
+VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4
+ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE
+AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV
+qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s
+hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z
+POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2
+Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp
+8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC
+bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu
+g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p
+vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr
+qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=
+-----END CERTIFICATE-----
+
+Security Communication Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP
+U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw
+8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM
+DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX
+5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd
+DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2
+JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw
+DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g
+0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a
+mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ
+s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ
+6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
+FL39vmwLAw==
+-----END CERTIFICATE-----
+
+Sonera Class 2 Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG
+U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw
+NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh
+IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3
+/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT
+dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG
+f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P
+tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH
+nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT
+XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt
+0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI
+cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph
+Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx
+EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
+llpwrN9M
+-----END CERTIFICATE-----
+
+Camerfirma Chambers of Commerce Root
+====================================
+-----BEGIN CERTIFICATE-----
+MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
+QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
+ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx
+NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp
+cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn
+MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC
+AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU
+xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH
+NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW
+DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV
+d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud
+EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v
+cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P
+AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh
+bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD
+VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
+aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi
+fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD
+L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN
+UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n
+ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1
+erfutGWaIZDgqtCYvDi1czyL+Nw=
+-----END CERTIFICATE-----
+
+Camerfirma Global Chambersign Root
+==================================
+-----BEGIN CERTIFICATE-----
+MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
+QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
+ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx
+NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt
+YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg
+MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw
+ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J
+1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O
+by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl
+6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c
+8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/
+BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j
+aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B
+Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj
+aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y
+ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
+bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA
+PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y
+gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ
+PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4
+IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
+t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
+-----END CERTIFICATE-----
+
+XRamp Global CA Root
+====================
+-----BEGIN CERTIFICATE-----
+MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE
+BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj
+dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx
+HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg
+U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu
+IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx
+foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE
+zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs
+AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry
+xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap
+oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC
+AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc
+/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
+qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n
+nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz
+8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=
+-----END CERTIFICATE-----
+
+Go Daddy Class 2 CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY
+VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG
+A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
+RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD
+ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
+2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32
+qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j
+YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY
+vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O
+BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o
+atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu
+MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim
+PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt
+I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
+HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI
+Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b
+vZ8=
+-----END CERTIFICATE-----
+
+Starfield Class 2 CA
+====================
+-----BEGIN CERTIFICATE-----
+MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc
+U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg
+Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo
+MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG
+A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG
+SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY
+bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ
+JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm
+epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN
+F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF
+MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f
+hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo
+bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs
+afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM
+PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD
+KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3
+QBFGmh95DmK/D5fs4C8fF5Q=
+-----END CERTIFICATE-----
+
+StartCom Certification Authority
+================================
+-----BEGIN CERTIFICATE-----
+MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
+U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
+ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
+NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
+LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
+U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
+o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
+Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
+eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
+2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
+6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
+osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
+untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
+UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
+37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
+FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0
+Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj
+YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH
+AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw
+Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg
+U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5
+LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh
+cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT
+dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC
+AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh
+3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm
+vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk
+fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3
+fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ
+EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
+yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl
+1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/
+lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro
+g14=
+-----END CERTIFICATE-----
+
+Taiwan GRCA
+===========
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG
+EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X
+DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv
+dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN
+w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5
+BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O
+1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO
+htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov
+J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7
+Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t
+B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB
+O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8
+lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV
+HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2
+09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
+TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj
+Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2
+Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU
+D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz
+DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk
+Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk
+7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ
+CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root CA
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
+IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx
+MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
+ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO
+9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy
+UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW
+/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy
+oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf
+GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF
+66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq
+hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc
+EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn
+SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i
+8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
+-----END CERTIFICATE-----
+
+DigiCert Global Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
+HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw
+MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
+dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn
+TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5
+BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H
+4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y
+7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB
+o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm
+8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF
+BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr
+EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt
+tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886
+UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
+
+DigiCert High Assurance EV Root CA
+==================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
+KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
+MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
+MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
+Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
+Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
+OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
+MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
+NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
+h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
+Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
+JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
+V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
+myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
+mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
+vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
+-----END CERTIFICATE-----
+
+Certplus Class 2 Primary CA
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE
+BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN
+OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy
+dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR
+5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ
+Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO
+YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e
+e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME
+CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ
+YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t
+L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD
+P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R
+TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+
+7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW
+//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
+l7+ijrRU
+-----END CERTIFICATE-----
+
+DST Root CA X3
+==============
+-----BEGIN CERTIFICATE-----
+MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK
+ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X
+DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1
+cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT
+rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9
+UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy
+xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d
+utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ
+MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug
+dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE
+GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw
+RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS
+fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
+-----END CERTIFICATE-----
+
+DST ACES CA X6
+==============
+-----BEGIN CERTIFICATE-----
+MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT
+MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha
+MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE
+CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI
+DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa
+pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow
+GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy
+MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud
+EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu
+Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy
+dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU
+CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2
+5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t
+Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
+nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs
+vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3
+oKfN5XozNmr6mis=
+-----END CERTIFICATE-----
+
+SwissSign Gold CA - G2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw
+EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN
+MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp
+c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq
+t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C
+jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg
+vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF
+ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR
+AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend
+jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO
+peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR
+7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi
+GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64
+OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
+L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm
+5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr
+44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf
+Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m
+Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp
+mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk
+vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf
+KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br
+NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj
+viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
+-----END CERTIFICATE-----
+
+SwissSign Silver CA - G2
+========================
+-----BEGIN CERTIFICATE-----
+MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT
+BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X
+DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3
+aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644
+N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm
++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH
+6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu
+MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h
+qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5
+FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs
+ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc
+celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X
+CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB
+tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
+cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P
+4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F
+kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L
+3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx
+/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa
+DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP
+e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu
+WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ
+DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub
+DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ
+cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN
+b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9
+nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge
+RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt
+tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI
+hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K
+Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN
+NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa
+Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG
+1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
+-----END CERTIFICATE-----
+
+thawte Primary Root CA
+======================
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3
+MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg
+SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv
+KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT
+FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs
+oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ
+1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc
+q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K
+aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p
+afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF
+AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE
+uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
+xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89
+jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH
+z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G5
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh
+dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz
+j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD
+Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r
+fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/
+BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
+Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
+aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
+SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+
+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE
+KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC
+Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE
+ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
+-----END CERTIFICATE-----
+
+SecureTrust CA
+==============
+-----BEGIN CERTIFICATE-----
+MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy
+dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe
+BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX
+OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t
+DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH
+GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b
+01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH
+ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/
+BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj
+aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu
+SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf
+mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ
+nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
+3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
+-----END CERTIFICATE-----
+
+Secure Global CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG
+EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH
+bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg
+MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx
+YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ
+bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g
+8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV
+HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi
+0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn
+oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA
+MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+
+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn
+CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5
+3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
+f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
+-----END CERTIFICATE-----
+
+COMODO Certification Authority
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE
+BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
+A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb
+MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD
+T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH
++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww
+xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV
+4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA
+1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI
+rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k
+b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC
+AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP
+OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
+RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc
+IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN
++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==
+-----END CERTIFICATE-----
+
+Network Solutions Certificate Authority
+=======================================
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG
+EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr
+IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx
+MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
+MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx
+jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT
+aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT
+crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc
+/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB
+AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv
+bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA
+A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q
+4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/
+GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
+wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
+ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
+-----END CERTIFICATE-----
+
+COMODO ECC Certification Authority
+==================================
+-----BEGIN CERTIFICATE-----
+MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC
+R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
+ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix
+GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X
+4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni
+wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG
+FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
+U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
+-----END CERTIFICATE-----
+
+Security Communication EV RootCA1
+=================================
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh
+dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE
+BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl
+Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO
+/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX
+WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z
+ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4
+bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK
+9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
+SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm
+iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG
+Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW
+mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW
+T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
+-----END CERTIFICATE-----
+
+OISTE WISeKey Global Root GA CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE
+BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG
+A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH
+bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD
+VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw
+IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5
+IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9
+Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg
+Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD
+d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ
+/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R
+LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ
+KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm
+MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4
++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
+hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
+okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
+-----END CERTIFICATE-----
+
+Certigna
+========
+-----BEGIN CERTIFICATE-----
+MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw
+EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3
+MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI
+Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q
+XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH
+GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p
+ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg
+DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf
+Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ
+tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ
+BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J
+SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA
+hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+
+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu
+PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY
+1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
+WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
+-----END CERTIFICATE-----
+
+Deutsche Telekom Root CA 2
+==========================
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT
+RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG
+A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5
+MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G
+A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS
+b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5
+bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI
+KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY
+AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK
+Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV
+jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV
+HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr
+E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy
+zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8
+rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G
+dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
+Cm26OWMohpLzGITY+9HPBVZkVw==
+-----END CERTIFICATE-----
+
+Cybertrust Global Root
+======================
+-----BEGIN CERTIFICATE-----
+MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li
+ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4
+MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD
+ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW
+0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL
+AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin
+89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT
+8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2
+MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G
+A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO
+lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi
+5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2
+hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T
+X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
+WL1WMRJOEcgh4LMRkWXbtKaIOM5V
+-----END CERTIFICATE-----
+
+ePKI Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG
+EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx
+MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq
+MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs
+IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi
+lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv
+qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX
+12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O
+WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+
+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao
+lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/
+vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi
+Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi
+MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
+ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0
+1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq
+KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV
+xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP
+NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r
+GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE
+xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx
+gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy
+sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD
+BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw=
+-----END CERTIFICATE-----
+
+T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3
+=============================================================================================================================
+-----BEGIN CERTIFICATE-----
+MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH
+DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q
+aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry
+b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV
+BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg
+S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4
+MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl
+IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF
+n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl
+IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft
+dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl
+cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO
+Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1
+xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR
+6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
+hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd
+BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4
+N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT
+y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh
+LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
+dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
+-----END CERTIFICATE-----
+
+certSIGN ROOT CA
+================
+-----BEGIN CERTIFICATE-----
+MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD
+VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa
+Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE
+CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I
+JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH
+rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2
+ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD
+0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943
+AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
+Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB
+AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8
+SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0
+x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt
+vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
+TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G3
+=============================================
+-----BEGIN CERTIFICATE-----
+MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE
+BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0
+IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy
+eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz
+NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo
+YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT
+LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j
+K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE
+c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C
+IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu
+dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr
+2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9
+cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE
+Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
+AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s
+t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G2
+===========================
+-----BEGIN CERTIFICATE-----
+MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC
+VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu
+IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg
+Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV
+MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG
+b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt
+IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS
+LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5
+8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
+mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN
+G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K
+rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
+-----END CERTIFICATE-----
+
+thawte Primary Root CA - G3
+===========================
+-----BEGIN CERTIFICATE-----
+MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE
+BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2
+aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv
+cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w
+ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
+d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD
+VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG
+A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At
+P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC
++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY
+7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW
+vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ
+KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK
+A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
+t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC
+8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm
+er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A=
+-----END CERTIFICATE-----
+
+GeoTrust Primary Certification Authority - G2
+=============================================
+-----BEGIN CERTIFICATE-----
+MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1
+OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
+MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl
+b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG
+BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc
+KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD
+VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+
+EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m
+ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2
+npaqBA+K
+-----END CERTIFICATE-----
+
+VeriSign Universal Root Certification Authority
+===============================================
+-----BEGIN CERTIFICATE-----
+MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO
+ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk
+IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj
+1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP
+MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72
+9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I
+AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR
+tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G
+CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O
+a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
+DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3
+Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx
+Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx
+P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P
+wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4
+mJO37M2CYfE45k+XmCpajQ==
+-----END CERTIFICATE-----
+
+VeriSign Class 3 Public Primary Certification Authority - G4
+============================================================
+-----BEGIN CERTIFICATE-----
+MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC
+VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3
+b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz
+ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo
+b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8
+Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz
+rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw
+HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u
+Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD
+A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx
+AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
+-----END CERTIFICATE-----
+
+NetLock Arany (Class Gold) Főtanúsítvány
+========================================
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
+A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
+dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB
+cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx
+MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO
+ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv
+biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6
+c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu
+0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw
+/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk
+H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw
+fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1
+neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB
+BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW
+qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta
+YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
+bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna
+NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu
+dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
+-----END CERTIFICATE-----
+
+Staat der Nederlanden Root CA - G2
+==================================
+-----BEGIN CERTIFICATE-----
+MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC
+TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
+ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ
+5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn
+vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj
+CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil
+e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR
+OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI
+CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65
+48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi
+trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737
+qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB
+AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC
+ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA
+A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz
++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj
+f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN
+kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk
+CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF
+URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb
+CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h
+oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV
+IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
+66+KAQ==
+-----END CERTIFICATE-----
+
+Hongkong Post Root CA 1
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT
+DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx
+NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n
+IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1
+ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr
+auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh
+qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY
+V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV
+HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i
+h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio
+l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei
+IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps
+T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT
+c4afU9hDDl3WY4JxHYB0yvbiAmvZWg==
+-----END CERTIFICATE-----
+
+SecureSign RootCA11
+===================
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi
+SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS
+b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw
+KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1
+cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL
+TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO
+wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq
+g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP
+O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA
+bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX
+t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh
+OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r
+bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ
+Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01
+y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061
+lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I=
+-----END CERTIFICATE-----
+
+ACEDICOM Root
+=============
+-----BEGIN CERTIFICATE-----
+MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD
+T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4
+MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG
+A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk
+WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD
+YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew
+MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb
+m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk
+HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT
+xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2
+3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9
+2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq
+TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz
+4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU
+9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
+bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg
+aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP
+eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk
+zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1
+ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI
+KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq
+nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE
+I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp
+MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o
+tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA==
+-----END CERTIFICATE-----
+
+Microsec e-Szigno Root CA 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER
+MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv
+c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
+dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE
+BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt
+U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA
+fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG
+0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA
+pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm
+1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC
+AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf
+QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE
+FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o
+lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX
+I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
+tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02
+yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi
+LXpUq3DDfSJlgnCW
+-----END CERTIFICATE-----
+
+GlobalSign Root CA - R3
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv
+YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh
+bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT
+aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln
+bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt
+iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ
+0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3
+rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl
+OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2
+xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7
+lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8
+EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E
+bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18
+YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r
+kpeDMdmztcpHWD9f
+-----END CERTIFICATE-----
+
+Autoridad de Certificacion Firmaprofesional CIF A62634068
+=========================================================
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA
+BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
+MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw
+QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB
+NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD
+Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P
+B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY
+7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH
+ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI
+plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX
+MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX
+LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK
+bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU
+vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud
+EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH
+DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
+cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA
+bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx
+ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx
+51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk
+R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP
+T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f
+Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl
+osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR
+crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR
+saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD
+KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi
+6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
+-----END CERTIFICATE-----
+
+Izenpe.com
+==========
+-----BEGIN CERTIFICATE-----
+MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG
+EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz
+MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu
+QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ
+03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK
+ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU
++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC
+PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT
+OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK
+F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK
+0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+
+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB
+leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID
+AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+
+SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG
+NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
+MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
+BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l
+Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga
+kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q
+hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs
+g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5
+aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5
+nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC
+ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo
+Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z
+WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
+-----END CERTIFICATE-----
+
+Chambers of Commerce Root - 2008
+================================
+-----BEGIN CERTIFICATE-----
+MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy
+Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl
+ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF
+EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl
+cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA
+XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj
+h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/
+ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk
+NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g
+D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331
+lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ
+0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
+ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2
+EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI
+G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ
+BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh
+bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh
+bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC
+CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH
+AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1
+wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH
+3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU
+RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6
+M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1
+YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF
+9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK
+zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG
+nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
+OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ
+-----END CERTIFICATE-----
+
+Global Chambersign Root - 2008
+==============================
+-----BEGIN CERTIFICATE-----
+MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD
+MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv
+bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu
+QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx
+NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg
+Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ
+QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
+aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf
+VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf
+XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0
+ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB
+/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA
+TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M
+H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe
+Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF
+HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
+wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB
+AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT
+BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE
+BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm
+aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm
+aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp
+1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0
+dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG
+/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6
+ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s
+dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg
+9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH
+foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du
+qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr
+P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq
+c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
+09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
+-----END CERTIFICATE-----
+
+Go Daddy Root Certificate Authority - G2
+========================================
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu
+MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
+MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G
+A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq
+9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD
++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd
+fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl
+NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9
+BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac
+vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r
+5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV
+N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
+LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1
+-----END CERTIFICATE-----
+
+Starfield Root Certificate Authority - G2
+=========================================
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0
+eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw
+DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg
+VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB
+dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv
+W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs
+bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk
+N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf
+ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU
+JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol
+TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx
+4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw
+F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ
+c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+
+Starfield Services Root Certificate Authority - G2
+==================================================
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s
+b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl
+IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT
+dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg
+Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2
+h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa
+hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP
+LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB
+rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG
+SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP
+E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy
+xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
+iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza
+YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6
+-----END CERTIFICATE-----
+
+AffirmTrust Commercial
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw
+MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb
+DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV
+C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6
+BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww
+MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV
+HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG
+hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi
+qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv
+0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh
+sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
+-----END CERTIFICATE-----
+
+AffirmTrust Networking
+======================
+-----BEGIN CERTIFICATE-----
+MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw
+MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly
+bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE
+Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI
+dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24
+/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb
+h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV
+HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu
+UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6
+12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23
+WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9
+/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
+-----END CERTIFICATE-----
+
+AffirmTrust Premium
+===================
+-----BEGIN CERTIFICATE-----
+MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS
+BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy
+OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy
+dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn
+BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV
+5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs
++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd
+GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R
+p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI
+S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04
+6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5
+/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo
++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv
+MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
+Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC
+6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S
+L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK
++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV
+BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg
+IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60
+g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb
+zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw==
+-----END CERTIFICATE-----
+
+AffirmTrust Premium ECC
+=======================
+-----BEGIN CERTIFICATE-----
+MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV
+BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx
+MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U
+cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ
+N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW
+BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK
+BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X
+57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM
+eQ==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA
+=========================
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK
+ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy
+MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU
+ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC
+l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J
+J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4
+fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0
+cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB
+Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw
+DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj
+jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1
+mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj
+Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
+03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
+-----END CERTIFICATE-----
+
+Certinomis - Autorité Racine
+============================
+-----BEGIN CERTIFICATE-----
+MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
+Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
+LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG
+A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw
+JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa
+wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly
+Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw
+2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N
+jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q
+c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC
+lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb
+xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g
+530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna
+4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
+KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x
+WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva
+R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40
+nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B
+CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv
+JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE
+qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b
+WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE
+wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
+vgt2Fl43N+bYdJeimUV5
+-----END CERTIFICATE-----
+
+TWCA Root Certification Authority
+=================================
+-----BEGIN CERTIFICATE-----
+MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ
+VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG
+EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB
+IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx
+QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC
+oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP
+4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r
+y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB
+BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG
+9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC
+mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW
+QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY
+T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny
+Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
+-----END CERTIFICATE-----
+
+Security Communication RootCA2
+==============================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh
+dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC
+SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy
+aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++
++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R
+3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV
+spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K
+EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8
+QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
+CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj
+u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk
+3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q
+tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
+mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
+-----END CERTIFICATE-----
+
+EC-ACC
+======
+-----BEGIN CERTIFICATE-----
+MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE
+BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w
+ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD
+VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE
+CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT
+BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7
+MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt
+SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl
+Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh
+cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK
+w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT
+ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4
+HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a
+E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw
+0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD
+VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0
+Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l
+dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ
+lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa
+Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe
+l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2
+E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D
+5EI=
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions RootCA 2011
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
+O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
+aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
+IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
+AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
+IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
+IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
+1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
+71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
+8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
+3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
+MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
+MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
+b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
+XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
+TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
+/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
+7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
+-----END CERTIFICATE-----
+
+Actalis Authentication Root CA
+==============================
+-----BEGIN CERTIFICATE-----
+MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM
+BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE
+AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky
+MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz
+IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
+IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ
+wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa
+by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6
+zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f
+YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2
+oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l
+EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7
+hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8
+EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5
+jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY
+iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
+ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI
+WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0
+JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx
+K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+
+Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC
+4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo
+2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz
+lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem
+OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9
+vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
+-----END CERTIFICATE-----
+
+Trustis FPS Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG
+EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290
+IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV
+BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ
+RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk
+H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa
+cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt
+o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA
+AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd
+BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c
+GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC
+yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P
+8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV
+l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl
+iB6XzCGcKQENZetX2fNXlrtIzYE=
+-----END CERTIFICATE-----
+
+StartCom Certification Authority
+================================
+-----BEGIN CERTIFICATE-----
+MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
+U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu
+ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0
+NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk
+LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg
+U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y
+o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/
+Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d
+eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt
+2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z
+6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ
+osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/
+untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc
+UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT
+37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ
+Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0
+dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu
+c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv
+bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0
+aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t
+L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG
+cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5
+fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm
+N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN
+Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T
+tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX
+e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA
+2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs
+HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
+JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib
+D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8=
+-----END CERTIFICATE-----
+
+StartCom Certification Authority G2
+===================================
+-----BEGIN CERTIFICATE-----
+MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN
+U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
+RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE
+ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O
+o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG
+4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi
+Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul
+Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs
+O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H
+vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L
+nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS
+FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa
+z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ
+KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
+2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk
+J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+
+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG
+/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc
+nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld
+blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc
+l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm
+7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm
+obp573PYtlNXLfbQ4ddI
+-----END CERTIFICATE-----
+
+Buypass Class 2 Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X
+DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
+eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1
+g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn
+9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b
+/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU
+CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff
+awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI
+zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn
+Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX
+Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs
+M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
+AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
+A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI
+osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S
+aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd
+DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD
+LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0
+oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC
+wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS
+CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN
+rJgWVqA=
+-----END CERTIFICATE-----
+
+Buypass Class 3 Root CA
+=======================
+-----BEGIN CERTIFICATE-----
+MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
+QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X
+DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1
+eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH
+sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR
+5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh
+7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ
+ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH
+2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV
+/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ
+RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA
+Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq
+j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF
+AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
+cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G
+uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG
+Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8
+ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2
+KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz
+6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug
+UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe
+eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi
+Cp/HuZc=
+-----END CERTIFICATE-----
+
+T-TeleSec GlobalRoot Class 3
+============================
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
+IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
+cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx
+MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
+dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
+ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK
+9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU
+NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF
+iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W
+0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr
+AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb
+fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT
+ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h
+P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
+e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==
+-----END CERTIFICATE-----
+
+EE Certification Centre Root CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG
+EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy
+dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw
+MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB
+UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy
+ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM
+TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2
+rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw
+93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN
+P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T
+AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ
+MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF
+BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj
+xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM
+lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
+uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU
+3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM
+dcGWxZ0=
+-----END CERTIFICATE-----
+
+TURKTRUST Certificate Services Provider Root 2007
+=================================================
+-----BEGIN CERTIFICATE-----
+MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF
+bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP
+MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg
+QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X
+DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl
+a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN
+BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
+bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N
+YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv
+KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya
+KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT
+rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC
+AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP
+BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s
+Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
+aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO
+Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb
+BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK
+poRq0Tl9
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 2009
+==============================
+-----BEGIN CERTIFICATE-----
+MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe
+Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE
+LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD
+ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA
+BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv
+KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z
+p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC
+AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ
+4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y
+eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw
+MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G
+PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw
+OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm
+2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
+o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV
+dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph
+X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=
+-----END CERTIFICATE-----
+
+D-TRUST Root Class 3 CA 2 EV 2009
+=================================
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK
+DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw
+OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS
+egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh
+zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T
+7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60
+sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35
+11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv
+cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v
+ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El
+MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp
+b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh
+c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+
+PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
+nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX
+ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA
+NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv
+w9y4AyHqnxbxLFS1
+-----END CERTIFICATE-----
+
+PSCProcert
+==========
+-----BEGIN CERTIFICATE-----
+MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk
+ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ
+MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz
+dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl
+cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw
+IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw
+MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w
+DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD
+ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp
+Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC
+wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA
+3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh
+RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO
+EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2
+0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
+0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU
+td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw
+Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp
+r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/
+AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz
+Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId
+xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp
+ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH
+EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h
+Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k
+ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG
+9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG
+MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG
+LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52
+ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy
+YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
+Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o
+dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq
+T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN
+g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q
+uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1
+n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn
+FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo
+5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq
+3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5
+poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y
+eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
+-----END CERTIFICATE-----
+
+CA Disig Root R1
+================
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw
+EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
+ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx
+EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
+c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy
+3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8
+u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2
+m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk
+CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa
+YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6
+vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL
+LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX
+ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is
+XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ
+04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
+xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B
+LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM
+CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb
+VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85
+YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS
+ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix
+lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N
+UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ
+a7+h89n07eLw4+1knj0vllJPgFOL
+-----END CERTIFICATE-----
+
+CA Disig Root R2
+================
+-----BEGIN CERTIFICATE-----
+MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw
+EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
+ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx
+EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
+c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC
+w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia
+xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7
+A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S
+GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV
+g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa
+5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE
+koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A
+Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i
+Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV
+HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u
+Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
+tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV
+sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je
+dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8
+1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx
+mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01
+utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0
+sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg
+UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV
+7+ZtsH8tZ/3zbBt1RqPlShfppNcL
+-----END CERTIFICATE-----
+
+ACCVRAIZ1
+=========
+-----BEGIN CERTIFICATE-----
+MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB
+SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1
+MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH
+UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM
+jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0
+RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD
+aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ
+0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG
+WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7
+8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR
+5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J
+9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK
+Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw
+Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu
+Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
+VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM
+Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA
+QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh
+AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA
+YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj
+AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA
+IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk
+aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0
+dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2
+MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI
+hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E
+R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN
+YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49
+nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ
+TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3
+sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
+I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg
+Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd
+3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p
+EfbRD0tVNEYqi4Y7
+-----END CERTIFICATE-----
+
+TWCA Global Root CA
+===================
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT
+CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD
+QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK
+EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg
+Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C
+nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV
+r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR
+Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV
+tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W
+KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99
+sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p
+yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn
+kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI
+zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g
+cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
+LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M
+8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg
+/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg
+lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP
+A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m
+i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8
+EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3
+zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=
+-----END CERTIFICATE-----
+
+TeliaSonera Root CA v1
+======================
+-----BEGIN CERTIFICATE-----
+MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE
+CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4
+MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW
+VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+
+6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA
+3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k
+B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn
+Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH
+oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3
+F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ
+oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7
+gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc
+TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB
+AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW
+DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm
+zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
+0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW
+pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV
+G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc
+c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT
+JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2
+qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6
+Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems
+WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
+-----END CERTIFICATE-----
+
+E-Tugra Certification Authority
+===============================
+-----BEGIN CERTIFICATE-----
+MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w
+DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls
+ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
+ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw
+NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx
+QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl
+cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD
+DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd
+hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K
+CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g
+ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ
+BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0
+E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz
+rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq
+jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
+rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5
+dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB
+/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG
+MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK
+kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO
+XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807
+VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo
+a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc
+dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV
+KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT
+Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0
+8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G
+C7TbO6Orb1wdtn7os4I07QZcJA==
+-----END CERTIFICATE-----
+
+T-TeleSec GlobalRoot Class 2
+============================
+-----BEGIN CERTIFICATE-----
+MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM
+IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU
+cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx
+MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz
+dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD
+ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ
+SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F
+vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970
+2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV
+WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy
+YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4
+r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf
+vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR
+3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
+9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==
+-----END CERTIFICATE-----
+
+Atos TrustedRoot 2011
+=====================
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU
+cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4
+MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG
+A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV
+hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr
+54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+
+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320
+HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR
+z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R
+l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ
+bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
+CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h
+k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh
+TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9
+61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G
+3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 1 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE
+PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm
+PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6
+Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN
+ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l
+g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV
+7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX
+9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f
+iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg
+t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI
+hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
+MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3
+GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct
+Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP
++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh
+3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa
+wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6
+O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0
+FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV
+hMJKzRwuJIczYOXD
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 2 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh
+ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY
+NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t
+oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o
+MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l
+V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo
+L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ
+sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD
+6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh
+lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI
+hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
+AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K
+pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9
+x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz
+dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X
+U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw
+mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD
+zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN
+JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr
+O3jtZsSOeWmD3n+M
+-----END CERTIFICATE-----
+
+QuoVadis Root CA 3 G3
+=====================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG
+A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv
+b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg
+RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286
+IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL
+Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe
+6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3
+I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U
+VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7
+5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi
+Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM
+dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt
+rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI
+hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
+KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS
+t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ
+TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du
+DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib
+Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD
+hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX
+0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW
+dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2
+PpxxVJkES/1Y+Zj0
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root G2
+===========================
+-----BEGIN CERTIFICATE-----
+MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw
+IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw
+MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL
+ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH
+35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq
+bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw
+VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP
+YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn
+lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO
+w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv
+0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz
+d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW
+hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M
+jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
+IhNzbM8m9Yop5w==
+-----END CERTIFICATE-----
+
+DigiCert Assured ID Root G3
+===========================
+-----BEGIN CERTIFICATE-----
+MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD
+VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
+MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb
+RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs
+KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF
+UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy
+YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy
+1vUhZscv6pZjamVFkpUBtA==
+-----END CERTIFICATE-----
+
+DigiCert Global Root G2
+=======================
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw
+HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx
+MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
+dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ
+kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO
+3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV
+BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM
+UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB
+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu
+5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr
+F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U
+WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH
+QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/
+iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
+MrY=
+-----END CERTIFICATE-----
+
+DigiCert Global Root G3
+=======================
+-----BEGIN CERTIFICATE-----
+MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD
+VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw
+MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C
+AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O
+YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp
+Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y
+3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34
+VOKa5Vt8sycX
+-----END CERTIFICATE-----
+
+DigiCert Trusted Root G4
+========================
+-----BEGIN CERTIFICATE-----
+MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG
+EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw
+HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1
+MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp
+pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o
+k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa
+vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY
+QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6
+MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm
+mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7
+f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH
+dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8
+oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
+DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
+ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY
+ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr
+yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy
+7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah
+ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN
+5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb
+/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa
+5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK
+G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP
+82Z+
+-----END CERTIFICATE-----
+
+WoSign
+======
+-----BEGIN CERTIFICATE-----
+MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQG
+EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNVBAMTIUNlcnRpZmljYXRpb24g
+QXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJ
+BgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
+vcqNrLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1UfcIiePyO
+CbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcSccf+Hb0v1naMQFXQoOXXDX
+2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2ZjC1vt7tj/id07sBMOby8w7gLJKA84X5
+KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4Mx1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR
++ScPewavVIMYe+HdVHpRaG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ez
+EC8wQjchzDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDaruHqk
+lWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221KmYo0SLwX3OSACCK2
+8jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvASh0JWzko/amrzgD5LkhLJuYwTKVY
+yrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWvHYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0C
+AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R
+8bNLtwYgFP6HEtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1
+LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJMuYhOZO9sxXq
+T2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2eJXLOC62qx1ViC777Y7NhRCOj
+y+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VNg64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC
+2nz4SNAzqfkHx5Xh9T71XXG68pWpdIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes
+5cVAWubXbHssw1abR80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/
+EaEQPkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGcexGATVdVh
+mVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+J7x6v+Db9NpSvd4MVHAx
+kUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMlOtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGi
+kpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWTee5Ehr7XHuQe+w==
+-----END CERTIFICATE-----
+
+WoSign China
+============
+-----BEGIN CERTIFICATE-----
+MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQG
+EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMMEkNBIOayg+mAmuagueiv
+geS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgwMTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYD
+VQQKExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjAN
+BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k
+8H/rD195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld19AXbbQs5
+uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExfv5RxadmWPgxDT74wwJ85
+dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnkUkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5
+Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+LNVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFy
+b7Ao65vh4YOhn0pdr8yb+gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc
+76DbT52VqyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6KyX2m
++Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0GAbQOXDBGVWCvOGU6
+yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaKJ/kR8slC/k7e3x9cxKSGhxYzoacX
+GKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwECAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUA
+A4ICAQBqinA4WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6
+yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj/feTZU7n85iY
+r83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6jBAyvd0zaziGfjk9DgNyp115
+j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0A
+kLppRQjbbpCBhqcqBT/mhDn4t/lXX0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97
+qA4bLJyuQHCH2u2nFoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Y
+jj4Du9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10lO1Hm13ZB
+ONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Leie2uPAmvylezkolwQOQv
+T8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR12KvxAmLBsX5VYc8T1yaw15zLKYs4SgsO
+kI26oQ==
+-----END CERTIFICATE-----
+
+COMODO RSA Certification Authority
+==================================
+-----BEGIN CERTIFICATE-----
+MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE
+BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
+A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC
+R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE
+ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn
+dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ
+FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+
+5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG
+x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX
+2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL
+OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3
+sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C
+GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5
+WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
+FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt
+rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+
+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg
+tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW
+sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp
+pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA
+zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq
+ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52
+7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I
+LaZRfyHBNVOFBkpdn627G190
+-----END CERTIFICATE-----
+
+USERTrust RSA Certification Authority
+=====================================
+-----BEGIN CERTIFICATE-----
+MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE
+BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
+ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE
+BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK
+ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz
+0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j
+Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn
+RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O
++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq
+/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE
+Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM
+lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8
+yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+
+eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
+BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
+MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW
+FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ
+7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ
+Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM
+8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi
+FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi
+yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c
+J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw
+sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx
+Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9
+-----END CERTIFICATE-----
+
+USERTrust ECC Certification Authority
+=====================================
+-----BEGIN CERTIFICATE-----
+MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC
+VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
+aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2
+0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez
+nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV
+HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB
+HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu
+9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
+-----END CERTIFICATE-----
+
+GlobalSign ECC Root CA - R4
+===========================
+-----BEGIN CERTIFICATE-----
+MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprl
+OQcJFspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAwDgYDVR0P
+AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61FuOJAf/sKbvu+M8k8o4TV
+MAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGXkPoUVy0D7O48027KqGx2vKLeuwIgJ6iF
+JzWbVsaj8kfSt24bAgAXqmemFZHe+pTsewv4n4Q=
+-----END CERTIFICATE-----
+
+GlobalSign ECC Root CA - R5
+===========================
+-----BEGIN CERTIFICATE-----
+MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb
+R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD
+EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6
+SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS
+h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx
+uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7
+yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3
+-----END CERTIFICATE-----
+
+Staat der Nederlanden Root CA - G3
+==================================
+-----BEGIN CERTIFICATE-----
+MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC
+TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l
+ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y
+olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t
+x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy
+EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K
+Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur
+mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5
+1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp
+07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo
+FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE
+41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB
+AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu
+yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD
+U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq
+KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1
+v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA
+8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b
+8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r
+mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq
+1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI
+JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV
+tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk=
+-----END CERTIFICATE-----
+
+Staat der Nederlanden EV Root CA
+================================
+-----BEGIN CERTIFICATE-----
+MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE
+CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g
+RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M
+MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl
+cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk
+SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW
+O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r
+0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8
+Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV
+XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr
+08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV
+0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd
+74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx
+fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC
+MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa
+ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
+eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu
+c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq
+5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN
+b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN
+f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi
+5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4
+WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK
+DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy
+eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg==
+-----END CERTIFICATE-----
+
+IdenTrust Commercial Root CA 1
+==============================
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG
+EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS
+b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES
+MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB
+IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld
+hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/
+mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi
+1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C
+XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl
+3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy
+NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV
+WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg
+xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix
+uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI
+hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
+6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg
+ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt
+ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV
+YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX
+feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro
+kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe
+2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz
+Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R
+cGzM7vRX+Bi6hG6H
+-----END CERTIFICATE-----
+
+IdenTrust Public Sector Root CA 1
+=================================
+-----BEGIN CERTIFICATE-----
+MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG
+EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv
+ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV
+UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS
+b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy
+P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6
+Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI
+rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf
+qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS
+mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn
+ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh
+LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v
+iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL
+4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B
+Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw
+DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
+t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A
+mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt
+GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt
+m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx
+NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4
+Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI
+ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC
+ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ
+3Wl9af0AVqW3rLatt8o+Ae+c
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority - G2
+=========================================
+-----BEGIN CERTIFICATE-----
+MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV
+BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy
+bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug
+b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw
+HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT
+DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx
+OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s
+eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP
+/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz
+HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU
+s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y
+TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx
+AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6
+0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z
+iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
+Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi
+nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+
+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO
+e4pIb4tF9g==
+-----END CERTIFICATE-----
+
+Entrust Root Certification Authority - EC1
+==========================================
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx
+FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn
+YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl
+ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw
+FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs
+LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
+IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy
+AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef
+9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
+FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h
+vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8
+kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
+-----END CERTIFICATE-----
+
+CFCA EV ROOT
+============
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE
+CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB
+IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw
+MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD
+DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV
+BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD
+7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN
+uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW
+ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7
+xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f
+py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K
+gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol
+hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ
+tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf
+BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
+ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q
+ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua
+4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG
+E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX
+BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn
+aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy
+PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX
+kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C
+ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
+-----END CERTIFICATE-----
+
+TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5
+====================================================
+-----BEGIN CERTIFICATE-----
+MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN
+BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
+bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1Qg
+RWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAw
+ODA3MDFaFw0yMzA0MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0w
+SwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnE
+n2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRp
+ZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEApCUZ4WWe60ghUEoI5RHwWrom/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537
+jVJp45wnEFPzpALFp/kRGml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1m
+ep5Fimh34khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z5UNP
+9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0hO8EuPbJbKoCPrZV
+4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QIDAQABo0IwQDAdBgNVHQ4EFgQUVpkH
+HtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggEBAJ5FdnsXSDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPo
+BP5yCccLqh0lVX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq
+URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nfpeYVhDfwwvJl
+lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8
+B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=
+-----END CERTIFICATE-----
+
+Certinomis - Root CA
+====================
+-----BEGIN CERTIFICATE-----
+MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
+Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg
+LSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx
+EzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD
+ZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos
+P5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo
+d5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap
+z8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00
+8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x
+RLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE
+6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t
+FvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV
+PZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH
+i5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj
+YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I
+6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF
+AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV
+WVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw
+Pk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX
+lCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ
+y29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9
+Iff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng
+DwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi
+I0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM
+cyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr
+hkIGuUE=
+-----END CERTIFICATE-----
+
+OISTE WISeKey Global Root GB CA
+===============================
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG
+EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
+ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw
+MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD
+VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds
+b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX
+scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP
+rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk
+9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o
+Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg
+GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI
+hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD
+dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0
+VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui
+HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
+Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
+-----END CERTIFICATE-----
+
+Certification Authority of WoSign G2
+====================================
+-----BEGIN CERTIFICATE-----
+MIIDfDCCAmSgAwIBAgIQayXaioidfLwPBbOxemFFRDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQG
+EwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxLTArBgNVBAMTJENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5IG9mIFdvU2lnbiBHMjAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMFgx
+CzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsGA1UEAxMkQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgb2YgV29TaWduIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAvsXEoCKASU+/2YcRxlPhuw+9YH+v9oIOH9ywjj2X4FA8jzrvZjtFB5sg+OPXJYY1kBai
+XW8wGQiHC38Gsp1ij96vkqVg1CuAmlI/9ZqD6TRay9nVYlzmDuDfBpgOgHzKtB0TiGsOqCR3A9Du
+W/PKaZE1OVbFbeP3PU9ekzgkyhjpJMuSA93MHD0JcOQg5PGurLtzaaNjOg9FD6FKmsLRY6zLEPg9
+5k4ot+vElbGs/V6r+kHLXZ1L3PR8du9nfwB6jdKgGlxNIuG12t12s9R23164i5jIFFTMaxeSt+BK
+v0mUYQs4kI9dJGwlezt52eJ+na2fmKEG/HgUYFf47oB3sQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC
+AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU+mCp62XF3RYUCE4MD42b4Pdkr2cwDQYJKoZI
+hvcNAQELBQADggEBAFfDejaCnI2Y4qtAqkePx6db7XznPWZaOzG73/MWM5H8fHulwqZm46qwtyeY
+P0nXYGdnPzZPSsvxFPpahygc7Y9BMsaV+X3avXtbwrAh449G3CE4Q3RM+zD4F3LBMvzIkRfEzFg3
+TgvMWvchNSiDbGAtROtSjFA9tWwS1/oJu2yySrHFieT801LYYRf+epSEj3m2M1m6D8QL4nCgS3gu
++sif/a+RZQp4OBXllxcU3fngLDT4ONCEIgDAFFEYKwLcMFrw6AF8NTojrwjkr6qOKEJJLvD1mTS+
+7Q9LGOHSJDy7XUe3IfKN0QqZjuNuPq1w4I+5ysxugTH2e5x6eeRncRg=
+-----END CERTIFICATE-----
+
+CA WoSign ECC Root
+==================
+-----BEGIN CERTIFICATE-----
+MIICCTCCAY+gAwIBAgIQaEpYcIBr8I8C+vbe6LCQkDAKBggqhkjOPQQDAzBGMQswCQYDVQQGEwJD
+TjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMTEkNBIFdvU2lnbiBFQ0MgUm9v
+dDAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4NThaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQK
+ExFXb1NpZ24gQ0EgTGltaXRlZDEbMBkGA1UEAxMSQ0EgV29TaWduIEVDQyBSb290MHYwEAYHKoZI
+zj0CAQYFK4EEACIDYgAE4f2OuEMkq5Z7hcK6C62N4DrjJLnSsb6IOsq/Srj57ywvr1FQPEd1bPiU
+t5v8KB7FVMxjnRZLU8HnIKvNrCXSf4/CwVqCXjCLelTOA7WRf6qU0NGKSMyCBSah1VES1ns2o0Iw
+QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUqv3VWqP2h4syhf3R
+MluARZPzA7gwCgYIKoZIzj0EAwMDaAAwZQIxAOSkhLCB1T2wdKyUpOgOPQB0TKGXa/kNUTyh2Tv0
+Daupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu
+a/GRspBl9JrmkO5K
+-----END CERTIFICATE-----
+
+SZAFIR ROOT CA2
+===============
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG
+A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV
+BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ
+BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD
+VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q
+qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK
+DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE
+2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ
+ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi
+ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC
+AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5
+O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67
+oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul
+4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6
++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA 2
+===========================
+-----BEGIN CERTIFICATE-----
+MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE
+BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1
+bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y
+ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ
+TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB
+IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9
+7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o
+CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b
+Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p
+uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130
+GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ
+9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB
+Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye
+hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM
+BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI
+hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW
+Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA
+L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo
+clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM
+pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb
+w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo
+J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm
+ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX
+is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7
+zAYspsbiDrW5viSP
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions RootCA 2015
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT
+BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0
+aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl
+YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx
+MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg
+QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV
+BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw
+MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv
+bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh
+iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+
+6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd
+FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr
+i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F
+GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2
+fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu
+iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc
+Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI
+hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+
+D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM
+d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y
+d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn
+82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb
+davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F
+Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt
+J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa
+JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q
+p/UsQu0yrbYhnr68
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions ECC RootCA 2015
+===========================================================
+-----BEGIN CERTIFICATE-----
+MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0
+aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u
+cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj
+aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw
+MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj
+IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD
+VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290
+Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP
+dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK
+Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
+BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA
+GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn
+dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
+-----END CERTIFICATE-----
+
+Certplus Root CA G1
+===================
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV
+BAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe
+Fw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD
+ZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN
+r49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx
+Qv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj
+BYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv
+LRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2
+z4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc
+4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd
+4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj
+jy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+
+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G
+A1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY
+lwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
+66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG
+YX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/
+2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F
+6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX
+CNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe
+tUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC
+VRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/
++mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+
+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
+-----END CERTIFICATE-----
+
+Certplus Root CA G2
+===================
+-----BEGIN CERTIFICATE-----
+MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT
+AkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x
+NDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0
+cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA
+BM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN
+Am8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud
+IwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV
+HdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl
+vPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw==
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G1
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx
+MB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa
+Yp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87
+ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO
+YheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9
+xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO
+9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq
+3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi
+n6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9
+URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr
+TJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px
+N3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
+PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv
+uVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK
+n0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh
+X4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80
+nR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm
+GS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/
+bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o
+4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA
+OXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G2
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy
+MB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+
+Ntmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz
+4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV
+eYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt
+UdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz
+3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj
+3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz
+9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0
+0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT
+y3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59
+M4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
+Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI
+mUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG
+S4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp
+EndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ
+6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr
+gCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo
+SmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0
+YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm
+u7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G3
+====================
+-----BEGIN CERTIFICATE-----
+MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT
+AkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X
+DTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w
+ZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B
+ta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB
+/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf
+BgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM
+BBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta
+3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB
+-----END CERTIFICATE-----
+
+ISRG Root X1
+============
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE
+BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD
+EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG
+EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT
+DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r
+Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1
+3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K
+b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN
+Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ
+4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf
+1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu
+hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH
+usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r
+OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G
+A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY
+9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
+ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV
+0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt
+hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw
+TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx
+e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA
+JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD
+YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n
+JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ
+m+kXQ99b21/+jh5Xos1AnX5iItreGCc=
+-----END CERTIFICATE-----
+
+AC RAIZ FNMT-RCM
+================
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT
+AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw
+MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD
+TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf
+qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr
+btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL
+j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou
+08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw
+WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT
+tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ
+47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC
+ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa
+i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o
+dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
+nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s
+D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ
+j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT
+Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW
++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7
+Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d
+8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm
+5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG
+rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
+-----END CERTIFICATE-----
+
+Amazon Root CA 1
+================
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD
+VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1
+MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
+bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH
+FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ
+gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t
+dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce
+VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3
+DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM
+CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy
+8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa
+2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2
+xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5
+-----END CERTIFICATE-----
+
+Amazon Root CA 2
+================
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD
+VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1
+MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
+bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4
+kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp
+N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9
+AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd
+fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx
+kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS
+btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0
+Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN
+c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+
+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw
+DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA
+A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE
+YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW
+xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ
+gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW
+aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV
+Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3
+KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi
+JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw=
+-----END CERTIFICATE-----
+
+Amazon Root CA 3
+================
+-----BEGIN CERTIFICATE-----
+MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG
+EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy
+NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
+MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB
+f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr
+Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43
+rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc
+eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw==
+-----END CERTIFICATE-----
+
+Amazon Root CA 4
+================
+-----BEGIN CERTIFICATE-----
+MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG
+EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy
+NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
+MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN
+/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri
+83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA
+MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1
+AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA==
+-----END CERTIFICATE-----
+
+LuxTrust Global Root 2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG
+A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh
+bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW
+MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC
+AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm
+Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2
+xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC
+wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm
+1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm
+FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF
+wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/
+a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U
+ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ
+MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB
+/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5
+Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT
++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ
+FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN
+H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW
+7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu
+ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA
+VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR
+TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt
+/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc
+7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I
+iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr
+-----END CERTIFICATE-----
+
+TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT
+D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr
+IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g
+TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp
+ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD
+VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt
+c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth
+bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11
+IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8
+6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc
+wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0
+3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9
+WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU
+ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh
+AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc
+lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R
+e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j
+q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
+-----END CERTIFICATE-----
diff --git a/stripe-php/vendor/stripe/stripe-php/examples/oauth.php b/stripe-php/vendor/stripe/stripe-php/examples/oauth.php
new file mode 100644
index 00000000..abde1569
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/examples/oauth.php
@@ -0,0 +1,55 @@
+<?php
+
+require('../init.php');
+
+\Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET_KEY'));
+\Stripe\Stripe::setClientId(getenv('STRIPE_CLIENT_ID'));
+
+
+if (isset($_GET['code'])) {
+    // The user was redirected back from the OAuth form with an authorization code.
+    $code = $_GET['code'];
+
+    try {
+        $resp = \Stripe\OAuth::token([
+            'grant_type' => 'authorization_code',
+            'code' => $code,
+        ]);
+    } catch (\Stripe\Error\OAuth\OAuthBase $e) {
+        exit("Error: " . $e->getMessage());
+    }
+
+    $accountId = $resp->stripe_user_id;
+
+    echo "<p>Success! Account <code>$accountId</code> is connected.</p>\n";
+    echo "<p>Click <a href=\"?deauth=$accountId\">here</a> to disconnect the account.</p>\n";
+
+} elseif (isset($_GET['error'])) {
+    // The user was redirect back from the OAuth form with an error.
+    $error = $_GET['error'];
+    $error_description = $_GET['error_description'];
+
+    echo "<p>Error: code=" . htmlspecialchars($error, ENT_QUOTES) . ", description=" . htmlspecialchars($error_description, ENT_QUOTES) . "</p>\n";
+    echo "<p>Click <a href=\"?\">here</a> to restart the OAuth flow.</p>\n";
+
+} elseif (isset($_GET['deauth'])) {
+    // Deauthorization request
+    $accountId = $_GET['deauth'];
+
+    try {
+        \Stripe\OAuth::deauthorize([
+            'stripe_user_id' => $accountId,
+        ]);
+    } catch (\Stripe\Error\OAuth\OAuthBase $e) {
+        exit("Error: " . $e->getMessage());
+    }
+
+    echo "<p>Success! Account <code>" . htmlspecialchars($accountId, ENT_QUOTES) . "</code> is disconnected.</p>\n";
+    echo "<p>Click <a href=\"?\">here</a> to restart the OAuth flow.</p>\n";
+
+} else {
+    $url = \Stripe\OAuth::authorizeUrl([
+        'scope' => 'read_only',
+    ]);
+    echo "<a href=\"$url\">Connect with Stripe</a>\n";
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/init.php b/stripe-php/vendor/stripe/stripe-php/init.php
new file mode 100644
index 00000000..79cf657a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/init.php
@@ -0,0 +1,105 @@
+<?php
+
+// Stripe singleton
+require(dirname(__FILE__) . '/lib/Stripe.php');
+
+// Utilities
+require(dirname(__FILE__) . '/lib/Util/AutoPagingIterator.php');
+require(dirname(__FILE__) . '/lib/Util/LoggerInterface.php');
+require(dirname(__FILE__) . '/lib/Util/DefaultLogger.php');
+require(dirname(__FILE__) . '/lib/Util/RandomGenerator.php');
+require(dirname(__FILE__) . '/lib/Util/RequestOptions.php');
+require(dirname(__FILE__) . '/lib/Util/Set.php');
+require(dirname(__FILE__) . '/lib/Util/Util.php');
+
+// HttpClient
+require(dirname(__FILE__) . '/lib/HttpClient/ClientInterface.php');
+require(dirname(__FILE__) . '/lib/HttpClient/CurlClient.php');
+
+// Errors
+require(dirname(__FILE__) . '/lib/Error/Base.php');
+require(dirname(__FILE__) . '/lib/Error/Api.php');
+require(dirname(__FILE__) . '/lib/Error/ApiConnection.php');
+require(dirname(__FILE__) . '/lib/Error/Authentication.php');
+require(dirname(__FILE__) . '/lib/Error/Card.php');
+require(dirname(__FILE__) . '/lib/Error/Idempotency.php');
+require(dirname(__FILE__) . '/lib/Error/InvalidRequest.php');
+require(dirname(__FILE__) . '/lib/Error/Permission.php');
+require(dirname(__FILE__) . '/lib/Error/RateLimit.php');
+require(dirname(__FILE__) . '/lib/Error/SignatureVerification.php');
+
+// OAuth errors
+require(dirname(__FILE__) . '/lib/Error/OAuth/OAuthBase.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidClient.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidGrant.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidRequest.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidScope.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/UnsupportedGrantType.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/UnsupportedResponseType.php');
+
+// API operations
+require(dirname(__FILE__) . '/lib/ApiOperations/All.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Create.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Delete.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/NestedResource.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Request.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Retrieve.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Update.php');
+
+// Plumbing
+require(dirname(__FILE__) . '/lib/ApiResponse.php');
+require(dirname(__FILE__) . '/lib/StripeObject.php');
+require(dirname(__FILE__) . '/lib/ApiRequestor.php');
+require(dirname(__FILE__) . '/lib/ApiResource.php');
+require(dirname(__FILE__) . '/lib/SingletonApiResource.php');
+
+// Stripe API Resources
+require(dirname(__FILE__) . '/lib/Account.php');
+require(dirname(__FILE__) . '/lib/AlipayAccount.php');
+require(dirname(__FILE__) . '/lib/ApplePayDomain.php');
+require(dirname(__FILE__) . '/lib/ApplicationFee.php');
+require(dirname(__FILE__) . '/lib/ApplicationFeeRefund.php');
+require(dirname(__FILE__) . '/lib/Balance.php');
+require(dirname(__FILE__) . '/lib/BalanceTransaction.php');
+require(dirname(__FILE__) . '/lib/BankAccount.php');
+require(dirname(__FILE__) . '/lib/BitcoinReceiver.php');
+require(dirname(__FILE__) . '/lib/BitcoinTransaction.php');
+require(dirname(__FILE__) . '/lib/Card.php');
+require(dirname(__FILE__) . '/lib/Charge.php');
+require(dirname(__FILE__) . '/lib/Collection.php');
+require(dirname(__FILE__) . '/lib/CountrySpec.php');
+require(dirname(__FILE__) . '/lib/Coupon.php');
+require(dirname(__FILE__) . '/lib/Customer.php');
+require(dirname(__FILE__) . '/lib/Dispute.php');
+require(dirname(__FILE__) . '/lib/EphemeralKey.php');
+require(dirname(__FILE__) . '/lib/Event.php');
+require(dirname(__FILE__) . '/lib/ExchangeRate.php');
+require(dirname(__FILE__) . '/lib/FileUpload.php');
+require(dirname(__FILE__) . '/lib/Invoice.php');
+require(dirname(__FILE__) . '/lib/InvoiceItem.php');
+require(dirname(__FILE__) . '/lib/LoginLink.php');
+require(dirname(__FILE__) . '/lib/Order.php');
+require(dirname(__FILE__) . '/lib/OrderReturn.php');
+require(dirname(__FILE__) . '/lib/Payout.php');
+require(dirname(__FILE__) . '/lib/Plan.php');
+require(dirname(__FILE__) . '/lib/Product.php');
+require(dirname(__FILE__) . '/lib/Recipient.php');
+require(dirname(__FILE__) . '/lib/RecipientTransfer.php');
+require(dirname(__FILE__) . '/lib/Refund.php');
+require(dirname(__FILE__) . '/lib/SKU.php');
+require(dirname(__FILE__) . '/lib/Source.php');
+require(dirname(__FILE__) . '/lib/SourceTransaction.php');
+require(dirname(__FILE__) . '/lib/Subscription.php');
+require(dirname(__FILE__) . '/lib/SubscriptionItem.php');
+require(dirname(__FILE__) . '/lib/ThreeDSecure.php');
+require(dirname(__FILE__) . '/lib/Token.php');
+require(dirname(__FILE__) . '/lib/Topup.php');
+require(dirname(__FILE__) . '/lib/Transfer.php');
+require(dirname(__FILE__) . '/lib/TransferReversal.php');
+
+// OAuth
+require(dirname(__FILE__) . '/lib/OAuth.php');
+
+// Webhooks
+require(dirname(__FILE__) . '/lib/Webhook.php');
+require(dirname(__FILE__) . '/lib/WebhookSignature.php');
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Account.php b/stripe-php/vendor/stripe/stripe-php/lib/Account.php
new file mode 100644
index 00000000..d77d60de
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Account.php
@@ -0,0 +1,234 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Account
+ *
+ * @property string $id
+ * @property string $object
+ * @property string $business_logo
+ * @property string $business_name
+ * @property string $business_primary_color
+ * @property string $business_url
+ * @property bool $charges_enabled
+ * @property string $country
+ * @property int $created
+ * @property bool $debit_negative_balances
+ * @property mixed $decline_charge_on
+ * @property string $default_currency
+ * @property bool $details_submitted
+ * @property string $display_name
+ * @property string $email
+ * @property mixed $external_accounts
+ * @property mixed $legal_entity
+ * @property StripeObject $metadata
+ * @property mixed $payout_schedule
+ * @property string $payout_statement_descriptor
+ * @property bool $payouts_enabled
+ * @property string $product_description
+ * @property string $statement_descriptor
+ * @property string $support_email
+ * @property string $support_phone
+ * @property string $timezone
+ * @property mixed $tos_acceptance
+ * @property mixed $verification
+ *
+ * @package Stripe
+ */
+class Account extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve {
+        retrieve as protected _retrieve;
+    }
+    use ApiOperations\Update;
+
+    public static function getSavedNestedResources()
+    {
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set([
+                'external_account',
+                'bank_account',
+            ]);
+        }
+        return $savedNestedResources;
+    }
+
+    const PATH_EXTERNAL_ACCOUNTS = '/external_accounts';
+    const PATH_LOGIN_LINKS = '/login_links';
+
+    public function instanceUrl()
+    {
+        if ($this['id'] === null) {
+            return '/v1/account';
+        } else {
+            return parent::instanceUrl();
+        }
+    }
+
+    /**
+     * @param array|string|null $id The ID of the account to retrieve, or an
+     *     options array containing an `id` key.
+     * @param array|string|null $opts
+     *
+     * @return Account
+     */
+    public static function retrieve($id = null, $opts = null)
+    {
+        if (!$opts && is_string($id) && substr($id, 0, 3) === 'sk_') {
+            $opts = $id;
+            $id = null;
+        }
+        return self::_retrieve($id, $opts);
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return Account The rejected account.
+     */
+    public function reject($params = null, $opts = null)
+    {
+        $url = $this->instanceUrl() . '/reject';
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|null $clientId
+     * @param array|string|null $opts
+     *
+     * @return StripeObject Object containing the response from the API.
+     */
+    public function deauthorize($clientId = null, $opts = null)
+    {
+        $params = [
+            'client_id' => $clientId,
+            'stripe_user_id' => $this->id,
+        ];
+        OAuth::deauthorize($params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account on which to create the external account.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return BankAccount|Card
+     */
+    public static function createExternalAccount($id, $params = null, $opts = null)
+    {
+        return self::_createNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account to which the external account belongs.
+     * @param array|null $externalAccountId The ID of the external account to retrieve.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return BankAccount|Card
+     */
+    public static function retrieveExternalAccount($id, $externalAccountId, $params = null, $opts = null)
+    {
+        return self::_retrieveNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account to which the external account belongs.
+     * @param array|null $externalAccountId The ID of the external account to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return BankAccount|Card
+     */
+    public static function updateExternalAccount($id, $externalAccountId, $params = null, $opts = null)
+    {
+        return self::_updateNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account to which the external account belongs.
+     * @param array|null $externalAccountId The ID of the external account to delete.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return BankAccount|Card
+     */
+    public static function deleteExternalAccount($id, $externalAccountId, $params = null, $opts = null)
+    {
+        return self::_deleteNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account on which to retrieve the external accounts.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return BankAccount|Card
+     */
+    public static function allExternalAccounts($id, $params = null, $opts = null)
+    {
+        return self::_allNestedResources($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account on which to create the login link.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return LoginLink
+     */
+    public static function createLoginLink($id, $params = null, $opts = null)
+    {
+        return self::_createNestedResource($id, static::PATH_LOGIN_LINKS, $params, $opts);
+    }
+
+    public function serializeParameters($force = false)
+    {
+        $update = parent::serializeParameters($force);
+        if (isset($this->_values['legal_entity'])) {
+            $entity = $this['legal_entity'];
+            if (isset($entity->_values['additional_owners'])) {
+                $owners = $entity['additional_owners'];
+                $entityUpdate = isset($update['legal_entity']) ? $update['legal_entity'] : [];
+                $entityUpdate['additional_owners'] = $this->serializeAdditionalOwners($entity, $owners);
+                $update['legal_entity'] = $entityUpdate;
+            }
+        }
+        return $update;
+    }
+
+    private function serializeAdditionalOwners($legalEntity, $additionalOwners)
+    {
+        if (isset($legalEntity->_originalValues['additional_owners'])) {
+            $originalValue = $legalEntity->_originalValues['additional_owners'];
+        } else {
+            $originalValue = [];
+        }
+        if (($originalValue) && (count($originalValue) > count($additionalOwners))) {
+            throw new \InvalidArgumentException(
+                "You cannot delete an item from an array, you must instead set a new array"
+            );
+        }
+
+        $updateArr = [];
+        foreach ($additionalOwners as $i => $v) {
+            $update = ($v instanceof StripeObject) ? $v->serializeParameters() : $v;
+
+            if ($update !== []) {
+                if (!$originalValue || ($update != $legalEntity->serializeParamsValue($originalValue[$i], null, false, true))) {
+                    $updateArr[$i] = $update;
+                }
+            }
+        }
+        return $updateArr;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/AlipayAccount.php b/stripe-php/vendor/stripe/stripe-php/lib/AlipayAccount.php
new file mode 100644
index 00000000..fb7d5bb7
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/AlipayAccount.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class AlipayAccount
+ *
+ * @package Stripe
+ *
+ * @deprecated Alipay accounts are deprecated. Please use the sources API instead.
+ * @link https://stripe.com/docs/sources/alipay
+ */
+class AlipayAccount extends ApiResource
+{
+    use ApiOperations\Delete;
+    use ApiOperations\Update;
+
+    /**
+     * @return string The instance URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public function instanceUrl()
+    {
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+        } else {
+            $msg = "Alipay accounts cannot be accessed without a customer ID.";
+            throw new Error\InvalidRequest($msg, null);
+        }
+        $parentExtn = urlencode(Util\Util::utf8($parent));
+        $extn = urlencode(Util\Util::utf8($this['id']));
+        return "$base/$parentExtn/$path/$extn";
+    }
+
+    /**
+     * @param array|string $_id
+     * @param array|string|null $_opts
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     *
+     * @deprecated Alipay accounts are deprecated. Please use the sources API instead.
+     * @link https://stripe.com/docs/sources/alipay
+     */
+    public static function retrieve($_id, $_opts = null)
+    {
+        $msg = "Alipay accounts cannot be accessed without a customer ID. " .
+               "Retrieve an Alipay account using \$customer->sources->retrieve('alipay_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
+    /**
+     * @param string $_id
+     * @param array|null $_params
+     * @param array|string|null $_options
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     *
+     * @deprecated Alipay accounts are deprecated. Please use the sources API instead.
+     * @link https://stripe.com/docs/sources/alipay
+     */
+    public static function update($_id, $_params = null, $_options = null)
+    {
+        $msg = "Alipay accounts cannot be accessed without a customer ID. " .
+               "Call save() on \$customer->sources->retrieve('alipay_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/All.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/All.php
new file mode 100644
index 00000000..3b0a0c63
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/All.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for listable resources. Adds a `all()` static method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait All
+{
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return Collection of ApiResources
+     */
+    public static function all($params = null, $opts = null)
+    {
+        self::_validateParams($params);
+        $url = static::classUrl();
+
+        list($response, $opts) = static::_staticRequest('get', $url, $params, $opts);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        if (!is_a($obj, 'Stripe\\Collection')) {
+            $class = get_class($obj);
+            $message = "Expected type \"Stripe\\Collection\", got \"$class\" instead";
+            throw new Error\Api($message);
+        }
+        $obj->setLastResponse($response);
+        $obj->setRequestParams($params);
+        return $obj;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Create.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Create.php
new file mode 100644
index 00000000..bbe9202b
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Create.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for creatable resources. Adds a `create()` static method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Create
+{
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return ApiResource The created resource.
+     */
+    public static function create($params = null, $options = null)
+    {
+        self::_validateParams($params);
+        $url = static::classUrl();
+
+        list($response, $opts) = static::_staticRequest('post', $url, $params, $options);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Delete.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Delete.php
new file mode 100644
index 00000000..622db1ff
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Delete.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for deletable resources. Adds a `delete()` method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Delete
+{
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource The deleted resource.
+     */
+    public function delete($params = null, $opts = null)
+    {
+        self::_validateParams($params);
+
+        $url = $this->instanceUrl();
+        list($response, $opts) = $this->_request('delete', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/NestedResource.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/NestedResource.php
new file mode 100644
index 00000000..88605bc7
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/NestedResource.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for resources that have nested resources.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait NestedResource
+{
+    /**
+     * @param string $method
+     * @param string $url
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Stripe\StripeObject
+     */
+    protected static function _nestedResourceOperation($method, $url, $params = null, $options = null)
+    {
+        self::_validateParams($params);
+
+        list($response, $opts) = static::_staticRequest($method, $url, $params, $options);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param string|null $nestedId
+     *
+     * @return string
+     */
+    protected static function _nestedResourceUrl($id, $nestedPath, $nestedId = null)
+    {
+        $url = static::resourceUrl($id) . $nestedPath;
+        if ($nestedId !== null) {
+            $url .= "/$nestedId";
+        }
+        return $url;
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Stripe\StripeObject
+     */
+    protected static function _createNestedResource($id, $nestedPath, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath);
+        return self::_nestedResourceOperation('post', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Stripe\StripeObject
+     */
+    protected static function _retrieveNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId);
+        return self::_nestedResourceOperation('get', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Stripe\StripeObject
+     */
+    protected static function _updateNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId);
+        return self::_nestedResourceOperation('post', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Stripe\StripeObject
+     */
+    protected static function _deleteNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId);
+        return self::_nestedResourceOperation('delete', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Stripe\StripeObject
+     */
+    protected static function _allNestedResources($id, $nestedPath, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath);
+        return self::_nestedResourceOperation('get', $url, $params, $options);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Request.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Request.php
new file mode 100644
index 00000000..4339a837
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Request.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for resources that need to make API requests.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Request
+{
+    /**
+     * @param array|null|mixed $params The list of parameters to validate
+     *
+     * @throws Stripe\Error\Api if $params exists and is not an array
+     */
+    protected static function _validateParams($params = null)
+    {
+        if ($params && !is_array($params)) {
+            $message = "You must pass an array as the first argument to Stripe API "
+               . "method calls.  (HINT: an example call to create a charge "
+               . "would be: \"Stripe\\Charge::create(['amount' => 100, "
+               . "'currency' => 'usd', 'source' => 'tok_1234'])\")";
+            throw new Error\Api($message);
+        }
+    }
+
+    /**
+     * @param string $method HTTP method ('get', 'post', etc.)
+     * @param string $url URL for the request
+     * @param array $params list of parameters for the request
+     * @param array|string|null $options
+     *
+     * @return array tuple containing (the JSON response, $options)
+     */
+    protected function _request($method, $url, $params = [], $options = null)
+    {
+        $opts = $this->_opts->merge($options);
+        list($resp, $options) = static::_staticRequest($method, $url, $params, $opts);
+        $this->setLastResponse($resp);
+        return [$resp->json, $options];
+    }
+
+    /**
+     * @param string $method HTTP method ('get', 'post', etc.)
+     * @param string $url URL for the request
+     * @param array $params list of parameters for the request
+     * @param array|string|null $options
+     *
+     * @return array tuple containing (the JSON response, $options)
+     */
+    protected static function _staticRequest($method, $url, $params, $options)
+    {
+        $opts = \Stripe\Util\RequestOptions::parse($options);
+        $requestor = new \Stripe\ApiRequestor($opts->apiKey, static::baseUrl());
+        list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers);
+        $opts->discardNonPersistentHeaders();
+        return [$response, $opts];
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Retrieve.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Retrieve.php
new file mode 100644
index 00000000..1f21d47a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Retrieve.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for retrievable resources. Adds a `retrieve()` static method to the
+ * class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Retrieve
+{
+    /**
+     * @param array|string $id The ID of the API resource to retrieve,
+     *     or an options array containing an `id` key.
+     * @param array|string|null $opts
+     *
+     * @return Stripe\StripeObject
+     */
+    public static function retrieve($id, $opts = null)
+    {
+        $opts = \Stripe\Util\RequestOptions::parse($opts);
+        $instance = new static($id, $opts);
+        $instance->refresh();
+        return $instance;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Update.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Update.php
new file mode 100644
index 00000000..85463ae2
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiOperations/Update.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for updatable resources. Adds an `update()` static method and a
+ * `save()` method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Update
+{
+    /**
+     * @param string $id The ID of the resource to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource The updated resource.
+     */
+    public static function update($id, $params = null, $opts = null)
+    {
+        self::_validateParams($params);
+        $url = static::resourceUrl($id);
+
+        list($response, $opts) = static::_staticRequest('post', $url, $params, $opts);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+
+    /**
+     * @param array|string|null $opts
+     *
+     * @return ApiResource The saved resource.
+     */
+    public function save($opts = null)
+    {
+        $params = $this->serializeParameters();
+        if (count($params) > 0) {
+            $url = $this->instanceUrl();
+            list($response, $opts) = $this->_request('post', $url, $params, $opts);
+            $this->refreshFrom($response, $opts);
+        }
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiRequestor.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiRequestor.php
new file mode 100644
index 00000000..334d8442
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiRequestor.php
@@ -0,0 +1,327 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ApiRequestor
+ *
+ * @package Stripe
+ */
+class ApiRequestor
+{
+    private $_apiKey;
+
+    private $_apiBase;
+
+    private static $_httpClient;
+
+    public function __construct($apiKey = null, $apiBase = null)
+    {
+        $this->_apiKey = $apiKey;
+        if (!$apiBase) {
+            $apiBase = Stripe::$apiBase;
+        }
+        $this->_apiBase = $apiBase;
+    }
+
+    private static function _encodeObjects($d)
+    {
+        if ($d instanceof ApiResource) {
+            return Util\Util::utf8($d->id);
+        } elseif ($d === true) {
+            return 'true';
+        } elseif ($d === false) {
+            return 'false';
+        } elseif (is_array($d)) {
+            $res = [];
+            foreach ($d as $k => $v) {
+                $res[$k] = self::_encodeObjects($v);
+            }
+            return $res;
+        } else {
+            return Util\Util::utf8($d);
+        }
+    }
+
+    /**
+     * @param string $method
+     * @param string $url
+     * @param array|null $params
+     * @param array|null $headers
+     *
+     * @return array An array whose first element is an API response and second
+     *    element is the API key used to make the request.
+     */
+    public function request($method, $url, $params = null, $headers = null)
+    {
+        $params = $params ?: [];
+        $headers = $headers ?: [];
+        list($rbody, $rcode, $rheaders, $myApiKey) =
+        $this->_requestRaw($method, $url, $params, $headers);
+        $json = $this->_interpretResponse($rbody, $rcode, $rheaders);
+        $resp = new ApiResponse($rbody, $rcode, $rheaders, $json);
+        return [$resp, $myApiKey];
+    }
+
+    /**
+     * @param string $rbody A JSON string.
+     * @param int $rcode
+     * @param array $rheaders
+     * @param array $resp
+     *
+     * @throws Error\InvalidRequest if the error is caused by the user.
+     * @throws Error\Idempotency if the error is caused by an idempotency key.
+     * @throws Error\Authentication if the error is caused by a lack of
+     *    permissions.
+     * @throws Error\Permission if the error is caused by insufficient
+     *    permissions.
+     * @throws Error\Card if the error is the error code is 402 (payment
+     *    required)
+     * @throws Error\RateLimit if the error is caused by too many requests
+     *    hitting the API.
+     * @throws Error\Api otherwise.
+     */
+    public function handleErrorResponse($rbody, $rcode, $rheaders, $resp)
+    {
+        if (!is_array($resp) || !isset($resp['error'])) {
+            $msg = "Invalid response object from API: $rbody "
+              . "(HTTP response code was $rcode)";
+            throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders);
+        }
+
+        $errorData = $resp['error'];
+
+        $error = null;
+        if (is_string($errorData)) {
+            $error = self::_specificOAuthError($rbody, $rcode, $rheaders, $resp, $errorData);
+        }
+        if (!$error) {
+            $error = self::_specificAPIError($rbody, $rcode, $rheaders, $resp, $errorData);
+        }
+
+        throw $error;
+    }
+
+    private static function _specificAPIError($rbody, $rcode, $rheaders, $resp, $errorData)
+    {
+        $msg = isset($errorData['message']) ? $errorData['message'] : null;
+        $param = isset($errorData['param']) ? $errorData['param'] : null;
+        $code = isset($errorData['code']) ? $errorData['code'] : null;
+        $type = isset($errorData['type']) ? $errorData['type'] : null;
+
+        switch ($rcode) {
+            case 400:
+                // 'rate_limit' code is deprecated, but left here for backwards compatibility
+                // for API versions earlier than 2015-09-08
+                if ($code == 'rate_limit') {
+                    return new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders);
+                }
+                if ($type == 'idempotency_error') {
+                    return new Error\Idempotency($msg, $rcode, $rbody, $resp, $rheaders);
+                }
+
+                // intentional fall-through
+            case 404:
+                return new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp, $rheaders);
+            case 401:
+                return new Error\Authentication($msg, $rcode, $rbody, $resp, $rheaders);
+            case 402:
+                return new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders);
+            case 403:
+                return new Error\Permission($msg, $rcode, $rbody, $resp, $rheaders);
+            case 429:
+                return new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders);
+            default:
+                return new Error\Api($msg, $rcode, $rbody, $resp, $rheaders);
+        }
+    }
+
+    private static function _specificOAuthError($rbody, $rcode, $rheaders, $resp, $errorCode)
+    {
+        $description = isset($resp['error_description']) ? $resp['error_description'] : $errorCode;
+
+        switch ($errorCode) {
+            case 'invalid_client':
+                return new Error\OAuth\InvalidClient($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'invalid_grant':
+                return new Error\OAuth\InvalidGrant($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'invalid_request':
+                return new Error\OAuth\InvalidRequest($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'invalid_scope':
+                return new Error\OAuth\InvalidScope($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'unsupported_grant_type':
+                return new Error\OAuth\UnsupportedGrantType($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'unsupported_response_type':
+                return new Error\OAuth\UnsupportedResponseType($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+        }
+
+        return null;
+    }
+
+    private static function _formatAppInfo($appInfo)
+    {
+        if ($appInfo !== null) {
+            $string = $appInfo['name'];
+            if ($appInfo['version'] !== null) {
+                $string .= '/' . $appInfo['version'];
+            }
+            if ($appInfo['url'] !== null) {
+                $string .= ' (' . $appInfo['url'] . ')';
+            }
+            return $string;
+        } else {
+            return null;
+        }
+    }
+
+    private static function _defaultHeaders($apiKey, $clientInfo = null)
+    {
+        $uaString = 'Stripe/v1 PhpBindings/' . Stripe::VERSION;
+
+        $langVersion = phpversion();
+        $uname = php_uname();
+
+        $appInfo = Stripe::getAppInfo();
+        $ua = [
+            'bindings_version' => Stripe::VERSION,
+            'lang' => 'php',
+            'lang_version' => $langVersion,
+            'publisher' => 'stripe',
+            'uname' => $uname,
+        ];
+        if ($clientInfo) {
+            $ua = array_merge($clientInfo, $ua);
+        }
+        if ($appInfo !== null) {
+            $uaString .= ' ' . self::_formatAppInfo($appInfo);
+            $ua['application'] = $appInfo;
+        }
+
+        $defaultHeaders = [
+            'X-Stripe-Client-User-Agent' => json_encode($ua),
+            'User-Agent' => $uaString,
+            'Authorization' => 'Bearer ' . $apiKey,
+        ];
+        return $defaultHeaders;
+    }
+
+    private function _requestRaw($method, $url, $params, $headers)
+    {
+        $myApiKey = $this->_apiKey;
+        if (!$myApiKey) {
+            $myApiKey = Stripe::$apiKey;
+        }
+
+        if (!$myApiKey) {
+            $msg = 'No API key provided.  (HINT: set your API key using '
+              . '"Stripe::setApiKey(<API-KEY>)".  You can generate API keys from '
+              . 'the Stripe web interface.  See https://stripe.com/api for '
+              . 'details, or email support@stripe.com if you have any questions.';
+            throw new Error\Authentication($msg);
+        }
+
+        // Clients can supply arbitrary additional keys to be included in the
+        // X-Stripe-Client-User-Agent header via the optional getUserAgentInfo()
+        // method
+        $clientUAInfo = null;
+        if (method_exists($this->httpClient(), 'getUserAgentInfo')) {
+            $clientUAInfo = $this->httpClient()->getUserAgentInfo();
+        }
+
+        $absUrl = $this->_apiBase.$url;
+        $params = self::_encodeObjects($params);
+        $defaultHeaders = $this->_defaultHeaders($myApiKey, $clientUAInfo);
+        if (Stripe::$apiVersion) {
+            $defaultHeaders['Stripe-Version'] = Stripe::$apiVersion;
+        }
+
+        if (Stripe::$accountId) {
+            $defaultHeaders['Stripe-Account'] = Stripe::$accountId;
+        }
+
+        $hasFile = false;
+        $hasCurlFile = class_exists('\CURLFile', false);
+        foreach ($params as $k => $v) {
+            if (is_resource($v)) {
+                $hasFile = true;
+                $params[$k] = self::_processResourceParam($v, $hasCurlFile);
+            } elseif ($hasCurlFile && $v instanceof \CURLFile) {
+                $hasFile = true;
+            }
+        }
+
+        if ($hasFile) {
+            $defaultHeaders['Content-Type'] = 'multipart/form-data';
+        } else {
+            $defaultHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
+        }
+
+        $combinedHeaders = array_merge($defaultHeaders, $headers);
+        $rawHeaders = [];
+
+        foreach ($combinedHeaders as $header => $value) {
+            $rawHeaders[] = $header . ': ' . $value;
+        }
+
+        list($rbody, $rcode, $rheaders) = $this->httpClient()->request(
+            $method,
+            $absUrl,
+            $rawHeaders,
+            $params,
+            $hasFile
+        );
+        return [$rbody, $rcode, $rheaders, $myApiKey];
+    }
+
+    private function _processResourceParam($resource, $hasCurlFile)
+    {
+        if (get_resource_type($resource) !== 'stream') {
+            throw new Error\Api(
+                'Attempted to upload a resource that is not a stream'
+            );
+        }
+
+        $metaData = stream_get_meta_data($resource);
+        if ($metaData['wrapper_type'] !== 'plainfile') {
+            throw new Error\Api(
+                'Only plainfile resource streams are supported'
+            );
+        }
+
+        if ($hasCurlFile) {
+            // We don't have the filename or mimetype, but the API doesn't care
+            return new \CURLFile($metaData['uri']);
+        } else {
+            return '@'.$metaData['uri'];
+        }
+    }
+
+    private function _interpretResponse($rbody, $rcode, $rheaders)
+    {
+        $resp = json_decode($rbody, true);
+        $jsonError = json_last_error();
+        if ($resp === null && $jsonError !== JSON_ERROR_NONE) {
+            $msg = "Invalid response body from API: $rbody "
+              . "(HTTP response code was $rcode, json_last_error() was $jsonError)";
+            throw new Error\Api($msg, $rcode, $rbody);
+        }
+
+        if ($rcode < 200 || $rcode >= 300) {
+            $this->handleErrorResponse($rbody, $rcode, $rheaders, $resp);
+        }
+        return $resp;
+    }
+
+    public static function setHttpClient($client)
+    {
+        self::$_httpClient = $client;
+    }
+
+    private function httpClient()
+    {
+        if (!self::$_httpClient) {
+            self::$_httpClient = HttpClient\CurlClient::instance();
+        }
+        return self::$_httpClient;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiResource.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiResource.php
new file mode 100644
index 00000000..3e0156e5
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiResource.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ApiResource
+ *
+ * @package Stripe
+ */
+abstract class ApiResource extends StripeObject
+{
+    use ApiOperations\Request;
+
+    /**
+     * @return Stripe\Util\Set A list of fields that can be their own type of
+     * API resource (say a nested card under an account for example), and if
+     * that resource is set, it should be transmitted to the API on a create or
+     * update. Doing so is not the default behavior because API resources
+     * should normally be persisted on their own RESTful endpoints.
+     */
+    public static function getSavedNestedResources()
+    {
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set();
+        }
+        return $savedNestedResources;
+    }
+
+    /**
+     * @var boolean A flag that can be set a behavior that will cause this
+     * resource to be encoded and sent up along with an update of its parent
+     * resource. This is usually not desirable because resources are updated
+     * individually on their own endpoints, but there are certain cases,
+     * replacing a customer's source for example, where this is allowed.
+     */
+    public $saveWithParent = false;
+
+    public function __set($k, $v)
+    {
+        parent::__set($k, $v);
+        $v = $this->$k;
+        if ((static::getSavedNestedResources()->includes($k)) &&
+            ($v instanceof ApiResource)) {
+            $v->saveWithParent = true;
+        }
+        return $v;
+    }
+
+    /**
+     * @return ApiResource The refreshed resource.
+     */
+    public function refresh()
+    {
+        $requestor = new ApiRequestor($this->_opts->apiKey, static::baseUrl());
+        $url = $this->instanceUrl();
+
+        list($response, $this->_opts->apiKey) = $requestor->request(
+            'get',
+            $url,
+            $this->_retrieveOptions,
+            $this->_opts->headers
+        );
+        $this->setLastResponse($response);
+        $this->refreshFrom($response->json, $this->_opts);
+        return $this;
+    }
+
+    /**
+     * @return string The name of the class, with namespacing and underscores
+     *    stripped.
+     */
+    public static function className()
+    {
+        $class = get_called_class();
+        // Useful for namespaces: Foo\Charge
+        if ($postfixNamespaces = strrchr($class, '\\')) {
+            $class = substr($postfixNamespaces, 1);
+        }
+        // Useful for underscored 'namespaces': Foo_Charge
+        if ($postfixFakeNamespaces = strrchr($class, '')) {
+            $class = $postfixFakeNamespaces;
+        }
+        if (substr($class, 0, strlen('Stripe')) == 'Stripe') {
+            $class = substr($class, strlen('Stripe'));
+        }
+        $class = str_replace('_', '', $class);
+        $name = urlencode($class);
+        $name = strtolower($name);
+        return $name;
+    }
+
+    /**
+     * @return string The base URL for the given class.
+     */
+    public static function baseUrl()
+    {
+        return Stripe::$apiBase;
+    }
+
+    /**
+     * @return string The endpoint URL for the given class.
+     */
+    public static function classUrl()
+    {
+        $base = static::className();
+        return "/v1/${base}s";
+    }
+
+    /**
+     * @return string The instance endpoint URL for the given class.
+     */
+    public static function resourceUrl($id)
+    {
+        if ($id === null) {
+            $class = get_called_class();
+            $message = "Could not determine which URL to request: "
+               . "$class instance has invalid ID: $id";
+            throw new Error\InvalidRequest($message, null);
+        }
+        $id = Util\Util::utf8($id);
+        $base = static::classUrl();
+        $extn = urlencode($id);
+        return "$base/$extn";
+    }
+
+    /**
+     * @return string The full API URL for this API resource.
+     */
+    public function instanceUrl()
+    {
+        return static::resourceUrl($this['id']);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApiResponse.php b/stripe-php/vendor/stripe/stripe-php/lib/ApiResponse.php
new file mode 100644
index 00000000..31f54a50
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApiResponse.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ApiResponse
+ *
+ * @package Stripe
+ */
+class ApiResponse
+{
+    public $headers;
+    public $body;
+    public $json;
+    public $code;
+
+    /**
+     * @param string $body
+     * @param integer $code
+     * @param array|null $headers
+     * @param array|null $json
+     *
+     * @return obj An APIResponse
+     */
+    public function __construct($body, $code, $headers, $json)
+    {
+        $this->body = $body;
+        $this->code = $code;
+        $this->headers = $headers;
+        $this->json = $json;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApplePayDomain.php b/stripe-php/vendor/stripe/stripe-php/lib/ApplePayDomain.php
new file mode 100644
index 00000000..124c5dc0
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApplePayDomain.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ApplePayDomain
+ *
+ * @package Stripe
+ */
+class ApplePayDomain extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+
+    /**
+     * @return string The class URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public static function classUrl()
+    {
+        return '/v1/apple_pay/domains';
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApplicationFee.php b/stripe-php/vendor/stripe/stripe-php/lib/ApplicationFee.php
new file mode 100644
index 00000000..05b94ea9
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApplicationFee.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ApplicationFee
+ *
+ * @property string $id
+ * @property string $object
+ * @property string $account
+ * @property int $amount
+ * @property int $amount_refunded
+ * @property string $application
+ * @property string $balance_transaction
+ * @property string $charge
+ * @property int $created
+ * @property string $currency
+ * @property bool $livemode
+ * @property string $originating_transaction
+ * @property bool $refunded
+ * @property Collection $refunds
+ *
+ * @package Stripe
+ */
+class ApplicationFee extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve;
+
+    const PATH_REFUNDS = '/refunds';
+
+    /**
+     * This is a special case because the application fee endpoint has an
+     *    underscore in it. The parent `className` function strips underscores.
+     *
+     * @return string The name of the class.
+     */
+    public static function className()
+    {
+        return 'application_fee';
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApplicationFee The refunded application fee.
+     */
+    public function refund($params = null, $opts = null)
+    {
+        $this->refunds->create($params, $opts);
+        $this->refresh();
+        return $this;
+    }
+
+    /**
+     * @param array|null $id The ID of the application fee on which to create the refund.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApplicationFeeRefund
+     */
+    public static function createRefund($id, $params = null, $opts = null)
+    {
+        return self::_createNestedResource($id, static::PATH_REFUNDS, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the application fee to which the refund belongs.
+     * @param array|null $refundId The ID of the refund to retrieve.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApplicationFeeRefund
+     */
+    public static function retrieveRefund($id, $refundId, $params = null, $opts = null)
+    {
+        return self::_retrieveNestedResource($id, static::PATH_REFUNDS, $refundId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the application fee to which the refund belongs.
+     * @param array|null $refundId The ID of the refund to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApplicationFeeRefund
+     */
+    public static function updateRefund($id, $refundId, $params = null, $opts = null)
+    {
+        return self::_updateNestedResource($id, static::PATH_REFUNDS, $refundId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the application fee on which to retrieve the refunds.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApplicationFeeRefund
+     */
+    public static function allRefunds($id, $params = null, $opts = null)
+    {
+        return self::_allNestedResources($id, static::PATH_REFUNDS, $params, $opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ApplicationFeeRefund.php b/stripe-php/vendor/stripe/stripe-php/lib/ApplicationFeeRefund.php
new file mode 100644
index 00000000..4b1c425a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ApplicationFeeRefund.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ApplicationFeeRefund
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property string $fee
+ * @property StripeObject $metadata
+ *
+ * @package Stripe
+ */
+class ApplicationFeeRefund extends ApiResource
+{
+    use ApiOperations\Update {
+        save as protected _save;
+    }
+
+    /**
+     * @return string The API URL for this Stripe refund.
+     */
+    public function instanceUrl()
+    {
+        $id = $this['id'];
+        $fee = $this['fee'];
+        if (!$id) {
+            throw new Error\InvalidRequest(
+                "Could not determine which URL to request: " .
+                "class instance has invalid ID: $id",
+                null
+            );
+        }
+        $id = Util\Util::utf8($id);
+        $fee = Util\Util::utf8($fee);
+
+        $base = ApplicationFee::classUrl();
+        $feeExtn = urlencode($fee);
+        $extn = urlencode($id);
+        return "$base/$feeExtn/refunds/$extn";
+    }
+
+    /**
+     * @param array|string|null $opts
+     *
+     * @return ApplicationFeeRefund The saved refund.
+     */
+    public function save($opts = null)
+    {
+        return $this->_save($opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Balance.php b/stripe-php/vendor/stripe/stripe-php/lib/Balance.php
new file mode 100644
index 00000000..115307f8
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Balance.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Balance
+ *
+ * @property string $object
+ * @property array $available
+ * @property bool $livemode
+ * @property array $pending
+ *
+ * @package Stripe
+ */
+class Balance extends SingletonApiResource
+{
+    /**
+     * @param array|string|null $opts
+     *
+     * @return Balance
+     */
+    public static function retrieve($opts = null)
+    {
+        return self::_singletonRetrieve($opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/BalanceTransaction.php b/stripe-php/vendor/stripe/stripe-php/lib/BalanceTransaction.php
new file mode 100644
index 00000000..f82888ab
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/BalanceTransaction.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class BalanceTransaction
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $available_on
+ * @property int $created
+ * @property string $currency
+ * @property string $description
+ * @property float $exchange_rate
+ * @property int $fee
+ * @property mixed $fee_details
+ * @property int $net
+ * @property string $source
+ * @property string $status
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class BalanceTransaction extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+
+    /**
+     * @return string The class URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public static function classUrl()
+    {
+        return "/v1/balance/history";
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/BankAccount.php b/stripe-php/vendor/stripe/stripe-php/lib/BankAccount.php
new file mode 100644
index 00000000..2afc312d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/BankAccount.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class BankAccount
+ *
+ * @property string $id
+ * @property string $object
+ * @property string $account
+ * @property string $account_holder_name
+ * @property string $account_holder_type
+ * @property string $bank_name
+ * @property string $country
+ * @property string $currency
+ * @property string $customer
+ * @property bool $default_for_currency
+ * @property string $fingerprint
+ * @property string $last4
+ * @property StripeObject $metadata
+ * @property string $routing_number
+ * @property string $status
+ *
+ * @package Stripe
+ */
+class BankAccount extends ApiResource
+{
+    use ApiOperations\Delete;
+    use ApiOperations\Update;
+
+    /**
+     * @return string The instance URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public function instanceUrl()
+    {
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+        } elseif ($this['account']) {
+            $base = Account::classUrl();
+            $parent = $this['account'];
+            $path = 'external_accounts';
+        } else {
+            $msg = "Bank accounts cannot be accessed without a customer ID or account ID.";
+            throw new Error\InvalidRequest($msg, null);
+        }
+        $parentExtn = urlencode(Util\Util::utf8($parent));
+        $extn = urlencode(Util\Util::utf8($this['id']));
+        return "$base/$parentExtn/$path/$extn";
+    }
+
+    /**
+     * @param array|string $_id
+     * @param array|string|null $_opts
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function retrieve($_id, $_opts = null)
+    {
+        $msg = "Bank accounts cannot be accessed without a customer ID or account ID. " .
+               "Retrieve a bank account using \$customer->sources->retrieve('bank_account_id') or " .
+               "\$account->external_accounts->retrieve('bank_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
+    /**
+     * @param string $_id
+     * @param array|null $_params
+     * @param array|string|null $_options
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function update($_id, $_params = null, $_options = null)
+    {
+        $msg = "Bank accounts cannot be accessed without a customer ID or account ID. " .
+               "Call save() on \$customer->sources->retrieve('bank_account_id') or " .
+               "\$account->external_accounts->retrieve('bank_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
+   /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return BankAccount The verified bank account.
+     */
+    public function verify($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/verify';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/BitcoinReceiver.php b/stripe-php/vendor/stripe/stripe-php/lib/BitcoinReceiver.php
new file mode 100644
index 00000000..8d04256b
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/BitcoinReceiver.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class BitcoinReceiver
+ *
+ * @package Stripe
+ *
+ * @deprecated Bitcoin receivers are deprecated. Please use the sources API instead.
+ * @link https://stripe.com/docs/sources/bitcoin
+ */
+class BitcoinReceiver extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+
+    /**
+     * @return string The class URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public static function classUrl()
+    {
+        return "/v1/bitcoin/receivers";
+    }
+
+    /**
+     * @return string The instance URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public function instanceUrl()
+    {
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+            $parentExtn = urlencode(Util\Util::utf8($parent));
+            $extn = urlencode(Util\Util::utf8($this['id']));
+            return "$base/$parentExtn/$path/$extn";
+        } else {
+            $base = BitcoinReceiver::classUrl();
+            $extn = urlencode(Util\Util::utf8($this['id']));
+            return "$base/$extn";
+        }
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/BitcoinTransaction.php b/stripe-php/vendor/stripe/stripe-php/lib/BitcoinTransaction.php
new file mode 100644
index 00000000..6b8e5421
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/BitcoinTransaction.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class BitcoinTransaction
+ *
+ * @package Stripe
+ */
+class BitcoinTransaction extends ApiResource
+{
+
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Card.php b/stripe-php/vendor/stripe/stripe-php/lib/Card.php
new file mode 100644
index 00000000..7e574236
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Card.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Card
+ *
+ * @property string $id
+ * @property string $object
+ * @property string $address_city
+ * @property string $address_country
+ * @property string $address_line1
+ * @property string $address_line1_check
+ * @property string $address_line2
+ * @property string $address_state
+ * @property string $address_zip
+ * @property string $address_zip_check
+ * @property string $brand
+ * @property string $country
+ * @property string $customer
+ * @property string $cvc_check
+ * @property string $dynamic_last4
+ * @property int $exp_month
+ * @property int $exp_year
+ * @property string $fingerprint
+ * @property string $funding
+ * @property string $last4
+ * @property StripeObject $metadata
+ * @property string $name
+ * @property string $tokenization_method
+ *
+ * @package Stripe
+ */
+class Card extends ApiResource
+{
+    use ApiOperations\Delete;
+    use ApiOperations\Update;
+
+    /**
+     * @return string The instance URL for this resource. It needs to be special
+     *    cased because cards are nested resources that may belong to different
+     *    top-level resources.
+     */
+    public function instanceUrl()
+    {
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+        } elseif ($this['account']) {
+            $base = Account::classUrl();
+            $parent = $this['account'];
+            $path = 'external_accounts';
+        } elseif ($this['recipient']) {
+            $base = Recipient::classUrl();
+            $parent = $this['recipient'];
+            $path = 'cards';
+        } else {
+            $msg = "Cards cannot be accessed without a customer ID, account ID or recipient ID.";
+            throw new Error\InvalidRequest($msg, null);
+        }
+        $parentExtn = urlencode(Util\Util::utf8($parent));
+        $extn = urlencode(Util\Util::utf8($this['id']));
+        return "$base/$parentExtn/$path/$extn";
+    }
+
+    /**
+     * @param array|string $_id
+     * @param array|string|null $_opts
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function retrieve($_id, $_opts = null)
+    {
+        $msg = "Cards cannot be accessed without a customer, recipient or account ID. " .
+               "Retrieve a card using \$customer->sources->retrieve('card_id'), " .
+               "\$recipient->cards->retrieve('card_id'), or";
+               "\$account->external_accounts->retrieve('card_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
+    /**
+     * @param string $_id
+     * @param array|null $_params
+     * @param array|string|null $_options
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function update($_id, $_params = null, $_options = null)
+    {
+        $msg = "Cards cannot be accessed without a customer, recipient or account ID. " .
+               "Call save() on \$customer->sources->retrieve('card_id'), " .
+               "\$recipient->cards->retrieve('card_id'), or";
+               "\$account->external_accounts->retrieve('card_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Charge.php b/stripe-php/vendor/stripe/stripe-php/lib/Charge.php
new file mode 100644
index 00000000..6d448022
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Charge.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Charge
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $amount_refunded
+ * @property string $application
+ * @property string $application_fee
+ * @property string $balance_transaction
+ * @property bool $captured
+ * @property int $created
+ * @property string $currency
+ * @property string $customer
+ * @property string $description
+ * @property string $destination
+ * @property string $dispute
+ * @property string $failure_code
+ * @property string $failure_message
+ * @property mixed $fraud_details
+ * @property string $invoice
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $on_behalf_of
+ * @property string $order
+ * @property mixed $outcome
+ * @property bool $paid
+ * @property string $receipt_email
+ * @property string $receipt_number
+ * @property bool $refunded
+ * @property Collection $refunds
+ * @property string $review
+ * @property mixed $shipping
+ * @property mixed $source
+ * @property string $source_transfer
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property string $transfer
+ * @property string $transfer_group
+ *
+ * @package Stripe
+ */
+class Charge extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Charge The refunded charge.
+     */
+    public function refund($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/refund';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Charge The captured charge.
+     */
+    public function capture($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/capture';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @deprecated Use the `save` method on the Dispute object
+     *
+     * @return array The updated dispute.
+     */
+    public function updateDispute($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/dispute';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom(['dispute' => $response], $opts, true);
+        return $this->dispute;
+    }
+
+    /**
+     * @param array|string|null $options
+     *
+     * @deprecated Use the `close` method on the Dispute object
+     *
+     * @return Charge The updated charge.
+     */
+    public function closeDispute($options = null)
+    {
+        $url = $this->instanceUrl() . '/dispute/close';
+        list($response, $opts) = $this->_request('post', $url, null, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|string|null $opts
+     *
+     * @return Charge The updated charge.
+     */
+    public function markAsFraudulent($opts = null)
+    {
+        $params = ['fraud_details' => ['user_report' => 'fraudulent']];
+        $url = $this->instanceUrl();
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|string|null $opts
+     *
+     * @return Charge The updated charge.
+     */
+    public function markAsSafe($opts = null)
+    {
+        $params = ['fraud_details' => ['user_report' => 'safe']];
+        $url = $this->instanceUrl();
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Collection.php b/stripe-php/vendor/stripe/stripe-php/lib/Collection.php
new file mode 100644
index 00000000..cfb4014a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Collection.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Collection
+ *
+ * @property string $object
+ * @property string $url
+ * @property bool $has_more
+ * @property mixed $data
+ *
+ * @package Stripe
+ */
+class Collection extends StripeObject
+{
+    use ApiOperations\Request;
+
+    protected $_requestParams = [];
+
+    /**
+     * @return string The base URL for the given class.
+     */
+    public static function baseUrl()
+    {
+        return Stripe::$apiBase;
+    }
+
+    public function setRequestParams($params)
+    {
+        $this->_requestParams = $params;
+    }
+
+    public function all($params = null, $opts = null)
+    {
+        list($url, $params) = $this->extractPathAndUpdateParams($params);
+
+        list($response, $opts) = $this->_request('get', $url, $params, $opts);
+        $this->_requestParams = $params;
+        return Util\Util::convertToStripeObject($response, $opts);
+    }
+
+    public function create($params = null, $opts = null)
+    {
+        list($url, $params) = $this->extractPathAndUpdateParams($params);
+
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->_requestParams = $params;
+        return Util\Util::convertToStripeObject($response, $opts);
+    }
+
+    public function retrieve($id, $params = null, $opts = null)
+    {
+        list($url, $params) = $this->extractPathAndUpdateParams($params);
+
+        $id = Util\Util::utf8($id);
+        $extn = urlencode($id);
+        list($response, $opts) = $this->_request(
+            'get',
+            "$url/$extn",
+            $params,
+            $opts
+        );
+        $this->_requestParams = $params;
+        return Util\Util::convertToStripeObject($response, $opts);
+    }
+
+    /**
+     * @return Util\AutoPagingIterator An iterator that can be used to iterate
+     *    across all objects across all pages. As page boundaries are
+     *    encountered, the next page will be fetched automatically for
+     *    continued iteration.
+     */
+    public function autoPagingIterator()
+    {
+        return new Util\AutoPagingIterator($this, $this->_requestParams);
+    }
+
+    private function extractPathAndUpdateParams($params)
+    {
+        $url = parse_url($this->url);
+        if (!isset($url['path'])) {
+            throw new Error\Api("Could not parse list url into parts: $url");
+        }
+
+        if (isset($url['query'])) {
+            // If the URL contains a query param, parse it out into $params so they
+            // don't interact weirdly with each other.
+            $query = [];
+            parse_str($url['query'], $query);
+            $params = array_merge($params ?: [], $query);
+        }
+
+        return [$url['path'], $params];
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/CountrySpec.php b/stripe-php/vendor/stripe/stripe-php/lib/CountrySpec.php
new file mode 100644
index 00000000..cdaa4e84
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/CountrySpec.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class CountrySpec
+ *
+ * @property string $id
+ * @property string $object
+ * @property string $default_currency
+ * @property mixed $supported_bank_account_currencies
+ * @property string[] $supported_payment_currencies
+ * @property string[] $supported_payment_methods
+ * @property mixed $verification_fields
+ *
+ * @package Stripe
+ */
+class CountrySpec extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+
+    /**
+     * This is a special case because the country specs endpoint has an
+     *    underscore in it. The parent `className` function strips underscores.
+     *
+     * @return string The name of the class.
+     */
+    public static function className()
+    {
+        return 'country_spec';
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Coupon.php b/stripe-php/vendor/stripe/stripe-php/lib/Coupon.php
new file mode 100644
index 00000000..a2d44443
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Coupon.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Coupon
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount_off
+ * @property int $created
+ * @property string $currency
+ * @property string $duration
+ * @property int $duration_in_months
+ * @property bool $livemode
+ * @property int $max_redemptions
+ * @property StripeObject $metadata
+ * @property int $percent_off
+ * @property int $redeem_by
+ * @property int $times_redeemed
+ * @property bool $valid
+ *
+ * @package Stripe
+ */
+class Coupon extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Customer.php b/stripe-php/vendor/stripe/stripe-php/lib/Customer.php
new file mode 100644
index 00000000..8d31b5ac
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Customer.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Customer
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $account_balance
+ * @property string $business_vat_id
+ * @property string $created
+ * @property string $currency
+ * @property string $default_source
+ * @property bool $delinquent
+ * @property string $description
+ * @property mixed $discount
+ * @property string $email
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $shipping
+ * @property Collection $sources
+ * @property Collection $subscriptions
+ *
+ * @package Stripe
+ */
+class Customer extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    public static function getSavedNestedResources()
+    {
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set([
+                'source',
+            ]);
+        }
+        return $savedNestedResources;
+    }
+
+    const PATH_SOURCES = '/sources';
+
+    /**
+     * @param array|null $params
+     *
+     * @return InvoiceItem The resulting invoice item.
+     */
+    public function addInvoiceItem($params = null)
+    {
+        $params = $params ?: [];
+        $params['customer'] = $this->id;
+        $ii = InvoiceItem::create($params, $this->_opts);
+        return $ii;
+    }
+
+    /**
+     * @param array|null $params
+     *
+     * @return array An array of the customer's Invoices.
+     */
+    public function invoices($params = null)
+    {
+        $params = $params ?: [];
+        $params['customer'] = $this->id;
+        $invoices = Invoice::all($params, $this->_opts);
+        return $invoices;
+    }
+
+    /**
+     * @param array|null $params
+     *
+     * @return array An array of the customer's InvoiceItems.
+     */
+    public function invoiceItems($params = null)
+    {
+        $params = $params ?: [];
+        $params['customer'] = $this->id;
+        $iis = InvoiceItem::all($params, $this->_opts);
+        return $iis;
+    }
+
+    /**
+     * @param array|null $params
+     *
+     * @return array An array of the customer's Charges.
+     */
+    public function charges($params = null)
+    {
+        $params = $params ?: [];
+        $params['customer'] = $this->id;
+        $charges = Charge::all($params, $this->_opts);
+        return $charges;
+    }
+
+    /**
+     * @param array|null $params
+     *
+     * @return Subscription The updated subscription.
+     */
+    public function updateSubscription($params = null)
+    {
+        $url = $this->instanceUrl() . '/subscription';
+        list($response, $opts) = $this->_request('post', $url, $params);
+        $this->refreshFrom(['subscription' => $response], $opts, true);
+        return $this->subscription;
+    }
+
+    /**
+     * @param array|null $params
+     *
+     * @return Subscription The cancelled subscription.
+     */
+    public function cancelSubscription($params = null)
+    {
+        $url = $this->instanceUrl() . '/subscription';
+        list($response, $opts) = $this->_request('delete', $url, $params);
+        $this->refreshFrom(['subscription' => $response], $opts, true);
+        return $this->subscription;
+    }
+
+    /**
+     * @return Customer The updated customer.
+     */
+    public function deleteDiscount()
+    {
+        $url = $this->instanceUrl() . '/discount';
+        list($response, $opts) = $this->_request('delete', $url);
+        $this->refreshFrom(['discount' => null], $opts, true);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer on which to create the source.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function createSource($id, $params = null, $opts = null)
+    {
+        return self::_createNestedResource($id, static::PATH_SOURCES, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer to which the source belongs.
+     * @param array|null $sourceId The ID of the source to retrieve.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function retrieveSource($id, $sourceId, $params = null, $opts = null)
+    {
+        return self::_retrieveNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer to which the source belongs.
+     * @param array|null $sourceId The ID of the source to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function updateSource($id, $sourceId, $params = null, $opts = null)
+    {
+        return self::_updateNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer to which the source belongs.
+     * @param array|null $sourceId The ID of the source to delete.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function deleteSource($id, $sourceId, $params = null, $opts = null)
+    {
+        return self::_deleteNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer on which to retrieve the sources.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function allSources($id, $params = null, $opts = null)
+    {
+        return self::_allNestedResources($id, static::PATH_SOURCES, $params, $opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Dispute.php b/stripe-php/vendor/stripe/stripe-php/lib/Dispute.php
new file mode 100644
index 00000000..eb0263aa
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Dispute.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Dispute
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property mixed $balance_transactions
+ * @property string $charge
+ * @property int $created
+ * @property string $currency
+ * @property mixed $evidence
+ * @property mixed $evidence_details
+ * @property bool $is_charge_refundable
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $reason
+ * @property string $status
+ *
+ * @package Stripe
+ */
+class Dispute extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @param array|string|null $options
+     *
+     * @return Dispute The closed dispute.
+     */
+    public function close($options = null)
+    {
+        $url = $this->instanceUrl() . '/close';
+        list($response, $opts) = $this->_request('post', $url, null, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/EphemeralKey.php b/stripe-php/vendor/stripe/stripe-php/lib/EphemeralKey.php
new file mode 100644
index 00000000..5bbef091
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/EphemeralKey.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class EphemeralKey
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $created
+ * @property int $expires
+ * @property bool $livemode
+ * @property string $secret
+ * @property array $associated_objects
+ *
+ * @package Stripe
+ */
+class EphemeralKey extends ApiResource
+{
+    use ApiOperations\Create {
+        create as protected _create;
+    }
+    use ApiOperations\Delete;
+
+    /**
+     * This is a special case because the ephemeral key endpoint has an
+     *    underscore in it. The parent `className` function strips underscores.
+     *
+     * @return string The name of the class.
+     */
+    public static function className()
+    {
+        return 'ephemeral_key';
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return EphemeralKey The created key.
+     */
+    public static function create($params = null, $opts = null)
+    {
+        if (!$opts['stripe_version']) {
+            throw new \InvalidArgumentException('stripe_version must be specified to create an ephemeral key');
+        }
+        return self::_create($params, $opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/Api.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/Api.php
new file mode 100644
index 00000000..dc7d069e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/Api.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Stripe\Error;
+
+class Api extends Base
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/ApiConnection.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/ApiConnection.php
new file mode 100644
index 00000000..06d097ea
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/ApiConnection.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Stripe\Error;
+
+class ApiConnection extends Base
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/Authentication.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/Authentication.php
new file mode 100644
index 00000000..c50b4f09
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/Authentication.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Stripe\Error;
+
+class Authentication extends Base
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/Base.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/Base.php
new file mode 100644
index 00000000..c0051e6a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/Base.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Stripe\Error;
+
+use Exception;
+
+abstract class Base extends Exception
+{
+    public function __construct(
+        $message,
+        $httpStatus = null,
+        $httpBody = null,
+        $jsonBody = null,
+        $httpHeaders = null
+    ) {
+        parent::__construct($message);
+        $this->httpStatus = $httpStatus;
+        $this->httpBody = $httpBody;
+        $this->jsonBody = $jsonBody;
+        $this->httpHeaders = $httpHeaders;
+        $this->requestId = null;
+
+        // TODO: make this a proper constructor argument in the next major
+        //       release.
+        $this->stripeCode = isset($jsonBody["error"]["code"]) ? $jsonBody["error"]["code"] : null;
+
+        if ($httpHeaders && isset($httpHeaders['Request-Id'])) {
+            $this->requestId = $httpHeaders['Request-Id'];
+        }
+    }
+
+    public function getStripeCode()
+    {
+        return $this->stripeCode;
+    }
+
+    public function getHttpStatus()
+    {
+        return $this->httpStatus;
+    }
+
+    public function getHttpBody()
+    {
+        return $this->httpBody;
+    }
+
+    public function getJsonBody()
+    {
+        return $this->jsonBody;
+    }
+
+    public function getHttpHeaders()
+    {
+        return $this->httpHeaders;
+    }
+
+    public function getRequestId()
+    {
+        return $this->requestId;
+    }
+
+    public function __toString()
+    {
+        $id = $this->requestId ? " from API request '{$this->requestId}'": "";
+        $message = explode("\n", parent::__toString());
+        $message[0] .= $id;
+        return implode("\n", $message);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/Card.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/Card.php
new file mode 100644
index 00000000..f3ff343d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/Card.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Stripe\Error;
+
+class Card extends Base
+{
+    public function __construct(
+        $message,
+        $stripeParam,
+        $stripeCode,
+        $httpStatus,
+        $httpBody,
+        $jsonBody,
+        $httpHeaders = null
+    ) {
+        parent::__construct($message, $httpStatus, $httpBody, $jsonBody, $httpHeaders);
+        $this->stripeParam = $stripeParam;
+
+        // TODO: once Error\Base accepts the error code as an argument, pass it
+        //       in the call to parent::__construct() and stop setting it here.
+        $this->stripeCode = $stripeCode;
+
+        // This one is not like the others because it was added later and we're
+        // trying to do our best not to change the public interface of this class'
+        // constructor.
+        // TODO: make this a proper constructor argument in the next major
+        //       release.
+        $this->declineCode = isset($jsonBody["error"]["decline_code"]) ? $jsonBody["error"]["decline_code"] : null;
+    }
+
+    public function getDeclineCode()
+    {
+        return $this->declineCode;
+    }
+
+    public function getStripeParam()
+    {
+        return $this->stripeParam;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/Idempotency.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/Idempotency.php
new file mode 100644
index 00000000..ea44d12e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/Idempotency.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Stripe\Error;
+
+class Idempotency extends Base
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/InvalidRequest.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/InvalidRequest.php
new file mode 100644
index 00000000..493bc184
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/InvalidRequest.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Stripe\Error;
+
+class InvalidRequest extends Base
+{
+    public function __construct(
+        $message,
+        $stripeParam,
+        $httpStatus = null,
+        $httpBody = null,
+        $jsonBody = null,
+        $httpHeaders = null
+    ) {
+        parent::__construct($message, $httpStatus, $httpBody, $jsonBody, $httpHeaders);
+        $this->stripeParam = $stripeParam;
+    }
+
+    public function getStripeParam()
+    {
+        return $this->stripeParam;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidClient.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidClient.php
new file mode 100644
index 00000000..b47346cd
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidClient.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidClient is raised when authentication fails.
+ */
+class InvalidClient extends OAuthBase
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidGrant.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidGrant.php
new file mode 100644
index 00000000..62df56c2
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidGrant.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidGrant is raised when a specified code doesn't exist, is
+ * expired, has been used, or doesn't belong to you; a refresh token doesn't
+ * exist, or doesn't belong to you; or if an API key's mode (live or test)
+ * doesn't match the mode of a code or refresh token.
+ */
+class InvalidGrant extends OAuthBase
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidRequest.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidRequest.php
new file mode 100644
index 00000000..7f0e8b4e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidRequest.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidRequest is raised when a code, refresh token, or grant type
+ * parameter is not provided, but was required.
+ */
+class InvalidRequest extends OAuthBase
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidScope.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidScope.php
new file mode 100644
index 00000000..03cc67e6
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/InvalidScope.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidScope is raised when an invalid scope parameter is provided.
+ */
+class InvalidScope extends OAuthBase
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/OAuthBase.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/OAuthBase.php
new file mode 100644
index 00000000..2f172133
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/OAuthBase.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+class OAuthBase extends \Stripe\Error\Base
+{
+    public function __construct(
+        $code,
+        $description,
+        $httpStatus = null,
+        $httpBody = null,
+        $jsonBody = null,
+        $httpHeaders = null
+    ) {
+        parent::__construct($description, $httpStatus, $httpBody, $jsonBody, $httpHeaders);
+        $this->code = $code;
+    }
+
+    public function getErrorCode()
+    {
+        return $this->code;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/UnsupportedGrantType.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/UnsupportedGrantType.php
new file mode 100644
index 00000000..421adc99
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/UnsupportedGrantType.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * UnsupportedGrantType is raised when an unuspported grant type
+ * parameter is specified.
+ */
+class UnsupportedGrantType extends OAuthBase
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/UnsupportedResponseType.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/UnsupportedResponseType.php
new file mode 100644
index 00000000..ed148789
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/OAuth/UnsupportedResponseType.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * UnsupportedResponseType is raised when an unsupported response type
+ * parameter is specified.
+ */
+class UnsupportedResponseType extends OAuthBase
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/Permission.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/Permission.php
new file mode 100644
index 00000000..df2b7874
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/Permission.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Stripe\Error;
+
+class Permission extends Base
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/RateLimit.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/RateLimit.php
new file mode 100644
index 00000000..2fa3be4e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/RateLimit.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Stripe\Error;
+
+class RateLimit extends InvalidRequest
+{
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Error/SignatureVerification.php b/stripe-php/vendor/stripe/stripe-php/lib/Error/SignatureVerification.php
new file mode 100644
index 00000000..98ddd3b0
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Error/SignatureVerification.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Stripe\Error;
+
+class SignatureVerification extends Base
+{
+    public function __construct(
+        $message,
+        $sigHeader,
+        $httpBody = null
+    ) {
+        parent::__construct($message, null, $httpBody, null, null);
+        $this->sigHeader = $sigHeader;
+    }
+
+    public function getSigHeader()
+    {
+        return $this->sigHeader;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Event.php b/stripe-php/vendor/stripe/stripe-php/lib/Event.php
new file mode 100644
index 00000000..c30d3e3d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Event.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Event
+ *
+ * @property string $id
+ * @property string $object
+ * @property string $api_version
+ * @property int $created
+ * @property mixed $data
+ * @property bool $livemode
+ * @property int $pending_webhooks
+ * @property mixed $request
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class Event extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ExchangeRate.php b/stripe-php/vendor/stripe/stripe-php/lib/ExchangeRate.php
new file mode 100644
index 00000000..40763261
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ExchangeRate.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ExchangeRate
+ *
+ * @package Stripe
+ */
+class ExchangeRate extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+
+    /**
+     * This is a special case because the exchange rates endpoint has an
+     *    underscore in it. The parent `className` function strips underscores.
+     *
+     * @return string The name of the class.
+     */
+    public static function className()
+    {
+        return 'exchange_rate';
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/FileUpload.php b/stripe-php/vendor/stripe/stripe-php/lib/FileUpload.php
new file mode 100644
index 00000000..aa0b7339
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/FileUpload.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class FileUpload
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $created
+ * @property string $purpose
+ * @property int $size
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class FileUpload extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+
+    public static function baseUrl()
+    {
+        return Stripe::$apiUploadBase;
+    }
+
+    public static function className()
+    {
+        return 'file';
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/HttpClient/ClientInterface.php b/stripe-php/vendor/stripe/stripe-php/lib/HttpClient/ClientInterface.php
new file mode 100644
index 00000000..606ddb1a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/HttpClient/ClientInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Stripe\HttpClient;
+
+interface ClientInterface
+{
+    /**
+     * @param string $method The HTTP method being used
+     * @param string $absUrl The URL being requested, including domain and protocol
+     * @param array $headers Headers to be used in the request (full strings, not KV pairs)
+     * @param array $params KV pairs for parameters. Can be nested for arrays and hashes
+     * @param boolean $hasFile Whether or not $params references a file (via an @ prefix or
+     *                         CurlFile)
+     * @throws \Stripe\Error\Api & \Stripe\Error\ApiConnection
+     * @return [$rawBody, $httpStatusCode, $httpHeader]
+     */
+    public function request($method, $absUrl, $headers, $params, $hasFile);
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/HttpClient/CurlClient.php b/stripe-php/vendor/stripe/stripe-php/lib/HttpClient/CurlClient.php
new file mode 100644
index 00000000..30ea686d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/HttpClient/CurlClient.php
@@ -0,0 +1,334 @@
+<?php
+
+namespace Stripe\HttpClient;
+
+use Stripe\Stripe;
+use Stripe\Error;
+use Stripe\Util;
+
+// cURL constants are not defined in PHP < 5.5
+
+// @codingStandardsIgnoreStart
+// PSR2 requires all constants be upper case. Sadly, the CURL_SSLVERSION
+// constants do not abide by those rules.
+
+// Note the values 1 and 6 come from their position in the enum that
+// defines them in cURL's source code.
+if (!defined('CURL_SSLVERSION_TLSv1')) {
+    define('CURL_SSLVERSION_TLSv1', 1);
+}
+if (!defined('CURL_SSLVERSION_TLSv1_2')) {
+    define('CURL_SSLVERSION_TLSv1_2', 6);
+}
+// @codingStandardsIgnoreEnd
+
+class CurlClient implements ClientInterface
+{
+    private static $instance;
+
+    public static function instance()
+    {
+        if (!self::$instance) {
+            self::$instance = new self();
+        }
+        return self::$instance;
+    }
+
+    protected $defaultOptions;
+
+    protected $userAgentInfo;
+
+    /**
+     * CurlClient constructor.
+     *
+     * Pass in a callable to $defaultOptions that returns an array of CURLOPT_* values to start
+     * off a request with, or an flat array with the same format used by curl_setopt_array() to
+     * provide a static set of options. Note that many options are overridden later in the request
+     * call, including timeouts, which can be set via setTimeout() and setConnectTimeout().
+     *
+     * Note that request() will silently ignore a non-callable, non-array $defaultOptions, and will
+     * throw an exception if $defaultOptions returns a non-array value.
+     *
+     * @param array|callable|null $defaultOptions
+     */
+    public function __construct($defaultOptions = null, $randomGenerator = null)
+    {
+        $this->defaultOptions = $defaultOptions;
+        $this->randomGenerator = $randomGenerator ?: new Util\RandomGenerator();
+        $this->initUserAgentInfo();
+    }
+
+    public function initUserAgentInfo()
+    {
+        $curlVersion = curl_version();
+        $this->userAgentInfo = [
+            'httplib' =>  'curl ' . $curlVersion['version'],
+            'ssllib' => $curlVersion['ssl_version'],
+        ];
+    }
+
+    public function getDefaultOptions()
+    {
+        return $this->defaultOptions;
+    }
+
+    public function getUserAgentInfo()
+    {
+        return $this->userAgentInfo;
+    }
+
+    // USER DEFINED TIMEOUTS
+
+    const DEFAULT_TIMEOUT = 80;
+    const DEFAULT_CONNECT_TIMEOUT = 30;
+
+    private $timeout = self::DEFAULT_TIMEOUT;
+    private $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT;
+
+    public function setTimeout($seconds)
+    {
+        $this->timeout = (int) max($seconds, 0);
+        return $this;
+    }
+
+    public function setConnectTimeout($seconds)
+    {
+        $this->connectTimeout = (int) max($seconds, 0);
+        return $this;
+    }
+
+    public function getTimeout()
+    {
+        return $this->timeout;
+    }
+
+    public function getConnectTimeout()
+    {
+        return $this->connectTimeout;
+    }
+
+    // END OF USER DEFINED TIMEOUTS
+
+    public function request($method, $absUrl, $headers, $params, $hasFile)
+    {
+        $method = strtolower($method);
+
+        $opts = [];
+        if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value
+            $opts = call_user_func_array($this->defaultOptions, func_get_args());
+            if (!is_array($opts)) {
+                throw new Error\Api("Non-array value returned by defaultOptions CurlClient callback");
+            }
+        } elseif (is_array($this->defaultOptions)) { // set default curlopts from array
+            $opts = $this->defaultOptions;
+        }
+
+        if ($method == 'get') {
+            if ($hasFile) {
+                throw new Error\Api(
+                    "Issuing a GET request with a file parameter"
+                );
+            }
+            $opts[CURLOPT_HTTPGET] = 1;
+            if (count($params) > 0) {
+                $encoded = Util\Util::urlEncode($params);
+                $absUrl = "$absUrl?$encoded";
+            }
+        } elseif ($method == 'post') {
+            $opts[CURLOPT_POST] = 1;
+            $opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : Util\Util::urlEncode($params);
+        } elseif ($method == 'delete') {
+            $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
+            if (count($params) > 0) {
+                $encoded = Util\Util::urlEncode($params);
+                $absUrl = "$absUrl?$encoded";
+            }
+        } else {
+            throw new Error\Api("Unrecognized method $method");
+        }
+
+        // It is only safe to retry network failures on POST requests if we
+        // add an Idempotency-Key header
+        if (($method == 'post') && (Stripe::$maxNetworkRetries > 0)) {
+            if (!isset($headers['Idempotency-Key'])) {
+                array_push($headers, 'Idempotency-Key: ' . $this->randomGenerator->uuid());
+            }
+        }
+
+        // Create a callback to capture HTTP headers for the response
+        $rheaders = [];
+        $headerCallback = function ($curl, $header_line) use (&$rheaders) {
+            // Ignore the HTTP request line (HTTP/1.1 200 OK)
+            if (strpos($header_line, ":") === false) {
+                return strlen($header_line);
+            }
+            list($key, $value) = explode(":", trim($header_line), 2);
+            $rheaders[trim($key)] = trim($value);
+            return strlen($header_line);
+        };
+
+        // By default for large request body sizes (> 1024 bytes), cURL will
+        // send a request without a body and with a `Expect: 100-continue`
+        // header, which gives the server a chance to respond with an error
+        // status code in cases where one can be determined right away (say
+        // on an authentication problem for example), and saves the "large"
+        // request body from being ever sent.
+        //
+        // Unfortunately, the bindings don't currently correctly handle the
+        // success case (in which the server sends back a 100 CONTINUE), so
+        // we'll error under that condition. To compensate for that problem
+        // for the time being, override cURL's behavior by simply always
+        // sending an empty `Expect:` header.
+        array_push($headers, 'Expect: ');
+
+        $absUrl = Util\Util::utf8($absUrl);
+        $opts[CURLOPT_URL] = $absUrl;
+        $opts[CURLOPT_RETURNTRANSFER] = true;
+        $opts[CURLOPT_CONNECTTIMEOUT] = $this->connectTimeout;
+        $opts[CURLOPT_TIMEOUT] = $this->timeout;
+        $opts[CURLOPT_HEADERFUNCTION] = $headerCallback;
+        $opts[CURLOPT_HTTPHEADER] = $headers;
+        $opts[CURLOPT_CAINFO] = Stripe::getCABundlePath();
+        if (!Stripe::getVerifySslCerts()) {
+            $opts[CURLOPT_SSL_VERIFYPEER] = false;
+        }
+
+        list($rbody, $rcode) = $this->executeRequestWithRetries($opts, $absUrl);
+
+        return [$rbody, $rcode, $rheaders];
+    }
+
+    /**
+     * @param array $opts cURL options
+     */
+    private function executeRequestWithRetries($opts, $absUrl)
+    {
+        $numRetries = 0;
+
+        while (true) {
+            $rcode = 0;
+            $errno = 0;
+
+            $curl = curl_init();
+            curl_setopt_array($curl, $opts);
+            $rbody = curl_exec($curl);
+
+            if ($rbody === false) {
+                $errno = curl_errno($curl);
+                $message = curl_error($curl);
+            } else {
+                $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+            }
+            curl_close($curl);
+
+            if ($this->shouldRetry($errno, $rcode, $numRetries)) {
+                $numRetries += 1;
+                $sleepSeconds = $this->sleepTime($numRetries);
+                usleep(intval($sleepSeconds * 1000000));
+            } else {
+                break;
+            }
+        }
+
+        if ($rbody === false) {
+            $this->handleCurlError($absUrl, $errno, $message, $numRetries);
+        }
+
+        return [$rbody, $rcode];
+    }
+
+    /**
+     * @param string $url
+     * @param number $errno
+     * @param string $message
+     * @param int $numRetries
+     * @throws Error\ApiConnection
+     */
+    private function handleCurlError($url, $errno, $message, $numRetries)
+    {
+        switch ($errno) {
+            case CURLE_COULDNT_CONNECT:
+            case CURLE_COULDNT_RESOLVE_HOST:
+            case CURLE_OPERATION_TIMEOUTED:
+                $msg = "Could not connect to Stripe ($url).  Please check your "
+                 . "internet connection and try again.  If this problem persists, "
+                 . "you should check Stripe's service status at "
+                 . "https://twitter.com/stripestatus, or";
+                break;
+            case CURLE_SSL_CACERT:
+            case CURLE_SSL_PEER_CERTIFICATE:
+                $msg = "Could not verify Stripe's SSL certificate.  Please make sure "
+                 . "that your network is not intercepting certificates.  "
+                 . "(Try going to $url in your browser.)  "
+                 . "If this problem persists,";
+                break;
+            default:
+                $msg = "Unexpected error communicating with Stripe.  "
+                 . "If this problem persists,";
+        }
+        $msg .= " let us know at support@stripe.com.";
+
+        $msg .= "\n\n(Network error [errno $errno]: $message)";
+
+        if ($numRetries > 0) {
+            $msg .= "\n\nRequest was retried $numRetries times.";
+        }
+
+        throw new Error\ApiConnection($msg);
+    }
+
+    /**
+     * Checks if an error is a problem that we should retry on. This includes both
+     * socket errors that may represent an intermittent problem and some special
+     * HTTP statuses.
+     * @param int $errno
+     * @param int $rcode
+     * @param int $numRetries
+     * @return bool
+     */
+    private function shouldRetry($errno, $rcode, $numRetries)
+    {
+        if ($numRetries >= Stripe::getMaxNetworkRetries()) {
+            return false;
+        }
+
+        // Retry on timeout-related problems (either on open or read).
+        if ($errno === CURLE_OPERATION_TIMEOUTED) {
+            return true;
+        }
+
+        // Destination refused the connection, the connection was reset, or a
+        // variety of other connection failures. This could occur from a single
+        // saturated server, so retry in case it's intermittent.
+        if ($errno === CURLE_COULDNT_CONNECT) {
+            return true;
+        }
+
+        // 409 conflict
+        if ($rcode === 409) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private function sleepTime($numRetries)
+    {
+        // Apply exponential backoff with $initialNetworkRetryDelay on the
+        // number of $numRetries so far as inputs. Do not allow the number to exceed
+        // $maxNetworkRetryDelay.
+        $sleepSeconds = min(
+            Stripe::getInitialNetworkRetryDelay() * 1.0 * pow(2, $numRetries - 1),
+            Stripe::getMaxNetworkRetryDelay()
+        );
+
+        // Apply some jitter by randomizing the value in the range of
+        // ($sleepSeconds / 2) to ($sleepSeconds).
+        $sleepSeconds *= 0.5 * (1 + $this->randomGenerator->randFloat());
+
+        // But never sleep less than the base sleep seconds.
+        $sleepSeconds = max(Stripe::getInitialNetworkRetryDelay(), $sleepSeconds);
+
+        return $sleepSeconds;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Invoice.php b/stripe-php/vendor/stripe/stripe-php/lib/Invoice.php
new file mode 100644
index 00000000..e9f53359
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Invoice.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Invoice
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount_due
+ * @property int $application_fee
+ * @property int $attempt_count
+ * @property bool $attempted
+ * @property string $billing
+ * @property string $charge
+ * @property bool $closed
+ * @property string $currency
+ * @property string $customer
+ * @property int $date
+ * @property string $description
+ * @property mixed $discount
+ * @property int $due_date
+ * @property int $ending_balance
+ * @property bool $forgiven
+ * @property Collection $lines
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property int $next_payment_attempt
+ * @property string $number
+ * @property bool $paid
+ * @property int $period_end
+ * @property int $period_start
+ * @property string $receipt_number
+ * @property int $starting_balance
+ * @property string $statement_descriptor
+ * @property string $subscription
+ * @property int $subscription_proration_date
+ * @property int $subtotal
+ * @property int $tax
+ * @property float $tax_percent
+ * @property int $total
+ * @property int $webhooks_delivered_at
+ *
+ * @package Stripe
+ */
+class Invoice extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return Invoice The upcoming invoice.
+     */
+    public static function upcoming($params = null, $opts = null)
+    {
+        $url = static::classUrl() . '/upcoming';
+        list($response, $opts) = static::_staticRequest('get', $url, $params, $opts);
+        $obj = Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+
+    /**
+     * @return Invoice The paid invoice.
+     */
+    public function pay($params = null, $opts = null)
+    {
+        $url = $this->instanceUrl() . '/pay';
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/InvoiceItem.php b/stripe-php/vendor/stripe/stripe-php/lib/InvoiceItem.php
new file mode 100644
index 00000000..37e39f3e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/InvoiceItem.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class InvoiceItem
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $currency
+ * @property string $customer
+ * @property int $date
+ * @property string $description
+ * @property bool $discountable
+ * @property string $invoice
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $period
+ * @property Plan $plan
+ * @property bool $proration
+ * @property int $quantity
+ * @property string $subscription
+ * @property string $subscription_item
+ *
+ * @package Stripe
+ */
+class InvoiceItem extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/LoginLink.php b/stripe-php/vendor/stripe/stripe-php/lib/LoginLink.php
new file mode 100644
index 00000000..7f01ae20
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/LoginLink.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class LoginLink
+ *
+ * @package Stripe
+ */
+class LoginLink extends ApiResource
+{
+
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/OAuth.php b/stripe-php/vendor/stripe/stripe-php/lib/OAuth.php
new file mode 100644
index 00000000..416ce498
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/OAuth.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace Stripe;
+
+abstract class OAuth
+{
+    /**
+     * Generates a URL to Stripe's OAuth form.
+     *
+     * @param array|null $params
+     * @param array|null $opts
+     *
+     * @return string The URL to Stripe's OAuth form.
+     */
+    public static function authorizeUrl($params = null, $opts = null)
+    {
+        $params = $params ?: [];
+
+        $base = ($opts && array_key_exists('connect_base', $opts)) ? $opts['connect_base'] : Stripe::$connectBase;
+
+        $params['client_id'] = self::_getClientId($params);
+        if (!array_key_exists('response_type', $params)) {
+            $params['response_type'] = 'code';
+        }
+        $query = Util\Util::urlEncode($params);
+
+        return $base . '/oauth/authorize?' . $query;
+    }
+
+    /**
+     * Use an authoriztion code to connect an account to your platform and
+     * fetch the user's credentials.
+     *
+     * @param array|null $params
+     * @param array|null $opts
+     *
+     * @return StripeObject Object containing the response from the API.
+     */
+    public static function token($params = null, $opts = null)
+    {
+        $base = ($opts && array_key_exists('connect_base', $opts)) ? $opts['connect_base'] : Stripe::$connectBase;
+        $requestor = new ApiRequestor(null, $base);
+        list($response, $apiKey) = $requestor->request(
+            'post',
+            '/oauth/token',
+            $params,
+            null
+        );
+        return Util\Util::convertToStripeObject($response->json, $opts);
+    }
+
+    /**
+     * Disconnects an account from your platform.
+     *
+     * @param array|null $params
+     * @param array|null $opts
+     *
+     * @return StripeObject Object containing the response from the API.
+     */
+    public static function deauthorize($params = null, $opts = null)
+    {
+        $params = $params ?: [];
+        $base = ($opts && array_key_exists('connect_base', $opts)) ? $opts['connect_base'] : Stripe::$connectBase;
+        $requestor = new ApiRequestor(null, $base);
+        $params['client_id'] = self::_getClientId($params);
+        list($response, $apiKey) = $requestor->request(
+            'post',
+            '/oauth/deauthorize',
+            $params,
+            null
+        );
+        return Util\Util::convertToStripeObject($response->json, $opts);
+    }
+
+    private static function _getClientId($params = null)
+    {
+        $clientId = ($params && array_key_exists('client_id', $params)) ? $params['client_id'] : null;
+        if ($clientId === null) {
+            $clientId = Stripe::getClientId();
+        }
+        if ($clientId === null) {
+            $msg = 'No client_id provided.  (HINT: set your client_id using '
+              . '"Stripe::setClientId(<CLIENT-ID>)".  You can find your client_ids '
+              . 'in your Stripe dashboard at '
+              . 'https://dashboard.stripe.com/account/applications/settings, '
+              . 'after registering your account as a platform. See '
+              . 'https://stripe.com/docs/connect/standard-accounts for details, '
+              . 'or email support@stripe.com if you have any questions.';
+            throw new Error\Authentication($msg);
+        }
+        return $clientId;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Order.php b/stripe-php/vendor/stripe/stripe-php/lib/Order.php
new file mode 100644
index 00000000..14215bc9
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Order.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Order
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $amount_returned
+ * @property string $application
+ * @property int $application_fee
+ * @property string $charge
+ * @property int $created
+ * @property string $currency
+ * @property string $customer
+ * @property string $email
+ * @property string $external_coupon_code
+ * @property mixed $items
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property Collection $returns
+ * @property string $selected_shipping_method
+ * @property mixed $shipping
+ * @property mixed $shipping_methods
+ * @property string $status
+ * @property mixed $status_transitions
+ * @property int $updated
+ * @property string $upstream_id
+ *
+ * @package Stripe
+ */
+class Order extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @return Order The paid order.
+     */
+    public function pay($params = null, $opts = null)
+    {
+        $url = $this->instanceUrl() . '/pay';
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @return OrderReturn The newly created return.
+     */
+    public function returnOrder($params = null, $opts = null)
+    {
+        $url = $this->instanceUrl() . '/returns';
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        return Util\Util::convertToStripeObject($response, $opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/OrderReturn.php b/stripe-php/vendor/stripe/stripe-php/lib/OrderReturn.php
new file mode 100644
index 00000000..45a69e37
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/OrderReturn.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class OrderReturn
+ *
+ * @package Stripe
+ */
+class OrderReturn extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+
+    /**
+     * This is a special case because the order returns endpoint has an
+     *    underscore in it. The parent `className` function strips underscores.
+     *
+     * @return string The name of the class.
+     */
+    public static function className()
+    {
+        return 'order_return';
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Payout.php b/stripe-php/vendor/stripe/stripe-php/lib/Payout.php
new file mode 100644
index 00000000..0849cae3
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Payout.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Payout
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $balance_transaction
+ * @property string $cancellation_balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property int $arrival_date
+ * @property string $destination
+ * @property string $failure_code
+ * @property string $failure_message
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $method
+ * @property string $recipient
+ * @property string $source_type
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class Payout extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @return Payout The canceled payout.
+     */
+    public function cancel()
+    {
+        $url = $this->instanceUrl() . '/cancel';
+        list($response, $opts) = $this->_request('post', $url);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Plan.php b/stripe-php/vendor/stripe/stripe-php/lib/Plan.php
new file mode 100644
index 00000000..8b0126b6
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Plan.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Plan
+ *
+ * @package Stripe
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $created
+ * @property string $currency
+ * @property string $interval
+ * @property int $interval_count
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $nickname
+ * @property string $product
+ * @property int $trial_period_days
+ */
+class Plan extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Product.php b/stripe-php/vendor/stripe/stripe-php/lib/Product.php
new file mode 100644
index 00000000..20bd4e1a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Product.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Product
+ *
+ * @property string $id
+ * @property string $object
+ * @property bool $active
+ * @property string[] $attributes
+ * @property string $caption
+ * @property int $created
+ * @property string[] $deactivate_on
+ * @property string $description
+ * @property array $images
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $name
+ * @property mixed $package_dimensions
+ * @property bool $shippable
+ * @property Collection $skus
+ * @property string $statement_descriptor
+ * @property string $type
+ * @property int $updated
+ * @property string $url
+ *
+ * @package Stripe
+ */
+class Product extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Recipient.php b/stripe-php/vendor/stripe/stripe-php/lib/Recipient.php
new file mode 100644
index 00000000..9ec6a1ec
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Recipient.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Recipient
+ *
+ * @package Stripe
+ */
+class Recipient extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     *
+     * @return Collection of the Recipient's Transfers
+     */
+    public function transfers($params = null)
+    {
+        $params = $params ?: [];
+        $params['recipient'] = $this->id;
+        $transfers = Transfer::all($params, $this->_opts);
+        return $transfers;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/RecipientTransfer.php b/stripe-php/vendor/stripe/stripe-php/lib/RecipientTransfer.php
new file mode 100644
index 00000000..610514dc
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/RecipientTransfer.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class RecipientTransfer
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $amount_reversed
+ * @property string $balance_transaction
+ * @property string $bank_account
+ * @property string $card
+ * @property int $created
+ * @property string $currency
+ * @property int $date
+ * @property string $description
+ * @property string $destination
+ * @property string $failure_code
+ * @property string $failure_message
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $method
+ * @property string $recipient
+ * @property mixed $reversals
+ * @property bool $reversed
+ * @property string $source_type
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class RecipientTransfer extends ApiResource
+{
+
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Refund.php b/stripe-php/vendor/stripe/stripe-php/lib/Refund.php
new file mode 100644
index 00000000..0288895d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Refund.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Refund
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $balance_transaction
+ * @property string $charge
+ * @property int $created
+ * @property string $currency
+ * @property string $failure_balance_transaction
+ * @property string failure_reason
+ * @property StripeObject $metadata
+ * @property mixed $reason
+ * @property mixed $receipt_number
+ * @property string $status
+ *
+ * @package Stripe
+ */
+class Refund extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/SKU.php b/stripe-php/vendor/stripe/stripe-php/lib/SKU.php
new file mode 100644
index 00000000..1f35da4c
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/SKU.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class SKU
+ *
+ * @property string $id
+ * @property string $object
+ * @property bool $active
+ * @property mixed $attributes
+ * @property int $created
+ * @property string $currency
+ * @property string $image
+ * @property mixed $inventory
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $package_dimensions
+ * @property int $price
+ * @property string $product
+ * @property int $updated
+ *
+ * @package Stripe
+ */
+class SKU extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/SingletonApiResource.php b/stripe-php/vendor/stripe/stripe-php/lib/SingletonApiResource.php
new file mode 100644
index 00000000..c6de4448
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/SingletonApiResource.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class SingletonApiResource
+ *
+ * @package Stripe
+ */
+abstract class SingletonApiResource extends ApiResource
+{
+    protected static function _singletonRetrieve($options = null)
+    {
+        $opts = Util\RequestOptions::parse($options);
+        $instance = new static(null, $opts);
+        $instance->refresh();
+        return $instance;
+    }
+
+    /**
+     * @return string The endpoint associated with this singleton class.
+     */
+    public static function classUrl()
+    {
+        $base = static::className();
+        return "/v1/${base}";
+    }
+
+    /**
+     * @return string The endpoint associated with this singleton API resource.
+     */
+    public function instanceUrl()
+    {
+        return static::classUrl();
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Source.php b/stripe-php/vendor/stripe/stripe-php/lib/Source.php
new file mode 100644
index 00000000..4a889c5a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Source.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Source
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $client_secret
+ * @property mixed $code_verification
+ * @property int $created
+ * @property string $currency
+ * @property string $flow
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $owner
+ * @property mixed $receiver
+ * @property mixed $redirect
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property string $type
+ * @property string $usage
+ *
+ * @package Stripe
+ */
+class Source extends ApiResource
+{
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Source The detached source.
+     */
+    public function detach($params = null, $options = null)
+    {
+        self::_validateParams($params);
+
+        $id = $this['id'];
+        if (!$id) {
+            $class = get_class($this);
+            $msg = "Could not determine which URL to request: $class instance "
+             . "has invalid ID: $id";
+            throw new Error\InvalidRequest($msg, null);
+        }
+
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parentExtn = urlencode(Util\Util::utf8($this['customer']));
+            $extn = urlencode(Util\Util::utf8($id));
+            $url = "$base/$parentExtn/sources/$extn";
+
+            list($response, $opts) = $this->_request('delete', $url, $params, $options);
+            $this->refreshFrom($response, $opts);
+            return $this;
+        } else {
+            $message = "This source object does not appear to be currently attached "
+               . "to a customer object.";
+            throw new Error\Api($message);
+        }
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Source The detached source.
+     *
+     * @deprecated Use the `detach` method instead.
+     */
+    public function delete($params = null, $options = null)
+    {
+        $this->detach($params, $options);
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Collection The list of source transactions.
+     */
+    public function sourceTransactions($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/source_transactions';
+        list($response, $opts) = $this->_request('get', $url, $params, $options);
+        $obj = Util\Util::convertToStripeObject($response, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Source The verified source.
+     */
+    public function verify($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/verify';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/SourceTransaction.php b/stripe-php/vendor/stripe/stripe-php/lib/SourceTransaction.php
new file mode 100644
index 00000000..a0c8d776
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/SourceTransaction.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class SourceTransaction
+ *
+ * @package Stripe
+ */
+class SourceTransaction extends ApiResource
+{
+
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Stripe.php b/stripe-php/vendor/stripe/stripe-php/lib/Stripe.php
new file mode 100644
index 00000000..c51ef25a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Stripe.php
@@ -0,0 +1,241 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Stripe
+ *
+ * @package Stripe
+ */
+class Stripe
+{
+    // @var string The Stripe API key to be used for requests.
+    public static $apiKey;
+
+    // @var string The Stripe client_id to be used for Connect requests.
+    public static $clientId;
+
+    // @var string The base URL for the Stripe API.
+    public static $apiBase = 'https://api.stripe.com';
+
+    // @var string The base URL for the OAuth API.
+    public static $connectBase = 'https://connect.stripe.com';
+
+    // @var string The base URL for the Stripe API uploads endpoint.
+    public static $apiUploadBase = 'https://uploads.stripe.com';
+
+    // @var string|null The version of the Stripe API to use for requests.
+    public static $apiVersion = null;
+
+    // @var string|null The account ID for connected accounts requests.
+    public static $accountId = null;
+
+    // @var string Path to the CA bundle used to verify SSL certificates
+    public static $caBundlePath = null;
+
+    // @var boolean Defaults to true.
+    public static $verifySslCerts = true;
+
+    // @var array The application's information (name, version, URL)
+    public static $appInfo = null;
+
+    // @var Util\LoggerInterface|null The logger to which the library will
+    //   produce messages.
+    public static $logger = null;
+
+    // @var int Maximum number of request retries
+    public static $maxNetworkRetries = 0;
+
+    // @var float Maximum delay between retries, in seconds
+    private static $maxNetworkRetryDelay = 2.0;
+
+    // @var float Initial delay between retries, in seconds
+    private static $initialNetworkRetryDelay = 0.5;
+
+    const VERSION = '6.3.0';
+
+    /**
+     * @return string The API key used for requests.
+     */
+    public static function getApiKey()
+    {
+        return self::$apiKey;
+    }
+
+    /**
+     * @return string The client_id used for Connect requests.
+     */
+    public static function getClientId()
+    {
+        return self::$clientId;
+    }
+
+    /**
+     * @return Util\LoggerInterface The logger to which the library will
+     *   produce messages.
+     */
+    public static function getLogger()
+    {
+        if (self::$logger == null) {
+            return new Util\DefaultLogger();
+        }
+        return self::$logger;
+    }
+
+    /**
+     * @param Util\LoggerInterface $logger The logger to which the library
+     *   will produce messages.
+     */
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
+    /**
+     * Sets the API key to be used for requests.
+     *
+     * @param string $apiKey
+     */
+    public static function setApiKey($apiKey)
+    {
+        self::$apiKey = $apiKey;
+    }
+
+    /**
+     * Sets the client_id to be used for Connect requests.
+     *
+     * @param string $clientId
+     */
+    public static function setClientId($clientId)
+    {
+        self::$clientId = $clientId;
+    }
+
+    /**
+     * @return string The API version used for requests. null if we're using the
+     *    latest version.
+     */
+    public static function getApiVersion()
+    {
+        return self::$apiVersion;
+    }
+
+    /**
+     * @param string $apiVersion The API version to use for requests.
+     */
+    public static function setApiVersion($apiVersion)
+    {
+        self::$apiVersion = $apiVersion;
+    }
+
+    /**
+     * @return string
+     */
+    private static function getDefaultCABundlePath()
+    {
+        return realpath(dirname(__FILE__) . '/../data/ca-certificates.crt');
+    }
+
+    /**
+     * @return string
+     */
+    public static function getCABundlePath()
+    {
+        return self::$caBundlePath ?: self::getDefaultCABundlePath();
+    }
+
+    /**
+     * @param string $caBundlePath
+     */
+    public static function setCABundlePath($caBundlePath)
+    {
+        self::$caBundlePath = $caBundlePath;
+    }
+
+    /**
+     * @return boolean
+     */
+    public static function getVerifySslCerts()
+    {
+        return self::$verifySslCerts;
+    }
+
+    /**
+     * @param boolean $verify
+     */
+    public static function setVerifySslCerts($verify)
+    {
+        self::$verifySslCerts = $verify;
+    }
+
+    /**
+     * @return string | null The Stripe account ID for connected account
+     *   requests.
+     */
+    public static function getAccountId()
+    {
+        return self::$accountId;
+    }
+
+    /**
+     * @param string $accountId The Stripe account ID to set for connected
+     *   account requests.
+     */
+    public static function setAccountId($accountId)
+    {
+        self::$accountId = $accountId;
+    }
+
+    /**
+     * @return array | null The application's information
+     */
+    public static function getAppInfo()
+    {
+        return self::$appInfo;
+    }
+
+    /**
+     * @param string $appName The application's name
+     * @param string $appVersion The application's version
+     * @param string $appUrl The application's URL
+     */
+    public static function setAppInfo($appName, $appVersion = null, $appUrl = null)
+    {
+        self::$appInfo = self::$appInfo ?: [];
+        self::$appInfo['name'] = $appName;
+        self::$appInfo['version'] = $appVersion;
+        self::$appInfo['url'] = $appUrl;
+    }
+
+    /**
+     * @return int Maximum number of request retries
+     */
+    public static function getMaxNetworkRetries()
+    {
+        return self::$maxNetworkRetries;
+    }
+
+    /**
+     * @param int $maxNetworkRetries Maximum number of request retries
+     */
+    public static function setMaxNetworkRetries($maxNetworkRetries)
+    {
+        self::$maxNetworkRetries = $maxNetworkRetries;
+    }
+
+    /**
+     * @return float Maximum delay between retries, in seconds
+     */
+    public static function getMaxNetworkRetryDelay()
+    {
+        return self::$maxNetworkRetryDelay;
+    }
+
+    /**
+     * @return float Initial delay between retries, in seconds
+     */
+    public static function getInitialNetworkRetryDelay()
+    {
+        return self::$initialNetworkRetryDelay;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/StripeObject.php b/stripe-php/vendor/stripe/stripe-php/lib/StripeObject.php
new file mode 100644
index 00000000..6f0d9f87
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/StripeObject.php
@@ -0,0 +1,443 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class StripeObject
+ *
+ * @package Stripe
+ */
+class StripeObject implements \ArrayAccess, \Countable, \JsonSerializable
+{
+    protected $_opts;
+    protected $_originalValues;
+    protected $_values;
+    protected $_unsavedValues;
+    protected $_transientValues;
+    protected $_retrieveOptions;
+    protected $_lastResponse;
+
+    /**
+     * @return Util\Set Attributes that should not be sent to the API because
+     *    they're not updatable (e.g. ID).
+     */
+    public static function getPermanentAttributes()
+    {
+        static $permanentAttributes = null;
+        if ($permanentAttributes === null) {
+            $permanentAttributes = new Util\Set([
+                'id',
+            ]);
+        }
+        return $permanentAttributes;
+    }
+
+    public function __construct($id = null, $opts = null)
+    {
+        list($id, $this->_retrieveOptions) = Util\Util::normalizeId($id);
+        $this->_opts = Util\RequestOptions::parse($opts);
+        $this->_originalValues = [];
+        $this->_values = [];
+        $this->_unsavedValues = new Util\Set();
+        $this->_transientValues = new Util\Set();
+        if ($id !== null) {
+            $this->_values['id'] = $id;
+        }
+    }
+
+    // Standard accessor magic methods
+    public function __set($k, $v)
+    {
+        if (static::getPermanentAttributes()->includes($k)) {
+            throw new \InvalidArgumentException(
+                "Cannot set $k on this object. HINT: you can't set: " .
+                join(', ', static::getPermanentAttributes()->toArray())
+            );
+        }
+
+        if ($v === "") {
+            throw new \InvalidArgumentException(
+                'You cannot set \''.$k.'\'to an empty string. '
+                .'We interpret empty strings as NULL in requests. '
+                .'You may set obj->'.$k.' = NULL to delete the property'
+            );
+        }
+
+        $this->_values[$k] = Util\Util::convertToStripeObject($v, $this->_opts);
+        $this->dirtyValue($this->_values[$k]);
+        $this->_unsavedValues->add($k);
+    }
+
+    public function __isset($k)
+    {
+        return isset($this->_values[$k]);
+    }
+
+    public function __unset($k)
+    {
+        unset($this->_values[$k]);
+        $this->_transientValues->add($k);
+        $this->_unsavedValues->discard($k);
+    }
+
+    public function &__get($k)
+    {
+        // function should return a reference, using $nullval to return a reference to null
+        $nullval = null;
+        if (!empty($this->_values) && array_key_exists($k, $this->_values)) {
+            return $this->_values[$k];
+        } else if (!empty($this->_transientValues) && $this->_transientValues->includes($k)) {
+            $class = get_class($this);
+            $attrs = join(', ', array_keys($this->_values));
+            $message = "Stripe Notice: Undefined property of $class instance: $k. "
+                    . "HINT: The $k attribute was set in the past, however. "
+                    . "It was then wiped when refreshing the object "
+                    . "with the result returned by Stripe's API, "
+                    . "probably as a result of a save(). The attributes currently "
+                    . "available on this object are: $attrs";
+            Stripe::getLogger()->error($message);
+            return $nullval;
+        } else {
+            $class = get_class($this);
+            Stripe::getLogger()->error("Stripe Notice: Undefined property of $class instance: $k");
+            return $nullval;
+        }
+    }
+
+    // Magic method for var_dump output. Only works with PHP >= 5.6
+    public function __debugInfo()
+    {
+        return $this->_values;
+    }
+
+    // ArrayAccess methods
+    public function offsetSet($k, $v)
+    {
+        $this->$k = $v;
+    }
+
+    public function offsetExists($k)
+    {
+        return array_key_exists($k, $this->_values);
+    }
+
+    public function offsetUnset($k)
+    {
+        unset($this->$k);
+    }
+
+    public function offsetGet($k)
+    {
+        return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
+    }
+
+    // Countable method
+    public function count()
+    {
+        return count($this->_values);
+    }
+
+    public function keys()
+    {
+        return array_keys($this->_values);
+    }
+
+    public function values()
+    {
+        return array_values($this->_values);
+    }
+
+    /**
+     * This unfortunately needs to be public to be used in Util\Util
+     *
+     * @param array $values
+     * @param null|string|array|Util\RequestOptions $opts
+     *
+     * @return StripeObject The object constructed from the given values.
+     */
+    public static function constructFrom($values, $opts = null)
+    {
+        $obj = new static(isset($values['id']) ? $values['id'] : null);
+        $obj->refreshFrom($values, $opts);
+        return $obj;
+    }
+
+    /**
+     * Refreshes this object using the provided values.
+     *
+     * @param array $values
+     * @param null|string|array|Util\RequestOptions $opts
+     * @param boolean $partial Defaults to false.
+     */
+    public function refreshFrom($values, $opts, $partial = false)
+    {
+        $this->_opts = Util\RequestOptions::parse($opts);
+
+        $this->_originalValues = self::deepCopy($values);
+
+        if ($values instanceof StripeObject) {
+            $values = $values->__toArray(true);
+        }
+
+        // Wipe old state before setting new.  This is useful for e.g. updating a
+        // customer, where there is no persistent card parameter.  Mark those values
+        // which don't persist as transient
+        if ($partial) {
+            $removed = new Util\Set();
+        } else {
+            $removed = new Util\Set(array_diff(array_keys($this->_values), array_keys($values)));
+        }
+
+        foreach ($removed->toArray() as $k) {
+            unset($this->$k);
+        }
+
+        $this->updateAttributes($values, $opts, false);
+        foreach ($values as $k => $v) {
+            $this->_transientValues->discard($k);
+            $this->_unsavedValues->discard($k);
+        }
+    }
+
+    /**
+     * Mass assigns attributes on the model.
+     *
+     * @param array $values
+     * @param null|string|array|Util\RequestOptions $opts
+     * @param boolean $dirty Defaults to true.
+     */
+    public function updateAttributes($values, $opts = null, $dirty = true)
+    {
+        foreach ($values as $k => $v) {
+            // Special-case metadata to always be cast as a StripeObject
+            // This is necessary in case metadata is empty, as PHP arrays do
+            // not differentiate between lists and hashes, and we consider
+            // empty arrays to be lists.
+            if ($k === "metadata") {
+                $this->_values[$k] = StripeObject::constructFrom($v, $opts);
+            } else {
+                $this->_values[$k] = Util\Util::convertToStripeObject($v, $opts);
+            }
+            if ($dirty) {
+                $this->dirtyValue($this->_values[$k]);
+            }
+            $this->_unsavedValues->add($k);
+        }
+    }
+
+    /**
+     * @return array A recursive mapping of attributes to values for this object,
+     *    including the proper value for deleted attributes.
+     */
+    public function serializeParameters($force = false)
+    {
+        $updateParams = [];
+
+        foreach ($this->_values as $k => $v) {
+            // There are a few reasons that we may want to add in a parameter for
+            // update:
+            //
+            //   1. The `$force` option has been set.
+            //   2. We know that it was modified.
+            //   3. Its value is a StripeObject. A StripeObject may contain modified
+            //      values within in that its parent StripeObject doesn't know about.
+            //
+            $original = array_key_exists($k, $this->_originalValues) ? $this->_originalValues[$k] : null;
+            $unsaved = $this->_unsavedValues->includes($k);
+            if ($force || $unsaved || $v instanceof StripeObject) {
+                $updateParams[$k] = $this->serializeParamsValue(
+                    $this->_values[$k],
+                    $original,
+                    $unsaved,
+                    $force,
+                    $k
+                );
+            }
+        }
+
+        // a `null` that makes it out of `serializeParamsValue` signals an empty
+        // value that we shouldn't appear in the serialized form of the object
+        $updateParams = array_filter(
+            $updateParams,
+            function ($v) {
+                return $v !== null;
+            }
+        );
+
+        return $updateParams;
+    }
+
+
+    public function serializeParamsValue($value, $original, $unsaved, $force, $key = null)
+    {
+        // The logic here is that essentially any object embedded in another
+        // object that had a `type` is actually an API resource of a different
+        // type that's been included in the response. These other resources must
+        // be updated from their proper endpoints, and therefore they are not
+        // included when serializing even if they've been modified.
+        //
+        // There are _some_ known exceptions though.
+        //
+        // For example, if the value is unsaved (meaning the user has set it), and
+        // it looks like the API resource is persisted with an ID, then we include
+        // the object so that parameters are serialized with a reference to its
+        // ID.
+        //
+        // Another example is that on save API calls it's sometimes desirable to
+        // update a customer's default source by setting a new card (or other)
+        // object with `->source=` and then saving the customer. The
+        // `saveWithParent` flag to override the default behavior allows us to
+        // handle these exceptions.
+        //
+        // We throw an error if a property was set explicitly but we can't do
+        // anything with it because the integration is probably not working as the
+        // user intended it to.
+        if ($value === null) {
+            return "";
+        } elseif (($value instanceof APIResource) && (!$value->saveWithParent)) {
+            if (!$unsaved) {
+                return null;
+            } elseif (isset($value->id)) {
+                return $value;
+            } else {
+                throw new \InvalidArgumentException(
+                    "Cannot save property `$key` containing an API resource of type " .
+                    get_class($value) . ". It doesn't appear to be persisted and is " .
+                    "not marked as `saveWithParent`."
+                );
+            }
+        } elseif (is_array($value)) {
+            if (Util\Util::isList($value)) {
+                // Sequential array, i.e. a list
+                $update = [];
+                foreach ($value as $v) {
+                    array_push($update, $this->serializeParamsValue($v, null, true, $force));
+                }
+                // This prevents an array that's unchanged from being resent.
+                if ($update !== $this->serializeParamsValue($original, null, true, $force, $key)) {
+                    return $update;
+                }
+            } else {
+                // Associative array, i.e. a map
+                return Util\Util::convertToStripeObject($value, $this->_opts)->serializeParameters();
+            }
+        } elseif ($value instanceof StripeObject) {
+            $update = $value->serializeParameters($force);
+            if ($original && $unsaved) {
+                $update = array_merge(self::emptyValues($original), $update);
+            }
+            return $update;
+        } else {
+            return $value;
+        }
+    }
+
+    public function jsonSerialize()
+    {
+        return $this->__toArray(true);
+    }
+
+    public function __toJSON()
+    {
+        return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
+    }
+
+    public function __toString()
+    {
+        $class = get_class($this);
+        return $class . ' JSON: ' . $this->__toJSON();
+    }
+
+    public function __toArray($recursive = false)
+    {
+        if ($recursive) {
+            return Util\Util::convertStripeObjectToArray($this->_values);
+        } else {
+            return $this->_values;
+        }
+    }
+
+    /**
+     * Sets all keys within the StripeObject as unsaved so that they will be
+     * included with an update when `serializeParameters` is called. This
+     * method is also recursive, so any StripeObjects contained as values or
+     * which are values in a tenant array are also marked as dirty.
+     */
+    public function dirty()
+    {
+        $this->_unsavedValues = new Util\Set(array_keys($this->_values));
+        foreach ($this->_values as $k => $v) {
+            $this->dirtyValue($v);
+        }
+    }
+
+    protected function dirtyValue($value)
+    {
+        if (is_array($value)) {
+            foreach ($value as $v) {
+                $this->dirtyValue($v);
+            }
+        } elseif ($value instanceof StripeObject) {
+            $value->dirty();
+        }
+    }
+
+    /**
+     * Produces a deep copy of the given object including support for arrays
+     * and StripeObjects.
+     */
+    protected static function deepCopy($obj)
+    {
+        if (is_array($obj)) {
+            $copy = [];
+            foreach ($obj as $k => $v) {
+                $copy[$k] = self::deepCopy($v);
+            }
+            return $copy;
+        } elseif ($obj instanceof StripeObject) {
+            return $obj::constructFrom(
+                self::deepCopy($obj->_values),
+                clone $obj->_opts
+            );
+        } else {
+            return $obj;
+        }
+    }
+
+    /**
+     * Returns a hash of empty values for all the values that are in the given
+     * StripeObject.
+     */
+    public static function emptyValues($obj)
+    {
+        if (is_array($obj)) {
+            $values = $obj;
+        } elseif ($obj instanceof StripeObject) {
+            $values = $obj->_values;
+        } else {
+            throw new InvalidArgumentException(
+                "empty_values got got unexpected object type: " . get_class($obj)
+            );
+        }
+        $update = array_fill_keys(array_keys($values), "");
+        return $update;
+    }
+
+    /**
+     * @return object The last response from the Stripe API
+     */
+    public function getLastResponse()
+    {
+        return $this->_lastResponse;
+    }
+
+    /**
+     * @param ApiResponse
+     *
+     * @return void Set the last response from the Stripe API
+     */
+    public function setLastResponse($resp)
+    {
+        $this->_lastResponse = $resp;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Subscription.php b/stripe-php/vendor/stripe/stripe-php/lib/Subscription.php
new file mode 100644
index 00000000..a5a8da1b
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Subscription.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Subscription
+ *
+ * @property string $id
+ * @property string $object
+ * @property float $application_fee_percent
+ * @property string $billing
+ * @property bool $cancel_at_period_end
+ * @property int $canceled_at
+ * @property int $created
+ * @property int current_period_end
+ * @property int current_period_start
+ * @property string $customer
+ * @property int $days_until_due
+ * @property mixed $discount
+ * @property int $ended_at
+ * @property Collection $items
+ * @property boolean $livemode
+ * @property StripeObject $metadata
+ * @property Plan $plan
+ * @property int $quantity
+ * @property int $start
+ * @property string $status
+ * @property float $tax_percent
+ * @property int $trial_end
+ * @property int $trial_start
+ *
+ * @package Stripe
+ */
+class Subscription extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete {
+        delete as protected _delete;
+    }
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * These constants are possible representations of the status field.
+     *
+     * @link https://stripe.com/docs/api#subscription_object-status
+     */
+    const STATUS_ACTIVE = 'active';
+    const STATUS_CANCELED = 'canceled';
+    const STATUS_PAST_DUE = 'past_due';
+    const STATUS_TRIALING = 'trialing';
+    const STATUS_UNPAID = 'unpaid';
+
+    public static function getSavedNestedResources()
+    {
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set([
+                'source',
+            ]);
+        }
+        return $savedNestedResources;
+    }
+
+    /**
+     * @param array|null $params
+     *
+     * @return Subscription The deleted subscription.
+     */
+    public function cancel($params = null, $opts = null)
+    {
+        return $this->_delete($params, $opts);
+    }
+
+    /**
+     * @return Subscription The updated subscription.
+     */
+    public function deleteDiscount()
+    {
+        $url = $this->instanceUrl() . '/discount';
+        list($response, $opts) = $this->_request('delete', $url);
+        $this->refreshFrom(['discount' => null], $opts, true);
+    }
+
+    public function serializeParameters($force = false)
+    {
+        $update = parent::serializeParameters($force);
+        if ($this->_unsavedValues->includes('items')) {
+            $update['items'] = $this->serializeParamsValue($this->items, null, true, $force, 'items');
+        }
+        return $update;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/SubscriptionItem.php b/stripe-php/vendor/stripe/stripe-php/lib/SubscriptionItem.php
new file mode 100644
index 00000000..9efc726a
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/SubscriptionItem.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class SubscriptionItem
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $created
+ * @property StripeObject $metadata
+ * @property Plan $plan
+ * @property int $quantity
+ * @property string $subscription
+ *
+ * @package Stripe
+ */
+class SubscriptionItem extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * This is a special case because the subscription items endpoint has an
+     *    underscore in it. The parent `className` function strips underscores.
+     *
+     * @return string The name of the class.
+     */
+    public static function className()
+    {
+        return 'subscription_item';
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/ThreeDSecure.php b/stripe-php/vendor/stripe/stripe-php/lib/ThreeDSecure.php
new file mode 100644
index 00000000..7210bfd1
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/ThreeDSecure.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Stripe;
+
+class ThreeDSecure extends ApiResource
+{
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+
+    /**
+     * @return string The endpoint URL for the given class.
+     */
+    public static function classUrl()
+    {
+        return "/v1/3d_secure";
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Token.php b/stripe-php/vendor/stripe/stripe-php/lib/Token.php
new file mode 100644
index 00000000..fbf19bbf
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Token.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Token
+ *
+ * @property string $id
+ * @property string $object
+ * @property mixed $bank_account
+ * @property mixed $card
+ * @property string $client_ip
+ * @property int $created
+ * @property bool $livemode
+ * @property string $type
+ * @property bool $used
+ *
+ * @package Stripe
+ */
+class Token extends ApiResource
+{
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Topup.php b/stripe-php/vendor/stripe/stripe-php/lib/Topup.php
new file mode 100644
index 00000000..33d081e3
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Topup.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Topup
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property string $description
+ * @property int $expected_availability_date
+ * @property string $failure_code
+ * @property string $failure_message
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $source
+ * @property string $statement_descriptor
+ * @property string $status
+ *
+ * @package Stripe
+ */
+class Topup extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Transfer.php b/stripe-php/vendor/stripe/stripe-php/lib/Transfer.php
new file mode 100644
index 00000000..87d8b79c
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Transfer.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Transfer
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $amount_reversed
+ * @property string $balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property string $destination
+ * @property string $destination_payment
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property Collection $reversals
+ * @property bool $reversed
+ * @property string $source_transaction
+ * @property string $source_type
+ * @property string $transfer_group
+ *
+ * @package Stripe
+ */
+class Transfer extends ApiResource
+{
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    const PATH_REVERSALS = '/reversals';
+
+    /**
+     * @return TransferReversal The created transfer reversal.
+     */
+    public function reverse($params = null, $opts = null)
+    {
+        $url = $this->instanceUrl() . '/reversals';
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @return Transfer The canceled transfer.
+     */
+    public function cancel()
+    {
+        $url = $this->instanceUrl() . '/cancel';
+        list($response, $opts) = $this->_request('post', $url);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|null $id The ID of the transfer on which to create the reversal.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return TransferReversal
+     */
+    public static function createReversal($id, $params = null, $opts = null)
+    {
+        return self::_createNestedResource($id, static::PATH_REVERSALS, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the transfer to which the reversal belongs.
+     * @param array|null $reversalId The ID of the reversal to retrieve.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return TransferReversal
+     */
+    public static function retrieveReversal($id, $reversalId, $params = null, $opts = null)
+    {
+        return self::_retrieveNestedResource($id, static::PATH_REVERSALS, $reversalId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the transfer to which the reversal belongs.
+     * @param array|null $reversalId The ID of the reversal to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return TransferReversal
+     */
+    public static function updateReversal($id, $reversalId, $params = null, $opts = null)
+    {
+        return self::_updateNestedResource($id, static::PATH_REVERSALS, $reversalId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the transfer on which to retrieve the reversals.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return TransferReversal
+     */
+    public static function allReversals($id, $params = null, $opts = null)
+    {
+        return self::_allNestedResources($id, static::PATH_REVERSALS, $params, $opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/TransferReversal.php b/stripe-php/vendor/stripe/stripe-php/lib/TransferReversal.php
new file mode 100644
index 00000000..cf2a3946
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/TransferReversal.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class TransferReversal
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property StripeObject $metadata
+ * @property string $transfer
+ *
+ * @package Stripe
+ */
+class TransferReversal extends ApiResource
+{
+    use ApiOperations\Update {
+        save as protected _save;
+    }
+
+    /**
+     * @return string The API URL for this Stripe transfer reversal.
+     */
+    public function instanceUrl()
+    {
+        $id = $this['id'];
+        $transfer = $this['transfer'];
+        if (!$id) {
+            throw new Error\InvalidRequest(
+                "Could not determine which URL to request: " .
+                "class instance has invalid ID: $id",
+                null
+            );
+        }
+        $id = Util\Util::utf8($id);
+        $transfer = Util\Util::utf8($transfer);
+
+        $base = Transfer::classUrl();
+        $transferExtn = urlencode($transfer);
+        $extn = urlencode($id);
+        return "$base/$transferExtn/reversals/$extn";
+    }
+
+    /**
+     * @param array|string|null $opts
+     *
+     * @return TransferReversal The saved reversal.
+     */
+    public function save($opts = null)
+    {
+        return $this->_save($opts);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Util/AutoPagingIterator.php b/stripe-php/vendor/stripe/stripe-php/lib/Util/AutoPagingIterator.php
new file mode 100644
index 00000000..167d1258
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Util/AutoPagingIterator.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Stripe\Util;
+
+class AutoPagingIterator implements \Iterator
+{
+    private $lastId = null;
+    private $page = null;
+    private $pageOffset = 0;
+    private $params = [];
+
+    public function __construct($collection, $params)
+    {
+        $this->page = $collection;
+        $this->params = $params;
+    }
+
+    public function rewind()
+    {
+        // Actually rewinding would require making a copy of the original page.
+    }
+
+    public function current()
+    {
+        $item = current($this->page->data);
+        $this->lastId = $item !== false ? $item['id'] : null;
+
+        return $item;
+    }
+
+    public function key()
+    {
+        return key($this->page->data) + $this->pageOffset;
+    }
+
+    public function next()
+    {
+        $item = next($this->page->data);
+        if ($item === false) {
+            // If we've run out of data on the current page, try to fetch another one
+            // and increase the offset the new page would start at
+            $this->pageOffset += count($this->page->data);
+            if ($this->page['has_more']) {
+                $this->params = array_merge(
+                    $this->params ?: [],
+                    ['starting_after' => $this->lastId]
+                );
+                $this->page = $this->page->all($this->params);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public function valid()
+    {
+        $key = key($this->page->data);
+        $valid = ($key !== null && $key !== false);
+        return $valid;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Util/DefaultLogger.php b/stripe-php/vendor/stripe/stripe-php/lib/Util/DefaultLogger.php
new file mode 100644
index 00000000..cd56b166
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Util/DefaultLogger.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Stripe\Util;
+
+/**
+ * A very basic implementation of LoggerInterface that has just enough
+ * functionality that it can be the default for this library.
+ */
+class DefaultLogger implements LoggerInterface
+{
+    public function error($message, array $context = [])
+    {
+        if (count($context) > 0) {
+            throw new Exception('DefaultLogger does not currently implement context. Please implement if you need it.');
+        }
+        error_log($message);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Util/LoggerInterface.php b/stripe-php/vendor/stripe/stripe-php/lib/Util/LoggerInterface.php
new file mode 100644
index 00000000..bbdfc929
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Util/LoggerInterface.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Stripe\Util;
+
+/**
+ * Describes a logger instance.
+ *
+ * This is a subset of the interface of the same name in the PSR-3 logger
+ * interface. We guarantee to keep it compatible, but we'd redefined it here so
+ * that we don't have to pull in the extra dependencies for users who don't want
+ * it.
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
+ * for the full interface specification.
+ *
+ * The message MUST be a string or object implementing __toString().
+ *
+ * The message MAY contain placeholders in the form: {foo} where foo
+ * will be replaced by the context data in key "foo".
+ *
+ * The context array can contain arbitrary data, the only assumption that
+ * can be made by implementors is that if an Exception instance is given
+ * to produce a stack trace, it MUST be in a key named "exception".
+ */
+interface LoggerInterface
+{
+    /**
+     * Runtime errors that do not require immediate action but should typically
+     * be logged and monitored.
+     *
+     * @param string $message
+     * @param array $context
+     * @return null
+     */
+    public function error($message, array $context = []);
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Util/RandomGenerator.php b/stripe-php/vendor/stripe/stripe-php/lib/Util/RandomGenerator.php
new file mode 100644
index 00000000..470f2bce
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Util/RandomGenerator.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Stripe\Util;
+
+/**
+ * A basic random generator. This is in a separate class so we the generator
+ * can be injected as a dependency and replaced with a mock in tests.
+ */
+class RandomGenerator
+{
+    /**
+     * Returns a random value between 0 and $max.
+     *
+     * @param float $max (optional)
+     * @return float
+     */
+    public function randFloat($max = 1.0)
+    {
+        return mt_rand() / mt_getrandmax() * $max;
+    }
+
+    /**
+     * Returns a v4 UUID.
+     *
+     * @return string
+     */
+    public function uuid()
+    {
+        $arr = array_values(unpack('N1a/n4b/N1c', openssl_random_pseudo_bytes(16)));
+        $arr[2] = ($arr[2] & 0x0fff) | 0x4000;
+        $arr[3] = ($arr[3] & 0x3fff) | 0x8000;
+        return vsprintf('%08x-%04x-%04x-%04x-%04x%08x', $arr);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Util/RequestOptions.php b/stripe-php/vendor/stripe/stripe-php/lib/Util/RequestOptions.php
new file mode 100644
index 00000000..a31f4d32
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Util/RequestOptions.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Stripe\Util;
+
+use Stripe\Error;
+
+class RequestOptions
+{
+    /**
+     * @var array A list of headers that should be persisted across requests.
+     */
+    public static $HEADERS_TO_PERSIST = [
+        'Stripe-Account',
+        'Stripe-Version',
+    ];
+
+    public $headers;
+    public $apiKey;
+
+    public function __construct($key = null, $headers = [])
+    {
+        $this->apiKey = $key;
+        $this->headers = $headers;
+    }
+
+    /**
+     * Unpacks an options array and merges it into the existing RequestOptions
+     * object.
+     * @param array|string|null $options a key => value array
+     *
+     * @return RequestOptions
+     */
+    public function merge($options)
+    {
+        $other_options = self::parse($options);
+        if ($other_options->apiKey === null) {
+            $other_options->apiKey = $this->apiKey;
+        }
+        $other_options->headers = array_merge($this->headers, $other_options->headers);
+        return $other_options;
+    }
+
+    /**
+     * Discards all headers that we don't want to persist across requests.
+     */
+    public function discardNonPersistentHeaders()
+    {
+        foreach ($this->headers as $k => $v) {
+            if (!in_array($k, self::$HEADERS_TO_PERSIST)) {
+                unset($this->headers[$k]);
+            }
+        }
+    }
+
+    /**
+     * Unpacks an options array into an RequestOptions object
+     * @param array|string|null $options a key => value array
+     *
+     * @return RequestOptions
+     */
+    public static function parse($options)
+    {
+        if ($options instanceof self) {
+            return $options;
+        }
+
+        if (is_null($options)) {
+            return new RequestOptions(null, []);
+        }
+
+        if (is_string($options)) {
+            return new RequestOptions($options, []);
+        }
+
+        if (is_array($options)) {
+            $headers = [];
+            $key = null;
+            if (array_key_exists('api_key', $options)) {
+                $key = $options['api_key'];
+            }
+            if (array_key_exists('idempotency_key', $options)) {
+                $headers['Idempotency-Key'] = $options['idempotency_key'];
+            }
+            if (array_key_exists('stripe_account', $options)) {
+                $headers['Stripe-Account'] = $options['stripe_account'];
+            }
+            if (array_key_exists('stripe_version', $options)) {
+                $headers['Stripe-Version'] = $options['stripe_version'];
+            }
+            return new RequestOptions($key, $headers);
+        }
+
+        $message = 'The second argument to Stripe API method calls is an '
+           . 'optional per-request apiKey, which must be a string, or '
+           . 'per-request options, which must be an array. (HINT: you can set '
+           . 'a global apiKey by "Stripe::setApiKey(<apiKey>)")';
+        throw new Error\Api($message);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Util/Set.php b/stripe-php/vendor/stripe/stripe-php/lib/Util/Set.php
new file mode 100644
index 00000000..d8beb8b1
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Util/Set.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Stripe\Util;
+
+use IteratorAggregate;
+use ArrayIterator;
+
+class Set implements IteratorAggregate
+{
+    private $_elts;
+
+    public function __construct($members = [])
+    {
+        $this->_elts = [];
+        foreach ($members as $item) {
+            $this->_elts[$item] = true;
+        }
+    }
+
+    public function includes($elt)
+    {
+        return isset($this->_elts[$elt]);
+    }
+
+    public function add($elt)
+    {
+        $this->_elts[$elt] = true;
+    }
+
+    public function discard($elt)
+    {
+        unset($this->_elts[$elt]);
+    }
+
+    public function toArray()
+    {
+        return array_keys($this->_elts);
+    }
+
+    public function getIterator()
+    {
+        return new ArrayIterator($this->toArray());
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Util/Util.php b/stripe-php/vendor/stripe/stripe-php/lib/Util/Util.php
new file mode 100644
index 00000000..82fd337d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Util/Util.php
@@ -0,0 +1,238 @@
+<?php
+
+namespace Stripe\Util;
+
+use Stripe\StripeObject;
+
+abstract class Util
+{
+    private static $isMbstringAvailable = null;
+    private static $isHashEqualsAvailable = null;
+
+    /**
+     * Whether the provided array (or other) is a list rather than a dictionary.
+     * A list is defined as an array for which all the keys are consecutive
+     * integers starting at 0. Empty arrays are considered to be lists.
+     *
+     * @param array|mixed $array
+     * @return boolean true if the given object is a list.
+     */
+    public static function isList($array)
+    {
+        if (!is_array($array)) {
+            return false;
+        }
+        if ($array === []) {
+            return true;
+        }
+        if (array_keys($array) !== range(0, count($array) - 1)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Recursively converts the PHP Stripe object to an array.
+     *
+     * @param array $values The PHP Stripe object to convert.
+     * @return array
+     */
+    public static function convertStripeObjectToArray($values)
+    {
+        $results = [];
+        foreach ($values as $k => $v) {
+            // FIXME: this is an encapsulation violation
+            if ($k[0] == '_') {
+                continue;
+            }
+            if ($v instanceof StripeObject) {
+                $results[$k] = $v->__toArray(true);
+            } elseif (is_array($v)) {
+                $results[$k] = self::convertStripeObjectToArray($v);
+            } else {
+                $results[$k] = $v;
+            }
+        }
+        return $results;
+    }
+
+    /**
+     * Converts a response from the Stripe API to the corresponding PHP object.
+     *
+     * @param array $resp The response from the Stripe API.
+     * @param array $opts
+     * @return StripeObject|array
+     */
+    public static function convertToStripeObject($resp, $opts)
+    {
+        $types = [
+            // data structures
+            'list' => 'Stripe\\Collection',
+
+            // business objects
+            'account' => 'Stripe\\Account',
+            'alipay_account' => 'Stripe\\AlipayAccount',
+            'apple_pay_domain' => 'Stripe\\ApplePayDomain',
+            'application_fee' => 'Stripe\\ApplicationFee',
+            'balance' => 'Stripe\\Balance',
+            'balance_transaction' => 'Stripe\\BalanceTransaction',
+            'bank_account' => 'Stripe\\BankAccount',
+            'bitcoin_receiver' => 'Stripe\\BitcoinReceiver',
+            'bitcoin_transaction' => 'Stripe\\BitcoinTransaction',
+            'card' => 'Stripe\\Card',
+            'charge' => 'Stripe\\Charge',
+            'country_spec' => 'Stripe\\CountrySpec',
+            'coupon' => 'Stripe\\Coupon',
+            'customer' => 'Stripe\\Customer',
+            'dispute' => 'Stripe\\Dispute',
+            'ephemeral_key' => 'Stripe\\EphemeralKey',
+            'event' => 'Stripe\\Event',
+            'exchange_rate' => 'Stripe\\ExchangeRate',
+            'fee_refund' => 'Stripe\\ApplicationFeeRefund',
+            'file_upload' => 'Stripe\\FileUpload',
+            'invoice' => 'Stripe\\Invoice',
+            'invoiceitem' => 'Stripe\\InvoiceItem',
+            'login_link' => 'Stripe\\LoginLink',
+            'order' => 'Stripe\\Order',
+            'order_return' => 'Stripe\\OrderReturn',
+            'payout' => 'Stripe\\Payout',
+            'plan' => 'Stripe\\Plan',
+            'product' => 'Stripe\\Product',
+            'recipient' => 'Stripe\\Recipient',
+            'recipient_transfer' => 'Stripe\\RecipientTransfer',
+            'refund' => 'Stripe\\Refund',
+            'sku' => 'Stripe\\SKU',
+            'source' => 'Stripe\\Source',
+            'source_transaction' => 'Stripe\\SourceTransaction',
+            'subscription' => 'Stripe\\Subscription',
+            'subscription_item' => 'Stripe\\SubscriptionItem',
+            'three_d_secure' => 'Stripe\\ThreeDSecure',
+            'token' => 'Stripe\\Token',
+            'topup' => 'Stripe\\Topup',
+            'transfer' => 'Stripe\\Transfer',
+            'transfer_reversal' => 'Stripe\\TransferReversal',
+        ];
+        if (self::isList($resp)) {
+            $mapped = [];
+            foreach ($resp as $i) {
+                array_push($mapped, self::convertToStripeObject($i, $opts));
+            }
+            return $mapped;
+        } elseif (is_array($resp)) {
+            if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']])) {
+                $class = $types[$resp['object']];
+            } else {
+                $class = 'Stripe\\StripeObject';
+            }
+            return $class::constructFrom($resp, $opts);
+        } else {
+            return $resp;
+        }
+    }
+
+    /**
+     * @param string|mixed $value A string to UTF8-encode.
+     *
+     * @return string|mixed The UTF8-encoded string, or the object passed in if
+     *    it wasn't a string.
+     */
+    public static function utf8($value)
+    {
+        if (self::$isMbstringAvailable === null) {
+            self::$isMbstringAvailable = function_exists('mb_detect_encoding');
+
+            if (!self::$isMbstringAvailable) {
+                trigger_error("It looks like the mbstring extension is not enabled. " .
+                    "UTF-8 strings will not properly be encoded. Ask your system " .
+                    "administrator to enable the mbstring extension, or write to " .
+                    "support@stripe.com if you have any questions.", E_USER_WARNING);
+            }
+        }
+
+        if (is_string($value) && self::$isMbstringAvailable && mb_detect_encoding($value, "UTF-8", true) != "UTF-8") {
+            return utf8_encode($value);
+        } else {
+            return $value;
+        }
+    }
+
+    /**
+     * Compares two strings for equality. The time taken is independent of the
+     * number of characters that match.
+     *
+     * @param string $a one of the strings to compare.
+     * @param string $b the other string to compare.
+     * @return bool true if the strings are equal, false otherwise.
+     */
+    public static function secureCompare($a, $b)
+    {
+        if (self::$isHashEqualsAvailable === null) {
+            self::$isHashEqualsAvailable = function_exists('hash_equals');
+        }
+
+        if (self::$isHashEqualsAvailable) {
+            return hash_equals($a, $b);
+        } else {
+            if (strlen($a) != strlen($b)) {
+                return false;
+            }
+
+            $result = 0;
+            for ($i = 0; $i < strlen($a); $i++) {
+                $result |= ord($a[$i]) ^ ord($b[$i]);
+            }
+            return ($result == 0);
+        }
+    }
+
+    /**
+     * @param array $arr A map of param keys to values.
+     * @param string|null $prefix
+     *
+     * @return string A querystring, essentially.
+     */
+    public static function urlEncode($arr, $prefix = null)
+    {
+        if (!is_array($arr)) {
+            return $arr;
+        }
+
+        $r = [];
+        foreach ($arr as $k => $v) {
+            if (is_null($v)) {
+                continue;
+            }
+
+            if ($prefix) {
+                if ($k !== null && (!is_int($k) || is_array($v))) {
+                    $k = $prefix."[".$k."]";
+                } else {
+                    $k = $prefix."[]";
+                }
+            }
+
+            if (is_array($v)) {
+                $enc = self::urlEncode($v, $k);
+                if ($enc) {
+                    $r[] = $enc;
+                }
+            } else {
+                $r[] = urlencode($k)."=".urlencode($v);
+            }
+        }
+
+        return implode("&", $r);
+    }
+
+    public static function normalizeId($id)
+    {
+        if (is_array($id)) {
+            $params = $id;
+            $id = $params['id'];
+            unset($params['id']);
+        } else {
+            $params = [];
+        }
+        return [$id, $params];
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/Webhook.php b/stripe-php/vendor/stripe/stripe-php/lib/Webhook.php
new file mode 100644
index 00000000..e0ab3021
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/Webhook.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Stripe;
+
+abstract class Webhook
+{
+    const DEFAULT_TOLERANCE = 300;
+
+    /**
+     * Returns an Event instance using the provided JSON payload. Throws a
+     * \UnexpectedValueException if the payload is not valid JSON, and a
+     * \Stripe\SignatureVerificationException if the signature verification
+     * fails for any reason.
+     *
+     * @param string $payload the payload sent by Stripe.
+     * @param string $sigHeader the contents of the signature header sent by
+     *  Stripe.
+     * @param string $secret secret used to generate the signature.
+     * @param int $tolerance maximum difference allowed between the header's
+     *  timestamp and the current time
+     * @return \Stripe\Event the Event instance
+     * @throws \UnexpectedValueException if the payload is not valid JSON,
+     * @throws \Stripe\Error\SignatureVerification if the verification fails.
+     */
+    public static function constructEvent($payload, $sigHeader, $secret, $tolerance = self::DEFAULT_TOLERANCE)
+    {
+        $data = json_decode($payload, true);
+        $jsonError = json_last_error();
+        if ($data === null && $jsonError !== JSON_ERROR_NONE) {
+            $msg = "Invalid payload: $payload "
+              . "(json_last_error() was $jsonError)";
+            throw new \UnexpectedValueException($msg);
+        }
+        $event = Event::constructFrom($data);
+
+        WebhookSignature::verifyHeader($payload, $sigHeader, $secret, $tolerance);
+
+        return $event;
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/lib/WebhookSignature.php b/stripe-php/vendor/stripe/stripe-php/lib/WebhookSignature.php
new file mode 100644
index 00000000..73e70dbd
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/lib/WebhookSignature.php
@@ -0,0 +1,133 @@
+<?php
+
+namespace Stripe;
+
+abstract class WebhookSignature
+{
+    const EXPECTED_SCHEME = "v1";
+
+    /**
+     * Verifies the signature header sent by Stripe. Throws a
+     * SignatureVerification exception if the verification fails for any
+     * reason.
+     *
+     * @param string $payload the payload sent by Stripe.
+     * @param string $header the contents of the signature header sent by
+     *  Stripe.
+     * @param string $secret secret used to generate the signature.
+     * @param int $tolerance maximum difference allowed between the header's
+     *  timestamp and the current time
+     * @throws \Stripe\Error\SignatureVerification if the verification fails.
+     * @return bool
+     */
+    public static function verifyHeader($payload, $header, $secret, $tolerance = null)
+    {
+        // Extract timestamp and signatures from header
+        $timestamp = self::getTimestamp($header);
+        $signatures = self::getSignatures($header, self::EXPECTED_SCHEME);
+        if ($timestamp == -1) {
+            throw new Error\SignatureVerification(
+                "Unable to extract timestamp and signatures from header",
+                $header,
+                $payload
+            );
+        }
+        if (empty($signatures)) {
+            throw new Error\SignatureVerification(
+                "No signatures found with expected scheme",
+                $header,
+                $payload
+            );
+        }
+
+        // Check if expected signature is found in list of signatures from
+        // header
+        $signedPayload = "$timestamp.$payload";
+        $expectedSignature = self::computeSignature($signedPayload, $secret);
+        $signatureFound = false;
+        foreach ($signatures as $signature) {
+            if (Util\Util::secureCompare($expectedSignature, $signature)) {
+                $signatureFound = true;
+                break;
+            }
+        }
+        if (!$signatureFound) {
+            throw new Error\SignatureVerification(
+                "No signatures found matching the expected signature for payload",
+                $header,
+                $payload
+            );
+        }
+
+        // Check if timestamp is within tolerance
+        if (($tolerance > 0) && ((time() - $timestamp) > $tolerance)) {
+            throw new Error\SignatureVerification(
+                "Timestamp outside the tolerance zone",
+                $header,
+                $payload
+            );
+        }
+
+        return true;
+    }
+
+    /**
+     * Extracts the timestamp in a signature header.
+     *
+     * @param string $header the signature header
+     * @return int the timestamp contained in the header, or -1 if no valid
+     *  timestamp is found
+     */
+    private static function getTimestamp($header)
+    {
+        $items = explode(",", $header);
+
+        foreach ($items as $item) {
+            $itemParts = explode("=", $item, 2);
+            if ($itemParts[0] == "t") {
+                if (!is_numeric($itemParts[1])) {
+                    return -1;
+                }
+                return intval($itemParts[1]);
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Extracts the signatures matching a given scheme in a signature header.
+     *
+     * @param string $header the signature header
+     * @param string $scheme the signature scheme to look for.
+     * @return array the list of signatures matching the provided scheme.
+     */
+    private static function getSignatures($header, $scheme)
+    {
+        $signatures = [];
+        $items = explode(",", $header);
+
+        foreach ($items as $item) {
+            $itemParts = explode("=", $item, 2);
+            if ($itemParts[0] == $scheme) {
+                array_push($signatures, $itemParts[1]);
+            }
+        }
+
+        return $signatures;
+    }
+
+    /**
+     * Computes the signature for a given payload and secret.
+     *
+     * The current scheme used by Stripe ("v1") is HMAC/SHA-256.
+     *
+     * @param string $payload the payload to sign.
+     * @param string $secret the secret used to generate the signature.
+     * @return string the signature as a string.
+     */
+    private static function computeSignature($payload, $secret)
+    {
+        return hash_hmac("sha256", $payload, $secret);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/phpunit.no_autoload.xml b/stripe-php/vendor/stripe/stripe-php/phpunit.no_autoload.xml
new file mode 100644
index 00000000..f418611d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/phpunit.no_autoload.xml
@@ -0,0 +1,15 @@
+<phpunit bootstrap="tests/bootstrap.no_autoload.php" colors="true">
+    <testsuites>
+        <testsuite name="Stripe PHP Test Suite">
+            <directory suffix="Test.php">tests</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist>
+            <directory>lib</directory>
+        </whitelist>
+    </filter>
+    <logging>
+        <log type="coverage-clover" target="clover.xml"/>
+    </logging>
+</phpunit>
diff --git a/stripe-php/vendor/stripe/stripe-php/phpunit.xml b/stripe-php/vendor/stripe/stripe-php/phpunit.xml
new file mode 100644
index 00000000..7a73276d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/phpunit.xml
@@ -0,0 +1,15 @@
+<phpunit bootstrap="tests/bootstrap.php" colors="true">
+    <testsuites>
+        <testsuite name="Stripe PHP Test Suite">
+            <directory suffix="Test.php">tests</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist>
+            <directory>lib</directory>
+        </whitelist>
+    </filter>
+    <logging>
+        <log type="coverage-clover" target="clover.xml"/>
+    </logging>
+</phpunit>
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/AccountTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/AccountTest.php
new file mode 100644
index 00000000..4c621690
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/AccountTest.php
@@ -0,0 +1,365 @@
+<?php
+
+namespace Stripe;
+
+class AccountTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'acct_123';
+    const TEST_EXTERNALACCOUNT_ID = 'ba_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/accounts'
+        );
+        $resources = Account::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Account", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Account::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Account", $resource);
+    }
+
+    public function testIsRetrievableWithoutId()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/account'
+        );
+        $resource = Account::retrieve();
+        $this->assertInstanceOf("Stripe\\Account", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/accounts'
+        );
+        $resource = Account::create(["type" => "custom"]);
+        $this->assertInstanceOf("Stripe\\Account", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Account::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/accounts/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Account", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Account::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Account", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = Account::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/accounts/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\Account", $resource);
+    }
+
+    public function testIsRejectable()
+    {
+        $account = Account::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/accounts/' . $account->id . '/reject'
+        );
+        $resource = $account->reject(["reason" => "fraud"]);
+        $this->assertInstanceOf("Stripe\\Account", $resource);
+        $this->assertSame($resource, $account);
+    }
+
+    public function testIsDeauthorizable()
+    {
+        $resource = Account::retrieve(self::TEST_RESOURCE_ID);
+        $this->stubRequest(
+            'post',
+            '/oauth/deauthorize',
+            [
+                'client_id' => Stripe::getClientId(),
+                'stripe_user_id' => $resource->id,
+            ],
+            null,
+            false,
+            [
+                'stripe_user_id' => $resource->id,
+            ],
+            200,
+            Stripe::$connectBase
+        );
+        $resource->deauthorize();
+    }
+
+    public function testCanCreateExternalAccount()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts'
+        );
+        $resource = Account::createExternalAccount(self::TEST_RESOURCE_ID, [
+            "external_account" => "btok_123",
+        ]);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+
+    public function testCanRetrieveExternalAccount()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts/' . self::TEST_EXTERNALACCOUNT_ID
+        );
+        $resource = Account::retrieveExternalAccount(self::TEST_RESOURCE_ID, self::TEST_EXTERNALACCOUNT_ID);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+
+    public function testCanUpdateExternalAccount()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts/' . self::TEST_EXTERNALACCOUNT_ID
+        );
+        $resource = Account::updateExternalAccount(self::TEST_RESOURCE_ID, self::TEST_EXTERNALACCOUNT_ID, [
+            "name" => "name",
+        ]);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+
+    public function testCanDeleteExternalAccount()
+    {
+        $this->expectsRequest(
+            'delete',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts/' . self::TEST_EXTERNALACCOUNT_ID
+        );
+        $resource = Account::deleteExternalAccount(self::TEST_RESOURCE_ID, self::TEST_EXTERNALACCOUNT_ID);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+
+    public function testCanListExternalAccounts()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID . '/external_accounts'
+        );
+        $resources = Account::allExternalAccounts(self::TEST_RESOURCE_ID);
+        $this->assertTrue(is_array($resources->data));
+    }
+
+    public function testCanCreateLoginLink()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/accounts/' . self::TEST_RESOURCE_ID . '/login_links'
+        );
+        $resource = Account::createLoginLink(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\LoginLink", $resource);
+    }
+
+    public function testSerializeNewAdditionalOwners()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+            'legal_entity' => StripeObject::constructFrom([]),
+        ], null);
+        $obj->legal_entity->additional_owners = [
+            ['first_name' => 'Joe'],
+            ['first_name' => 'Jane'],
+        ];
+
+        $expected = [
+            'legal_entity' => [
+                'additional_owners' => [
+                    0 => ['first_name' => 'Joe'],
+                    1 => ['first_name' => 'Jane'],
+                ],
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    public function testSerializePartiallyChangedAdditionalOwners()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+            'legal_entity' => [
+                'additional_owners' => [
+                    StripeObject::constructFrom(['first_name' => 'Joe']),
+                    StripeObject::constructFrom(['first_name' => 'Jane']),
+                ],
+            ],
+        ], null);
+        $obj->legal_entity->additional_owners[1]->first_name = 'Stripe';
+
+        $expected = [
+            'legal_entity' => [
+                'additional_owners' => [
+                    1 => ['first_name' => 'Stripe'],
+                ],
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    public function testSerializeUnchangedAdditionalOwners()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+            'legal_entity' => [
+                'additional_owners' => [
+                    StripeObject::constructFrom(['first_name' => 'Joe']),
+                    StripeObject::constructFrom(['first_name' => 'Jane']),
+                ],
+            ],
+        ], null);
+
+        $expected = [
+            'legal_entity' => [
+                'additional_owners' => [],
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    public function testSerializeUnsetAdditionalOwners()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+            'legal_entity' => [
+                'additional_owners' => [
+                    StripeObject::constructFrom(['first_name' => 'Joe']),
+                    StripeObject::constructFrom(['first_name' => 'Jane']),
+                ],
+            ],
+        ], null);
+        $obj->legal_entity->additional_owners = null;
+
+        // Note that the empty string that we send for this one has a special
+        // meaning for the server, which interprets it as an array unset.
+        $expected = [
+            'legal_entity' => [
+                'additional_owners' => '',
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSerializeAdditionalOwnersDeletedItem()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+            'legal_entity' => [
+                'additional_owners' => [
+                    StripeObject::constructFrom(['first_name' => 'Joe']),
+                    StripeObject::constructFrom(['first_name' => 'Jane']),
+                ],
+            ],
+        ], null);
+        unset($obj->legal_entity->additional_owners[0]);
+
+        $obj->serializeParameters();
+    }
+
+    public function testSerializeExternalAccountString()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+        ], null);
+        $obj->external_account = 'btok_123';
+
+        $expected = [
+            'external_account' => 'btok_123',
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    public function testSerializeExternalAccountHash()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+        ], null);
+        $obj->external_account = [
+            'object' => 'bank_account',
+            'routing_number' => '110000000',
+            'account_number' => '000123456789',
+            'country' => 'US',
+            'currency' => 'usd',
+        ];
+
+        $expected = [
+            'external_account' => [
+                'object' => 'bank_account',
+                'routing_number' => '110000000',
+                'account_number' => '000123456789',
+                'country' => 'US',
+                'currency' => 'usd',
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    public function testSerializeBankAccountString()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+        ], null);
+        $obj->bank_account = 'btok_123';
+
+        $expected = [
+            'bank_account' => 'btok_123',
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    public function testSerializeBankAccountHash()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'account',
+        ], null);
+        $obj->bank_account = [
+            'object' => 'bank_account',
+            'routing_number' => '110000000',
+            'account_number' => '000123456789',
+            'country' => 'US',
+            'currency' => 'usd',
+        ];
+
+        $expected = [
+            'bank_account' => [
+                'object' => 'bank_account',
+                'routing_number' => '110000000',
+                'account_number' => '000123456789',
+                'country' => 'US',
+                'currency' => 'usd',
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/AlipayAccountTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/AlipayAccountTest.php
new file mode 100644
index 00000000..7159f544
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/AlipayAccountTest.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Stripe;
+
+class AlipayAccountTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'aliacc_123';
+
+    // Because of the wildcard nature of sources, stripe-mock cannot currently
+    // reliably return sources of a given type, so we create a fixture manually
+    public function createFixture($params = [])
+    {
+        if (empty($params)) {
+            $params['customer'] = 'cus_123';
+        }
+        $base = [
+            'id' => self::TEST_RESOURCE_ID,
+            'object' => 'card',
+            'metadata' => [],
+        ];
+        return AlipayAccount::constructFrom(
+            array_merge($params, $base),
+            new Util\RequestOptions()
+        );
+    }
+
+    public function testHasCorrectUrlForCustomer()
+    {
+        $resource = $this->createFixture(['customer' => 'cus_123']);
+        $this->assertSame(
+            "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    /**
+     * @expectedException \Stripe\Error\InvalidRequest
+     */
+    public function testIsNotDirectlyRetrievable()
+    {
+        AlipayAccount::retrieve(self::TEST_RESOURCE_ID);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = $this->createFixture();
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource->save();
+        $this->assertSame("Stripe\\AlipayAccount", get_class($resource));
+    }
+
+    /**
+     * @expectedException \Stripe\Error\InvalidRequest
+     */
+    public function testIsNotDirectlyUpdatable()
+    {
+        AlipayAccount::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = $this->createFixture();
+        $this->expectsRequest(
+            'delete',
+            '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource->delete();
+        $this->assertSame("Stripe\\AlipayAccount", get_class($resource));
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApiRequestorTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApiRequestorTest.php
new file mode 100644
index 00000000..6fb8229c
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApiRequestorTest.php
@@ -0,0 +1,572 @@
+<?php
+
+namespace Stripe;
+
+use Stripe\HttpClient\CurlClient;
+
+class ApiRequestorTest extends TestCase
+{
+    public function testEncodeObjects()
+    {
+        $reflector = new \ReflectionClass('Stripe\\ApiRequestor');
+        $method = $reflector->getMethod('_encodeObjects');
+        $method->setAccessible(true);
+
+        $a = ['customer' => new Customer('abcd')];
+        $enc = $method->invoke(null, $a);
+        $this->assertSame($enc, ['customer' => 'abcd']);
+
+        // Preserves UTF-8
+        $v = ['customer' => "☃"];
+        $enc = $method->invoke(null, $v);
+        $this->assertSame($enc, $v);
+
+        // Encodes latin-1 -> UTF-8
+        $v = ['customer' => "\xe9"];
+        $enc = $method->invoke(null, $v);
+        $this->assertSame($enc, ['customer' => "\xc3\xa9"]);
+
+        // Encodes booleans
+        $v = true;
+        $enc = $method->invoke(null, $v);
+        $this->assertSame('true', $enc);
+
+        $v = false;
+        $enc = $method->invoke(null, $v);
+        $this->assertSame('false', $enc);
+    }
+
+    public function testHttpClientInjection()
+    {
+        $reflector = new \ReflectionClass('Stripe\\ApiRequestor');
+        $method = $reflector->getMethod('httpClient');
+        $method->setAccessible(true);
+
+        $curl = new CurlClient();
+        $curl->setTimeout(10);
+        ApiRequestor::setHttpClient($curl);
+
+        $injectedCurl = $method->invoke(new ApiRequestor());
+        $this->assertSame($injectedCurl, $curl);
+    }
+
+    public function testDefaultHeaders()
+    {
+        $reflector = new \ReflectionClass('Stripe\\ApiRequestor');
+        $method = $reflector->getMethod('_defaultHeaders');
+        $method->setAccessible(true);
+
+        // no way to stub static methods with PHPUnit 4.x :(
+        Stripe::setAppInfo('MyTestApp', '1.2.34', 'https://mytestapp.example');
+        $apiKey = 'sk_test_notarealkey';
+        $clientInfo = ['httplib' => 'testlib 0.1.2'];
+
+        $headers = $method->invoke(null, $apiKey, $clientInfo);
+
+        $ua = json_decode($headers['X-Stripe-Client-User-Agent']);
+        $this->assertSame($ua->application->name, 'MyTestApp');
+        $this->assertSame($ua->application->version, '1.2.34');
+        $this->assertSame($ua->application->url, 'https://mytestapp.example');
+
+        $this->assertSame($ua->httplib, 'testlib 0.1.2');
+
+        $this->assertSame(
+            $headers['User-Agent'],
+            'Stripe/v1 PhpBindings/' . Stripe::VERSION . ' MyTestApp/1.2.34 (https://mytestapp.example)'
+        );
+
+        $this->assertSame($headers['Authorization'], 'Bearer ' . $apiKey);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\Authentication
+     * @expectedExceptionMessageRegExp #No API key provided#
+     */
+    public function testRaisesAuthenticationErrorWhenNoApiKey()
+    {
+        Stripe::setApiKey(null);
+        Charge::create();
+    }
+
+    public function testRaisesInvalidRequestErrorOn400()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            null,
+            false,
+            [
+                'error' => [
+                    'type' => 'invalid_request_error',
+                    'message' => 'Missing id',
+                    'param' => 'id',
+                ],
+            ],
+            400
+        );
+
+        try {
+            Charge::create();
+            $this->fail("Did not raise error");
+        } catch (Error\InvalidRequest $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame('Missing id', $e->getMessage());
+            $this->assertSame('id', $e->getStripeParam());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesIdempotencyErrorOn400AndTypeIdempotencyError()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            array(),
+            null,
+            false,
+            array(
+                'error' => array(
+                    'type' => 'idempotency_error',
+                    'message' => "Keys for idempotent requests can only be used with the same parameters they were first used with. Try using a key other than 'abc' if you meant to execute a different request.",
+                ),
+            ),
+            400
+        );
+
+        try {
+            Charge::create();
+            $this->fail("Did not raise error");
+        } catch (Error\Idempotency $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame("Keys for idempotent requests can only be used with the same parameters they were first used with. Try using a key other than 'abc' if you meant to execute a different request.", $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesAuthenticationErrorOn401()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            null,
+            false,
+            [
+                'error' => [
+                    'type' => 'invalid_request_error',
+                    'message' => 'You did not provide an API key.',
+                ],
+            ],
+            401
+        );
+
+        try {
+            Charge::create();
+            $this->fail("Did not raise error");
+        } catch (Error\Authentication $e) {
+            $this->assertSame(401, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame('You did not provide an API key.', $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesCardErrorOn402()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            null,
+            false,
+            [
+                'error' => [
+                    'type' => 'card_error',
+                    'message' => 'Your card was declined.',
+                    'code' => 'card_declined',
+                    'decline_code' => 'generic_decline',
+                    'charge' => 'ch_declined_charge',
+                    'param' => 'exp_month',
+                ],
+            ],
+            402
+        );
+
+        try {
+            Charge::create();
+            $this->fail("Did not raise error");
+        } catch (Error\Card $e) {
+            $this->assertSame(402, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame('Your card was declined.', $e->getMessage());
+            $this->assertSame('card_declined', $e->getStripeCode());
+            $this->assertSame('generic_decline', $e->getDeclineCode());
+            $this->assertSame('exp_month', $e->getStripeParam());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesPermissionErrorOn403()
+    {
+        $this->stubRequest(
+            'GET',
+            '/v1/accounts/foo',
+            [],
+            null,
+            false,
+            [
+                'error' => [
+                    'type' => 'invalid_request_error',
+                    'message' => "The provided key 'sk_test_********************1234' does not have access to account 'foo' (or that account does not exist). Application access may have been revoked.",
+                ],
+            ],
+            403
+        );
+
+        try {
+            Account::retrieve('foo');
+            $this->fail("Did not raise error");
+        } catch (Error\Permission $e) {
+            $this->assertSame(403, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame("The provided key 'sk_test_********************1234' does not have access to account 'foo' (or that account does not exist). Application access may have been revoked.", $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesInvalidRequestErrorOn404()
+    {
+        $this->stubRequest(
+            'GET',
+            '/v1/charges/foo',
+            [],
+            null,
+            false,
+            [
+                'error' => [
+                    'type' => 'invalid_request_error',
+                    'message' => 'No such charge: foo',
+                    'param' => 'id',
+                ],
+            ],
+            404
+        );
+
+        try {
+            Charge::retrieve('foo');
+            $this->fail("Did not raise error");
+        } catch (Error\InvalidRequest $e) {
+            $this->assertSame(404, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame('No such charge: foo', $e->getMessage());
+            $this->assertSame('id', $e->getStripeParam());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesRateLimitErrorOn429()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            null,
+            false,
+            [
+                'error' => [
+                    'message' => 'Too many requests',
+                ],
+            ],
+            429
+        );
+
+        try {
+            Charge::create();
+            $this->fail("Did not raise error");
+        } catch (Error\RateLimit $e) {
+            $this->assertSame(429, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame('Too many requests', $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesRateLimitErrorOn400AndCodeRateLimit()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            null,
+            false,
+            [
+                'error' => [
+                    'code' => 'rate_limit',
+                    'message' => 'Too many requests',
+                ],
+            ],
+            400
+        );
+
+        try {
+            Charge::create();
+            $this->fail("Did not raise error");
+        } catch (Error\RateLimit $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertTrue(is_array($e->getJsonBody()));
+            $this->assertSame('Too many requests', $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesOAuthInvalidRequestError()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/token',
+            [],
+            null,
+            false,
+            [
+                'error' => 'invalid_request',
+                'error_description' => 'No grant type specified',
+            ],
+            400,
+            Stripe::$connectBase
+        );
+
+        try {
+            OAuth::token();
+            $this->fail("Did not raise error");
+        } catch (Error\OAuth\InvalidRequest $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertSame('invalid_request', $e->getErrorCode());
+            $this->assertSame('No grant type specified', $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesOAuthInvalidClientError()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/token',
+            [],
+            null,
+            false,
+            [
+                'error' => 'invalid_client',
+                'error_description' => 'No authentication was provided. Send your secret API key using the Authorization header, or as a client_secret POST parameter.',
+            ],
+            401,
+            Stripe::$connectBase
+        );
+
+        try {
+            OAuth::token();
+            $this->fail("Did not raise error");
+        } catch (Error\OAuth\InvalidClient $e) {
+            $this->assertSame(401, $e->getHttpStatus());
+            $this->assertSame('invalid_client', $e->getErrorCode());
+            $this->assertSame('No authentication was provided. Send your secret API key using the Authorization header, or as a client_secret POST parameter.', $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesOAuthInvalidGrantError()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/token',
+            [],
+            null,
+            false,
+            [
+                'error' => 'invalid_grant',
+                'error_description' => 'This authorization code has already been used. All tokens issued with this code have been revoked.',
+            ],
+            400,
+            Stripe::$connectBase
+        );
+
+        try {
+            OAuth::token();
+            $this->fail("Did not raise error");
+        } catch (Error\OAuth\InvalidGrant $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertSame('invalid_grant', $e->getErrorCode());
+            $this->assertSame('This authorization code has already been used. All tokens issued with this code have been revoked.', $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesOAuthInvalidScopeError()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/token',
+            [],
+            null,
+            false,
+            [
+                'error' => 'invalid_scope',
+                'error_description' => 'Invalid scope provided: invalid_scope.',
+            ],
+            400,
+            Stripe::$connectBase
+        );
+
+        try {
+            OAuth::token();
+            $this->fail("Did not raise error");
+        } catch (Error\OAuth\InvalidScope $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertSame('invalid_scope', $e->getErrorCode());
+            $this->assertSame('Invalid scope provided: invalid_scope.', $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesOAuthUnsupportedGrantTypeError()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/token',
+            [],
+            null,
+            false,
+            [
+                'error' => 'unsupported_grant_type',
+            ],
+            400,
+            Stripe::$connectBase
+        );
+
+        try {
+            OAuth::token();
+            $this->fail("Did not raise error");
+        } catch (Error\OAuth\UnsupportedGrantType $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertSame('unsupported_grant_type', $e->getErrorCode());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testRaisesOAuthUnsupportedResponseTypeError()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/token',
+            [],
+            null,
+            false,
+            [
+                'error' => 'unsupported_response_type',
+                'error_description' => "Only 'code' response_type is supported, but 'unsupported_response_type' was provided",
+            ],
+            400,
+            Stripe::$connectBase
+        );
+
+        try {
+            OAuth::token();
+            $this->fail("Did not raise error");
+        } catch (Error\OAuth\UnsupportedResponseType $e) {
+            $this->assertSame(400, $e->getHttpStatus());
+            $this->assertSame('unsupported_response_type', $e->getErrorCode());
+            $this->assertSame("Only 'code' response_type is supported, but 'unsupported_response_type' was provided", $e->getMessage());
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testHeaderStripeVersionGlobal()
+    {
+        Stripe::setApiVersion('2222-22-22');
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            [
+                'Stripe-Version: 2222-22-22',
+            ],
+            false,
+            [
+                'id' => 'ch_123',
+                'object' => 'charge',
+            ]
+        );
+        Charge::create();
+    }
+
+    public function testHeaderStripeVersionRequestOptions()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            [
+                'Stripe-Version: 2222-22-22',
+            ],
+            false,
+            [
+                'id' => 'ch_123',
+                'object' => 'charge',
+            ]
+        );
+        Charge::create([], ['stripe_version' => '2222-22-22']);
+    }
+
+    public function testHeaderStripeAccountGlobal()
+    {
+        Stripe::setAccountId('acct_123');
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            [
+                'Stripe-Account: acct_123',
+            ],
+            false,
+            [
+                'id' => 'ch_123',
+                'object' => 'charge',
+            ]
+        );
+        Charge::create();
+    }
+
+    public function testHeaderStripeAccountRequestOptions()
+    {
+        $this->stubRequest(
+            'POST',
+            '/v1/charges',
+            [],
+            [
+                'Stripe-Account: acct_123',
+            ],
+            false,
+            [
+                'id' => 'ch_123',
+                'object' => 'charge',
+            ]
+        );
+        Charge::create([], ['stripe_account' => 'acct_123']);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplePayDomainTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplePayDomainTest.php
new file mode 100644
index 00000000..d033f55c
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplePayDomainTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Stripe;
+
+class ApplePayDomainTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'apwc_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/apple_pay/domains'
+        );
+        $resources = ApplePayDomain::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\ApplePayDomain", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/apple_pay/domains/' . self::TEST_RESOURCE_ID
+        );
+        $resource = ApplePayDomain::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\ApplePayDomain", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/apple_pay/domains'
+        );
+        $resource = ApplePayDomain::create([
+            "domain_name" => "domain",
+        ]);
+        $this->assertInstanceOf("Stripe\\ApplePayDomain", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = ApplePayDomain::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/apple_pay/domains/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\ApplePayDomain", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplicationFeeRefundTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplicationFeeRefundTest.php
new file mode 100644
index 00000000..9af059ff
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplicationFeeRefundTest.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Stripe;
+
+class ApplicationFeeRefundTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'fr_123';
+    const TEST_FEE_ID = 'fee_123';
+
+    public function testIsSaveable()
+    {
+        $resource = ApplicationFee::retrieveRefund(self::TEST_FEE_ID, self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/application_fees/' . $resource->fee . '/refunds/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplicationFeeTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplicationFeeTest.php
new file mode 100644
index 00000000..66e007ff
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ApplicationFeeTest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Stripe;
+
+class ApplicationFeeTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'fee_123';
+    const TEST_FEEREFUND_ID = 'fr_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/application_fees'
+        );
+        $resources = ApplicationFee::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\ApplicationFee", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/application_fees/' . self::TEST_RESOURCE_ID
+        );
+        $resource = ApplicationFee::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\ApplicationFee", $resource);
+    }
+
+    public function testIsRefundable()
+    {
+        $fee = ApplicationFee::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/application_fees/' . $fee->id . '/refunds'
+        );
+        $resource = $fee->refund();
+        $this->assertInstanceOf("Stripe\\ApplicationFee", $resource);
+        $this->assertSame($resource, $fee);
+    }
+
+    public function testCanCreateRefund()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds'
+        );
+        $resource = ApplicationFee::createRefund(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource);
+    }
+
+    public function testCanRetrieveRefund()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds/' . self::TEST_FEEREFUND_ID
+        );
+        $resource = ApplicationFee::retrieveRefund(self::TEST_RESOURCE_ID, self::TEST_FEEREFUND_ID);
+        $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource);
+    }
+
+    public function testCanUpdateRefund()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds/' . self::TEST_FEEREFUND_ID
+        );
+        $resource = ApplicationFee::updateRefund(self::TEST_RESOURCE_ID, self::TEST_FEEREFUND_ID);
+        $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resource);
+    }
+
+    public function testCanListRefunds()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/application_fees/' . self::TEST_RESOURCE_ID . '/refunds'
+        );
+        $resources = ApplicationFee::allRefunds(self::TEST_RESOURCE_ID);
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\ApplicationFeeRefund", $resources->data[0]);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BalanceTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BalanceTest.php
new file mode 100644
index 00000000..ccbdbdfb
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BalanceTest.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Stripe;
+
+class BalanceTest extends TestCase
+{
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/balance'
+        );
+        $resource = Balance::retrieve();
+        $this->assertInstanceOf("Stripe\\Balance", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BalanceTransactionTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BalanceTransactionTest.php
new file mode 100644
index 00000000..8785e859
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BalanceTransactionTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Stripe;
+
+class BalanceTransactionTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'txn_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/balance/history'
+        );
+        $resources = BalanceTransaction::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\BalanceTransaction", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/balance/history/' . self::TEST_RESOURCE_ID
+        );
+        $resource = BalanceTransaction::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\BalanceTransaction", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BankAccountTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BankAccountTest.php
new file mode 100644
index 00000000..ab1dff9d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BankAccountTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Stripe;
+
+class BankAccountTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'ba_123';
+
+    // Because of the wildcard nature of sources, stripe-mock cannot currently
+    // reliably return sources of a given type, so we create a fixture manually
+    public function createFixture($params = [])
+    {
+        if (empty($params)) {
+            $params['customer'] = 'cus_123';
+        }
+        $base = [
+            'id' => self::TEST_RESOURCE_ID,
+            'object' => 'bank_account',
+            'metadata' => [],
+        ];
+        return BankAccount::constructFrom(
+            array_merge($params, $base),
+            new Util\RequestOptions()
+        );
+    }
+
+    public function testHasCorrectUrlForCustomer()
+    {
+        $resource = $this->createFixture(['customer' => 'cus_123']);
+        $this->assertSame(
+            "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    public function testHasCorrectUrlForAccount()
+    {
+        $resource = $this->createFixture(['account' => 'acct_123']);
+        $this->assertSame(
+            "/v1/accounts/acct_123/external_accounts/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    /**
+     * @expectedException \Stripe\Error\InvalidRequest
+     */
+    public function testIsNotDirectlyRetrievable()
+    {
+        BankAccount::retrieve(self::TEST_RESOURCE_ID);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = $this->createFixture();
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource->save();
+        $this->assertSame("Stripe\\BankAccount", get_class($resource));
+    }
+
+    /**
+     * @expectedException \Stripe\Error\InvalidRequest
+     */
+    public function testIsNotDirectlyUpdatable()
+    {
+        BankAccount::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = $this->createFixture();
+        $this->expectsRequest(
+            'delete',
+            '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource->delete();
+        $this->assertSame("Stripe\\BankAccount", get_class($resource));
+    }
+
+    public function testIsVerifiable()
+    {
+        $resource = $this->createFixture();
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID . "/verify",
+            [
+                "amounts" => [1, 2]
+            ]
+        );
+        $resource->verify(["amounts" => [1, 2]]);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BitcoinReceiverTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BitcoinReceiverTest.php
new file mode 100644
index 00000000..cea999fc
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/BitcoinReceiverTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Stripe;
+
+class BitcoinReceiverTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'btcrcv_123';
+
+    // Because of the wildcard nature of sources, stripe-mock cannot currently
+    // reliably return sources of a given type, so we create a fixture manually
+    public function createFixture($params = [])
+    {
+        $base = [
+            'id' => self::TEST_RESOURCE_ID,
+            'object' => 'bitcoin_receiver',
+            'metadata' => [],
+        ];
+        return BitcoinReceiver::constructFrom(
+            array_merge($params, $base),
+            new Util\RequestOptions()
+        );
+    }
+
+    public function testHasCorrectStandaloneUrl()
+    {
+        $resource = $this->createFixture();
+        $this->assertSame(
+            "/v1/bitcoin/receivers/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    public function testHasCorrectUrlForCustomer()
+    {
+        $resource = $this->createFixture(['customer' => 'cus_123']);
+        $this->assertSame(
+            "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/bitcoin/receivers'
+        );
+        $resources = BitcoinReceiver::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertSame("Stripe\\BitcoinReceiver", get_class($resources->data[0]));
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/bitcoin/receivers/' . self::TEST_RESOURCE_ID
+        );
+        $resource = BitcoinReceiver::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertSame("Stripe\\BitcoinReceiver", get_class($resource));
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CardTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CardTest.php
new file mode 100644
index 00000000..8976eff6
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CardTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Stripe;
+
+class CardTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'card_123';
+
+    // Because of the wildcard nature of sources, stripe-mock cannot currently
+    // reliably return sources of a given type, so we create a fixture manually
+    public function createFixture($params = [])
+    {
+        if (empty($params)) {
+            $params['customer'] = 'cus_123';
+        }
+        $base = [
+            'id' => self::TEST_RESOURCE_ID,
+            'object' => 'card',
+            'metadata' => [],
+        ];
+        return Card::constructFrom(
+            array_merge($params, $base),
+            new Util\RequestOptions()
+        );
+    }
+
+    public function testHasCorrectUrlForCustomer()
+    {
+        $resource = $this->createFixture(['customer' => 'cus_123']);
+        $this->assertSame(
+            "/v1/customers/cus_123/sources/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    public function testHasCorrectUrlForAccount()
+    {
+        $resource = $this->createFixture(['account' => 'acct_123']);
+        $this->assertSame(
+            "/v1/accounts/acct_123/external_accounts/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    public function testHasCorrectUrlForRecipient()
+    {
+        $resource = $this->createFixture(['recipient' => 'rp_123']);
+        $this->assertSame(
+            "/v1/recipients/rp_123/cards/" . self::TEST_RESOURCE_ID,
+            $resource->instanceUrl()
+        );
+    }
+
+    /**
+     * @expectedException \Stripe\Error\InvalidRequest
+     */
+    public function testIsNotDirectlyRetrievable()
+    {
+        Card::retrieve(self::TEST_RESOURCE_ID);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = $this->createFixture();
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource->save();
+        $this->assertSame("Stripe\\Card", get_class($resource));
+    }
+
+    /**
+     * @expectedException \Stripe\Error\InvalidRequest
+     */
+    public function testIsNotDirectlyUpdatable()
+    {
+        Card::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = $this->createFixture();
+        $this->expectsRequest(
+            'delete',
+            '/v1/customers/cus_123/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource->delete();
+        $this->assertSame("Stripe\\Card", get_class($resource));
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ChargeTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ChargeTest.php
new file mode 100644
index 00000000..b8c6cbab
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ChargeTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Stripe;
+
+class ChargeTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'ch_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/charges'
+        );
+        $resources = Charge::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Charge", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/charges/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/charges'
+        );
+        $resource = Charge::create([
+            "amount" => 100,
+            "currency" => "usd",
+            "source" => "tok_123"
+        ]);
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Charge::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+    }
+
+    public function testCanRefund()
+    {
+        $charge = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . $charge->id . '/refund'
+        );
+        $resource = $charge->refund();
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+        $this->assertSame($resource, $charge);
+    }
+
+    public function testCanCapture()
+    {
+        $charge = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . $charge->id . '/capture'
+        );
+        $resource = $charge->capture();
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+        $this->assertSame($resource, $charge);
+    }
+
+    public function testCanUpdateDispute()
+    {
+        $charge = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . $charge->id . '/dispute'
+        );
+        $resource = $charge->updateDispute();
+        $this->assertInstanceOf("Stripe\\Dispute", $resource);
+    }
+
+    public function testCanCloseDispute()
+    {
+        $charge = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . $charge->id . '/dispute/close'
+        );
+        $resource = $charge->closeDispute();
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+        $this->assertSame($resource, $charge);
+    }
+
+    public function testCanMarkAsFraudulent()
+    {
+        $charge = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . $charge->id,
+            ['fraud_details' => ['user_report' => 'fraudulent']]
+        );
+        $resource = $charge->markAsFraudulent();
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+        $this->assertSame($resource, $charge);
+    }
+
+    public function testCanMarkAsSafe()
+    {
+        $charge = Charge::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/charges/' . $charge->id,
+            ['fraud_details' => ['user_report' => 'safe']]
+        );
+        $resource = $charge->markAsSafe();
+        $this->assertInstanceOf("Stripe\\Charge", $resource);
+        $this->assertSame($resource, $charge);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CollectionTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CollectionTest.php
new file mode 100644
index 00000000..560085ae
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CollectionTest.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Stripe;
+
+class CollectionTest extends TestCase
+{
+    /**
+     * @before
+     */
+    public function setUpFixture()
+    {
+        $this->fixture = Collection::constructFrom([
+            'data' => [['id' => 1]],
+            'has_more' => true,
+            'url' => '/things',
+        ]);
+    }
+
+    public function testCanList()
+    {
+        $this->stubRequest(
+            'GET',
+            '/things',
+            [],
+            null,
+            false,
+            [
+                'data' => [['id' => 1]],
+                'has_more' => true,
+                'url' => '/things',
+            ]
+        );
+
+        $resources = $this->fixture->all();
+        $this->assertTrue(is_array($resources->data));
+    }
+
+    public function testCanRetrieve()
+    {
+        $this->stubRequest(
+            'GET',
+            '/things/1',
+            [],
+            null,
+            false,
+            [
+                'id' => 1,
+            ]
+        );
+
+        $this->fixture->retrieve(1);
+    }
+
+    public function testCanCreate()
+    {
+        $this->stubRequest(
+            'POST',
+            '/things',
+            [
+                'foo' => 'bar',
+            ],
+            null,
+            false,
+            [
+                'id' => 2,
+            ]
+        );
+
+        $this->fixture->create([
+            'foo' => 'bar',
+        ]);
+    }
+
+    public function testProvidesAutoPagingIterator()
+    {
+        $this->stubRequest(
+            'GET',
+            '/things',
+            [
+                'starting_after' => 1,
+            ],
+            null,
+            false,
+            [
+                'data' => [['id' => 2], ['id' => 3]],
+                'has_more' => false,
+            ]
+        );
+
+        $seen = [];
+        foreach ($this->fixture->autoPagingIterator() as $item) {
+            array_push($seen, $item['id']);
+        }
+
+        $this->assertSame([1, 2, 3], $seen);
+    }
+
+    public function testSupportsIteratorToArray()
+    {
+        $this->stubRequest(
+            'GET',
+            '/things',
+            [
+                'starting_after' => 1,
+            ],
+            null,
+            false,
+            [
+                'data' => [['id' => 2], ['id' => 3]],
+                'has_more' => false,
+            ]
+        );
+
+        $seen = [];
+        foreach (iterator_to_array($this->fixture->autoPagingIterator()) as $item) {
+            array_push($seen, $item['id']);
+        }
+
+        $this->assertSame([1, 2, 3], $seen);
+    }
+
+    public function testHeaders()
+    {
+        $this->stubRequest(
+            'POST',
+            '/things',
+            [
+                'foo' => 'bar',
+            ],
+            [
+                'Stripe-Account: acct_foo',
+                'Idempotency-Key: qwertyuiop',
+            ],
+            false,
+            [
+                'id' => 2,
+            ]
+        );
+
+        $this->fixture->create([
+            'foo' => 'bar',
+        ], [
+            'stripe_account' => 'acct_foo',
+            'idempotency_key' => 'qwertyuiop',
+        ]);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CountrySpecTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CountrySpecTest.php
new file mode 100644
index 00000000..cccd4116
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CountrySpecTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Stripe;
+
+class CountrySpecTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'US';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/country_specs'
+        );
+        $resources = CountrySpec::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\CountrySpec", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/country_specs/' . self::TEST_RESOURCE_ID
+        );
+        $resource = CountrySpec::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\CountrySpec", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CouponTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CouponTest.php
new file mode 100644
index 00000000..8a6fbf63
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CouponTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Stripe;
+
+class CouponTest extends TestCase
+{
+    const TEST_RESOURCE_ID = '25OFF';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/coupons'
+        );
+        $resources = Coupon::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Coupon", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/coupons/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Coupon::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Coupon", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/coupons'
+        );
+        $resource = Coupon::create([
+            "percent_off" => 25,
+            "duration" => "repeating",
+            "duration_in_months" => 3,
+            "id" => self::TEST_RESOURCE_ID,
+        ]);
+        $this->assertInstanceOf("Stripe\\Coupon", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Coupon::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/coupons/' . self::TEST_RESOURCE_ID
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Coupon", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/coupons/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Coupon::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Coupon", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = Coupon::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/coupons/' . self::TEST_RESOURCE_ID
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\Coupon", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CustomerTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CustomerTest.php
new file mode 100644
index 00000000..e279e548
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/CustomerTest.php
@@ -0,0 +1,269 @@
+<?php
+
+namespace Stripe;
+
+class CustomerTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'cus_123';
+    const TEST_SOURCE_ID = 'ba_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/customers'
+        );
+        $resources = Customer::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Customer", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/customers/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Customer", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/customers'
+        );
+        $resource = Customer::create();
+        $this->assertInstanceOf("Stripe\\Customer", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Customer", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Customer::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Customer", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/customers/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\Customer", $resource);
+    }
+
+    public function testCanAddInvoiceItem()
+    {
+        $customer = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/invoiceitems',
+            [
+                "amount" => 100,
+                "currency" => "usd",
+                "customer" => $customer->id
+            ]
+        );
+        $resource = $customer->addInvoiceItem([
+            "amount" => 100,
+            "currency" => "usd"
+        ]);
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resource);
+    }
+
+    public function testCanListInvoices()
+    {
+        $customer = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'get',
+            '/v1/invoices',
+            ["customer" => $customer->id]
+        );
+        $resources = $customer->invoices();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Invoice", $resources->data[0]);
+    }
+
+    public function testCanListInvoiceItems()
+    {
+        $customer = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'get',
+            '/v1/invoiceitems',
+            ["customer" => $customer->id]
+        );
+        $resources = $customer->invoiceItems();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resources->data[0]);
+    }
+
+    public function testCanListCharges()
+    {
+        $customer = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'get',
+            '/v1/charges',
+            ["customer" => $customer->id]
+        );
+        $resources = $customer->charges();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Charge", $resources->data[0]);
+    }
+
+    public function testCanUpdateSubscription()
+    {
+        $customer = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->stubRequest(
+            'post',
+            '/v1/customers/' . $customer->id . '/subscription',
+            ["plan" => "plan"],
+            null,
+            false,
+            [
+                "object" => "subscription",
+                "id" => "sub_foo"
+            ]
+        );
+        $resource = $customer->updateSubscription(["plan" => "plan"]);
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+        $this->assertSame("sub_foo", $customer->subscription->id);
+    }
+
+    public function testCanCancelSubscription()
+    {
+        $customer = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->stubRequest(
+            'delete',
+            '/v1/customers/' . $customer->id . '/subscription',
+            [],
+            null,
+            false,
+            [
+                "object" => "subscription",
+                "id" => "sub_foo"
+            ]
+        );
+        $resource = $customer->cancelSubscription();
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+        $this->assertSame("sub_foo", $customer->subscription->id);
+    }
+
+    public function testCanDeleteDiscount()
+    {
+        $customer = Customer::retrieve(self::TEST_RESOURCE_ID);
+        $this->stubRequest(
+            'delete',
+            '/v1/customers/' . $customer->id . '/discount'
+        );
+        $customer->deleteDiscount();
+        $this->assertSame($customer->discount, null);
+    }
+
+    public function testCanCreateSource()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources'
+        );
+        $resource = Customer::createSource(self::TEST_RESOURCE_ID, ["source" => "btok_123"]);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+
+    public function testCanRetrieveSource()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources/' . self::TEST_SOURCE_ID
+        );
+        $resource = Customer::retrieveSource(self::TEST_RESOURCE_ID, self::TEST_SOURCE_ID);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+
+    public function testCanUpdateSource()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources/' . self::TEST_SOURCE_ID
+        );
+        $resource = Customer::updateSource(self::TEST_RESOURCE_ID, self::TEST_SOURCE_ID, ["name" => "name"]);
+        // stripe-mock returns a Card on this method and not a bank account
+        $this->assertInstanceOf("Stripe\\Card", $resource);
+    }
+
+    public function testCanDeleteSource()
+    {
+        $this->expectsRequest(
+            'delete',
+            '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources/' . self::TEST_SOURCE_ID
+        );
+        $resource = Customer::deleteSource(self::TEST_RESOURCE_ID, self::TEST_SOURCE_ID);
+        $this->assertInstanceOf("Stripe\\BankAccount", $resource);
+    }
+
+    public function testCanListSources()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/customers/' . self::TEST_RESOURCE_ID . '/sources'
+        );
+        $resources = Customer::allSources(self::TEST_RESOURCE_ID);
+        $this->assertTrue(is_array($resources->data));
+    }
+
+    public function testSerializeSourceString()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'customer',
+        ], null);
+        $obj->source = 'tok_visa';
+
+        $expected = [
+            'source' => 'tok_visa',
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+
+    public function testSerializeSourceMap()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'customer',
+        ], null);
+        $obj->source = [
+            'object' => 'card',
+            'number' => '4242424242424242',
+            'exp_month' => 12,
+            'exp_year' => 2032,
+        ];
+
+        $expected = [
+            'source' => [
+                'object' => 'card',
+                'number' => '4242424242424242',
+                'exp_month' => 12,
+                'exp_year' => 2032,
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/DisputeTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/DisputeTest.php
new file mode 100644
index 00000000..81d93da0
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/DisputeTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Stripe;
+
+class DisputeTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'dp_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/disputes'
+        );
+        $resources = Dispute::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Dispute", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/disputes/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Dispute::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Dispute", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Dispute::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/disputes/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Dispute", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/disputes/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Dispute::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Dispute", $resource);
+    }
+
+    public function testIsClosable()
+    {
+        $dispute = Dispute::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/disputes/' . $dispute->id . '/close'
+        );
+        $resource = $dispute->close();
+        $this->assertInstanceOf("Stripe\\Dispute", $resource);
+        $this->assertSame($resource, $dispute);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/EphemeralKeyTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/EphemeralKeyTest.php
new file mode 100644
index 00000000..e8a1fc0e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/EphemeralKeyTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Stripe;
+
+class EphemeralKeyTest extends TestCase
+{
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/ephemeral_keys',
+            null,
+            ["Stripe-Version: 2017-05-25"]
+        );
+        $resource = EphemeralKey::create([
+            "customer" => "cus_123",
+        ], ["stripe_version" => "2017-05-25"]);
+        $this->assertInstanceOf("Stripe\\EphemeralKey", $resource);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testIsNotCreatableWithoutAnExplicitApiVersion()
+    {
+        $resource = EphemeralKey::create([
+            "customer" => "cus_123",
+        ]);
+    }
+
+    public function testIsDeletable()
+    {
+        $key = EphemeralKey::create([
+            "customer" => "cus_123",
+        ], ["stripe_version" => "2017-05-25"]);
+        $this->expectsRequest(
+            'delete',
+            '/v1/ephemeral_keys/' . $key->id
+        );
+        $resource = $key->delete();
+        $this->assertInstanceOf("Stripe\\EphemeralKey", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Error/BaseTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Error/BaseTest.php
new file mode 100644
index 00000000..4c2732ed
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Error/BaseTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Stripe;
+
+class BaseTest extends TestCase
+{
+    public function createFixture($params = [])
+    {
+        return $this->getMockForAbstractClass('Stripe\\Error\\Base', [
+            'message',
+            200,
+            '{"key": "value"}',
+            ['key' => 'value'],
+            [
+                'Some-Header' => 'Some Value',
+                'Request-Id' => 'req_test',
+            ],
+        ]);
+    }
+
+    public function testGetters()
+    {
+        $e = $this->createFixture();
+        $this->assertSame(200, $e->getHttpStatus());
+        $this->assertSame('{"key": "value"}', $e->getHttpBody());
+        $this->assertSame(['key' => 'value'], $e->getJsonBody());
+        $this->assertSame('Some Value', $e->getHttpHeaders()['Some-Header']);
+        $this->assertSame('req_test', $e->getRequestId());
+    }
+
+    public function testToString()
+    {
+        $e = $this->createFixture();
+        $this->assertContains("from API request 'req_test'", (string)$e);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Error/SignatureVerificationTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Error/SignatureVerificationTest.php
new file mode 100644
index 00000000..020a41f8
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Error/SignatureVerificationTest.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Stripe;
+
+class SignatureVerificationTest extends TestCase
+{
+    public function testGetters()
+    {
+        $e = new Error\SignatureVerification('message', 'sig_header');
+        $this->assertSame('sig_header', $e->getSigHeader());
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/EventTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/EventTest.php
new file mode 100644
index 00000000..2e3c92f0
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/EventTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Stripe;
+
+class EventTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'evt_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/events'
+        );
+        $resources = Event::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Event", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/events/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Event::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Event", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ExchangeRateTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ExchangeRateTest.php
new file mode 100644
index 00000000..8b07b5a1
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ExchangeRateTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Stripe;
+
+class ExchangeRateTest extends TestCase
+{
+    public function testIsListable()
+    {
+        $this->stubRequest(
+            'get',
+            '/v1/exchange_rates',
+            [],
+            null,
+            false,
+            [
+                'object' => 'list',
+                'data' => [
+                    [
+                        'id' => 'eur',
+                        'object' => 'exchange_rate',
+                        'rates' => ['usd' => 1.18221],
+                    ],
+                    [
+                        'id' => 'usd',
+                        'object' => 'exchange_rate',
+                        'rates' => ['eur' => 0.845876],
+                    ],
+                ],
+            ]
+        );
+
+        $listRates = ExchangeRate::all();
+        $this->assertTrue(is_array($listRates->data));
+        $this->assertEquals('exchange_rate', $listRates->data[0]->object);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->stubRequest(
+            'get',
+            '/v1/exchange_rates/usd',
+            [],
+            null,
+            false,
+            [
+                'id' => 'usd',
+                'object' => 'exchange_rate',
+                'rates' => ['eur' => 0.845876],
+            ]
+        );
+        $rates = ExchangeRate::retrieve("usd");
+        $this->assertEquals('exchange_rate', $rates->object);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/FileUploadTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/FileUploadTest.php
new file mode 100644
index 00000000..21bb0b56
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/FileUploadTest.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace Stripe;
+
+class FileUploadTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'file_123';
+
+    /**
+     * @before
+     */
+    public function setUpFixture()
+    {
+        // PHP <= 5.5 does not support arrays as class constants, so we set up
+        // the fixture as an instance variable.
+        $this->fixture = [
+            'id' => self::TEST_RESOURCE_ID,
+            'object' => 'file_upload',
+        ];
+    }
+
+    public function testIsListable()
+    {
+        $this->stubRequest(
+            'get',
+            '/v1/files',
+            [],
+            null,
+            false,
+            [
+                'object' => 'list',
+                'data' => [$this->fixture],
+                'resource_url' => '/v1/files',
+            ],
+            200,
+            Stripe::$apiUploadBase
+        );
+
+        $resources = FileUpload::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\FileUpload", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->stubRequest(
+            'get',
+            '/v1/files/' . self::TEST_RESOURCE_ID,
+            [],
+            null,
+            false,
+            $this->fixture,
+            200,
+            Stripe::$apiUploadBase
+        );
+        $resource = FileUpload::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\FileUpload", $resource);
+    }
+
+    public function testIsCreatableWithFileHandle()
+    {
+        $this->stubRequest(
+            'post',
+            '/v1/files',
+            null,
+            ['Content-Type: multipart/form-data'],
+            true,
+            $this->fixture,
+            200,
+            Stripe::$apiUploadBase
+        );
+        $fp = fopen(dirname(__FILE__) . '/../data/test.png', 'r');
+        $resource = FileUpload::create([
+            "purpose" => "dispute_evidence",
+            "file" => $fp,
+        ]);
+        $this->assertInstanceOf("Stripe\\FileUpload", $resource);
+    }
+
+    public function testIsCreatableWithCurlFile()
+    {
+        if (!class_exists('\CurlFile', false)) {
+            // Older PHP versions don't support this
+            return;
+        }
+
+        $this->stubRequest(
+            'post',
+            '/v1/files',
+            null,
+            ['Content-Type: multipart/form-data'],
+            true,
+            $this->fixture,
+            200,
+            Stripe::$apiUploadBase
+        );
+        $curlFile = new \CurlFile(dirname(__FILE__) . '/../data/test.png');
+        $resource = FileUpload::create([
+            "purpose" => "dispute_evidence",
+            "file" => $curlFile,
+        ]);
+        $this->assertInstanceOf("Stripe\\FileUpload", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/HttpClient/CurlClientTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/HttpClient/CurlClientTest.php
new file mode 100644
index 00000000..086e6d3e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/HttpClient/CurlClientTest.php
@@ -0,0 +1,228 @@
+<?php
+
+namespace Stripe;
+
+use Stripe\HttpClient\CurlClient;
+
+class CurlClientTest extends TestCase
+{
+    /**
+     * @before
+     */
+    public function saveOriginalNetworkValues()
+    {
+        $this->origMaxNetworkRetries = Stripe::getMaxNetworkRetries();
+        $this->origMaxNetworkRetryDelay = Stripe::getMaxNetworkRetryDelay();
+        $this->origInitialNetworkRetryDelay = Stripe::getInitialNetworkRetryDelay();
+    }
+
+    /**
+     * @before
+     */
+    public function setUpReflectors()
+    {
+        $stripeReflector = new \ReflectionClass('\Stripe\Stripe');
+
+        $this->maxNetworkRetryDelayProperty = $stripeReflector->getProperty('maxNetworkRetryDelay');
+        $this->maxNetworkRetryDelayProperty->setAccessible(true);
+
+        $this->initialNetworkRetryDelayProperty = $stripeReflector->getProperty('initialNetworkRetryDelay');
+        $this->initialNetworkRetryDelayProperty->setAccessible(true);
+
+        $curlClientReflector = new \ReflectionClass('Stripe\HttpClient\CurlClient');
+
+        $this->shouldRetryMethod = $curlClientReflector->getMethod('shouldRetry');
+        $this->shouldRetryMethod->setAccessible(true);
+
+        $this->sleepTimeMethod = $curlClientReflector->getMethod('sleepTime');
+        $this->sleepTimeMethod->setAccessible(true);
+    }
+
+    /**
+     * @after
+     */
+    public function restoreOriginalNetworkValues()
+    {
+        Stripe::setMaxNetworkRetries($this->origMaxNetworkRetries);
+        $this->setMaxNetworkRetryDelay($this->origMaxNetworkRetryDelay);
+        $this->setInitialNetworkRetryDelay($this->origInitialNetworkRetryDelay);
+    }
+
+    private function setMaxNetworkRetryDelay($maxNetworkRetryDelay)
+    {
+        $this->maxNetworkRetryDelayProperty->setValue(null, $maxNetworkRetryDelay);
+    }
+
+    private function setInitialNetworkRetryDelay($initialNetworkRetryDelay)
+    {
+        $this->initialNetworkRetryDelayProperty->setValue(null, $initialNetworkRetryDelay);
+    }
+
+    private function createFakeRandomGenerator($returnValue = 1.0)
+    {
+        $fakeRandomGenerator = $this->getMock('Stripe\Util\RandomGenetator', ['randFloat']);
+        $fakeRandomGenerator->method('randFloat')->willReturn($returnValue);
+        return $fakeRandomGenerator;
+    }
+
+    public function testTimeout()
+    {
+        $curl = new CurlClient();
+        $this->assertSame(CurlClient::DEFAULT_TIMEOUT, $curl->getTimeout());
+        $this->assertSame(CurlClient::DEFAULT_CONNECT_TIMEOUT, $curl->getConnectTimeout());
+
+        // implicitly tests whether we're returning the CurlClient instance
+        $curl = $curl->setConnectTimeout(1)->setTimeout(10);
+        $this->assertSame(1, $curl->getConnectTimeout());
+        $this->assertSame(10, $curl->getTimeout());
+
+        $curl->setTimeout(-1);
+        $curl->setConnectTimeout(-999);
+        $this->assertSame(0, $curl->getTimeout());
+        $this->assertSame(0, $curl->getConnectTimeout());
+    }
+
+    public function testUserAgentInfo()
+    {
+        $curl = new CurlClient();
+        $uaInfo = $curl->getUserAgentInfo();
+        $this->assertNotNull($uaInfo);
+        $this->assertNotNull($uaInfo['httplib']);
+        $this->assertNotNull($uaInfo['ssllib']);
+    }
+
+    public function testDefaultOptions()
+    {
+        // make sure options array loads/saves properly
+        $optionsArray = [CURLOPT_PROXY => 'localhost:80'];
+        $withOptionsArray = new CurlClient($optionsArray);
+        $this->assertSame($withOptionsArray->getDefaultOptions(), $optionsArray);
+
+        // make sure closure-based options work properly, including argument passing
+        $ref = null;
+        $withClosure = new CurlClient(function ($method, $absUrl, $headers, $params, $hasFile) use (&$ref) {
+            $ref = func_get_args();
+            return [];
+        });
+
+        $withClosure->request('get', 'https://httpbin.org/status/200', [], [], false);
+        $this->assertSame($ref, ['get', 'https://httpbin.org/status/200', [], [], false]);
+
+        // this is the last test case that will run, since it'll throw an exception at the end
+        $withBadClosure = new CurlClient(function () {
+            return 'thisShouldNotWork';
+        });
+        $this->setExpectedException('Stripe\Error\Api', "Non-array value returned by defaultOptions CurlClient callback");
+        $withBadClosure->request('get', 'https://httpbin.org/status/200', [], [], false);
+    }
+
+    public function testSslOption()
+    {
+        // make sure options array loads/saves properly
+        $optionsArray = [CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1];
+        $withOptionsArray = new CurlClient($optionsArray);
+        $this->assertSame($withOptionsArray->getDefaultOptions(), $optionsArray);
+    }
+
+    public function testShouldRetryOnTimeout()
+    {
+        Stripe::setMaxNetworkRetries(2);
+
+        $curlClient = new CurlClient();
+
+        $this->assertTrue($this->shouldRetryMethod->invoke($curlClient, CURLE_OPERATION_TIMEOUTED, 0, 0));
+    }
+
+    public function testShouldRetryOnConnectionFailure()
+    {
+        Stripe::setMaxNetworkRetries(2);
+
+        $curlClient = new CurlClient();
+
+        $this->assertTrue($this->shouldRetryMethod->invoke($curlClient, CURLE_COULDNT_CONNECT, 0, 0));
+    }
+
+    public function testShouldRetryOnConflict()
+    {
+        Stripe::setMaxNetworkRetries(2);
+
+        $curlClient = new CurlClient();
+
+        $this->assertTrue($this->shouldRetryMethod->invoke($curlClient, 0, 409, 0));
+    }
+
+    public function testShouldNotRetryAtMaximumCount()
+    {
+        Stripe::setMaxNetworkRetries(2);
+
+        $curlClient = new CurlClient();
+
+        $this->assertFalse($this->shouldRetryMethod->invoke($curlClient, 0, 0, Stripe::getMaxNetworkRetries()));
+    }
+
+    public function testShouldNotRetryOnCertValidationError()
+    {
+        Stripe::setMaxNetworkRetries(2);
+
+        $curlClient = new CurlClient();
+
+        $this->assertFalse($this->shouldRetryMethod->invoke($curlClient, CURLE_SSL_PEER_CERTIFICATE, -1, 0));
+    }
+
+    public function testSleepTimeShouldGrowExponentially()
+    {
+        $this->setMaxNetworkRetryDelay(999);
+
+        $curlClient = new CurlClient(null, $this->createFakeRandomGenerator());
+
+        $this->assertEquals(
+            Stripe::getInitialNetworkRetryDelay() * 1,
+            $this->sleepTimeMethod->invoke($curlClient, 1)
+        );
+        $this->assertEquals(
+            Stripe::getInitialNetworkRetryDelay() * 2,
+            $this->sleepTimeMethod->invoke($curlClient, 2)
+        );
+        $this->assertEquals(
+            Stripe::getInitialNetworkRetryDelay() * 4,
+            $this->sleepTimeMethod->invoke($curlClient, 3)
+        );
+        $this->assertEquals(
+            Stripe::getInitialNetworkRetryDelay() * 8,
+            $this->sleepTimeMethod->invoke($curlClient, 4)
+        );
+    }
+
+    public function testSleepTimeShouldEnforceMaxNetworkRetryDelay()
+    {
+        $this->setInitialNetworkRetryDelay(1);
+        $this->setMaxNetworkRetryDelay(2);
+
+        $curlClient = new CurlClient(null, $this->createFakeRandomGenerator());
+
+        $this->assertEquals(1, $this->sleepTimeMethod->invoke($curlClient, 1));
+        $this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 2));
+        $this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 3));
+        $this->assertEquals(2, $this->sleepTimeMethod->invoke($curlClient, 4));
+    }
+
+    public function testSleepTimeShouldAddSomeRandomness()
+    {
+        $randomValue = 0.8;
+        $this->setInitialNetworkRetryDelay(1);
+        $this->setMaxNetworkRetryDelay(8);
+
+        $curlClient = new CurlClient(null, $this->createFakeRandomGenerator($randomValue));
+
+        $baseValue = Stripe::getInitialNetworkRetryDelay() * (0.5 * (1 + $randomValue));
+
+        // the initial value cannot be smaller than the base,
+        // so the randomness is ignored
+        $this->assertEquals(Stripe::getInitialNetworkRetryDelay(), $this->sleepTimeMethod->invoke($curlClient, 1));
+
+        // after the first one, the randomness is applied
+        $this->assertEquals($baseValue * 2, $this->sleepTimeMethod->invoke($curlClient, 2));
+        $this->assertEquals($baseValue * 4, $this->sleepTimeMethod->invoke($curlClient, 3));
+        $this->assertEquals($baseValue * 8, $this->sleepTimeMethod->invoke($curlClient, 4));
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/InvoiceItemTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/InvoiceItemTest.php
new file mode 100644
index 00000000..ffe140aa
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/InvoiceItemTest.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Stripe;
+
+class InvoiceItemTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'ii_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/invoiceitems'
+        );
+        $resources = InvoiceItem::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/invoiceitems/' . self::TEST_RESOURCE_ID
+        );
+        $resource = InvoiceItem::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/invoiceitems'
+        );
+        $resource = InvoiceItem::create([
+            "amount" => 100,
+            "currency" => "usd",
+            "customer" => "cus_123"
+        ]);
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = InvoiceItem::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/invoiceitems/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/invoiceitems/' . self::TEST_RESOURCE_ID
+        );
+        $resource = InvoiceItem::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $invoiceItem = InvoiceItem::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/invoiceitems/' . $invoiceItem->id
+        );
+        $resource = $invoiceItem->delete();
+        $this->assertInstanceOf("Stripe\\InvoiceItem", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/InvoiceTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/InvoiceTest.php
new file mode 100644
index 00000000..f1ae9378
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/InvoiceTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Stripe;
+
+class InvoiceTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'in_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/invoices'
+        );
+        $resources = Invoice::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Invoice", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/invoices/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Invoice::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Invoice", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/invoices'
+        );
+        $resource = Invoice::create([
+            "customer" => "cus_123"
+        ]);
+        $this->assertInstanceOf("Stripe\\Invoice", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Invoice::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/invoices/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Invoice", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/invoices/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Invoice::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Invoice", $resource);
+    }
+
+    public function testCanRetrieveUpcoming()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/invoices/upcoming'
+        );
+        $resource = Invoice::upcoming(["customer" => "cus_123"]);
+        $this->assertInstanceOf("Stripe\\Invoice", $resource);
+    }
+
+    public function testIsPayable()
+    {
+        $invoice = Invoice::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/invoices/' . $invoice->id . '/pay'
+        );
+        $resource = $invoice->pay();
+        $this->assertInstanceOf("Stripe\\Invoice", $resource);
+        $this->assertSame($resource, $invoice);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OAuthTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OAuthTest.php
new file mode 100644
index 00000000..b4e43a88
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OAuthTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Stripe;
+
+class OAuthTest extends TestCase
+{
+    public function testAuthorizeUrl()
+    {
+        $uriStr = OAuth::authorizeUrl([
+            'scope' => 'read_write',
+            'state' => 'csrf_token',
+            'stripe_user' => [
+                'email' => 'test@example.com',
+                'url' => 'https://example.com/profile/test',
+                'country' => 'US',
+            ],
+        ]);
+
+        $uri = parse_url($uriStr);
+        parse_str($uri['query'], $params);
+
+        $this->assertSame('https', $uri['scheme']);
+        $this->assertSame('connect.stripe.com', $uri['host']);
+        $this->assertSame('/oauth/authorize', $uri['path']);
+
+        $this->assertSame('ca_123', $params['client_id']);
+        $this->assertSame('read_write', $params['scope']);
+        $this->assertSame('test@example.com', $params['stripe_user']['email']);
+        $this->assertSame('https://example.com/profile/test', $params['stripe_user']['url']);
+        $this->assertSame('US', $params['stripe_user']['country']);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\Authentication
+     * @expectedExceptionMessageRegExp #No client_id provided#
+     */
+    public function testRaisesAuthenticationErrorWhenNoClientId()
+    {
+        Stripe::setClientId(null);
+        OAuth::authorizeUrl();
+    }
+
+    public function testToken()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/token',
+            [
+                'grant_type' => 'authorization_code',
+                'code' => 'this_is_an_authorization_code',
+            ],
+            null,
+            false,
+            [
+                'access_token' => 'sk_access_token',
+                'scope' => 'read_only',
+                'livemode' => false,
+                'token_type' => 'bearer',
+                'refresh_token' => 'sk_refresh_token',
+                'stripe_user_id' => 'acct_test',
+                'stripe_publishable_key' => 'pk_test',
+            ],
+            200,
+            Stripe::$connectBase
+        );
+
+        $resp = OAuth::token([
+            'grant_type' => 'authorization_code',
+            'code' => 'this_is_an_authorization_code',
+        ]);
+        $this->assertSame('sk_access_token', $resp->access_token);
+    }
+
+    public function testDeauthorize()
+    {
+        $this->stubRequest(
+            'POST',
+            '/oauth/deauthorize',
+            [
+                'stripe_user_id' => 'acct_test_deauth',
+                'client_id' => 'ca_123',
+            ],
+            null,
+            false,
+            [
+                'stripe_user_id' => 'acct_test_deauth',
+            ],
+            200,
+            Stripe::$connectBase
+        );
+
+        $resp = OAuth::deauthorize([
+                'stripe_user_id' => 'acct_test_deauth',
+        ]);
+        $this->assertSame('acct_test_deauth', $resp->stripe_user_id);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OrderReturnTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OrderReturnTest.php
new file mode 100644
index 00000000..bb2d65c4
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OrderReturnTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Stripe;
+
+class OrderReturnTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'orret_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/order_returns'
+        );
+        $resources = OrderReturn::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\OrderReturn", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/order_returns/' . self::TEST_RESOURCE_ID
+        );
+        $resource = OrderReturn::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\OrderReturn", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OrderTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OrderTest.php
new file mode 100644
index 00000000..51d17e94
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/OrderTest.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Stripe;
+
+class OrderTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'or_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/orders'
+        );
+        $resources = Order::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Order", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/orders/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Order::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Order", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/orders'
+        );
+        $resource = Order::create([
+            'currency' => 'usd'
+        ]);
+        $this->assertInstanceOf("Stripe\\Order", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Order::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/orders/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Order", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/orders/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Order::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Order", $resource);
+    }
+
+    public function testIsPayable()
+    {
+        $resource = Order::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/orders/' . $resource->id . '/pay'
+        );
+        $resource->pay();
+        $this->assertInstanceOf("Stripe\\Order", $resource);
+    }
+
+    public function testIsReturnable()
+    {
+        $order = Order::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/orders/' . $order->id . '/returns'
+        );
+        $resource = $order->returnOrder();
+        $this->assertInstanceOf("Stripe\\OrderReturn", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/PayoutTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/PayoutTest.php
new file mode 100644
index 00000000..79c7b5fd
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/PayoutTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Stripe;
+
+class PayoutTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'po_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/payouts'
+        );
+        $resources = Payout::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Payout", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/payouts/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Payout::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Payout", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/payouts'
+        );
+        $resource = Payout::create([
+            "amount" => 100,
+            "currency" => "usd"
+        ]);
+        $this->assertInstanceOf("Stripe\\Payout", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Payout::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/payouts/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Payout", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/payouts/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Payout::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Payout", $resource);
+    }
+
+    public function testIsCancelable()
+    {
+        $resource = Payout::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/payouts/' . $resource->id . '/cancel'
+        );
+        $resource->cancel();
+        $this->assertInstanceOf("Stripe\\Payout", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/PlanTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/PlanTest.php
new file mode 100644
index 00000000..8d71745e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/PlanTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Stripe;
+
+class PlanTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'plan';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/plans'
+        );
+        $resources = Plan::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Plan", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/plans/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Plan::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Plan", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/plans'
+        );
+        $resource = Plan::create([
+            'amount' => 100,
+            'interval' => 'month',
+            'currency' => 'usd',
+            'name' => self::TEST_RESOURCE_ID,
+            'id' => self::TEST_RESOURCE_ID
+        ]);
+        $this->assertInstanceOf("Stripe\\Plan", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Plan::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/plans/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Plan", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/plans/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Plan::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Plan", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = Plan::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/plans/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\Plan", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ProductTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ProductTest.php
new file mode 100644
index 00000000..c2a3813c
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ProductTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Stripe;
+
+class ProductTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'prod_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/products'
+        );
+        $resources = Product::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Product", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/products/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Product::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Product", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/products'
+        );
+        $resource = Product::create([
+            'name' => 'name',
+            'type' => 'good'
+        ]);
+        $this->assertInstanceOf("Stripe\\Product", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Product::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/products/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Product", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/products/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Product::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Product", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = Product::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/products/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\Product", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/RecipientTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/RecipientTest.php
new file mode 100644
index 00000000..43dd6e82
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/RecipientTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Stripe;
+
+class RecipientTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'rp_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/recipients'
+        );
+        $resources = Recipient::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Recipient", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/recipients/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Recipient::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Recipient", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/recipients'
+        );
+        $resource = Recipient::create([
+            "name" => "name",
+            "type" => "individual"
+        ]);
+        $this->assertInstanceOf("Stripe\\Recipient", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Recipient::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/recipients/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Recipient", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/recipients/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Recipient::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Recipient", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = Recipient::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/recipients/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\Recipient", $resource);
+    }
+
+    public function testCanListTransfers()
+    {
+        $recipient = Recipient::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'get',
+            '/v1/transfers',
+            ["recipient" => $recipient->id]
+        );
+        $resources = $recipient->transfers();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Transfer", $resources->data[0]);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/RefundTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/RefundTest.php
new file mode 100644
index 00000000..788361d4
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/RefundTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Stripe;
+
+class RefundTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 're_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/refunds'
+        );
+        $resources = Refund::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Refund", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/refunds/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Refund::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Refund", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/refunds'
+        );
+        $resource = Refund::create([
+            "charge" => "ch_123"
+        ]);
+        $this->assertInstanceOf("Stripe\\Refund", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Refund::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/refunds/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Refund", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/refunds/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Refund::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Refund", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SKUTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SKUTest.php
new file mode 100644
index 00000000..d4fc2ad4
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SKUTest.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Stripe;
+
+class SKUTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'sku_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/skus'
+        );
+        $resources = SKU::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\SKU", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/skus/' . self::TEST_RESOURCE_ID
+        );
+        $resource = SKU::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\SKU", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/skus'
+        );
+        $resource = SKU::create([
+            'currency'  => 'usd',
+            'inventory' => [
+                'type'     => 'finite',
+                'quantity' => 1
+            ],
+            'price'     => 100,
+            'product'   => "prod_123"
+        ]);
+        $this->assertInstanceOf("Stripe\\SKU", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = SKU::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/skus/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\SKU", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/skus/' . self::TEST_RESOURCE_ID
+        );
+        $resource = SKU::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\SKU", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = SKU::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/skus/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\SKU", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SourceTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SourceTest.php
new file mode 100644
index 00000000..ad11b0d3
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SourceTest.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace Stripe;
+
+class SourceTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'src_123';
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Source::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Source", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/sources'
+        );
+        $resource = Source::create([
+            "type" => "card"
+        ]);
+        $this->assertInstanceOf("Stripe\\Source", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Source::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/sources/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Source", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/sources/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Source::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Source", $resource);
+    }
+
+    public function testCanSaveCardExpiryDate()
+    {
+        $response = [
+            'id' => 'src_foo',
+            'object' => 'source',
+            'card' => [
+                'exp_month' => 8,
+                'exp_year' => 2019,
+            ],
+        ];
+        $source = Source::constructFrom($response);
+
+        $response['card']['exp_month'] = 12;
+        $response['card']['exp_year'] = 2022;
+        $this->stubRequest(
+            'POST',
+            '/v1/sources/src_foo',
+            [
+                'card' => [
+                    'exp_month' => 12,
+                    'exp_year' => 2022,
+                ]
+            ],
+            null,
+            false,
+            $response
+        );
+
+        $source->card->exp_month = 12;
+        $source->card->exp_year = 2022;
+        $source->save();
+
+        $this->assertSame(12, $source->card->exp_month);
+        $this->assertSame(2022, $source->card->exp_year);
+    }
+
+    public function testIsDetachableWhenAttached()
+    {
+        $resource = Source::retrieve(self::TEST_RESOURCE_ID);
+        $resource->customer = "cus_123";
+        $this->expectsRequest(
+            'delete',
+            '/v1/customers/cus_123/sources/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\Source", $resource);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\Api
+     */
+    public function testIsNotDetachableWhenUnattached()
+    {
+        $resource = Source::retrieve(self::TEST_RESOURCE_ID);
+        $resource->detach();
+    }
+
+    public function testCanListSourceTransactions()
+    {
+        $source = Source::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'get',
+            '/v1/sources/' . $source->id . "/source_transactions"
+        );
+        $resources = $source->sourceTransactions();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\SourceTransaction", $resources->data[0]);
+    }
+
+    public function testCanVerify()
+    {
+        $resource = Source::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/sources/' . $resource->id . "/verify"
+        );
+        $resource->verify(["values" => [32, 45]]);
+        $this->assertInstanceOf("Stripe\\Source", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/StripeObjectTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/StripeObjectTest.php
new file mode 100644
index 00000000..af1cd936
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/StripeObjectTest.php
@@ -0,0 +1,453 @@
+<?php
+
+namespace Stripe;
+
+class StripeObjectTest extends TestCase
+{
+    /**
+     * @before
+     */
+    public function setUpReflectors()
+    {
+        // Sets up reflectors needed by some tests to access protected or
+        // private attributes.
+
+        // This is used to invoke the `deepCopy` protected function
+        $this->deepCopyReflector = new \ReflectionMethod('Stripe\\StripeObject', 'deepCopy');
+        $this->deepCopyReflector->setAccessible(true);
+
+        // This is used to access the `_opts` protected variable
+        $this->optsReflector = new \ReflectionProperty('Stripe\\StripeObject', '_opts');
+        $this->optsReflector->setAccessible(true);
+    }
+
+    public function testArrayAccessorsSemantics()
+    {
+        $s = new StripeObject();
+        $s['foo'] = 'a';
+        $this->assertSame($s['foo'], 'a');
+        $this->assertTrue(isset($s['foo']));
+        unset($s['foo']);
+        $this->assertFalse(isset($s['foo']));
+    }
+
+    public function testNormalAccessorsSemantics()
+    {
+        $s = new StripeObject();
+        $s->foo = 'a';
+        $this->assertSame($s->foo, 'a');
+        $this->assertTrue(isset($s->foo));
+        unset($s->foo);
+        $this->assertFalse(isset($s->foo));
+    }
+
+    public function testArrayAccessorsMatchNormalAccessors()
+    {
+        $s = new StripeObject();
+        $s->foo = 'a';
+        $this->assertSame($s['foo'], 'a');
+
+        $s['bar'] = 'b';
+        $this->assertSame($s->bar, 'b');
+    }
+
+    public function testCount()
+    {
+        $s = new StripeObject();
+        $this->assertSame(0, count($s));
+
+        $s['key1'] = 'value1';
+        $this->assertSame(1, count($s));
+
+        $s['key2'] = 'value2';
+        $this->assertSame(2, count($s));
+
+        unset($s['key1']);
+        $this->assertSame(1, count($s));
+    }
+
+    public function testKeys()
+    {
+        $s = new StripeObject();
+        $s->foo = 'bar';
+        $this->assertSame($s->keys(), ['foo']);
+    }
+
+    public function testValues()
+    {
+        $s = new StripeObject();
+        $s->foo = 'bar';
+        $this->assertSame($s->values(), ['bar']);
+    }
+
+    public function testToArray()
+    {
+        $s = new StripeObject();
+        $s->foo = 'a';
+
+        $converted = $s->__toArray();
+
+        $this->assertInternalType('array', $converted);
+        $this->assertArrayHasKey('foo', $converted);
+        $this->assertEquals('a', $converted['foo']);
+    }
+
+    public function testRecursiveToArray()
+    {
+        $s = new StripeObject();
+        $z = new StripeObject();
+
+        $s->child = $z;
+        $z->foo = 'a';
+
+        $converted = $s->__toArray(true);
+
+        $this->assertInternalType('array', $converted);
+        $this->assertArrayHasKey('child', $converted);
+        $this->assertInternalType('array', $converted['child']);
+        $this->assertArrayHasKey('foo', $converted['child']);
+        $this->assertEquals('a', $converted['child']['foo']);
+    }
+
+    public function testNonexistentProperty()
+    {
+        $s = new StripeObject();
+        $this->assertNull($s->nonexistent);
+    }
+
+    public function testPropertyDoesNotExists()
+    {
+        $s = new StripeObject();
+        $this->assertNull($s['nonexistent']);
+    }
+
+    public function testJsonEncode()
+    {
+        $s = new StripeObject();
+        $s->foo = 'a';
+
+        $this->assertEquals('{"foo":"a"}', json_encode($s));
+    }
+
+    public function testToString()
+    {
+        $s = new StripeObject();
+        $s->foo = 'a';
+
+        $string = $s->__toString();
+        $expected = <<<EOS
+Stripe\StripeObject JSON: {
+    "foo": "a"
+}
+EOS;
+        $this->assertEquals($expected, $string);
+    }
+
+    public function testReplaceNewNestedUpdatable()
+    {
+        $s = new StripeObject();
+
+        $s->metadata = ['bar'];
+        $this->assertSame($s->metadata, ['bar']);
+        $s->metadata = ['baz', 'qux'];
+        $this->assertSame($s->metadata, ['baz', 'qux']);
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetPermanentAttribute()
+    {
+        $s = new StripeObject();
+        $s->id = 'abc_123';
+    }
+
+    /**
+     * @expectedException \InvalidArgumentException
+     */
+    public function testSetEmptyStringValue()
+    {
+        $s = new StripeObject();
+        $s->foo = '';
+    }
+
+    public function testSerializeParametersOnEmptyObject()
+    {
+        $obj = StripeObject::constructFrom([]);
+        $this->assertSame([], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnNewObjectWithSubObject()
+    {
+        $obj = new StripeObject();
+        $obj->metadata = ['foo' => 'bar'];
+        $this->assertSame(['metadata' => ['foo' => 'bar']], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnBasicObject()
+    {
+        $obj = StripeObject::constructFrom(['foo' => null]);
+        $obj->updateAttributes(['foo' => 'bar']);
+        $this->assertSame(['foo' => 'bar'], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnMoreComplexObject()
+    {
+        $obj = StripeObject::constructFrom([
+            'foo' => StripeObject::constructFrom([
+                'bar' => null,
+                'baz' => null,
+            ]),
+        ]);
+        $obj->foo->bar = 'newbar';
+        $this->assertSame(['foo' => ['bar' => 'newbar']], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnArray()
+    {
+        $obj = StripeObject::constructFrom([
+            'foo' => null,
+        ]);
+        $obj->foo = ['new-value'];
+        $this->assertSame(['foo' => ['new-value']], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnArrayThatShortens()
+    {
+        $obj = StripeObject::constructFrom([
+            'foo' => ['0-index', '1-index', '2-index'],
+        ]);
+        $obj->foo = ['new-value'];
+        $this->assertSame(['foo' => ['new-value']], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnArrayThatLengthens()
+    {
+        $obj = StripeObject::constructFrom([
+            'foo' => ['0-index', '1-index', '2-index'],
+        ]);
+        $obj->foo = array_fill(0, 4, 'new-value');
+        $this->assertSame(['foo' => array_fill(0, 4, 'new-value')], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnArrayOfHashes()
+    {
+        $obj = StripeObject::constructFrom(['foo' => null]);
+        $obj->foo = [
+            StripeObject::constructFrom(['bar' => null]),
+        ];
+
+        $obj->foo[0]->bar = 'baz';
+        $this->assertSame(['foo' => [['bar' => 'baz']]], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersDoesNotIncludeUnchangedValues()
+    {
+        $obj = StripeObject::constructFrom([
+            'foo' => null,
+        ]);
+        $this->assertSame([], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersOnUnchangedArray()
+    {
+        $obj = StripeObject::constructFrom([
+            'foo' => ['0-index', '1-index', '2-index'],
+        ]);
+        $obj->foo = ['0-index', '1-index', '2-index'];
+        $this->assertSame([], $obj->serializeParameters());
+    }
+
+    public function testSerializeParametersWithStripeObject()
+    {
+        $obj = StripeObject::constructFrom([]);
+        $obj->metadata = StripeObject::constructFrom(['foo' => 'bar']);
+
+        $serialized = $obj->serializeParameters();
+        $this->assertSame(['foo' => 'bar'], $serialized['metadata']);
+    }
+
+    public function testSerializeParametersOnReplacedStripeObject()
+    {
+        $obj = StripeObject::constructFrom([
+            'metadata' => StripeObject::constructFrom(['bar' => 'foo']),
+        ]);
+        $obj->metadata = StripeObject::constructFrom(['baz' => 'foo']);
+
+        $serialized = $obj->serializeParameters();
+        $this->assertSame(['bar' => '', 'baz' => 'foo'], $serialized['metadata']);
+    }
+
+    public function testSerializeParametersOnArrayOfStripeObjects()
+    {
+        $obj = StripeObject::constructFrom([]);
+        $obj->metadata = [
+            StripeObject::constructFrom(['foo' => 'bar']),
+        ];
+
+        $serialized = $obj->serializeParameters();
+        $this->assertSame([['foo' => 'bar']], $serialized['metadata']);
+    }
+
+    public function testSerializeParametersOnSetApiResource()
+    {
+        $customer = Customer::constructFrom(['id' => 'cus_123']);
+        $obj = StripeObject::constructFrom([]);
+
+        // the key here is that the property is set explicitly (and therefore
+        // marked as unsaved), which is why it gets included below
+        $obj->customer = $customer;
+
+        $serialized = $obj->serializeParameters();
+        $this->assertSame(['customer' => $customer], $serialized);
+    }
+
+    public function testSerializeParametersOnNotSetApiResource()
+    {
+        $customer = Customer::constructFrom(['id' => 'cus_123']);
+        $obj = StripeObject::constructFrom(['customer' => $customer]);
+
+        $serialized = $obj->serializeParameters();
+        $this->assertSame([], $serialized);
+    }
+
+    public function testSerializeParametersOnApiResourceFlaggedWithSaveWithParent()
+    {
+        $customer = Customer::constructFrom(['id' => 'cus_123']);
+        $customer->saveWithParent = true;
+
+        $obj = StripeObject::constructFrom(['customer' => $customer]);
+
+        $serialized = $obj->serializeParameters();
+        $this->assertSame(['customer' => []], $serialized);
+    }
+
+    public function testSerializeParametersRaisesExceotionOnOtherEmbeddedApiResources()
+    {
+        // This customer doesn't have an ID and therefore the library doesn't know
+        // what to do with it and throws an InvalidArgumentException because it's
+        // probably not what the user expected to happen.
+        $customer = Customer::constructFrom([]);
+
+        $obj = StripeObject::constructFrom([]);
+        $obj->customer = $customer;
+
+        try {
+            $serialized = $obj->serializeParameters();
+            $this->fail("Did not raise error");
+        } catch (\InvalidArgumentException $e) {
+            $this->assertSame(
+                "Cannot save property `customer` containing an API resource of type Stripe\Customer. " .
+                "It doesn't appear to be persisted and is not marked as `saveWithParent`.",
+                $e->getMessage()
+            );
+        } catch (\Exception $e) {
+            $this->fail("Unexpected exception: " . get_class($e));
+        }
+    }
+
+    public function testSerializeParametersForce()
+    {
+        $obj = StripeObject::constructFrom([
+            'id' => 'id',
+            'metadata' => StripeObject::constructFrom([
+                'bar' => 'foo',
+            ]),
+        ]);
+
+        $serialized = $obj->serializeParameters(true);
+        $this->assertSame(['id' => 'id', 'metadata' => ['bar' => 'foo']], $serialized);
+    }
+
+    public function testDirty()
+    {
+        $obj = StripeObject::constructFrom([
+            'id' => 'id',
+            'metadata' => StripeObject::constructFrom([
+                'bar' => 'foo',
+            ]),
+        ]);
+
+        // note that `$force` and `dirty()` are for different things, but are
+        // functionally equivalent
+        $obj->dirty();
+
+        $serialized = $obj->serializeParameters();
+        $this->assertSame(['id' => 'id', 'metadata' => ['bar' => 'foo']], $serialized);
+    }
+
+    public function testDeepCopy()
+    {
+        $opts = [
+            "api_base" => Stripe::$apiBase,
+            "api_key" => "apikey",
+        ];
+        $values = [
+            "id" => 1,
+            "name" => "Stripe",
+            "arr" => [
+                StripeObject::constructFrom(["id" => "index0"], $opts),
+                "index1",
+                2,
+            ],
+            "map" => [
+                "0" => StripeObject::constructFrom(["id" => "index0"], $opts),
+                "1" => "index1",
+                "2" => 2
+            ],
+        ];
+
+        $copyValues = $this->deepCopyReflector->invoke(null, $values);
+
+        // we can't compare the hashes directly because they have embedded
+        // objects which are different from each other
+        $this->assertEquals($values["id"], $copyValues["id"]);
+        $this->assertEquals($values["name"], $copyValues["name"]);
+        $this->assertEquals(count($values["arr"]), count($copyValues["arr"]));
+
+        // internal values of the copied StripeObject should be the same,
+        // but the object itself should be new (hence the assertNotSame)
+        $this->assertEquals($values["arr"][0]["id"], $copyValues["arr"][0]["id"]);
+        $this->assertNotSame($values["arr"][0], $copyValues["arr"][0]);
+
+        // likewise, the Util\RequestOptions instance in _opts should have
+        // copied values but be a new instance
+        $this->assertEquals(
+            $this->optsReflector->getValue($values["arr"][0]),
+            $this->optsReflector->getValue($copyValues["arr"][0])
+        );
+        $this->assertNotSame(
+            $this->optsReflector->getValue($values["arr"][0]),
+            $this->optsReflector->getValue($copyValues["arr"][0])
+        );
+
+        // scalars however, can be compared
+        $this->assertEquals($values["arr"][1], $copyValues["arr"][1]);
+        $this->assertEquals($values["arr"][2], $copyValues["arr"][2]);
+
+        // and a similar story with the hash
+        $this->assertEquals($values["map"]["0"]["id"], $copyValues["map"]["0"]["id"]);
+        $this->assertNotSame($values["map"]["0"], $copyValues["map"]["0"]);
+        $this->assertNotSame(
+            $this->optsReflector->getValue($values["arr"][0]),
+            $this->optsReflector->getValue($copyValues["arr"][0])
+        );
+        $this->assertEquals(
+            $this->optsReflector->getValue($values["map"]["0"]),
+            $this->optsReflector->getValue($copyValues["map"]["0"])
+        );
+        $this->assertNotSame(
+            $this->optsReflector->getValue($values["map"]["0"]),
+            $this->optsReflector->getValue($copyValues["map"]["0"])
+        );
+        $this->assertEquals($values["map"]["1"], $copyValues["map"]["1"]);
+        $this->assertEquals($values["map"]["2"], $copyValues["map"]["2"]);
+    }
+
+    public function testDeepCopyMaintainClass()
+    {
+        $charge = Charge::constructFrom(["id" => 1], null);
+        $copyCharge = $this->deepCopyReflector->invoke(null, $charge);
+        $this->assertEquals(get_class($charge), get_class($copyCharge));
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/StripeTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/StripeTest.php
new file mode 100644
index 00000000..f594518d
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/StripeTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe;
+
+class StripeTest extends TestCase
+{
+    /**
+     * @before
+     */
+    public function saveOriginalValues()
+    {
+        $this->orig = [
+            'caBundlePath' => Stripe::$caBundlePath,
+        ];
+    }
+
+    /**
+     * @after
+     */
+    public function restoreOriginalValues()
+    {
+        Stripe::$caBundlePath = $this->orig['caBundlePath'];
+    }
+
+    public function testCABundlePathAccessors()
+    {
+        Stripe::setCABundlePath('path/to/ca/bundle');
+        $this->assertEquals('path/to/ca/bundle', Stripe::getCABundlePath());
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SubscriptionItemTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SubscriptionItemTest.php
new file mode 100644
index 00000000..09e766c1
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SubscriptionItemTest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Stripe;
+
+class SubscriptionItemTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'si_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/subscription_items'
+        );
+        $resources = SubscriptionItem::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\SubscriptionItem", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/subscription_items/' . self::TEST_RESOURCE_ID
+        );
+        $resource = SubscriptionItem::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/subscription_items'
+        );
+        $resource = SubscriptionItem::create([
+            "plan" => "plan",
+            "subscription" => "sub_123"
+        ]);
+        $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = SubscriptionItem::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/subscription_items/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/subscription_items/' . self::TEST_RESOURCE_ID
+        );
+        $resource = SubscriptionItem::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource);
+    }
+
+    public function testIsDeletable()
+    {
+        $resource = SubscriptionItem::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/subscription_items/' . $resource->id
+        );
+        $resource->delete();
+        $this->assertInstanceOf("Stripe\\SubscriptionItem", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SubscriptionTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SubscriptionTest.php
new file mode 100644
index 00000000..4a42e218
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/SubscriptionTest.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace Stripe;
+
+class SubscriptionTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'sub_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/subscriptions'
+        );
+        $resources = Subscription::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Subscription", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/subscriptions/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Subscription::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/subscriptions'
+        );
+        $resource = Subscription::create([
+            "customer" => "cus_123",
+            "plan" => "plan"
+        ]);
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Subscription::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/subscriptions/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/subscriptions/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Subscription::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+    }
+
+    public function testIsCancelable()
+    {
+        $resource = Subscription::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/subscriptions/' . $resource->id,
+            [
+                'at_period_end' => 'true',
+            ]
+        );
+        $resource->cancel([
+            'at_period_end' => true,
+        ]);
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+    }
+
+    public function testCanDeleteDiscount()
+    {
+        $resource = Subscription::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'delete',
+            '/v1/subscriptions/' . $resource->id . '/discount'
+        );
+        $resource->deleteDiscount();
+        $this->assertInstanceOf("Stripe\\Subscription", $resource);
+    }
+
+    public function testSerializeParametersItems()
+    {
+        $obj = Util\Util::convertToStripeObject([
+            'object' => 'subscription',
+            'items' => Util\Util::convertToStripeObject([
+                'object' => 'list',
+                'data' => [],
+            ], null),
+        ], null);
+        $obj->items = [
+            ['id' => 'si_foo', 'deleted' => true],
+            ['plan' => 'plan_bar'],
+        ];
+        $expected = [
+            'items' => [
+                0 => ['id' => 'si_foo', 'deleted' => true],
+                1 => ['plan' => 'plan_bar'],
+            ],
+        ];
+        $this->assertSame($expected, $obj->serializeParameters());
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ThreeDSecureTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ThreeDSecureTest.php
new file mode 100644
index 00000000..f56e649c
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/ThreeDSecureTest.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Stripe;
+
+class ThreeDSecureTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'tdsrc_123';
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/3d_secure/' . self::TEST_RESOURCE_ID
+        );
+        $resource = ThreeDSecure::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\ThreeDSecure", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/3d_secure'
+        );
+        $resource = ThreeDSecure::create([
+            "amount" => 100,
+            "currency" => "usd",
+            "return_url" => "url"
+        ]);
+        $this->assertInstanceOf("Stripe\\ThreeDSecure", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TokenTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TokenTest.php
new file mode 100644
index 00000000..36bb4bc8
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TokenTest.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Stripe;
+
+class TokenTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'tok_123';
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/tokens/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Token::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Token", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/tokens'
+        );
+        $resource = Token::create(["card" => "tok_visa"]);
+        $this->assertInstanceOf("Stripe\\Token", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TopupTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TopupTest.php
new file mode 100644
index 00000000..99444763
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TopupTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Stripe;
+
+class TopupTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'tu_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/topups'
+        );
+        $resources = Topup::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Topup", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/topups/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Topup::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Topup", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/topups'
+        );
+        $resource = Topup::create([
+            "amount" => 100,
+            "currency" => "usd",
+            "source" => "tok_123",
+            "description" => "description",
+            "statement_descriptor" => "statement descriptor"
+        ]);
+        $this->assertInstanceOf("Stripe\\Topup", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Topup::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/topups/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Topup", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/topups/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Topup::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Topup", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TransferReversalTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TransferReversalTest.php
new file mode 100644
index 00000000..37101c73
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TransferReversalTest.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Stripe;
+
+class TransferReversalTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'trr_123';
+    const TEST_TRANSFER_ID = 'tr_123';
+
+    public function testIsSaveable()
+    {
+        $resource = Transfer::retrieveReversal(self::TEST_TRANSFER_ID, self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/transfers/' . $resource->transfer . '/reversals/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\TransferReversal", $resource);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TransferTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TransferTest.php
new file mode 100644
index 00000000..d60d560e
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/TransferTest.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Stripe;
+
+class TransferTest extends TestCase
+{
+    const TEST_RESOURCE_ID = 'tr_123';
+    const TEST_REVERSAL_ID = 'trr_123';
+
+    public function testIsListable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/transfers'
+        );
+        $resources = Transfer::all();
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\Transfer", $resources->data[0]);
+    }
+
+    public function testIsRetrievable()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/transfers/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Transfer::retrieve(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\Transfer", $resource);
+    }
+
+    public function testIsCreatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/transfers'
+        );
+        $resource = Transfer::create([
+            "amount" => 100,
+            "currency" => "usd",
+            "destination" => "acct_123"
+        ]);
+        $this->assertInstanceOf("Stripe\\Transfer", $resource);
+    }
+
+    public function testIsSaveable()
+    {
+        $resource = Transfer::retrieve(self::TEST_RESOURCE_ID);
+        $resource->metadata["key"] = "value";
+        $this->expectsRequest(
+            'post',
+            '/v1/transfers/' . $resource->id
+        );
+        $resource->save();
+        $this->assertInstanceOf("Stripe\\Transfer", $resource);
+    }
+
+    public function testIsUpdatable()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/transfers/' . self::TEST_RESOURCE_ID
+        );
+        $resource = Transfer::update(self::TEST_RESOURCE_ID, [
+            "metadata" => ["key" => "value"],
+        ]);
+        $this->assertInstanceOf("Stripe\\Transfer", $resource);
+    }
+
+    public function testIsReversable()
+    {
+        $resource = Transfer::retrieve(self::TEST_RESOURCE_ID);
+        $this->expectsRequest(
+            'post',
+            '/v1/transfers/' . $resource->id . '/reversals'
+        );
+        $resource->reverse();
+        $this->assertInstanceOf("Stripe\\Transfer", $resource);
+    }
+
+    public function testIsCancelable()
+    {
+        $transfer = Transfer::retrieve(self::TEST_RESOURCE_ID);
+
+        // stripe-mock does not support this anymore so we stub it
+        $this->stubRequest(
+            'post',
+            '/v1/transfers/' . $transfer->id . '/cancel'
+        );
+        $resource = $transfer->cancel();
+        $this->assertInstanceOf("Stripe\\Transfer", $resource);
+        $this->assertSame($resource, $transfer);
+    }
+
+    public function testCanCreateReversal()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals'
+        );
+        $resource = Transfer::createReversal(self::TEST_RESOURCE_ID);
+        $this->assertInstanceOf("Stripe\\TransferReversal", $resource);
+    }
+
+    public function testCanRetrieveReversal()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals/' . self::TEST_REVERSAL_ID
+        );
+        $resource = Transfer::retrieveReversal(self::TEST_RESOURCE_ID, self::TEST_REVERSAL_ID);
+        $this->assertInstanceOf("Stripe\\TransferReversal", $resource);
+    }
+
+    public function testCanUpdateReversal()
+    {
+        $this->expectsRequest(
+            'post',
+            '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals/' . self::TEST_REVERSAL_ID
+        );
+        $resource = Transfer::updateReversal(
+            self::TEST_RESOURCE_ID,
+            self::TEST_REVERSAL_ID,
+            [
+                "metadata" => ["key" => "value"],
+            ]
+        );
+        $this->assertInstanceOf("Stripe\\TransferReversal", $resource);
+    }
+
+    public function testCanListReversal()
+    {
+        $this->expectsRequest(
+            'get',
+            '/v1/transfers/' . self::TEST_RESOURCE_ID . '/reversals'
+        );
+        $resources = Transfer::allReversals(self::TEST_RESOURCE_ID);
+        $this->assertTrue(is_array($resources->data));
+        $this->assertInstanceOf("Stripe\\TransferReversal", $resources->data[0]);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/DefaultLoggerTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/DefaultLoggerTest.php
new file mode 100644
index 00000000..711af03c
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/DefaultLoggerTest.php
@@ -0,0 +1,28 @@
+<?php
+
+// Test in a slightly different namespace than usual. See comment on
+// `error_log` below.
+namespace Stripe\Util;
+
+class UtilLoggerTest extends \Stripe\TestCase
+{
+    public function testDefaultLogger()
+    {
+        $logger = new DefaultLogger();
+        $logger->error("message");
+
+        global $lastMessage;
+        $this->assertSame($lastMessage, "message");
+    }
+}
+
+// This is a little terrible, but unfortunately there's no clean way to stub a
+// call to `error_log`. Here we overwrite it so that we can get the last arguments
+// that went to it. This is obviously bad, but luckily it's constrained to
+// being just in \Stripe\Util (i.e. won't interfere with PHPUnit for example)
+// and _just_ present when tests are running.
+function error_log($message)
+{
+    global $lastMessage;
+    $lastMessage = $message;
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/RequestOptionsTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/RequestOptionsTest.php
new file mode 100644
index 00000000..558f7398
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/RequestOptionsTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Stripe;
+
+class RequestOptionsTest extends TestCase
+{
+    public function testStringAPIKey()
+    {
+        $opts = Util\RequestOptions::parse("foo");
+        $this->assertSame("foo", $opts->apiKey);
+        $this->assertSame([], $opts->headers);
+    }
+
+    public function testNull()
+    {
+        $opts = Util\RequestOptions::parse(null);
+        $this->assertSame(null, $opts->apiKey);
+        $this->assertSame([], $opts->headers);
+    }
+
+    public function testEmptyArray()
+    {
+        $opts = Util\RequestOptions::parse([]);
+        $this->assertSame(null, $opts->apiKey);
+        $this->assertSame([], $opts->headers);
+    }
+
+    public function testAPIKeyArray()
+    {
+        $opts = Util\RequestOptions::parse(
+            [
+                'api_key' => 'foo',
+            ]
+        );
+        $this->assertSame('foo', $opts->apiKey);
+        $this->assertSame([], $opts->headers);
+    }
+
+    public function testIdempotentKeyArray()
+    {
+        $opts = Util\RequestOptions::parse(
+            [
+                'idempotency_key' => 'foo',
+            ]
+        );
+        $this->assertSame(null, $opts->apiKey);
+        $this->assertSame(['Idempotency-Key' => 'foo'], $opts->headers);
+    }
+
+    public function testKeyArray()
+    {
+        $opts = Util\RequestOptions::parse(
+            [
+                'idempotency_key' => 'foo',
+                'api_key' => 'foo'
+            ]
+        );
+        $this->assertSame('foo', $opts->apiKey);
+        $this->assertSame(['Idempotency-Key' => 'foo'], $opts->headers);
+    }
+
+    /**
+     * @expectedException Stripe\Error\Api
+     */
+    public function testWrongType()
+    {
+        $opts = Util\RequestOptions::parse(5);
+    }
+
+    public function testDiscardNonPersistentHeaders()
+    {
+        $opts = Util\RequestOptions::parse(
+            [
+                'stripe_account' => 'foo',
+                'idempotency_key' => 'foo',
+            ]
+        );
+        $opts->discardNonPersistentHeaders();
+        $this->assertSame(['Stripe-Account' => 'foo'], $opts->headers);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/UtilTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/UtilTest.php
new file mode 100644
index 00000000..cf513020
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/Util/UtilTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Stripe;
+
+class UtilTest extends TestCase
+{
+    public function testIsList()
+    {
+        $list = [5, 'nstaoush', []];
+        $this->assertTrue(Util\Util::isList($list));
+
+        $notlist = [5, 'nstaoush', [], 'bar' => 'baz'];
+        $this->assertFalse(Util\Util::isList($notlist));
+    }
+
+    public function testThatPHPHasValueSemanticsForArrays()
+    {
+        $original = ['php-arrays' => 'value-semantics'];
+        $derived = $original;
+        $derived['php-arrays'] = 'reference-semantics';
+
+        $this->assertSame('value-semantics', $original['php-arrays']);
+    }
+
+    public function testConvertStripeObjectToArrayIncludesId()
+    {
+        $customer = Util\Util::convertToStripeObject([
+            'id' => 'cus_123',
+            'object' => 'customer',
+        ], null);
+        $this->assertTrue(array_key_exists("id", $customer->__toArray(true)));
+    }
+
+    public function testUtf8()
+    {
+        // UTF-8 string
+        $x = "\xc3\xa9";
+        $this->assertSame(Util\Util::utf8($x), $x);
+
+        // Latin-1 string
+        $x = "\xe9";
+        $this->assertSame(Util\Util::utf8($x), "\xc3\xa9");
+
+        // Not a string
+        $x = true;
+        $this->assertSame(Util\Util::utf8($x), $x);
+    }
+
+    public function testUrlEncode()
+    {
+        $a = [
+            'my' => 'value',
+            'that' => ['your' => 'example'],
+            'bar' => 1,
+            'baz' => null
+        ];
+
+        $enc = Util\Util::urlEncode($a);
+        $this->assertSame('my=value&that%5Byour%5D=example&bar=1', $enc);
+
+        $a = ['that' => ['your' => 'example', 'foo' => null]];
+        $enc = Util\Util::urlEncode($a);
+        $this->assertSame('that%5Byour%5D=example', $enc);
+
+        $a = ['that' => 'example', 'foo' => ['bar', 'baz']];
+        $enc = Util\Util::urlEncode($a);
+        $this->assertSame('that=example&foo%5B%5D=bar&foo%5B%5D=baz', $enc);
+
+        $a = [
+            'my' => 'value',
+            'that' => ['your' => ['cheese', 'whiz', null]],
+            'bar' => 1,
+            'baz' => null
+        ];
+
+        $enc = Util\Util::urlEncode($a);
+        $expected = 'my=value&that%5Byour%5D%5B%5D=cheese'
+              . '&that%5Byour%5D%5B%5D=whiz&bar=1';
+        $this->assertSame($expected, $enc);
+
+        // Ignores an empty array
+        $enc = Util\Util::urlEncode(['foo' => [], 'bar' => 'baz']);
+        $expected = 'bar=baz';
+        $this->assertSame($expected, $enc);
+
+        $a = ['foo' => [['bar' => 'baz'], ['bar' => 'bin']]];
+        $enc = Util\Util::urlEncode($a);
+        $this->assertSame('foo%5B0%5D%5Bbar%5D=baz&foo%5B1%5D%5Bbar%5D=bin', $enc);
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/Stripe/WebhookTest.php b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/WebhookTest.php
new file mode 100644
index 00000000..dcd82671
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/Stripe/WebhookTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Stripe;
+
+class WebhookTest extends TestCase
+{
+    const EVENT_PAYLOAD = "{
+  \"id\": \"evt_test_webhook\",
+  \"object\": \"event\"
+}";
+    const SECRET = "whsec_test_secret";
+
+    private function generateHeader($opts = [])
+    {
+        $timestamp = array_key_exists('timestamp', $opts) ? $opts['timestamp'] : time();
+        $payload = array_key_exists('payload', $opts) ? $opts['payload'] : self::EVENT_PAYLOAD;
+        $secret = array_key_exists('secret', $opts) ? $opts['secret'] : self::SECRET;
+        $scheme = array_key_exists('scheme', $opts) ? $opts['scheme'] : WebhookSignature::EXPECTED_SCHEME;
+        $signature = array_key_exists('signature', $opts) ? $opts['signature'] : null;
+        if ($signature === null) {
+            $signedPayload = "$timestamp.$payload";
+            $signature = hash_hmac("sha256", $signedPayload, $secret);
+        }
+        return "t=$timestamp,$scheme=$signature";
+    }
+
+    public function testValidJsonAndHeader()
+    {
+        $sigHeader = $this->generateHeader();
+        $event = Webhook::constructEvent(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
+        $this->assertEquals("evt_test_webhook", $event->id);
+    }
+
+    /**
+     * @expectedException \UnexpectedValueException
+     */
+    public function testInvalidJson()
+    {
+        $payload = "this is not valid JSON";
+        $sigHeader = $this->generateHeader(["payload" => $payload]);
+        Webhook::constructEvent($payload, $sigHeader, self::SECRET);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\SignatureVerification
+     */
+    public function testValidJsonAndInvalidHeader()
+    {
+        $sigHeader = "bad_header";
+        Webhook::constructEvent(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\SignatureVerification
+     * @expectedExceptionMessage Unable to extract timestamp and signatures from header
+     */
+    public function testMalformedHeader()
+    {
+        $sigHeader = "i'm not even a real signature header";
+        WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\SignatureVerification
+     * @expectedExceptionMessage No signatures found with expected scheme
+     */
+    public function testNoSignaturesWithExpectedScheme()
+    {
+        $sigHeader = $this->generateHeader(["scheme" => "v0"]);
+        WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\SignatureVerification
+     * @expectedExceptionMessage No signatures found matching the expected signature for payload
+     */
+    public function testNoValidSignatureForPayload()
+    {
+        $sigHeader = $this->generateHeader(["signature" => "bad_signature"]);
+        WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET);
+    }
+
+    /**
+     * @expectedException \Stripe\Error\SignatureVerification
+     * @expectedExceptionMessage Timestamp outside the tolerance zone
+     */
+    public function testTimestampOutsideTolerance()
+    {
+        $sigHeader = $this->generateHeader(["timestamp" => time() - 15]);
+        WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10);
+    }
+
+    public function testValidHeaderAndSignature()
+    {
+        $sigHeader = $this->generateHeader();
+        $this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10));
+    }
+
+    public function testHeaderContainsValidSignature()
+    {
+        $sigHeader = $this->generateHeader() . ",v1=bad_signature";
+        $this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET, 10));
+    }
+
+    public function testTimestampOffButNoTolerance()
+    {
+        $sigHeader = $this->generateHeader(["timestamp" => 12345]);
+        $this->assertTrue(WebhookSignature::verifyHeader(self::EVENT_PAYLOAD, $sigHeader, self::SECRET));
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/TestCase.php b/stripe-php/vendor/stripe/stripe-php/tests/TestCase.php
new file mode 100644
index 00000000..840c0ec5
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/TestCase.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Base class for Stripe test cases.
+ */
+class TestCase extends \PHPUnit_Framework_TestCase
+{
+    /** @var string original API base URL */
+    protected $origApiBase;
+
+    /** @var string original API key */
+    protected $origApiKey;
+
+    /** @var string original client ID */
+    protected $origClientId;
+
+    /** @var string original API version */
+    protected $origApiVersion;
+
+    /** @var string original account ID */
+    protected $origAccountId;
+
+    /** @var object HTTP client mocker */
+    protected $clientMock;
+
+    protected function setUp()
+    {
+        // Save original values so that we can restore them after running tests
+        $this->origApiBase = Stripe::$apiBase;
+        $this->origApiKey = Stripe::getApiKey();
+        $this->origClientId = Stripe::getClientId();
+        $this->origApiVersion = Stripe::getApiVersion();
+        $this->origAccountId = Stripe::getAccountId();
+
+        // Set up host and credentials for stripe-mock
+        Stripe::$apiBase = "http://localhost:" . MOCK_PORT;
+        Stripe::setApiKey("sk_test_123");
+        Stripe::setClientId("ca_123");
+        Stripe::setApiVersion(null);
+        Stripe::setAccountId(null);
+
+        // Set up the HTTP client mocker
+        $this->clientMock = $this->getMock('\Stripe\HttpClient\ClientInterface');
+
+        // By default, use the real HTTP client
+        ApiRequestor::setHttpClient(HttpClient\CurlClient::instance());
+    }
+
+    protected function tearDown()
+    {
+        // Restore original values
+        Stripe::$apiBase = $this->origApiBase;
+        Stripe::setApiKey($this->origApiKey);
+        Stripe::setClientId($this->origClientId);
+        Stripe::setApiVersion($this->origApiVersion);
+        Stripe::setAccountId($this->origAccountId);
+    }
+
+    /**
+     * Sets up a request expectation with the provided parameters. The request
+     * will actually go through and be emitted.
+     *
+     * @param string $method HTTP method (e.g. 'post', 'get', etc.)
+     * @param string $path relative path (e.g. '/v1/charges')
+     * @param array|null $params array of parameters. If null, parameters will
+     *   not be checked.
+     * @param string[]|null $headers array of headers. Does not need to be
+     *   exhaustive. If null, headers are not checked.
+     * @param bool $hasFile Whether the request parameters contains a file.
+     *   Defaults to false.
+     */
+    protected function expectsRequest(
+        $method,
+        $path,
+        $params = null,
+        $headers = null,
+        $hasFile = false
+    ) {
+        $this->prepareRequestMock($method, $path, $params, $headers, $hasFile)
+            ->will($this->returnCallback(
+                function ($method, $absUrl, $headers, $params, $hasFile) {
+                    $curlClient = HttpClient\CurlClient::instance();
+                    ApiRequestor::setHttpClient($curlClient);
+                    return $curlClient->request($method, $absUrl, $headers, $params, $hasFile);
+                }
+            ));
+    }
+
+    /**
+     * Sets up a request expectation with the provided parameters. The request
+     * will not actually be emitted, instead the provided response parameters
+     * will be returned.
+     *
+     * @param string $method HTTP method (e.g. 'post', 'get', etc.)
+     * @param string $path relative path (e.g. '/v1/charges')
+     * @param array|null $params array of parameters. If null, parameters will
+     *   not be checked.
+     * @param string[]|null $headers array of headers. Does not need to be
+     *   exhaustive. If null, headers are not checked.
+     * @param bool $hasFile Whether the request parameters contains a file.
+     *   Defaults to false.
+     * @param array $response
+     * @param integer $rcode
+     * @param string|null $base
+     *
+     * @return array
+     */
+    protected function stubRequest(
+        $method,
+        $path,
+        $params = null,
+        $headers = null,
+        $hasFile = false,
+        $response = [],
+        $rcode = 200,
+        $base = null
+    ) {
+        $this->prepareRequestMock($method, $path, $params, $headers, $hasFile, $base)
+            ->willReturn([json_encode($response), $rcode, []]);
+    }
+
+    /**
+     * Prepares the client mocker for an invocation of the `request` method.
+     * This helper method is used by both `expectsRequest` and `stubRequest` to
+     * prepare the client mocker to expect an invocation of the `request` method
+     * with the provided arguments.
+     *
+     * @param string $method HTTP method (e.g. 'post', 'get', etc.)
+     * @param string $path relative path (e.g. '/v1/charges')
+     * @param array|null $params array of parameters. If null, parameters will
+     *   not be checked.
+     * @param string[]|null $headers array of headers. Does not need to be
+     *   exhaustive. If null, headers are not checked.
+     * @param bool $hasFile Whether the request parameters contains a file.
+     *   Defaults to false.
+     * @param string|null $base base URL (e.g. 'https://api.stripe.com')
+     *
+     * @return PHPUnit_Framework_MockObject_Builder_InvocationMocker
+     */
+    private function prepareRequestMock(
+        $method,
+        $path,
+        $params = null,
+        $headers = null,
+        $hasFile = false,
+        $base = null
+    ) {
+        ApiRequestor::setHttpClient($this->clientMock);
+
+        if ($base === null) {
+            $base = Stripe::$apiBase;
+        }
+        $absUrl = $base . $path;
+
+        return $this->clientMock
+            ->expects($this->once())
+            ->method('request')
+            ->with(
+                $this->identicalTo(strtolower($method)),
+                $this->identicalTo($absUrl),
+                // for headers, we only check that all of the headers provided in $headers are
+                // present in the list of headers of the actual request
+                $headers === null ? $this->anything() : $this->callback(function ($array) use ($headers) {
+                    foreach ($headers as $header) {
+                        if (!in_array($header, $array)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }),
+                $params === null ? $this->anything() : $this->identicalTo($params),
+                $this->identicalTo($hasFile)
+            );
+    }
+}
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/bootstrap.no_autoload.php b/stripe-php/vendor/stripe/stripe-php/tests/bootstrap.no_autoload.php
new file mode 100644
index 00000000..7011a3f4
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/bootstrap.no_autoload.php
@@ -0,0 +1,5 @@
+<?php
+
+require_once __DIR__ . '/../init.php';
+
+require_once __DIR__ . '/bootstrap.php';
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/bootstrap.php b/stripe-php/vendor/stripe/stripe-php/tests/bootstrap.php
new file mode 100644
index 00000000..470b7027
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/tests/bootstrap.php
@@ -0,0 +1,43 @@
+<?php
+
+define("MOCK_MINIMUM_VERSION", "0.5.0");
+define("MOCK_PORT", getenv("STRIPE_MOCK_PORT") ?: 12111);
+
+// Send a request to stripe-mock
+$ch = curl_init("http://localhost:" . MOCK_PORT . "/");
+curl_setopt($ch, CURLOPT_HEADER, 1);
+curl_setopt($ch, CURLOPT_NOBODY, 1);
+curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+$resp = curl_exec($ch);
+
+if (curl_errno($ch)) {
+    echo "Couldn't reach stripe-mock at `localhost:" . MOCK_PORT . "`. Is " .
+         "it running? Please see README for setup instructions.\n";
+    exit(1);
+}
+
+// Retrieve the Stripe-Mock-Version header
+$version = null;
+$headers = explode("\n", $resp);
+foreach ($headers as $header) {
+    $pair = explode(":", $header, 2);
+    if ($pair[0] == "Stripe-Mock-Version") {
+        $version = trim($pair[1]);
+    }
+}
+
+if ($version === null) {
+    echo "Could not retrieve Stripe-Mock-Version header. Are you sure " .
+         "that the server at `localhost:" . MOCK_PORT . "` is a stripe-mock " .
+         "instance?";
+    exit(1);
+}
+
+if (version_compare($version, MOCK_MINIMUM_VERSION) == -1) {
+    echo "Your version of stripe-mock (" . $version . ") is too old. The minimum " .
+         "version to run this test suite is " . MOCK_MINIMUM_VERSION . ". " .
+         "Please see its repository for upgrade instructions.\n";
+    exit(1);
+}
+
+require_once __DIR__ . '/TestCase.php';
diff --git a/stripe-php/vendor/stripe/stripe-php/tests/data/test.png b/stripe-php/vendor/stripe/stripe-php/tests/data/test.png
new file mode 100644
index 00000000..1914264c
Binary files /dev/null and b/stripe-php/vendor/stripe/stripe-php/tests/data/test.png differ
diff --git a/stripe-php/vendor/stripe/stripe-php/update_certs.php b/stripe-php/vendor/stripe/stripe-php/update_certs.php
new file mode 100644
index 00000000..daba4a05
--- /dev/null
+++ b/stripe-php/vendor/stripe/stripe-php/update_certs.php
@@ -0,0 +1,19 @@
+#!/usr/bin/env php
+<?php
+chdir(dirname(__FILE__));
+
+set_time_limit(0); // unlimited max execution time
+
+$fp = fopen(dirname(__FILE__) . '/data/ca-certificates.crt', 'w+');
+
+$options = array(
+  CURLOPT_FILE    => $fp,
+  CURLOPT_TIMEOUT =>  3600,
+  CURLOPT_URL     => 'https://curl.haxx.se/ca/cacert.pem',
+);
+
+$ch = curl_init();
+curl_setopt_array($ch, $options);
+curl_exec($ch);
+curl_close($ch);
+fclose($fp);
diff --git a/stylesheets/jsonstore.css b/stylesheets/jsonstore.css
new file mode 100644
index 00000000..2479560f
--- /dev/null
+++ b/stylesheets/jsonstore.css
@@ -0,0 +1,98 @@
+/******************************************************
+ *  json store  *
+\*****************************************************/
+body {
+  font-family: "Lucida Grande", Lucida, Helvetica, Arial, sans-serif;
+  background: #FFF;
+  color: #333;
+  margin: 0px;
+  padding: 0px;
+}
+
+#header {
+  background: rgba(68,102,154,1);
+background: -moz-linear-gradient(top, rgba(68,102,154,1) 0%, rgba(85,126,187,1) 100%);
+background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(68,102,154,1)), color-stop(100%, rgba(85,126,187,1)));
+background: -webkit-linear-gradient(top, rgba(68,102,154,1) 0%, rgba(85,126,187,1) 100%);
+background: -o-linear-gradient(top, rgba(68,102,154,1) 0%, rgba(85,126,187,1) 100%);
+background: -ms-linear-gradient(top, rgba(68,102,154,1) 0%, rgba(85,126,187,1) 100%);
+background: linear-gradient(to bottom, rgba(68,102,154,1) 0%, rgba(85,126,187,1) 100%);
+filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#44669a', endColorstr='#557ebb', GradientType=0 );
+  margin: 0px;
+  padding: 10px;
+  margin-bottom: 3em;
+}
+#header h1 {
+  font-family: Inconsolata, Monaco, Courier, mono;
+  color: #FFF;
+  margin: 0px;
+}
+
+.cart-info {
+  background: #fff;
+  color: #000;
+  font-size: 1em;
+  padding: 1em;
+  font-weight: bold;
+}
+img {
+  border: 0;
+}
+
+.item {
+  float:left;
+  width: 200px;
+  height: 300px;
+  text-align:center;
+  font-size: 12px;
+  padding: 1em;
+  margin-bottom:1em;
+}
+
+.item a{
+  text-decoration: none;
+  color: #3F3F3F;
+}
+
+.item a:hover {
+  text-decoration: none;
+  color: #3F3F3F;
+}
+.item-title {
+  font-weight: bold;
+}
+.item-description{
+  text-align: justify;
+}
+.item-artist {
+  padding-left: 2em;
+  padding-right: 2em;
+  font-weight: bold;
+  font-size: 15px;
+  color:#496B97;
+}
+.item-detail .item-image {
+  float:left;
+}
+.item-detail .item-info {
+  padding: 60px 75px 0px 10px;
+}
+
+.item-image{
+  margin-bottom:1em;
+}
+
+.item-price{
+  font-size: 14px;
+  font-weight: bold;
+  color:#464646;
+  margin-bottom: 1em;
+}
+
+.dblock{
+  display: inline-block;
+}
+
+.nav {
+  padding: 1.4em;
+}
\ No newline at end of file
diff --git a/templates/item.template b/templates/item.template
new file mode 100644
index 00000000..bddfbb2b
--- /dev/null
+++ b/templates/item.template
@@ -0,0 +1,11 @@
+    <div class="item col-md-3">
+  <a href="#/item/<%= id %>">
+    <div class="item-image row">
+      <img src="<%= item.image %>" alt="<%= item.title %>" />
+    </div>
+    <div class="row">
+      <h5 class="item-artist"><%= item.title %></h5>
+      <p class="item-price">$<%= item.price %></p>
+    </div>
+  </a>
+</div>
diff --git a/templates/item_detail.template b/templates/item_detail.template
new file mode 100644
index 00000000..390d88a3
--- /dev/null
+++ b/templates/item_detail.template
@@ -0,0 +1,34 @@
+<div class="item-detail">
+  <div class="col-md-6">
+    <div class="row">
+      <div class="back-link"><a href="#/">&laquo; Back to Items</a></div>
+    </div>
+    <div class="row">
+      <div class="item-image"><img src="<%= item.large_image %>" alt="<%= item.name %>" /></div>
+    </div>
+  </div>
+</div>
+<div class="col-md-6">
+  <div class="item-info">
+  <div class="item-artist text-right">Categoría: <%= item.category %></div>
+  <h1 class="item-title text-center" style="text-align= center;"><%= item.title %></h1>
+  <br>
+  
+  <div class="item-description"><%= item.description %></div>
+  <br>
+  <div class="item-price text-center bg-info">$<%= item.price %></div>
+  <div class="item-form center-" id="addCart">
+    <form action="#/cart" method="post" class="dblock">
+      <input type="hidden" name="item_id" value="<%= params['id'] %>" />
+      <p class="dblock">
+        <label>Cantidad:</label>
+        <input type="text" size="2" name="quantity" value="1" />
+      </p>
+      <p class="btn dblock"><input type="submit" value="Añadir a Carrito" /></p>
+    </form>
+  </div>
+  <form class='pay' method='POST' method="post" action="javascript:alert('Tu compra fue exitosa')">
+    <script src='https://checkout.stripe.com/checkout.js' class='stripe-button center-block' data-key='pk_test_LMeQ66Q4hSaSh774qEv4EzwZ'" data-image='<%= item.image %>' data-name='<%= item.name %>' data-description='<%= item.description %>' data-amount='<%= (item.price * 100) %>' data-label='Comprar' ></script>
+  </form>
+
+</div>
\ No newline at end of file