diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..8bed3523 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +docker-compose.yml +Dockerfile +docker +node_modules \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..577ddcf5 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,37 @@ +module.exports = { + 'env': { + 'browser': true, + 'es6': true, + 'node': true + }, + 'extends': 'eslint:recommended', + 'globals': { + 'Atomics': 'readonly', + 'SharedArrayBuffer': 'readonly' + }, + 'parserOptions': { + 'ecmaVersion': 2018, + 'sourceType': 'module' + }, + "plugins": [ + "html" + ], + 'rules': { + 'indent': [ + 'error', + 2 + ], + 'linebreak-style': [ + 'error', + 'unix' + ], + 'quotes': [ + 'error', + 'single' + ], + 'semi': [ + 'error', + 'always' + ] + } +}; \ No newline at end of file diff --git a/.gitignore b/.gitignore index 12ac6472..f78c1256 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ node_modules/ -.DS_Store \ No newline at end of file +.DS_Store +temp/ +reports/ +coverage/ +.nyc_output \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..fca5953c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: node_js +node_js: + - "10" + +# safelist +branches: + only: + - master + +env: + - DOCKER_COMPOSE_VERSION=1.23.2 + +before_install: + - sudo rm /usr/local/bin/docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin + - docker-compose build + - docker-compose up + +script: + - "npm test" + +after_script: + - docker-compose down \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..06cc4b91 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +FROM node:10 + +# update apt-get +RUN apt-get -y update && apt-get -y upgrade + +# install build-essential +RUN apt-get -y install build-essential binutils debootstrap netcat + +# install mon +RUN cd /tmp; git clone https://github.com/tj/mon; cd mon; make install + +# copy basic files +# Only copy package.json first to help with cache +ADD . /src +WORKDIR /src + +RUN export USER=root && cd /src && rm -rf ./node_modules/ && npm install && npm link + +# disable install modules for now +# RUN cd /src/modules && node install.js + +RUN addgroup workers +RUN adduser --gid 1000 --disabled-password --gecos '' worker + +RUN mkdir -p /var/chroot/bin +COPY ./bin /var/chroot/bin + +# fake ssl certificates +RUN mkdir -p /etc/letsencrypt/live/hook.io +COPY ./ssl/*.pem /etc/letsencrypt/live/hook.io/ + +# COPY /bin/bash /var/chroot/bin/bash + +# RUN debootstrap --arch i386 wheezy /var/chroot http://httpredir.debian.org/debian + +# dns resolve is probably missing... +# RUN mkdir -p /var/chroot/etc/ +# RUN echo 'nameserver 8.8.4.4' | tee -a /var/chroot/etc/resolv.conf + diff --git a/README.md b/README.md index 195c75d2..8ae8db88 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,31 @@ - + ## Open-Source Microservice Hosting Platform ### Build and deploy HTTP microservices in seconds -To start using hook.io visit the website at [http://hook.io](http://hook.io). Here you will find many examples and documentation on how to use hook.io - - -**Built with:** [Node.js](http://nodejs.org), [CouchDB](http://couchdb.com), and [Github Gist](http://gist.github.com). [Node Package Manager](http://npmjs.org) modules are fully supported. +#### Supported Service Programming Languages + + - c ( with `gcc` ) + - java + - javascript ( first-class support ) + - coffee-script + - common + - bash + - lua + - golang + - ocaml + - perl + - php + - python + - python3 + - ruby + - rust + - r + - scheme + - smalltalk + - tcl + +To start using hook.io visit the website at [https://hook.io](https://hook.io). Here you will find many examples and documentation on how to use hook.io Architectural details can be found a bit further down. @@ -18,80 +37,53 @@ If you'd like, you can run the following Curl command to opt-in to our mailing l Replace youremail@marak.com with your email address. -## What is the purpose of hook.io? +## Installing Local Hook.io Server -hook.io is an open-source hosting platform for webhooks and microservices. The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms. hook.io provides an easy way to create, host, and share microservices. Through developing many small re-usable microservices, you can reduce the complexity of your applications while improving stability. +Before setting up a local hook.io server, you should try the free hosted version at [https://hook.io](https://hook.io) +If you don't need a full hosting platform and only want to host microservices, we recommend using the [Microcule](https://github.com/stackvana/microcule) project. +```bash +git clone https://github.com/bigcompany/hook.io.git +cd hook.io +docker-compose build +docker-compose up +```` -## Why or how would I want to use hook.io? +At this point you should be able to load hook.io locally at: `http://localhost:9999` -You should want to use hook.io if it can make your life as a developer easier. +## What is the purpose of hook.io? +hook.io is an open-source hosting platform for webhooks and microservices. The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms. hook.io provides an easy way to create, host, and share microservices. Through developing many small re-usable microservices, you can reduce the complexity of your applications while improving stability. -The most *basic use-case* for hook.io is quick and free webhook hosting. You can instantly create a [simple hook](http://hook.io/Marak/echo) which parses the incoming parameters of an HTTP request and performs arbitrary actions on it. For instance: Send an SMS message every-time the Hook is requested as a webpage. Since NPM is supported, you can re-use any existing library from the extensive [NPM module repository](http://npmjs.org). You can also configure Hooks to be executed on a schedule using a [Cron pattern](http://hook.io/cron). +## Why or how would I want to use hook.io? +You should want to use hook.io if it can make your life as a developer easier. +The most *basic use-case* for hook.io is quick and free webhook hosting. You can instantly create a [simple hook](https://hook.io/examples/echo) which parses the incoming parameters of an HTTP request and performs arbitrary actions on it. For instance: Send an SMS message every-time the Hook is requested as a webpage. Since NPM is supported, you can re-use any existing library from the extensive [NPM module repository](http://npmjs.org). You can also configure Hooks to be executed on a schedule using a [Cron pattern](https://hook.io/cron). At this point, we will take note that Hooks are [fully streaming](https://github.com/substack/stream-handbook). Inside your Hook source code you have direct access to Node's [http.IncomingMessage](http://nodejs.org/api/http.html#http_http_incomingmessage) and [httpServer.ServerResponse](http://nodejs.org/api/http.html#http_class_http_serverresponse) request and response streams. This means you can treat the inside of a Hook the exact same way as if it were inside a streaming middleware in a regular node http server. Having direct access to these streams is extremely useful and I am unsure if any other microservice hosting providers currently offer this feature. +More *advanced use-cases* for hook.io would be replacing individual parts of your application with microservices. Instead of adding a new route or module to your application , you could instead create a Hook responsible for only one unit of functionality and call it using a regular HTTP request from inside your existing application. One specific example could be building a Hook with a [custom theme](https://hook.io/themes) which acts perfectly as a stand-alone sign-up form. This sign-up form can then be loaded server-side in your application using one HTTP get request. It might sound complicated at first, but integrating microservices with your existing application is actually very easy. In the upcoming weeks we'll work on releasing specific guides for separating application functionalities into microservices. - -More *advanced use-cases* for hook.io would be replacing individual parts of your application with microservices. Instead of adding a new route or module to your application , you could instead create a Hook responsible for only one unit of functionality and call it using a regular HTTP request from inside your existing application. One specific example could be building a Hook with a [custom theme](http://hook.io/themes) which acts perfectly as a stand-alone sign-up form. This sign-up form can then be loaded server-side in your application using one HTTP get request. It might sound complicated at first, but integrating microservices with your existing application is actually very easy. In the upcoming weeks we'll work on releasing specific guides for separating application functionalities into microservices. - - - -An *even more advanced usage* would be building a suite of Hooks and composing them to create new and unique applications! Since every Hook understands Standard In and Standard Out and Hooks can [easily call other Hooks](http://hook.io/Marak/merge) from inside each other, there are an endless amount of combinations to be made. This composability enables the foundation for [Flow-based Programming](http://en.wikipedia.org/wiki/Flow-based_programming) without imposing any specific rules for composition. A specific example could be building a Hook ( called "tar" ) responsible for taking in STDIN and streaming out a compressed tar file. Once this Hook is created, you could easily pipe the results of another Hook ( such as an image downloader ) into the "tar" Hook. These Hooks don't exist yet, but I am certain someone will build them in the near future. - - +An *even more advanced usage* would be building a suite of Hooks and composing them to create new and unique applications! Since every Hook understands Standard In and Standard Out and Hooks can [easily call other Hooks](https://hook.io/examples/javascript-stream-merge) from inside each other, there are an endless amount of combinations to be made. This composability enables the foundation for [Flow-based Programming](http://en.wikipedia.org/wiki/Flow-based_programming) without imposing any specific rules for composition. A specific example could be building a Hook ( called "tar" ) responsible for taking in STDIN and streaming out a compressed tar file. Once this Hook is created, you could easily pipe the results of another Hook ( such as an image downloader ) into the "tar" Hook. These Hooks don't exist yet, but I am certain someone will build them in the near future. ## Unix Pipes! -hook.io is very friendly with Unix Pipes. Using STDOUT and STDIN you can connect hook.io to your existing Unix Tool chain. The best way to explain this concept is to review the [Curl examples](http://hook.io/curl). - - - -Here is one specific example of using hook.io to flip a cat upside-down with `cat` and `curl`. You will need to provide your own cat.png - - - -``` -cat cat.png | curl -F 'degrees=180' -F 'image=@-;type=image/png' http://hook.io/Marak/image/rotate > upsidedown-cat.png -``` - - +hook.io is very friendly with Unix Pipes. Using STDOUT and STDIN you can connect hook.io to your existing Unix Tool chain. The best way to explain this concept is to review the [Curl examples](https://hook.io/curl). ## The Data! If you noticed in the last example, hook.io is fully capable of streaming binary data. It also supports streaming file uploads, multipart form uploads, and will assist in parsing all incoming form fields, JSON, and query string data. - - ## Software Architecture The core software architecture of hook.io is Resource-View-Presenter ( RVP ). Resources are created using the npm [resource](http://npmjs.org/package/resource) module. -View-Presenters are created using the npm [view](http://npmjs.org/package/view) module with regular HTML, CSS, and JavaScript. The same View-Presenter pattern is also used to implement custom theming for Hooks see: [hook.io/themes](http://hook.io/themes) - - -## Dependency Tree - -hook.io itself is not a very large application. The majority of what powers hook.io is already MIT open-sourced and available for immediate download. - -Learning about the following dependencies is a great way to start understanding how hook.io works. - -[mschema](http://github.com/mschema/mschema) - Provides validation through-out the entire stack. - -[big](http://npmjs.org/package/big) - Small application framework. Provides [website](https://github.com/bigcompany/big/blob/master/apps/website/index.js) app which hook.io extends. - -[resource-http](http://github.com/bigcompany/http) - Provides core HTTP server API. Helps in configuring [Express](http://expressjs.com) with middlewares like [Passport](http://passportjs.org/) - -[resource-mesh](http://github.com/bigcompany/mesh) - Provides a distributed event emitter mesh using a [star network topography](http://en.wikipedia.org/wiki/Network_topology#Star). hook.io primarily uses this module as a monitoring agent to report status back to our monitoring [sink](https://github.com/bigcompany/big/blob/master/apps/sink/index.js). - -[resource-user](http://github.com/bigcompany/user) - Provides basic user API ( signups / logins / encrypted passwords / password resets / etc ) +View-Presenters are created using the npm [view](http://npmjs.org/package/view) module with regular HTML, CSS, and JavaScript. The same View-Presenter pattern is also used to implement custom theming for Hooks see: [hook.io/themes](https://hook.io/themes) ## Server Architecture @@ -102,7 +94,7 @@ The front-facing server is responsible for serving static content, maintaining u Workers are responsible for executing user-submitted source code and piping their responses through the front-facing server to the client. -At this point, we will take note that communication between the Hook and client remains streaming throughout the entire architecture. This gives hook.io the ability to perform complex tasks like [transcoding large video streams](http://hook.io/Marak/transcodeVideo) without worrying about clogging up any parts of the system with large memory buffers. +At this point, we will take note that communication between the Hook and client remains streaming throughout the entire architecture. This gives hook.io the ability to perform complex tasks like transcoding large video streams without worrying about clogging up any parts of the system with large memory buffers. Hook Servers and Hook Workers are immutable and stateless to ensure stability of the platform. They are designed to fail fast and restart fast. [mon](http://github.com/tj/mon) is used as a process supervisor. @@ -114,7 +106,7 @@ Source code for Hooks is currently stored on Github as Github Gists. I'd imagine ## Creating new Hooks -It's very simple. Go to [http://hook.io/new](http://hook.io/new) +It's very simple. Go to [https://hook.io/new](https://hook.io/new) ## Support @@ -122,7 +114,7 @@ If you run into an issue, have a question, or have feedback with using hook.io y ## Adding new NPM Modules to hook.io -The fastest way to get an additional NPM module added to the hook.io platform is to open up a Pull Request modifying [this file](https://github.com/bigcompany/hook.io/blob/master/modules/modules.js). +NPM modules will automatically install if you attempt to require them in a Hook. The first time the Hook is run, hook.io will install the dependency. Re-run the hook a few moments later and it should just work. If your module requires additional dependencies outside of what NPM can install, you can create a custom build script in [this folder](https://github.com/bigcompany/hook.io/tree/master/modules/builds). The hosting environment for hook.io is Ubuntu 14.04.1 LTS (Trusty Tahr) Bash scripts are recommended. @@ -130,25 +122,22 @@ If your module requires additional dependencies outside of what NPM can install, If you only need to test your Hook code, you can run `./bin/test-hook` without having to setup the full hook.io stack. -## Setting up a private hook.io - -Before setting up a private hook.io server, you should try the free hosted version at [http://hook.io](http://hook.io) - -Setting up a private hook.io server is easy! You'll want to clone this repository, install the dependencies, and run the `start.sh` script. There currently are not detailed installation instructions and you will need to configure a few dependencies ( such as couchdb and github api ). - -If you want to run Hooks without any additional dependencies, try running `./bin/test-hook`. - ## Workers Hooks are executed on *stateless* isolated workers to help facilitate scaling and to ensure stability in the core application. These workers are responsible for running user-submitted Hook source code and piping their responses back to the main server. If running untrusted user-submitted code, you will need to isolate these workers. see: `./bin/worker` and `./bin/test-worker` -## User process isolation +## i18n Internationalization + +[https://github.com/bigcompany/hook.io-i18n](https://github.com/bigcompany/hook.io-i18n) -If you plan to run a hook.io server that allows user-submitted code, you will need to setup process isolation per user on every worker so that user-submitted Hooks will not potentially affect performance of other users or interact with parts of the system the user should not have permission to access. -Docker is a great tool to start with. +## Tests + +```bash +npm test +``` ## Contributing diff --git a/bin/cron b/bin/cron deleted file mode 100644 index 32829745..00000000 --- a/bin/cron +++ /dev/null @@ -1,8 +0,0 @@ -var cron = require('../lib/resources/cron'); - -cron.processAll(function(err, result){ - // wait 60 seconds and process again - setTimeout(function(){ - process.exit(0); - }, 60000) -}); \ No newline at end of file diff --git a/bin/crons/send-user-alerts b/bin/crons/send-user-alerts new file mode 100644 index 00000000..3a89e1a2 --- /dev/null +++ b/bin/crons/send-user-alerts @@ -0,0 +1,15 @@ +#!/usr/bin/env node +var PAUSE_TIME = 60000; +const alerts = require('../../lib/resources/alerts/alerts'); + +alerts.sendAlerts({}, function(err, result){ + console.log(err, result); + // TODO: set timeout + // wait 60 seconds and process again + setTimeout(function(){ + // this script is intended to be run with a process monitor + // by calling process.exit, we close the script and let the process + // monitor restart the cron process + process.exit(0); + }, PAUSE_TIME); +}); \ No newline at end of file diff --git a/bin/docker/initialize b/bin/docker/initialize new file mode 100755 index 00000000..c76bcb39 --- /dev/null +++ b/bin/docker/initialize @@ -0,0 +1,20 @@ +#!/bin/sh +if [ ! -e "/src/.setup_done" ] +then + echo "Creating admin..." + sh /src/config/couchdb/create_admin.sh + echo "Adding default users..." + /src/bin/docker/wait web -c node /src/scripts/install/add-default-users.js + echo "Adding default hooks..." + /src/bin/docker/wait web -c node /src/scripts/install/add-default-hooks.js + echo "Done adding default hooks..." + touch "/src/.setup_done" + echo + echo "================================================================" + echo " Load Balancer started locally: " + echo + echo " http://localhost:9999" + echo + echo + echo "================================================================" +fi diff --git a/bin/docker/wait b/bin/docker/wait new file mode 100755 index 00000000..3e9a980c --- /dev/null +++ b/bin/docker/wait @@ -0,0 +1,28 @@ +#!/bin/bash + +declare -A PORT +PORT[redis]=6379 +PORT[couch]=5984 +PORT[balancer]=9999 +PORT[worker0]=10000 +PORT[web]=11000 + +wait_for() { + local host=$1 + local port=${PORT[$host]} + + echo "Waiting for $host:$port..." + until $(nc -z $host $port); do + sleep 1 + done +} + +until [ "$1" == "-c" ]; do + wait_for $1 + echo "$1 is up" + shift +done + +shift +echo "Executing $@" +exec $@ diff --git a/bin/hpm-server b/bin/hpm-server new file mode 100644 index 00000000..93efde95 --- /dev/null +++ b/bin/hpm-server @@ -0,0 +1,13 @@ +#!/usr/local/bin/node + +var hpm = require('hpm'); + +hpm.onAny(function(data){ + console.log('event'); + console.log(this.event, data) +}); + +// TODO: move options to config +hpm.server.start({ port: 8888, host: "localhost" }, function (err, app){ + console.log(err, app.server.address()) +}); \ No newline at end of file diff --git a/bin/npm-install b/bin/npm-install new file mode 100755 index 00000000..bbdfbea6 --- /dev/null +++ b/bin/npm-install @@ -0,0 +1,26 @@ +#!/usr/local/bin/node + +// parse incoming command line arguments +var argv = require('minimist')(process.argv.slice(2)); + +if (typeof argv.p === "undefined" || argv.p.length === 0) { + console.log('Packages parameter required. Please pass in -p argument'); + process.exit(); +} + +var packages; + +try { + packages = JSON.parse(argv.p); +} catch (err) { + console.log('Invalid -p argument. Not JSON' + err.message); + process.exit(); +} + +var npm = require("../lib/resources/npm"); + +npm.install("/Users/chroot/", { packages: packages }, function(err, res){ + if (err) { + console.error(err.message) + } +}); \ No newline at end of file diff --git a/bin/server b/bin/server deleted file mode 100644 index ef761c86..00000000 --- a/bin/server +++ /dev/null @@ -1,6 +0,0 @@ -var server = require('../lib/server'); -server.start({}, function(err){ - if (err) { - throw err; - } -}); \ No newline at end of file diff --git a/bin/services/broadcast b/bin/services/broadcast new file mode 100755 index 00000000..bc0aecae --- /dev/null +++ b/bin/services/broadcast @@ -0,0 +1,8 @@ +#!/usr/bin/env node +var broadcast = require('../../lib/broadcast/broadcast'); +broadcast.start({}, function (err, app) { + if (err) { + throw err; + } + console.log('web server started', app.server.address()) +}); \ No newline at end of file diff --git a/bin/services/cron b/bin/services/cron new file mode 100644 index 00000000..08b5a101 --- /dev/null +++ b/bin/services/cron @@ -0,0 +1,17 @@ +#!/usr/bin/env node +var cron = require('../../lib/resources/cron/cron'); +var PAUSE_TIME = 60000; + +cron.run(function(err, result){ + if (err) { + throw err; + } + console.log('processed all crons', result) + // wait 60 seconds and process again + setTimeout(function(){ + // this script is intended to be run with a process monitor + // by calling process.exit, we close the script and let the process + // monitor restart the cron process + process.exit(0); + }, PAUSE_TIME); +}); \ No newline at end of file diff --git a/bin/services/load-balancer b/bin/services/load-balancer new file mode 100755 index 00000000..f2a9c971 --- /dev/null +++ b/bin/services/load-balancer @@ -0,0 +1,89 @@ +#!/usr/bin/env node +var lb = require('../../lib/load-balancer/load-balancer'); +var cluster = require('cluster'); +var config = require('../../config'); +config.env = 'prod'; +if (config.env === 'dev') { + console.log('using dev mode, no cluster'); + lb.start({}, function(err, app){ + if (err) { + throw err; + } + console.log('lb started', app.server.address()); + }); +} else { + + // the master node is responsible for performing clustering logic + if (cluster.isMaster) { + + // create a new worker for every available CPU + var numWorkers = require('os').cpus().length; + + console.log('Master cluster setting up ' + numWorkers + ' workers...'); + + for(var i = 0; i < numWorkers; i++) { + cluster.fork(); + } + + cluster.on('exit', function (worker, code, signal) { + console.log('Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal); + console.log('Starting a new worker'); + // If the worker exited after disconnect, don't do anything. It should have already forked. + if (worker.exitedAfterDisconnect === true) { + console.log('exited after disconnect. probably performing rolling update'); + } else { + // Else if the worker did not have disconnect event, assume it was a crash and fork() immediately + console.log('got exit event without disconnect, probably a process crash'); + } + cluster.fork(); + }); + + cluster.on('online', function(worker) { + console.log('Worker ' + worker.process.pid + ' is online'); + worker.on('disconnect', function () { + var id = worker.id; + console.log('node got disconnect event', id); + // once disconnected, this node is no longer accepting incoming traffic from the cluster + // so immediately kill the worker node + worker.kill(); + }); + + // message handler is used for worker to receieve messages from parent process + worker.on('message', function (message) { + if (message.event === 'restart') { + // disconnect one worker from the pool and wait + var workers = Object.keys(cluster.workers); + if (workers.length === 0) { + console.log('fatal error: cluster has exhausted all nodes'); + return; + } + // performing rolling update here where we iterate through each stale node + var workerId = workers[0]; + cluster.workers[workerId].disconnect(); + } + if (message.event === 'query') { + var queryResponse = {}; + for (var w in cluster.workers) { + queryResponse[w] = { + pid: cluster.workers[w].process.pid, + spawnfile: cluster.workers[w].process.spawnfile + }; + // TODO: add uptime() or ctime + } + worker.send({ event: 'queryResponse', data: queryResponse }); + } + }); + }); + cluster.on('listening', function (worker) { + console.log('Worker ' + worker.process.pid + ' is listening'); + }); + } else { + lb.start({}, function(err, app){ + if (err) { + throw err; + } + // process.send({ event: 'load-balancer::listening' }); + console.log('lb started', app.server.address()); + }); + } +} \ No newline at end of file diff --git a/bin/services/microtail-front b/bin/services/microtail-front new file mode 100644 index 00000000..1d6a26c0 --- /dev/null +++ b/bin/services/microtail-front @@ -0,0 +1,33 @@ +#!/usr/bin/env node +var colors = require('colors'); +var microtail = require('microtail'); +var path = require('path'); +var config = require('../../config'); + +microtail.start({ + http: { + port: 9980, + auth: { + basicAuth: config.tailAdmin + } + }, + tails: [{ + name: 'webserver logs', + path: path.resolve(__dirname + "/../../logs/web-logs.txt"), + lines: 10 + },{ + name: 'cron logs', + path: path.resolve(__dirname + "/../../logs/cron-logs.txt"), + lines: 10 + },{ + name: 'balancer logs', + path: path.resolve(__dirname + "/../../logs/balancer-logs.txt"), + lines: 10 + }] +}, function (err, app) { + if (err) { + console.log('microtail start error'.red); + throw err; + } + console.log('microtail server started'.blue, app.server()); +}); \ No newline at end of file diff --git a/bin/services/microtail-workers b/bin/services/microtail-workers new file mode 100644 index 00000000..bfdb632d --- /dev/null +++ b/bin/services/microtail-workers @@ -0,0 +1,24 @@ +#!/usr/bin/env node +var colors = require('colors'); +var microtail = require('microtail'); +var path = require('path'); + +microtail.start({ + http: { + port: 9980, + auth: { + basicAuth: config.tailAdmin + } + }, + tails: [{ + name: 'worker logs', + path: path.resolve(__dirname + "/../../logs/worker-logs.txt"), + lines: 10 + }] +}, function (err, app) { + if (err) { + console.log('microtail start error'.red); + throw err; + } + console.log('microtail server started'.blue, app.server()); +}); \ No newline at end of file diff --git a/bin/services/web b/bin/services/web new file mode 100755 index 00000000..dc31e89f --- /dev/null +++ b/bin/services/web @@ -0,0 +1,10 @@ +#!/usr/bin/env node +// front-facing web servers +// currently handles static assets / views / and api endpoints +var web = require('../../lib/web/web'); +web.start({}, function(err, app){ + if (err) { + throw err; + } + console.log('web server started', app.server.address()) +}); \ No newline at end of file diff --git a/bin/services/worker b/bin/services/worker new file mode 100755 index 00000000..968a2c01 --- /dev/null +++ b/bin/services/worker @@ -0,0 +1,11 @@ +#!/usr/bin/env node +var colors = require('colors'); +var worker = require('../../lib/worker/worker'); + +worker.start({}, function (err, app) { + if (err) { + console.log('worker error'.red); + throw err; + } + console.log('worker started'.blue, app.server.address()); +}); \ No newline at end of file diff --git a/bin/test-hook b/bin/test-hook deleted file mode 100644 index d1f9a17c..00000000 --- a/bin/test-hook +++ /dev/null @@ -1,46 +0,0 @@ -var hook = require('../lib/resources/hook'); - -/*********************************************************/ - // - // Put your hook code here - // - - var exampleHook = function exampleHook (hook) { - hook.res.end('Success!'); - } - -/*********************************************************/ - // - // This code runs the hook locally - // - - var http = require('resource-http'); - var request = require('request'); - - http.listen({ port: 9999 }, function(err, app){ - app.get('/test', function(req, res){ - req.resource = {}; - // Set custom Hook.params here - req.resource.params = { - "foo": "bar" - } - req.hook = {}; - req.hook.env = {}; - hook.runUntrustedHook({ req: req, res: res, params: "format" } , exampleHook, function(err){ - if (err) { - throw err; - } - }); - }); - request.get('http://localhost:9999/test', function(err, res, body){ - console.log(err, body); - process.exit(); - }); - }); - -/*********************************************************/ - -// -// TODO: move this code into a separate module -// see: https://github.com/bigcompany/hook.io/issues/17 -// \ No newline at end of file diff --git a/bin/test-worker b/bin/test-worker deleted file mode 100644 index 7329a56a..00000000 --- a/bin/test-worker +++ /dev/null @@ -1,12 +0,0 @@ -var request = require('hyperquest'); -var net = require('net'); - -var http = require('http'); - -var server = http.createServer(function(req, res){ - var _url = 'http://localhost:9999/runHook' + req.url; - var stream = request.post(_url); - req.pipe(stream).pipe(res); -}); - -server.listen(8888); \ No newline at end of file diff --git a/bin/worker b/bin/worker deleted file mode 100644 index bda51ec2..00000000 --- a/bin/worker +++ /dev/null @@ -1,6 +0,0 @@ -var worker = require('../lib/worker'); -worker.start({}, function (err){ - if (err) { - throw err; - } -}); \ No newline at end of file diff --git a/config/couchdb/Dockerfile b/config/couchdb/Dockerfile new file mode 100644 index 00000000..7292a6ff --- /dev/null +++ b/config/couchdb/Dockerfile @@ -0,0 +1,3 @@ +FROM klaemo/couchdb + +COPY local.ini /usr/local/etc/couchdb/ diff --git a/config/couchdb/create_admin.sh b/config/couchdb/create_admin.sh new file mode 100755 index 00000000..810bc1a4 --- /dev/null +++ b/config/couchdb/create_admin.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +HOST=http://couch:5984 + +curl -s -X PUT $HOST/_node/nonode@nohost/_config/admins/admin -d '"password"' +curl -s -X PUT -u admin:password $HOST/_users +curl -s -X PUT -u admin:password $HOST/_replicator +curl -s -X PUT -u admin:password $HOST/_global_changes + +#curl -X PUT $HOST/_config/admins/admin -d '"password"' diff --git a/config/dev/index.js b/config/dev/index.js new file mode 100644 index 00000000..59e223eb --- /dev/null +++ b/config/dev/index.js @@ -0,0 +1,183 @@ +const app = { + name: "hook.io", + // host: "https://hook.io", + url: "http://localhost:9999", + ws: "ws://localhost:9999", + //url: "https://hook.io", + domain: "localhost", + port: "9999", + logo: "http://localhost:9999/img/logo.png", + logoInverse: "http://localhost:9999/img/logo-inverse.png", + adminEmail: "hookmaster@hook.io" +}; + +const redis = { + port: 6379, + password: "password", + host: "0.0.0.0" +}; + +const couch = { + "username": "admin", + "password": "password", + "port": 5984, + "host": "0.0.0.0" +}; + +const cluster = { + registerWithLoadBalancer: true, + pools : { + // Note: All services will auto-port up based on the first available port after the starting port + // this allows us define ranges of ports of the elastic pool instead of pre-configuring the pool statically + web: [], + worker: [], + lb: [] + } +}; + +const balancer = { + port: 9999, + host: "0.0.0.0", + https: false, + publicIP: "127.0.0.1", + roots: ["hookio", "0.0.0.0", "localhost", "hook.io", "www.hook.io", "couch"], + secret: "change secret", + redis: { + prefix: '/session/', + port: redis.port, + password: redis.password, + host: redis.host + } +}; + +const broadcast = { + port: 9998, + host: "0.0.0.0", + https: false, + publicIP: "127.0.0.1", + roots: ["ws.hookio", "0.0.0.0", "localhost", "ws.hook.io"], + secret: "change secret", + redis: { + prefix: '/session/', + port: redis.port, + password: redis.password, + host: redis.host + } +}; + +const web = { + port: 11000, + host: "app", + https: false, + registerWithLoadBalancer: cluster.registerWithLoadBalancer, + roots: ["hookio", "0.0.0.0", "localhost", "hook.io", "www.hook.io"], + secret: "change secret", + redis: { + prefix: '/session/', + port: redis.port, + password: redis.password, + host: redis.host + } +}; + +const worker = { + host: '0.0.0.0', + startingPort: 10000, + registerWithLoadBalancer: cluster.registerWithLoadBalancer, + nproc: { + soft: 15000, + hard: 20000 + }, + //npmPath: __dirname + '/../../../../stackvana/microcule/' || "/Users/chroot/", + npmPath: "/var/chroot/root/microcule/", + publicIP: 'worker0', + chrootUser: 'worker', + useChroot: false, + useNSJAIL: false, + nsJailArgs: [ '-Mo', '--disable_clone_newnet', '--chroot', '/var/chroot/', '--user', '99999', '--group', '99999', '--rlimit_as', '9999', '--log', 'nsjail.log', '--rlimit_nproc', '1000' ,'--quiet', '--'] +}; + +module['exports'] = { + usingDocker: true, + superadmin: { + "name": "superadmin", + "password": "footy", + "super_private_key": "1234" + }, + tailAdmin: { + "username": "tails", + "password": "password" + }, + // contains definitions for elastic server pools + pools : { + // Note: All services will auto-port up based on the first available port after the starting port + // this allows us define ranges of ports of the elastic pool instead of pre-configuring the pool statically + web: cluster.pools.web, + worker: cluster.pools.worker, + lb: cluster.pools.lb + }, + web: web, + balancer: balancer, + broadcast: broadcast, + baseUrl: "http://localhost:9999", + couch: { + "database": "hook", + "type": "couch2", + "username": couch.username, + "password": couch.password, + "port": couch.port, + "host": couch.host + }, + redisCache: { + port: redis.port, + password: redis.password, + host: redis.host + }, + redis: { + port: redis.port, + password: redis.password, + host: redis.host + }, + github: { + accessName: "", + accessToken: "", + // working github tokens are given + // these are dedicated for hook.io testing + // don't use these in production as they may be revoked and refeshed at anytime + CLIENT_ID: "321de11108ccdacf2279", + CLIENT_SECRET: "14ed41431983aaceef121d32f2f3f3087e0434ac", + OAUTH_CALLBACK: "http://localhost:9999/login/github/callback" + }, + defaultTheme : "http://localhost:9999/themes/none/index.html", // should be https? + defaultPresenter : "http://localhost:9999/themes/none/index.js", + stripe: { + secretKey: "sk_test_ZXdJj4I3Db2iB9ZRm0gqyzDV", + publicKey: "pk_test_axAR0vF3Qam8zs09JE7t8ZIo" + }, + email: { + "provider": "mock", + "api_user": "abc", + "api_key": "1234" + }, + cacheView: false, + // tempDirectory: __dirname + "/../temp/", + tempDirectory: "/hook-temp/", + sslKeyDirectory: '/src/ssl/', + chrootDirectory: '/var/chroot', + //sslKeyDirectory: __dirname + '/../ssl/', + //chrootDirectory: '/Users/worker', + locales: { + locales: ['en', 'de'] + }, + worker: worker, + customDomains: false, + MAX_SERVICE_EXECUTIONS_PER_CYCLE: Infinity, + MAX_SERVICE_CONCURRENCY: 10, + UNTRUSTED_HOOK_TIMEOUT: 10000, + messages: { + childProcessSpawnError: require('../messages/childProcessSpawnError'), + serviceExecutionTimeout: require('../messages/serviceExecutionTimeout'), + unauthorizedRoleAccess: require('../messages/unauthorizedRoleAccess') + }, + app: app +}; diff --git a/config/docker/index.js b/config/docker/index.js new file mode 100644 index 00000000..07268ad4 --- /dev/null +++ b/config/docker/index.js @@ -0,0 +1,183 @@ +const app = { + name: "hook.io", + // host: "https://hook.io", + url: "http://localhost:9999", + ws: "ws://localhost:9999", + //url: "https://hook.io", + domain: "localhost", + port: "9999", + logo: "http://localhost:9999/img/logo.png", + logoInverse: "http://localhost:9999/img/logo-inverse.png", + adminEmail: "hookmaster@hook.io" +}; + +const redis = { + port: 6379, + password: "password", + host: "redis" +}; + +const couch = { + "username": "admin", + "password": "password", + "port": 5984, + "host": "couch" +}; + +const cluster = { + registerWithLoadBalancer: true, + pools : { + // Note: All services will auto-port up based on the first available port after the starting port + // this allows us define ranges of ports of the elastic pool instead of pre-configuring the pool statically + web: [], + worker: [], + lb: [] + } +}; + +const balancer = { + port: 9999, + host: "app", + https: false, + publicIP: "127.0.0.1", + roots: ["hookio", "0.0.0.0", "localhost", "hook.io", "www.hook.io", "couch"], + secret: "change secret", + redis: { + prefix: '/session/', + port: redis.port, + password: redis.password, + host: redis.host + } +}; + +const broadcast = { + port: 9998, + host: "0.0.0.0", + https: false, + publicIP: "127.0.0.1", + roots: ["ws.hookio", "0.0.0.0", "localhost", "ws.hook.io"], + secret: "change secret", + redis: { + prefix: '/session/', + port: redis.port, + password: redis.password, + host: redis.host + } +}; + +const web = { + port: 11000, + host: "app", + https: false, + registerWithLoadBalancer: cluster.registerWithLoadBalancer, + roots: ["hookio", "0.0.0.0", "localhost", "hook.io", "www.hook.io"], + secret: "change secret", + redis: { + prefix: '/session/', + port: redis.port, + password: redis.password, + host: redis.host + } +}; + +const worker = { + host: 'worker0', + startingPort: 10000, + registerWithLoadBalancer: cluster.registerWithLoadBalancer, + nproc: { + soft: 15000, + hard: 20000 + }, + //npmPath: __dirname + '/../../../../stackvana/microcule/' || "/Users/chroot/", + npmPath: "/var/chroot/root/microcule/", + publicIP: 'worker0', + chrootUser: 'worker', + useChroot: false, + useNSJAIL: false, + nsJailArgs: [ '-Mo', '--disable_clone_newnet', '--chroot', '/var/chroot/', '--user', '99999', '--group', '99999', '--rlimit_as', '9999', '--log', 'nsjail.log', '--rlimit_nproc', '1000' ,'--quiet', '--'] +}; + +module['exports'] = { + usingDocker: true, + superadmin: { + "name": "superadmin", + "password": "footy", + "super_private_key": "1234" + }, + tailAdmin: { + "username": "tails", + "password": "password" + }, + // contains definitions for elastic server pools + pools : { + // Note: All services will auto-port up based on the first available port after the starting port + // this allows us define ranges of ports of the elastic pool instead of pre-configuring the pool statically + web: cluster.pools.web, + worker: cluster.pools.worker, + lb: cluster.pools.lb + }, + web: web, + balancer: balancer, + broadcast: broadcast, + baseUrl: "http://localhost:9999", + couch: { + "database": "hook", + "type": "couch2", + "username": couch.username, + "password": couch.password, + "port": couch.port, + "host": couch.host + }, + redisCache: { + port: redis.port, + password: redis.password, + host: redis.host + }, + redis: { + port: redis.port, + password: redis.password, + host: redis.host + }, + github: { + accessName: "", + accessToken: "", + // working github tokens are given + // these are dedicated for hook.io testing + // don't use these in production as they may be revoked and refeshed at anytime + CLIENT_ID: "321de11108ccdacf2279", + CLIENT_SECRET: "14ed41431983aaceef121d32f2f3f3087e0434ac", + OAUTH_CALLBACK: "http://localhost:9999/login/github/callback" + }, + defaultTheme : "http://localhost:9999/themes/none/index.html", // should be https? + defaultPresenter : "http://localhost:9999/themes/none/index.js", + stripe: { + secretKey: "", + publicKey: "" + }, + email: { + "provider": "mock", + "api_user": "abc", + "api_key": "1234" + }, + cacheView: false, + // tempDirectory: __dirname + "/../temp/", + tempDirectory: "/hook-temp/", + sslKeyDirectory: '/src/ssl/', + chrootDirectory: '/var/chroot', + //sslKeyDirectory: __dirname + '/../ssl/', + //chrootDirectory: '/Users/worker', + locales: { + locales: ['en', 'de'] + }, + worker: worker, + customDomains: false, + MAX_SERVICE_EXECUTIONS_PER_CYCLE: Infinity, + MAX_SERVICE_CONCURRENCY: 10, + UNTRUSTED_HOOK_TIMEOUT: 10000, + messages: { + childProcessSpawnError: require('../messages/childProcessSpawnError'), + serviceExecutionTimeout: require('../messages/serviceExecutionTimeout'), + unauthorizedRoleAccess: require('../messages/unauthorizedRoleAccess') + }, + app: app +}; diff --git a/config/index.js b/config/index.js index b8e4bbeb..6ebf0850 100644 --- a/config/index.js +++ b/config/index.js @@ -1,24 +1,15 @@ -module['exports'] = { - site: { - port: 9999, - https: false - }, - couch: { - "database": "hook", - "type": "couchdb", - "username": "admin", - "password": "password", - "port": 5984, - "host": "localhost" - }, - github: { - // working github tokens are given - // these are dedicated for hook.io testing - // don't use these in production as they may be revoked and refeshed at anytime - CLIENT_ID: "321de11108ccdacf2279", - CLIENT_SECRET: "14ed41431983aaceef121d32f2f3f3087e0434ac", - OAUTH_CALLBACK: "http://localhost:9999/login/callback" - }, - defaultTheme : "http://localhost:9999/themes/debug/index.html", - defaultPresenter : "http://localhost:9999/themes/debug/index.js", -}; \ No newline at end of file +const isDocker = require('is-docker'); + +if (isDocker()) { + module.exports = require('./docker'); + module.exports.env = "docker"; +} else { + module.exports = require('./dev'); + module.exports.env = "dev"; + if (process.env['NODE_ENV'] === 'production') { + module.exports = require('./production'); + module.exports.env = "production"; + } +} + +// console.log('Info: Using env: ' + module.exports.env); \ No newline at end of file diff --git a/config/messages/README.md b/config/messages/README.md new file mode 100644 index 00000000..838e8d79 --- /dev/null +++ b/config/messages/README.md @@ -0,0 +1,5 @@ +This directory contains messages which may be presented to the user. + +These messages are configurable, so we can adjust the copy of certain messages without having to touch any sensitive code paths. + +This is also useful if you intend to white-label this project. \ No newline at end of file diff --git a/config/messages/childProcessSpawnError.js b/config/messages/childProcessSpawnError.js new file mode 100644 index 00000000..f97ed513 --- /dev/null +++ b/config/messages/childProcessSpawnError.js @@ -0,0 +1,8 @@ +module['exports'] = function childProcessSpawnErrorMessage (args) { + // TODO: use a template instead of str concat + var str = ''; + str += 'Error in spawning child process. Error code: 1\n'; + str += 'We attempted to run the following command: \n\n' + str += args.join(" "); + return str; +} \ No newline at end of file diff --git a/config/messages/serviceExecutionTimeout.js b/config/messages/serviceExecutionTimeout.js new file mode 100644 index 00000000..583950d5 --- /dev/null +++ b/config/messages/serviceExecutionTimeout.js @@ -0,0 +1,12 @@ +module['exports'] = function serviceExecutionTimeoutMessage (seconds) { + // TODO: use a template instead of str concat + var str = ''; + str += 'Timeout Limit Hit. Request Aborted! \n\nHook source code took more than '; + str += seconds; + str += ' complete.\n\n'; + str += 'A delay of this long may indicate there is an error in the source code for the Hook. \n\n'; + str += 'If there are no errors and the Hook requires more than '; + str += seconds; + str += ' seconds to execute, you can upgrade to a paid account to increase your timeout limits.'; + return str; +} \ No newline at end of file diff --git a/config/messages/unauthorizedRoleAccess.js b/config/messages/unauthorizedRoleAccess.js new file mode 100644 index 00000000..ec67a593 --- /dev/null +++ b/config/messages/unauthorizedRoleAccess.js @@ -0,0 +1,55 @@ +var config; +process.nextTick(function(){ + config = require('../'); +}); + +module['exports'] = function unauthorizedRoleAccess (req, role) { + // TODO: use a template instead of str concat + var str = '', + errorType = "unauthorized-role-access"; + + // if no session, create temporary session scope for anonymous user error + if (typeof req.session === 'undefined') { + req.session = {}; + } + + if (typeof req.session.user === "undefined") { + req.session.user = "anonymous"; + } + + if (typeof req.resource.params.hook_private_key !== "undefined") { + // if key was provided but the role check failed ( since it reached here ) show specific error message + str += "A role access check was attempted but failed with key " + '"' + req.resource.params.hook_private_key + '"\n\n'; + errorType = "unauthorized-role-access"; + //str += "Try again with a diffirent `hook_private_key` value?\n\n"; + } + + if (typeof req.resource.keyName !== "undefined") { + str += ('"' + req.resource.keyName + '" does not have the role "' + role + '" which is required to access "' + req._parsedUrl.pathname + '"'); + } else { + str += ('"' + req.session.user + '" does not have the role "' + role + '" which is required to access "' + req._parsedUrl.pathname + '"'); + } + + if (req.session.user === "anonymous") { + str += "\n\nIf you are the owner of this resource try logging in at https://" + config.app.domain + "/login"; + } + + if (typeof req.resource.params.hook_private_key !== "undefined") { + // do nothing + } else { + // provide instructions to provide keys + str += "\n\nIf any access keys have been created you can provide a `hook_private_key` parameter to access this URL."; + } + + if (req.jsonResponse === true) { + str = JSON.stringify({ + error: true, + message: str, + user: req.session.user, + role: role, + type: errorType + }); + } + + return str; +} \ No newline at end of file diff --git a/config/redis/redis.conf b/config/redis/redis.conf new file mode 100644 index 00000000..02579403 --- /dev/null +++ b/config/redis/redis.conf @@ -0,0 +1,938 @@ +# Redis configuration file example + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################ GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /usr/local/var/run/redis.pid when daemonized. +daemonize yes + +# When running daemonized, Redis writes a pid file in /usr/local/var/run/redis.pid by +# default. You can specify a custom pid file location here. +pidfile /usr/local/var/run/redis.pid + +# Accept connections on the specified port, default is 6379. +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# By default Redis listens for connections from all the network interfaces +# available on the server. It is possible to listen to just one or multiple +# interfaces using the "bind" configuration directive, followed by one or +# more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +# bind 127.0.0.1 + +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 60 seconds. +tcp-keepalive 0 + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir /usr/local/var/db/redis/ + +################################# REPLICATION ################################# + +# Master-Slave replication. Use slaveof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of slaves. +# 2) Redis slaves are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition slaves automatically try to reconnect to masters +# and resynchronize with them. +# +# slaveof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the slave to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the slave request. +# +masterauth password + +# When a slave loses its connection with the master, or when the replication +# is still in progress, the slave can act in two different ways: +# +# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if slave-serve-stale-data is set to 'no' the slave will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO and SLAVEOF. +# +slave-serve-stale-data yes + +# You can configure a slave instance to accept writes or not. Writing against +# a slave instance may be useful to store some ephemeral data (because data +# written on a slave will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default slaves are read-only. +# +# Note: read only slaves are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only slave exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only slaves using 'rename-command' to shadow all the +# administrative / dangerous commands. +slave-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New slaves and reconnecting slaves that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the slaves. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the slaves incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to slave sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more slaves +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new slaves arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple slaves +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the slaves. +# +# This is important since once the transfer starts, it is not possible to serve +# new slaves arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more slaves arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# Slaves send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_slave_period option. The default value is 10 +# seconds. +# +# repl-ping-slave-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of slave. +# 2) Master timeout from the point of view of slaves (data, pings). +# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-slave-period otherwise a timeout will be detected +# every time there is low traffic between the master and the slave. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the slave socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to slaves. But this can add a delay for +# the data to appear on the slave side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the slave side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and slaves are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# slave data when slaves are disconnected for some time, so that when a slave +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the slave missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the slave can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a slave connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected slaves for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last slave disconnected, for +# the backlog buffer to be freed. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The slave priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a slave to promote into a +# master if the master is no longer working correctly. +# +# A slave with a low priority number is considered better for promotion, so +# for instance if there are three slaves with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the slave as not able to perform the +# role of master, so a slave with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +slave-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N slaves connected, having a lag less or equal than M seconds. +# +# The N slaves need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the slave, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough slaves +# are available, to the specified number of seconds. +# +# For example to require at least 3 slaves with a lag <= 10 seconds use: +# +# min-slaves-to-write 3 +# min-slaves-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-slaves-to-write is set to 0 (feature disabled) and +# min-slaves-max-lag is set to 10. + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +requirepass mypassword + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to slaves may cause problems. + +################################### LIMITS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +# Don't use more memory than the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU cache, or to set +# a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have slaves attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the slaves are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of slaves is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have slaves attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for slave +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> remove the key with an expire set using an LRU algorithm +# allkeys-lru -> remove any key according to the LRU algorithm +# volatile-random -> remove a random key with an expire set +# allkeys-random -> remove a random key, any key +# volatile-ttl -> remove the key with the nearest expire time (minor TTL) +# noeviction -> don't expire at all, just return an error on write operations +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs a bit more CPU. 3 is very fast but not very accurate. +# +# maxmemory-samples 5 + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### +# +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however +# in order to mark it as "mature" we need to wait for a non trivial percentage +# of users to deploy it in production. +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A slave of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a slave to actually have a exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple slaves able to failover, they exchange messages +# in order to try to give an advantage to the slave with the best +# replication offset (more data from the master processed). +# Slaves will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single slave computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the slave will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a slave will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * slave-validity-factor) + repl-ping-slave-period +# +# So for example if node-timeout is 30 seconds, and the slave-validity-factor +# is 10, and assuming a default repl-ping-slave-period of 10 seconds, the +# slave will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large slave-validity-factor may allow slaves with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a slave at all. +# +# For maximum availability, it is possible to set the slave-validity-factor +# to a value of 0, which means, that slaves will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-slave-validity-factor 10 + +# Cluster slaves are able to migrate to orphaned masters, that are masters +# that are left without working slaves. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working slaves. +# +# Slaves migrate to orphaned masters only if there are still at least a +# given number of other working slaves for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a slave +# will migrate only if there is at least 1 other working slave for its master +# and so forth. It usually reflects the number of slaves you want for every +# master in your cluster. +# +# Default is 1 (slaves migrate only if their masters remain with at least +# one slave). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Similarly to hashes, small lists are also encoded in a special way in order +# to save a lot of space. The special representation is only used when +# you are under the following limits: +list-max-ziplist-entries 512 +list-max-ziplist-value 64 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# slave -> slave clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and slave clients, since +# subscribers and slaves receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit slave 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..4beb1b72 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +version: '2' +services: + redis: + build: ./docker/redis + + couch: + build: ./docker/couchdb + ports: + - "9998:5984" + + app: + build: . + volumes: + - .:/src + - /src/node_modules + command: /src/bin/docker/wait redis couch worker0 balancer -c node /src/bin/services/web + depends_on: + - "redis" + - "couch" + ports: + - "11000:11000" + + balancer: + #build: . + volumes: + - .:/src + - /src/node_modules + image: "hookio_app" + command: /src/bin/docker/wait redis -c node /src/bin/services/load-balancer + ports: + - "9999:9999" + depends_on: + - "app" + links: + - "app:web" + + worker0: + #build: . + volumes: + - .:/src + - /src/node_modules + image: "hookio_app" + command: /src/bin/docker/wait redis couch -c node /src/bin/services/worker + privileged: true + ports: + - "10000:10000" + depends_on: + - "app" + links: + - "app:web" + + setup: + image: "hookio_app" + command: /src/bin/docker/wait couch -c /src/bin/docker/initialize + depends_on: + - "app" + links: + - "app:web" + diff --git a/docker/couchdb/Dockerfile b/docker/couchdb/Dockerfile new file mode 100644 index 00000000..2e5ea040 --- /dev/null +++ b/docker/couchdb/Dockerfile @@ -0,0 +1,4 @@ +FROM klaemo/couchdb:2.0.0 + +# Require valid user +COPY local.ini /usr/local/etc/couchdb/ diff --git a/docker/couchdb/local.ini b/docker/couchdb/local.ini new file mode 100644 index 00000000..811a3bed --- /dev/null +++ b/docker/couchdb/local.ini @@ -0,0 +1,100 @@ +; CouchDB Configuration Settings + +; Custom settings should be made in this file. They will override settings +; in default.ini, but unlike changes made to default.ini, this file won't be +; overwritten on server upgrade. + +[couchdb] +;max_document_size = 4294967296 ; bytes +uuid = b91491f15007724ce484335ac8e4e3aa + +[httpd] +;port = 5984 +;bind_address = 127.0.0.1 +; Options for the MochiWeb HTTP server. +;server_options = [{backlog, 128}, {acceptor_pool_size, 16}] +; For more socket options, consult Erlang's module 'inet' man page. +;socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}] + +; Uncomment next line to trigger basic-auth popup on unauthorized requests. +;WWW-Authenticate = Basic realm="administrator" + +; Uncomment next line to set the configuration modification whitelist. Only +; whitelisted values may be changed via the /_config URLs. To allow the admin +; to change this value over HTTP, remember to include {httpd,config_whitelist} +; itself. Excluding it from the list would require editing this file to update +; the whitelist. +;config_whitelist = [{httpd,config_whitelist}, {log,level}, {etc,etc}] + +[query_servers] +;nodejs = /usr/local/bin/couchjs-node /path/to/couchdb/share/server/main.js + + +[httpd_global_handlers] +;_google = {couch_httpd_proxy, handle_proxy_req, <<"http://www.google.com">>} + +[couch_httpd_auth] +; If you set this to true, you should also uncomment the WWW-Authenticate line +; above. If you don't configure a WWW-Authenticate header, CouchDB will send +; Basic realm="server" in order to prevent you getting logged out. +; require_valid_user = false +secret = ea37364d6ff204bbcf8ae40574dd9292 + +[log] +;level = debug + +[log_level_by_module] +; In this section you can specify any of the four log levels 'none', 'info', +; 'error' or 'debug' on a per-module basis. See src/*/*.erl for various +; modules. +;couch_httpd = error + + +[os_daemons] +; For any commands listed here, CouchDB will attempt to ensure that +; the process remains alive. Daemons should monitor their environment +; to know when to exit. This can most easily be accomplished by exiting +; when stdin is closed. +;foo = /path/to/command -with args + +[daemons] +; enable SSL support by uncommenting the following line and supply the PEM's below. +; the default ssl port CouchDB listens on is 6984 +; httpsd = {couch_httpd, start_link, [https]} + +[ssl] +;cert_file = /full/path/to/server_cert.pem +;key_file = /full/path/to/server_key.pem +;password = somepassword +; set to true to validate peer certificates +verify_ssl_certificates = false +; Path to file containing PEM encoded CA certificates (trusted +; certificates used for verifying a peer certificate). May be omitted if +; you do not want to verify the peer. +;cacert_file = /full/path/to/cacertf +; The verification fun (optional) if not specified, the default +; verification fun will be used. +;verify_fun = {Module, VerifyFun} +; maximum peer certificate depth +ssl_certificate_max_depth = 1 + +; To enable Virtual Hosts in CouchDB, add a vhost = path directive. All requests to +; the Virual Host will be redirected to the path. In the example below all requests +; to http://example.com/ are redirected to /database. +; If you run CouchDB on a specific port, include the port number in the vhost: +; example.com:5984 = /database +[vhosts] +;example.com = /database/ + +[update_notification] +;unique notifier name=/full/path/to/exe -with "cmd line arg" + +; To create an admin account uncomment the '[admins]' section below and add a +; line in the format 'username = password'. When you next start CouchDB, it +; will change the password to a hash (so that your passwords don't linger +; around in plain-text files). You can add more admin accounts with more +; 'username = password' lines. Don't forget to restart CouchDB after +; changing this. +[admins] +;admin = mysecretpassword +admin = password diff --git a/docker/redis/Dockerfile b/docker/redis/Dockerfile new file mode 100644 index 00000000..b7abe64e --- /dev/null +++ b/docker/redis/Dockerfile @@ -0,0 +1,6 @@ +FROM redis + +# Scripts use a password +COPY redis.conf /usr/local/etc/redis/ +RUN mkdir -p /usr/local/var/db/redis +CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ] diff --git a/docker/redis/redis.conf b/docker/redis/redis.conf new file mode 100644 index 00000000..6d68b90d --- /dev/null +++ b/docker/redis/redis.conf @@ -0,0 +1,1293 @@ +# Redis configuration file example. +# +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Notice option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################## MODULES ##################################### + +# Load modules at startup. If the server is not able to load modules +# it will abort. It is possible to use multiple loadmodule directives. +# +# loadmodule /path/to/my_module.so +# loadmodule /path/to/other_module.so + +################################## NETWORK ##################################### + +# By default, if no "bind" configuration directive is specified, Redis listens +# for connections from all the network interfaces available on the server. +# It is possible to listen to just one or multiple selected interfaces using +# the "bind" configuration directive, followed by one or more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +# bind 127.0.0.1 ::1 +# +# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the +# internet, binding to all the interfaces is dangerous and will expose the +# instance to everybody on the internet. So by default we uncomment the +# following bind directive, that will force Redis to listen only into +# the IPv4 lookback interface address (this means Redis will be able to +# accept connections only from clients running into the same computer it +# is running). +# +# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES +# JUST COMMENT THE FOLLOWING LINE. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#bind 127.0.0.1 + +# Protected mode is a layer of security protection, in order to avoid that +# Redis instances left open on the internet are accessed and exploited. +# +# When protected mode is on and if: +# +# 1) The server is not binding explicitly to a set of addresses using the +# "bind" directive. +# 2) No password is configured. +# +# The server only accepts connections from clients connecting from the +# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain +# sockets. +# +# By default protected mode is enabled. You should disable it only if +# you are sure you want clients from other hosts to connect to Redis +# even if no authentication is configured, nor a specific set of interfaces +# are explicitly listed using the "bind" directive. +protected-mode yes + +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need an high backlog in order +# to avoid slow clients connections issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# Unix socket. +# +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 300 seconds, which is the new +# Redis default starting with Redis 3.2.1. +tcp-keepalive 300 + +################################# GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize no + +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous liveness pings back to your supervisor. +supervised no + +# If a pid file is specified, Redis writes it where specified at startup +# and removes it at exit. +# +# When the server runs non daemonized, no pid file is created if none is +# specified in the configuration. When the server is daemonized, the pid file +# is used even if not specified, defaulting to "/var/run/redis.pid". +# +# Creating a pid file is best effort: if Redis is not able to create it +# nothing bad happens, the server will start and run normally. +pidfile /var/run/redis_6379.pid + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +# By default Redis shows an ASCII art logo only when started to log to the +# standard output and if the standard output is a TTY. Basically this means +# that normally a logo is displayed only in interactive sessions. +# +# However it is possible to force the pre-4.0 behavior and always show a +# ASCII art logo in startup logs by setting the following option to yes. +always-show-logo yes + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behaviour will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# For default that's set to 'yes' as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir ./ + +################################# REPLICATION ################################# + +# Master-Slave replication. Use slaveof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of slaves. +# 2) Redis slaves are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition slaves automatically try to reconnect to masters +# and resynchronize with them. +# +# slaveof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the slave to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the slave request. +# +# masterauth + +# When a slave loses its connection with the master, or when the replication +# is still in progress, the slave can act in two different ways: +# +# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) if slave-serve-stale-data is set to 'no' the slave will reply with +# an error "SYNC with master in progress" to all the kind of commands +# but to INFO and SLAVEOF. +# +slave-serve-stale-data yes + +# You can configure a slave instance to accept writes or not. Writing against +# a slave instance may be useful to store some ephemeral data (because data +# written on a slave will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default slaves are read-only. +# +# Note: read only slaves are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only slave exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only slaves using 'rename-command' to shadow all the +# administrative / dangerous commands. +slave-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# ------------------------------------------------------- +# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY +# ------------------------------------------------------- +# +# New slaves and reconnecting slaves that are not able to continue the replication +# process just receiving differences, need to do what is called a "full +# synchronization". An RDB file is transmitted from the master to the slaves. +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the slaves incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to slave sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more slaves +# can be queued and served with the RDB file as soon as the current child producing +# the RDB file finishes its work. With diskless replication instead once +# the transfer starts, new slaves arriving will be queued and a new transfer +# will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple slaves +# will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the slaves. +# +# This is important since once the transfer starts, it is not possible to serve +# new slaves arriving, that will be queued for the next RDB transfer, so the server +# waits a delay in order to let more slaves arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# Slaves send PINGs to server in a predefined interval. It's possible to change +# this interval with the repl_ping_slave_period option. The default value is 10 +# seconds. +# +# repl-ping-slave-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of slave. +# 2) Master timeout from the point of view of slaves (data, pings). +# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-slave-period otherwise a timeout will be detected +# every time there is low traffic between the master and the slave. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the slave socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to slaves. But this can add a delay for +# the data to appear on the slave side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the slave side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and slaves are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# slave data when slaves are disconnected for some time, so that when a slave +# wants to reconnect again, often a full resync is not needed, but a partial +# resync is enough, just passing the portion of data the slave missed while +# disconnected. +# +# The bigger the replication backlog, the longer the time the slave can be +# disconnected and later be able to perform a partial resynchronization. +# +# The backlog is only allocated once there is at least a slave connected. +# +# repl-backlog-size 1mb + +# After a master has no longer connected slaves for some time, the backlog +# will be freed. The following option configures the amount of seconds that +# need to elapse, starting from the time the last slave disconnected, for +# the backlog buffer to be freed. +# +# Note that slaves never free the backlog for timeout, since they may be +# promoted to masters later, and should be able to correctly "partially +# resynchronize" with the slaves: hence they should always accumulate backlog. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The slave priority is an integer number published by Redis in the INFO output. +# It is used by Redis Sentinel in order to select a slave to promote into a +# master if the master is no longer working correctly. +# +# A slave with a low priority number is considered better for promotion, so +# for instance if there are three slaves with priority 10, 100, 25 Sentinel will +# pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the slave as not able to perform the +# role of master, so a slave with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +slave-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N slaves connected, having a lag less or equal than M seconds. +# +# The N slaves need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the slave, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough slaves +# are available, to the specified number of seconds. +# +# For example to require at least 3 slaves with a lag <= 10 seconds use: +# +# min-slaves-to-write 3 +# min-slaves-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-slaves-to-write is set to 0 (feature disabled) and +# min-slaves-max-lag is set to 10. + +# A Redis master is able to list the address and port of the attached +# slaves in different ways. For example the "INFO replication" section +# offers this information, which is used, among other tools, by +# Redis Sentinel in order to discover slave instances. +# Another place where this info is available is in the output of the +# "ROLE" command of a master. +# +# The listed IP and address normally reported by a slave is obtained +# in the following way: +# +# IP: The address is auto detected by checking the peer address +# of the socket used by the slave to connect with the master. +# +# Port: The port is communicated by the slave during the replication +# handshake, and is normally the port that the slave is using to +# list for connections. +# +# However when port forwarding or Network Address Translation (NAT) is +# used, the slave may be actually reachable via different IP and port +# pairs. The following two options can be used by a slave in order to +# report to its master a specific set of IP and port, so that both INFO +# and ROLE will report those values. +# +# There is no need to use both the options if you need to override just +# the port or the IP address. +# +# slave-announce-ip 5.5.5.5 +# slave-announce-port 1234 + +################################## SECURITY ################################### + +# Require clients to issue AUTH before processing any other +# commands. This might be useful in environments in which you do not trust +# others with access to the host running redis-server. +# +# This should stay commented out for backward compatibility and because most +# people do not need auth (e.g. they run their own servers). +# +# Warning: since Redis is pretty fast an outside user can try up to +# 150k passwords per second against a good box. This means that you should +# use a very strong password otherwise it will be very easy to break. +# +requirepass password + +# Command renaming. +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to slaves may cause problems. + +################################### CLIENTS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# maxclients 10000 + +############################## MEMORY MANAGEMENT ################################ + +# Set a memory usage limit to the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU or LFU cache, or to +# set a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have slaves attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the slaves are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of slaves is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have slaves attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for slave +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select among five behaviors: +# +# volatile-lru -> Evict using approximated LRU among the keys with an expire set. +# allkeys-lru -> Evict any key using approximated LRU. +# volatile-lfu -> Evict using approximated LFU among the keys with an expire set. +# allkeys-lfu -> Evict any key using approximated LFU. +# volatile-random -> Remove a random key among the ones with an expire set. +# allkeys-random -> Remove a random key, any key. +# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) +# noeviction -> Don't evict anything, just return an error on write operations. +# +# LRU means Least Recently Used +# LFU means Least Frequently Used +# +# Both LRU, LFU and volatile-ttl are implemented using approximated +# randomized algorithms. +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. For default Redis will check five keys and pick the one that was +# used less recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs more CPU. 3 is faster but not very accurate. +# +# maxmemory-samples 5 + +############################# LAZY FREEING #################################### + +# Redis has two primitives to delete keys. One is called DEL and is a blocking +# deletion of the object. It means that the server stops processing new commands +# in order to reclaim all the memory associated with an object in a synchronous +# way. If the key deleted is associated with a small object, the time needed +# in order to execute the DEL command is very small and comparable to most other +# O(1) or O(log_N) commands in Redis. However if the key is associated with an +# aggregated value containing millions of elements, the server can block for +# a long time (even seconds) in order to complete the operation. +# +# For the above reasons Redis also offers non blocking deletion primitives +# such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and +# FLUSHDB commands, in order to reclaim memory in background. Those commands +# are executed in constant time. Another thread will incrementally free the +# object in the background as fast as possible. +# +# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled. +# It's up to the design of the application to understand when it is a good +# idea to use one or the other. However the Redis server sometimes has to +# delete keys or flush the whole database as a side effect of other operations. +# Specifically Redis deletes objects independently of a user call in the +# following scenarios: +# +# 1) On eviction, because of the maxmemory and maxmemory policy configurations, +# in order to make room for new data, without going over the specified +# memory limit. +# 2) Because of expire: when a key with an associated time to live (see the +# EXPIRE command) must be deleted from memory. +# 3) Because of a side effect of a command that stores data on a key that may +# already exist. For example the RENAME command may delete the old key +# content when it is replaced with another one. Similarly SUNIONSTORE +# or SORT with STORE option may delete existing keys. The SET command +# itself removes any old content of the specified key in order to replace +# it with the specified string. +# 4) During replication, when a slave performs a full resynchronization with +# its master, the content of the whole database is removed in order to +# load the RDB file just transfered. +# +# In all the above cases the default is to delete objects in a blocking way, +# like if DEL was called. However you can configure each case specifically +# in order to instead release memory in a non-blocking way like if UNLINK +# was called, using the following configuration directives: + +lazyfree-lazy-eviction no +lazyfree-lazy-expire no +lazyfree-lazy-server-del no +slave-lazy-flush no + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +# When rewriting the AOF file, Redis is able to use an RDB preamble in the +# AOF file for faster rewrites and recoveries. When this option is turned +# on the rewritten AOF file is composed of two different stanzas: +# +# [RDB file][AOF tail] +# +# When loading Redis recognizes that the AOF file starts with the "REDIS" +# string and loads the prefixed RDB file, and continues loading the AOF +# tail. +# +# This is currently turned off by default in order to avoid the surprise +# of a format change, but will at some point be used as the default. +aof-use-rdb-preamble no + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet called write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### +# +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# WARNING EXPERIMENTAL: Redis Cluster is considered to be stable code, however +# in order to mark it as "mature" we need to wait for a non trivial percentage +# of users to deploy it in production. +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A slave of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a slave to actually have an exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple slaves able to failover, they exchange messages +# in order to try to give an advantage to the slave with the best +# replication offset (more data from the master processed). +# Slaves will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single slave computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the slave will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a slave will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * slave-validity-factor) + repl-ping-slave-period +# +# So for example if node-timeout is 30 seconds, and the slave-validity-factor +# is 10, and assuming a default repl-ping-slave-period of 10 seconds, the +# slave will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large slave-validity-factor may allow slaves with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a slave at all. +# +# For maximum availability, it is possible to set the slave-validity-factor +# to a value of 0, which means, that slaves will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-slave-validity-factor 10 + +# Cluster slaves are able to migrate to orphaned masters, that are masters +# that are left without working slaves. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working slaves. +# +# Slaves migrate to orphaned masters only if there are still at least a +# given number of other working slaves for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a slave +# will migrate only if there is at least 1 other working slave for its master +# and so forth. It usually reflects the number of slaves you want for every +# master in your cluster. +# +# Default is 1 (slaves migrate only if their masters remain with at least +# one slave). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least an hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +########################## CLUSTER DOCKER/NAT support ######################## + +# In certain deployments, Redis Cluster nodes address discovery fails, because +# addresses are NAT-ted or because ports are forwarded (the typical case is +# Docker and other containers). +# +# In order to make Redis Cluster working in such environments, a static +# configuration where each node knows its public address is needed. The +# following two options are used for this scope, and are: +# +# * cluster-announce-ip +# * cluster-announce-port +# * cluster-announce-bus-port +# +# Each instruct the node about its address, client port, and cluster message +# bus port. The information is then published in the header of the bus packets +# so that other nodes will be able to correctly map the address of the node +# publishing the information. +# +# If the above options are not used, the normal Redis Cluster auto-detection +# will be used instead. +# +# Note that when remapped, the bus port may not be at the fixed offset of +# clients port + 10000, so you can specify any port and bus-port depending +# on how they get remapped. If the bus-port is not set, a fixed offset of +# 10000 will be used as usually. +# +# Example: +# +# cluster-announce-ip 10.1.1.5 +# cluster-announce-port 6379 +# cluster-announce-bus-port 6380 + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Lists are also encoded in a special way to save a lot of space. +# The number of entries allowed per internal list node can be specified +# as a fixed maximum size or a maximum number of elements. +# For a fixed maximum size, use -5 through -1, meaning: +# -5: max size: 64 Kb <-- not recommended for normal workloads +# -4: max size: 32 Kb <-- not recommended +# -3: max size: 16 Kb <-- probably not recommended +# -2: max size: 8 Kb <-- good +# -1: max size: 4 Kb <-- good +# Positive numbers mean store up to _exactly_ that number of elements +# per list node. +# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), +# but if your use case is unique, adjust the settings as necessary. +list-max-ziplist-size -2 + +# Lists may also be compressed. +# Compress depth is the number of quicklist ziplist nodes from *each* side of +# the list to *exclude* from compression. The head and tail of the list +# are always uncompressed for fast push/pop operations. Settings are: +# 0: disable all list compression +# 1: depth 1 means "don't start compressing until after 1 node into the list, +# going from either the head or tail" +# So: [head]->node->node->...->node->[tail] +# [head], [tail] will always be uncompressed; inner nodes will compress. +# 2: [head]->[next]->node->node->...->node->[prev]->[tail] +# 2 here means: don't compress head or head->next or tail->prev or tail, +# but compress all nodes between them. +# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] +# etc. +list-compress-depth 0 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# slave -> slave clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and slave clients, since +# subscribers and slaves receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit slave 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes + +# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good +# idea to start with the default settings and only change them after investigating +# how to improve the performances and how the keys LFU change over time, which +# is possible to inspect via the OBJECT FREQ command. +# +# There are two tunable parameters in the Redis LFU implementation: the +# counter logarithm factor and the counter decay time. It is important to +# understand what the two parameters mean before changing them. +# +# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis +# uses a probabilistic increment with logarithmic behavior. Given the value +# of the old counter, when a key is accessed, the counter is incremented in +# this way: +# +# 1. A random number R between 0 and 1 is extracted. +# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1). +# 3. The counter is incremented only if R < P. +# +# The default lfu-log-factor is 10. This is a table of how the frequency +# counter changes with a different number of accesses with different +# logarithmic factors: +# +# +--------+------------+------------+------------+------------+------------+ +# | factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits | +# +--------+------------+------------+------------+------------+------------+ +# | 0 | 104 | 255 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 1 | 18 | 49 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 10 | 10 | 18 | 142 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 100 | 8 | 11 | 49 | 143 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# +# NOTE: The above table was obtained by running the following commands: +# +# redis-benchmark -n 1000000 incr foo +# redis-cli object freq foo +# +# NOTE 2: The counter initial value is 5 in order to give new objects a chance +# to accumulate hits. +# +# The counter decay time is the time, in minutes, that must elapse in order +# for the key counter to be divided by two (or decremented if it has a value +# less <= 10). +# +# The default value for the lfu-decay-time is 1. A Special value of 0 means to +# decay the counter every time it happens to be scanned. +# +# lfu-log-factor 10 +# lfu-decay-time 1 + +########################### ACTIVE DEFRAGMENTATION ####################### +# +# WARNING THIS FEATURE IS EXPERIMENTAL. However it was stress tested +# even in production and manually tested by multiple engineers for some +# time. +# +# What is active defragmentation? +# ------------------------------- +# +# Active (online) defragmentation allows a Redis server to compact the +# spaces left between small allocations and deallocations of data in memory, +# thus allowing to reclaim back memory. +# +# Fragmentation is a natural process that happens with every allocator (but +# less so with Jemalloc, fortunately) and certain workloads. Normally a server +# restart is needed in order to lower the fragmentation, or at least to flush +# away all the data and create it again. However thanks to this feature +# implemented by Oran Agra for Redis 4.0 this process can happen at runtime +# in an "hot" way, while the server is running. +# +# Basically when the fragmentation is over a certain level (see the +# configuration options below) Redis will start to create new copies of the +# values in contiguous memory regions by exploiting certain specific Jemalloc +# features (in order to understand if an allocation is causing fragmentation +# and to allocate it in a better place), and at the same time, will release the +# old copies of the data. This process, repeated incrementally for all the keys +# will cause the fragmentation to drop back to normal values. +# +# Important things to understand: +# +# 1. This feature is disabled by default, and only works if you compiled Redis +# to use the copy of Jemalloc we ship with the source code of Redis. +# This is the default with Linux builds. +# +# 2. You never need to enable this feature if you don't have fragmentation +# issues. +# +# 3. Once you experience fragmentation, you can enable this feature when +# needed with the command "CONFIG SET activedefrag yes". +# +# The configuration parameters are able to fine tune the behavior of the +# defragmentation process. If you are not sure about what they mean it is +# a good idea to leave the defaults untouched. + +# Enabled active defragmentation +# activedefrag yes + +# Minimum amount of fragmentation waste to start active defrag +# active-defrag-ignore-bytes 100mb + +# Minimum percentage of fragmentation to start active defrag +# active-defrag-threshold-lower 10 + +# Maximum percentage of fragmentation at which we use maximum effort +# active-defrag-threshold-upper 100 + +# Minimal effort for defrag in CPU percentage +# active-defrag-cycle-min 25 + +# Maximal effort for defrag in CPU percentage +# active-defrag-cycle-max 75 + diff --git a/docs/Docker.md b/docs/Docker.md new file mode 100644 index 00000000..cf528fe7 --- /dev/null +++ b/docs/Docker.md @@ -0,0 +1,59 @@ + +Running a local development instance of hook.io is simple. + +First, you'll need to install [Docker](https://docs.docker.com/engine/installation/). + +### Docker??? + +It's important to note that hook.io is only using Docker for helping developers get setup with a local development instance. We are not using Docker in production and do not recommend using our Docker image and default configurations in production ( as there will be almost no service isolation ). + +### Installing hook.io + +You may directly use your computer with Docker, or use `docker-machine`: + +```bash +docker-machine create hook.io +eval $(docker-machine env hook.io) +``` + +Once `docker-machine` is installed and running, perform the following commands: + +``` bash +git clone https://github.com/bigcompany/hook.io +cd hook.io +docker-compose build +docker-compose up +``` + +This should start the following services: + + - 1 hook.io front-end + - 1 hook.io load balancer + - 1 hook.io worker + - 1 couchdb server + - 1 redis server + +*Note: The `cron` and `hpm` services are not currently included in our docker image. If you require using these services locally they should be easy to add.* + +### Configure the local running hook.io instance + +Once the services are started, you'll still need to run one more command. + +To find the current address of the Docker machine run: `docker-machine ip hook.io`. + +```bash +curl http://{{your_docker_machine_ip}}/_admin?setBase=1 +``` + +If directly running from your computer, just use `localhost` for `{{your_docker_machine_ip}}`. + +You should see something like: + +``` +set baseUrl to: http://{{your_docker_machine_ip}}:80 +``` + +Without running this last line, site formatting (CSS), most HTTP redirects, and AJAX gateways in the system will not work. + +You may now browse to `http://{{your_docker_machine_ip}}` to access your local hook.io instance. + diff --git a/lib/broadcast/broadcast.js b/lib/broadcast/broadcast.js new file mode 100644 index 00000000..885d9740 --- /dev/null +++ b/lib/broadcast/broadcast.js @@ -0,0 +1,359 @@ +/* + + broadcast/broadcast.js + + front-facing event broadcasting service for hook.io + responsible for handling broadcast events to websockets clients and vice versa + acts a bridge between hook.io webhooks input and connected websocket output + exists as separate service from load-balancer to ensure scalibity + +*/ + +var resource = require('resource'); +resource.setMaxListeners(999); +process.setMaxListeners(999); + +var secrets = {}; +var config = require('../../config'); + +if (process.platform === "darwin") { + config.sslKeyDirectory = __dirname + '/../../ssl/'; + config.chrootDirectory = '/Users/chroot'; + config.redis.host = "0.0.0.0"; + config.couch.host = "0.0.0.0"; +} + +var request = require("hyperquest"); +var rrequest = require('request'); +var http = require('resource-http'); +var colors = require('colors'); +var fs = require('fs'); + +var server = {}; +module['exports'] = server; + +var sslKeyDirectory = config.sslKeyDirectory; + +server.start = function start (opts, cb) { + + var sslPath, key, cert, ca; + if (process.platform === "darwin") { + /* Removed, migrated to godaddy to letsencrypt */ + key = fs.readFileSync(sslKeyDirectory + "server.key").toString(); + cert = fs.readFileSync(sslKeyDirectory + "server.crt").toString(); + ca = [fs.readFileSync(sslKeyDirectory + 'gd1.crt').toString(), fs.readFileSync(sslKeyDirectory + 'gd2.crt').toString(), fs.readFileSync(sslKeyDirectory + 'gd3.crt').toString()] + } else { + sslPath = '/etc/letsencrypt/live/hook.io/'; + key = fs.readFileSync(sslPath + "privkey.pem").toString(); + cert = fs.readFileSync(sslPath + "fullchain.pem").toString(); + } + + // sometimes in development you might mix and match a common ssl for projects + // comment this line out for production usage + process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 + + var secretConfig; + + var defaultConfig = { + "port": 9998, + "proxyPort": 9998, + "host": "0.0.0.0", + "https": true, + "roots": [ + "ws.hookio", + "0.0.0.0", + "localhost", + "ws.hook.io", + ], + noSession: true, + port: config.broadcast.port, + wss: true, // enables websocket server + enableUploads: false, + // host: config.balancer.host, + roots: config.broadcast.roots, + // passport: true, + // port: config.balancer.port, + // https: config.balancer.https, + https: false, + cert: cert, + key: key, + // ca: ca, + /* + session: config.web.session, + redis: config.balancer.redis, + */ + cacheView: config.cacheView, + sslRequired: false // will not force ssl connections on custom domains / subdomains + }; + + startServer(); + /* + // get remote configuration for server from oys + client.secret.get(['dev-load-balancer', 'dev-pool-webs'], function (err, _secrets) { + if (err) { + console.log('balancer: Remote secrets were not fetched, using default static configuration.'); + secrets = config; + } else { + // use discovered secrets as config + secrets = _secrets; + secretConfig = secrets['dev-load-balancer']; + //websPool = secrets['dev-pool-webs']; + // merge secretConfig over default config + for (var p in secretConfig) { + defaultConfig[p] = secretConfig[p]; + } + } + startServer(); + }); + */ + + function startServer () { + // TODO: Need to not start listening server in resource-http + // resource-http should return server and app instance to modify, + // after additional routes are added, then we can start listening. + // This is important due to cluster module thinking nodes are online and ready, but they are still adding new routes + // Until fixed, this could cause potential issue with zero-downtime updates of load balancer ( since nodes are coming online before additional routes are ready ) + http.listen(defaultConfig, function (err, app) { + + if (err) { + // any errors starting the load-balancer should result in process crash / do not start server + throw err; + } + + server.app = app; + + if (app.wss) { + app.wss.on('error', function errorHandler(err) { + console.log('warning wss.error emitted', err.message) + }) + + app.wss.on('connection', function connection (ws) { + + // verify or reject connection based on auth + var target = ws.upgradeReq.url; + + // console.log('incoming connection ->', target) + ws.on('error', function errorHandler (error) { + console.log('warning ws.error emitted', error.message) + proxyWebsocketMessage(ws, target, message) // is this a mistake? should it be error that is passed? message is going to be undefined everytime? + }); + ws.on('message', function incoming(message) { + console.log('incoming data stream'.green, message) + proxyWebsocketMessage(ws, target, message) + }); + }); + } + + if (app.secureWss) { + app.secureWss.on('error', function errorHandler (err) { + console.log('warning wss.error emitted', err.message) + }) + + app.secureWss.on('connection', function connection(ws) { + var target = ws.upgradeReq.url; + // console.log('incoming secure connection ->', target) + ws.on('error', function errorHandler(error) { + console.log('warning ws.error emitted', error.message) + proxyWebsocketMessage(ws, target, message) + }); + ws.on('message', function incoming(message) { + // console.log('incoming secure data stream', message) + proxyWebsocketMessage(ws, target, message) + }); + }); + } + + var request = require('request'); + + function proxyWebsocketMessage (ws, target, message) { + // send message out to all connected clients + + /* */ + // Broadcast to all. + /* + wss.broadcast = function broadcast(data) { + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); + }; + */ + + // Broadcast to everyone else. + app.wss.clients.forEach(function each(client) { + // do not send to self, only send to ready clients + if (client !== ws && client.readyState === "WebSocket.OPEN") { + console.log('SENDING THE MESSAGE', data); + client.send(message); + } + client.send(message); + + }); + + console.log('attenotubg ti broadcast message', message) + return; + try { + // attempt to parse message as JSON + message = JSON.parse(message); + } catch (err) { + // if the JSON parse fails, create new object and store string as `body` property + // note: this is somewhat hacky, but it does provide a functional api...feedback would be appreciated + message = { body: message }; + } + var w = config.pools.worker.pop(); + config.pools.worker.unshift(w); + var _url = 'http://' + w.host + ':' + w.port + target; + // console.log(new Date().toString() + ' - about to use worker', _url); + rrequest.post(_url, { json: message }, function (err, res, body) { + if (err) { + try { + ws.send(JSON.stringify({ "status": "error", "message": err.message }, true, 2)); + } catch (err) { + console.log('warning ws.send failed', err.message) + } + } else { + try { + ws.send(JSON.stringify(body, true, 2)); + } catch (err) { + console.log('warning ws.send failed', err.message) + } + } + }); + }; + + // Note: Due to the structure of the application, certain API / Web routes need to be registered here ( in the load-balancer ) to short-circuit the processing of the request + // If these routes are not registered, the requests will be passed to the worker pool as a microservice request ( with extra url paramters ) + // These special routes are reserved words used for helping manage certain properties / methods on the service by using a reserved url path + // Without this lookup table, we'd have to take a performance hit somewhere to redirect ( such as redirecting inside worker). + // This look-up table will minimize request processing time + var webRoutes = [ + '/account/:section', + '/auth/qrcode', + '/blog/:article', + '/files', + '/files/:method', +// '/api-gateway', +// '/api-gateway/:method', + '/keys/:method', + '/gateway/logs', + '/packages/:provider', + '/packages/:provider/:method', + '/login/:provider', + '/login/:provider/:callback', + '/datastore/:method', + '/:owner/:hook/admin', + '/:owner/:hook/_admin', + '/:owner/:hook/fork', + '/:owner/:hook/_fork', + '/:owner/events', + '/:owner/:hook/_rev', + '/:owner/:hook/_rev/:revision', + '/:owner/:hook/_src', + '/:owner/:hook/source', + '/:owner/:hook/view', + '/:owner/:hook/presenter', + '/:owner/:hook/_presenter', + '/:owner/:hook/logs', + '/:owner/:hook/package', + '/:owner/:hook/resource', + '/:owner/:hook/delete', + // '/:owner/:hook/refresh', + '/metrics/:owner/:metric', + '/metrics/:owner/:hook/:metric', + '/components/:component', + '/editor/:page', + '/emails/:email', + ]; + + webRoutes.forEach(function (r) { + app.all(r, server.handle404); + }); + + var hookHandler = require('../server/routeHandlers/hook'); + + function hookHandler (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + hookHandler(req, res); + }; + + app.all('/:owner/:hook', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + hookHandler(req, res) + }); + + app.all('/:owner/:hook/*', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + hookHandler(req, res); + }); + + app.get('/_cluster', function (req, res) { + if (typeof process.send !== 'function') { + return res.json({ error: true, message: 'process.send function not detected. is cluster mode enabled?'}) + } + if (req.resource.params.super_private_key === config.superadmin.super_private_key) { + var clusterQuery = {}; + process.once('message', function(message) { + clusterQuery = message; + return res.json(message); + }); + process.send({ event: 'query' }); + } else { + res.end('invalid credentials'); + } + }); + + app.get('/_restart', function (req, res) { + if (typeof process.send !== 'function') { + return res.json({ error: true, message: 'process.send function not detected. is cluster mode enabled?'}) + } + if (req.resource.params.super_private_key === config.superadmin.super_private_key) { + process.send({ event: 'restart' }) + res.end('restart nodes'); + } else { + res.end('invalid credentials'); + } + }); + + app.use(server.handle404); + cb(err, app); + }); + } + +}; + +server.handle404 = function handle404 (req, res) { + // console.log('falling back to 404'.blue, req.url, req.host); + // Remark: Available secrets should now automatically merge into config scope + // var remoteHandler = require('run-remote-service')({ pool: secrets['dev-pool-webs'] }); + var remoteHandler = require('run-remote-service')({ + pool: config.pools.web, + errorHandler: function (err, req, res) { + var errStr = 'Error communicating with ' + req.url + '\n\n'; + errStr += 'The streaming connection errored in recieving data.\n\n'; + errStr += 'Please copy and paste this entire error message to: ' + config.app.adminEmail + '.\n\n'; + if (req.jsonResponse) { + return res.json({ + error: true, + message: errStr, + result: { + url: req.url, + host: req.host, + time: new Date() + } + }); + } else { + res.write(errStr); + res.end(err.stack) + } + } + }); + + remoteHandler(req, res, function(){ + // console.log('complete'); + // next callback is not needed as response should have ended + }); +}; \ No newline at end of file diff --git a/lib/helpers/numberWithCommas.js b/lib/helpers/numberWithCommas.js new file mode 100644 index 00000000..3af578ae --- /dev/null +++ b/lib/helpers/numberWithCommas.js @@ -0,0 +1,5 @@ +function numberWithCommas (x) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); +} + +module.exports = numberWithCommas; \ No newline at end of file diff --git a/lib/load-balancer/load-balancer.js b/lib/load-balancer/load-balancer.js new file mode 100644 index 00000000..46c58313 --- /dev/null +++ b/lib/load-balancer/load-balancer.js @@ -0,0 +1,461 @@ +/* + + load-balancer/index.js + + front-facing load-balancer service for hook.io + responsible for routing requests to either `web` or `worker` instances + handles no direct logic for response processing, only routing + +*/ + +var big = require('big'); +// application has a lot of listeners +big.resource.setMaxListeners(999); +process.setMaxListeners(999); +big.mode = "Online"; +var secrets = {}; +var config = require('../../config'); +var websPool = {}; + +if (process.platform === "darwin") { + config.sslKeyDirectory = __dirname + '/../../ssl/'; + config.chrootDirectory = '/Users/chroot'; + config.redis.host = "0.0.0.0"; + config.couch.host = "0.0.0.0"; + config.worker.publicIP = "0.0.0.0"; + config.web.host = "0.0.0.0"; +} + +var request = require("hyperquest"); +var rrequest = require('request'); +var http = require('resource-http'); +var domain = require('../resources/domain'); +var cache = require('../resources/cache'); +var alerts = require('../resources/alerts/alerts'); + +var colors = require('colors'); +var fs = require('fs'); +var pool = config.pools.worker; + +var server = {}; +module['exports'] = server; + +var sslKeyDirectory = config.sslKeyDirectory; + +server.start = function start (opts, cb) { + + var sslPath, key, cert, ca; + if (process.platform === "darwin") { + /* Removed, migrated to godaddy to letsencrypt */ + key = fs.readFileSync(sslKeyDirectory + "localhost.key").toString(); + cert = fs.readFileSync(sslKeyDirectory + "localhost.crt").toString(); + //ca = [fs.readFileSync(sslKeyDirectory + 'gd1.crt').toString(), fs.readFileSync(sslKeyDirectory + 'gd2.crt').toString(), fs.readFileSync(sslKeyDirectory + 'gd3.crt').toString()] + } else { + sslPath = '/etc/letsencrypt/live/hook.io/'; + key = fs.readFileSync(sslPath + "privkey.pem").toString(); + cert = fs.readFileSync(sslPath + "fullchain.pem").toString(); + } + + // sometimes in development you might mix and match a common ssl for projects + // comment this line out for production usage + process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 + + domain.persist(config.couch); + + var secretConfig; + + var defaultConfig = { + "port": 9999, + "proxyPort": 9999, + "host": "0.0.0.0", + "https": true, + "roots": [ + "hookio", + "0.0.0.0", + "localhost", + "hook.io", + "www.hook.io" + ], + noSession: true, + port: config.balancer.port, + wss: true, // enables websocket server + enableUploads: false, + // host: config.balancer.host, + roots: config.balancer.roots, + passport: true, + // port: config.balancer.port, + https: config.balancer.https, + cert: cert, + key: key, + // ca: ca, + /* + session: config.web.session, + redis: config.balancer.redis, + */ + cacheView: config.cacheView, + customDomains: true, + sslRequired: false // will not force ssl connections on custom domains / subdomains + }; + + startServer(); + /* + // get remote configuration for server from oys + client.secret.get(['dev-load-balancer', 'dev-pool-webs'], function (err, _secrets) { + if (err) { + console.log('balancer: Remote secrets were not fetched, using default static configuration.'); + secrets = config; + } else { + // use discovered secrets as config + secrets = _secrets; + secretConfig = secrets['dev-load-balancer']; + //websPool = secrets['dev-pool-webs']; + // merge secretConfig over default config + for (var p in secretConfig) { + defaultConfig[p] = secretConfig[p]; + } + } + startServer(); + }); + */ + + function startServer () { + // TODO: Need to not start listening server in resource-http + // resource-http should return server and app instance to modify, + // after additional routes are added, then we can start listening. + // This is important due to cluster module thinking nodes are online and ready, but they are still adding new routes + // Until fixed, this could cause potential issue with zero-downtime updates of load balancer ( since nodes are coming online before additional routes are ready ) + http.listen(defaultConfig, function (err, app) { + + if (err) { + // any errors starting the load-balancer should result in process crash / do not start server + throw err; + } + + server.app = app; + big.server = server; + if (app.wss) { + app.wss.on('error', function errorHandler(err) { + console.log('warning wss.error emitted', err.message) + }) + + app.wss.on('connection', function connection(ws) { + var target = ws.upgradeReq.url; + // console.log('incoming connection ->', target) + ws.on('error', function errorHandler(error) { + console.log('warning ws.error emitted', error.message) + proxyWebsocketMessage(ws, target, message) + }); + ws.on('message', function incoming(message) { + // console.log('incoming data stream', message) + proxyWebsocketMessage(ws, target, message) + }); + }); + } + + if (app.secureWss) { + app.secureWss.on('error', function errorHandler(err) { + console.log('warning wss.error emitted', err.message) + }) + + app.secureWss.on('connection', function connection(ws) { + var target = ws.upgradeReq.url; + // console.log('incoming secure connection ->', target) + ws.on('error', function errorHandler(error) { + console.log('warning ws.error emitted', error.message) + proxyWebsocketMessage(ws, target, message) + }); + ws.on('message', function incoming(message) { + // console.log('incoming secure data stream', message) + proxyWebsocketMessage(ws, target, message) + }); + }); + } + + var request = require('request'); + + function proxyWebsocketMessage (ws, target, message) { + try { + // attempt to parse message as JSON + message = JSON.parse(message); + } catch (err) { + // if the JSON parse fails, create new object and store string as `body` property + // note: this is somewhat hacky, but it does provide a functional api...feedback would be appreciated + message = { body: message }; + } + var w = config.pools.worker.pop(); + config.pools.worker.unshift(w); + var _url = 'http://' + w.host + ':' + w.port + target; + // console.log(new Date().toString() + ' - about to use worker', _url); + rrequest.post(_url, { json: message }, function (err, res, body) { + if (err) { + try { + ws.send(JSON.stringify({ "status": "error", "message": err.message }, true, 2)); + } catch (err) { + console.log('warning ws.send failed', err.message) + } + } else { + try { + ws.send(JSON.stringify(body, true, 2)); + } catch (err) { + console.log('warning ws.send failed', err.message) + } + } + }); + }; + + // Note: Due to the structure of the application, certain API / Web routes need to be registered here ( in the load-balancer ) to short-circuit the processing of the request + // If these routes are not registered, the requests will be passed to the worker pool as a microservice request ( with extra url paramters ) + // These special routes are reserved words used for helping manage certain properties / methods on the service by using a reserved url path + // Without this lookup table, we'd have to take a performance hit somewhere to redirect ( such as redirecting inside worker). + // This look-up table will minimize request processing time + var webRoutes = [ + '/account/:section', + '/account/billing/:section', + '/blog/:article', + '/files', + '/files/:method', +// '/api-gateway', +// '/api-gateway/:method', + '/keys/:method', + '/cron/:method', + '/cron/:owner/:name', + '/cron/:owner/:name/:method', + '/gateway/logs', + '/packages/:provider', + '/packages/:provider/:method', + '/login/:provider', + '/stripe/webhook', + '/login/:provider/:callback', + '/datastore/:method', + '/:owner/:hook/admin', + '/:owner/:hook/_admin', + '/:owner/:hook/fork', + '/:owner/:hook/_fork', + '/:owner/events', + '/:owner/:hook/_rev', + '/:owner/:hook/_rev/:revision', + '/:owner/:hook/_src', + '/:owner/:hook/source', + '/:owner/:hook/view', + '/:owner/:hook/presenter', + '/:owner/:hook/_presenter', + '/:owner/:hook/logs', + '/:owner/:hook/package', + '/:owner/:hook/resource', + '/:owner/:hook/delete', + // '/:owner/:hook/refresh', + '/metrics/:owner/:metric', + '/metrics/:owner/:hook/:metric', + '/components/:component', + '/editor/:page', + '/emails/:email', + ]; + + webRoutes.forEach(function (r) { + app.all(r, server.handle404); + }); + + var hookHandler = require('../server/routeHandlers/hook'); + + function hookHandler (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + hookHandler(req, res); + }; + + app.all('/:owner/:hook', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + hookHandler(req, res) + }); + + app.all('/:owner/:hook/*', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + hookHandler(req, res); + }); + + app.get('/_cluster', function (req, res) { + if (typeof process.send !== 'function') { + return res.json({ error: true, message: 'process.send function not detected. is cluster mode enabled?'}) + } + if (req.resource.params.super_private_key === config.superadmin.super_private_key) { + var clusterQuery = {}; + process.once('message', function(message) { + clusterQuery = message; + return res.json(message); + }); + process.send({ event: 'query' }); + } else { + res.end('invalid credentials'); + } + }); + + app.get('/_restart', function (req, res) { + if (typeof process.send !== 'function') { + return res.json({ error: true, message: 'process.send function not detected. is cluster mode enabled?'}) + } + if (req.resource.params.super_private_key === config.superadmin.super_private_key) { + process.send({ event: 'restart' }) + res.end('restart nodes'); + } else { + res.end('invalid credentials'); + } + }); + app.use(server.handle404); + updateBalancingTable(function (err) { + // setTimeout to update the balancing table every 20 seconds + loopUpdates(); + cb(err, app); + }); + }); + } + +}; + +function loopUpdates () { + setTimeout(function () { + updateBalancingTable(function(err){ + if (err) { + console.log('error updating balancing table', err) + } + loopUpdates(); + }) + }, 1000); +}; + +function updateBalancingTable (cb) { + // get latest config from redis cache + cache.smembers('pools.web', function (err, webs) { + if (err) { + console.log('cant fetch pools.web', err); + return cb(err); + } + // console.log('got pools.web', webs.length); + if (webs !== null && typeof webs === "object") { + // if the pool is empty, simply assign the current webs + if (config.pools.web.length === 0) { + config.pools.web = webs; + } else { + // if the pool already contains web instances, + // for every existing web node... + config.pools.web.forEach(function(oldWeb, i){ + var found = false; + // check to see if it exists in the incoming table + webs.forEach(function(newWeb){ + if (newWeb && oldWeb && oldWeb.host === newWeb.host && oldWeb.port === newWeb.port) { + // we found a matching web already in the load balancer pool + found = true; + } else { + } + }); + if (!found) { + // if we didn't find a match, assume it's expired and remove it + console.log('remove web node from pool', oldWeb); + config.pools.web.splice(i, 1); + } + }); + // for every incoming web node + webs.forEach(function(newWeb){ + var found = false; + // check against all existing webs + config.pools.web.forEach(function(oldWeb, i){ + // console.log(newWeb, oldWeb) + if (newWeb && oldWeb && oldWeb.host === newWeb.host && oldWeb.port === newWeb.port) { + // we found a matching web already in the load balancer pool + found = true; + } + }); + if (!found) { + // if we didn't find a match, assume it's a new web and put it in the end + config.pools.web.unshift(newWeb); + } + }); + } + } + + cache.smembers('pools.worker', function (err, workers) { + if (err) { + console.log('cant fetch pools.worker', err); + return cb(err); + } + // console.log('got pools.worker', workers.length); + if (workers !== null && typeof workers === "object") { + var found = false; + // only update workers if they are new, add them to the end of the list + if (config.pools.worker.length === 0) { + config.pools.worker = workers; + return cb(null); + } + + // for every existing worker node + config.pools.worker.forEach(function(oldWorker, i){ + var found = false; + // check to see if it exists in the incoming table + workers.forEach(function(newWorker){ + if (newWorker && oldWorker && oldWorker.host === newWorker.host && oldWorker.port === newWorker.port) { + // we found a matching web already in the load balancer pool + found = true; + } + }); + if (!found) { + // if we didn't find a match, assume it's expired and remove it + console.log('remove worker node from pool', oldWorker); + config.pools.worker.splice(i, 1); + //config.pools.worker.splice(i, 0); + } + }); + + // for every incoming worker + workers.forEach(function(newWorker){ + var found = false; + // check against all existing workers + config.pools.worker.forEach(function(oldWorker){ + // console.log(newWorker, oldWorker) + if (newWorker && oldWorker && oldWorker.host === newWorker.host && oldWorker.port === newWorker.port) { + // we found a matching worker already in the load balancer pool + found = true; + } + }); + if (!found) { + // if we didn't find a match, assume it's a new worker and put it in the end + config.pools.worker.unshift(newWorker); + } + }); + } + cb(null); + }); + }); +} + +server.handle404 = function handle404 (req, res) { + // console.log('falling back to 404'.blue, req.url, req.host); + // Remark: Available secrets should now automatically merge into config scope + // var remoteHandler = require('run-remote-service')({ pool: secrets['dev-pool-webs'] }); + var remoteHandler = require('run-remote-service')({ + pool: config.pools.web, + errorHandler: function (err, req, res) { + var errStr = 'Error communicating with ' + req.url + '\n\n'; + errStr += 'The streaming connection errored in recieving data.\n\n'; + errStr += 'Please copy and paste this entire error message to: ' + config.app.adminEmail + '.\n\n'; + if (req.jsonResponse) { + return res.json({ + error: true, + message: errStr, + result: { + url: req.url, + host: req.host, + time: new Date() + } + }); + } else { + res.write(errStr); + res.end(err.stack) + } + } + }); + remoteHandler(req, res, function(){ + // console.log('complete'); + // next callback is not needed as response should have ended + }); +}; \ No newline at end of file diff --git a/lib/resources/alerts/alerts.js b/lib/resources/alerts/alerts.js new file mode 100644 index 00000000..d4ce5d01 --- /dev/null +++ b/lib/resources/alerts/alerts.js @@ -0,0 +1,142 @@ +var resource = require('resource'); +var colors = require('colors'); +var cache = require('../cache'); +var metric = require('../metric'); +var alerts = resource.define('alerts'); +var config = require('../../../config'); +var util = require('util'); +var fs = require('fs'); +var async = require('async'); +var email = require('resource-email'); + +var exceededLimitEmailTemplate = fs.readFileSync(__dirname + '/emails/exceeded-rate-limit.txt').toString(); +var approachingLimitEmailTemplate = fs.readFileSync(__dirname + '/emails/approaching-rate-limit.txt').toString(); + +alerts.timestamps(); + +alerts.property('name', { + 'type': 'string', + 'default': 'my-alert-name', + 'required': true, + 'minLength': 1, + 'maxLength': 50 +}); + +alerts.property('username', { + 'type': 'string', + 'required': true, + 'minLength': 1, + 'maxLength': 50 +}); + +alerts.property('email', { + 'type': 'string', + 'required': false, + 'default': 'hookmaster@hook.io' +}); + +alerts.property('subject', { + 'type': 'string', + 'required': true, + 'default': 'Alert from hook.io' +}); + +alerts.property('code', { + 'type': 'string', + 'required': true, + 'default': 'DEFAULT_ALERT_CODE' +}); + +alerts.property('message', { + 'type': 'string' +}); + +alerts.property('metadata', { + 'type': 'object' +}); + +alerts.property('status', { + 'type': 'string', + 'default': 'queued', + 'required': true, + 'enum': ['queued', 'silent', 'sent', 'error'] +}); + +alerts.after('create', function (data, next) { + // create + metric.zadd('alerts', new Date().getTime(), data.username, function (err) { + if (err) { + console.log('error: saving metrics/alerts', data.username, err); + } + next(null, data); + }); +}); + +alerts.persist(config.couch); +resource.on('usage::ratelimit', function (data) { + // by default, alerts should be silent ( no intended actions ) + var status = 'silent'; + // If the user has exceeded it's monthly rate limit, we do want to send out an alert, so queue it + if (data.code === 'RATE_LIMIT_EXCEEDED') { + status = 'queued'; + } + alerts.create({ + username: data.username, + email: data.email, + name: 'Rate Limit Exceeded', + status: status, + subject: 'Account Rate Limit Exceeded', + metadata: data, + code: data.code + }, function (err, alert) { + if (err) { + console.log('error creating alert:', err); + } + }); +}); + +// TODO: clear out / archive older alerts after some period of time ( perhaps 3 months ) +alerts.sendAlerts = function (data, finish) { + alerts.find({ status: 'queued' }, function (err, _alerts) { + console.log('Found alerts', _alerts.length); + // consolidate alerts into object keyed on email + // we shouldn't be sending out multiple alert emails at once, it probably means an error in alerting system + var deduped = {}; + _alerts.map(function(a){ + deduped[a.email] = a; + }); + console.log('Dedupled alerts to', Object.keys(deduped).length); + async.eachLimit(Object.keys(deduped), 5, processAlert, function (err, re){ + finish(); + }); + function processAlert (key, cb) { + var _alert = deduped[key]; + _alert.message = exceededLimitEmailTemplate; + _alert.message = _alert.message.replace('{{username}}', _alert.username); + _alert.message = _alert.message.replace(/{{servicePlan}}/g, _alert.metadata.servicePlan); + _alert.message = _alert.message.replace(/{{monthlyLimit}}/g, _alert.metadata.monthlyLimit); + console.log('sending alert', _alert.email, _alert); + email.send({ + provider: config.email.provider, + api_user: config.email.api_user, + api_key: config.email.api_key, + to: _alert.email, + bcc: ['marak@hook.io'], + from: 'hookmaster@hook.io', + subject: _alert.subject, + html: _alert.message + }, function (err) { + if (err) { + console.log(err); + cb(); + return; + } + console.log('alert email sent', data) + _alert.status = 'sent'; + _alert.save(cb); + }) + } + }); +}; + +module['exports'] = alerts; \ No newline at end of file diff --git a/lib/resources/alerts/emails/approaching-rate-limit.txt b/lib/resources/alerts/emails/approaching-rate-limit.txt new file mode 100644 index 00000000..d6f0a24d --- /dev/null +++ b/lib/resources/alerts/emails/approaching-rate-limit.txt @@ -0,0 +1,15 @@ +Hello {{username}}! + +This is a friendly reminder that your hook.io account has reached 75% of it's allotted requests this month. + +Current Monthly Requests: {{currentMonthlyTotal}} +Monthly Limit: {{monthlyTotalLimit}} +Service Plan: {{planName}} + +If your account exceeds it's monthly limit it's services may become temporarily unavailable. + +Please let me know if you have any questions. + +Sincerely, +Marak +Creator of hook.io \ No newline at end of file diff --git a/lib/resources/alerts/emails/exceeded-rate-limit.txt b/lib/resources/alerts/emails/exceeded-rate-limit.txt new file mode 100644 index 00000000..be098716 --- /dev/null +++ b/lib/resources/alerts/emails/exceeded-rate-limit.txt @@ -0,0 +1,14 @@ +Hello {{username}}!

+ +Your hook.io account has exceeded {{monthlyLimit}} requests this month which is above the limits of your current {{servicePlan}} plan.

+ +Allowed Monthly Requests: {{monthlyLimit}}
+Current Service Plan: {{servicePlan}}

+ +In order to keep your services online we ask that you upgrade your account at: https://hook.io/pricing

+ +Please let me know if you have any questions.

+ +Sincerely,
+Marak
+Creator of hook.io
\ No newline at end of file diff --git a/lib/resources/billing.js b/lib/resources/billing.js index fff2c1d5..a14b7f11 100644 --- a/lib/resources/billing.js +++ b/lib/resources/billing.js @@ -1,6 +1,6 @@ var resource = require('resource'); var billing = resource.define('billing'); -var config = require('../../config'); +var fs = require('fs'); billing.timestamps(); @@ -18,6 +18,11 @@ billing.property('amount', { "default": 500 }); +billing.property('paidUntil', { + "type": "string", + "required": false +}); + billing.property('plan', { "type": "string" }); @@ -30,7 +35,7 @@ billing.property('status', { billing.property('type', { "type": "string", - "enum": ["Credit Card", "Bitcoin"], + "enum": ["Credit Card"], "default": "Credit Card" }); @@ -38,26 +43,104 @@ billing.property('stripeID', { "type": "string" }); - -billing.property('card_number', { +billing.property('owner', { "type": "string", - "default": "" + "required": true }); -billing.property('card_exp', { +billing.property('email', { "type": "string", - "default": "" + "required": false }); -billing.property('card_ccv', { - "type": "string", - "default": "" -}); +billing.checkLimits = function checkLimits (cb) { + findAllUserReports(function(err, results){ + if (err) { + return cb(err); + } + cb(null, results); + }); +}; -billing.property('owner', { - "type": "string", - "required": true -}); +var config = require('../../config'); +var metric = require('../../lib/resources/metric'); +var async = require('async'); +var user = require('../../lib/resources/user'); +user.persist(config.couch); +var util = require('util'); +var hgetall = util.promisify(metric.client.hgetall); +var fs = require('fs'); +var callbacks = 0; + +// TODO: instead of this exhaustive search, instead we will perform a simple query for all user documents which are marked as exceeded and not contacted + +// Once we have this list, all we need to do is loop through it and email each user of their status, then set the user document to contacted and with a date +function findAllUserReports (cb) { + console.log('looking for users'); + let obj = {}; + metric.client.scan(0, 'MATCH', '*/report', 'COUNT', 999999, function (err, re) { + callbacks = re[1].length; + // get all the matching keys, now bring documents into memory + // console.log(err, re[1].length) + re[1].forEach(function(key){ + metric.client.hgetall(key, function(err, m){ + obj[key] = m; + callbacks--; + if (callbacks === 0) { + cb(null, obj); + // findExceededLimits(obj); + } + }); + }) + }) +}; + +// findUsers(); + +function findExceededLimits (data, cb) { + var exceeded = {}; + var callbacks = 0; + var unpaidUsers = {}; + // find all users who have exceeded 1,000 hits in the past month + for (var m in data) { + // TODO: do not hard-code date + var hits = data[m]['monthlyHits - 2/2019']; + // TODO: dynamic rate limits per servicePlan + if (hits > 1000) { + exceeded[m] = data[m]; + } + } + console.log(Object.keys(exceeded).length, 'over 1000 hits') + callbacks = Object.keys(exceeded).length; + // now take those users and cross-reference them with users documents to check for paid accounts + for (let u of Object.keys(exceeded)) { + //console.log(u) + var parts = u.split('/'); + var username = parts[2]; + user.findOne({ + name: username + }, function (err, _user) { + callbacks--; + if (err) { + console.log('error', err); + return; + } + if(_user.paidStatus === 'unpaid') { + console.log(_user.name, 'is currently', _user.paidStatus); + unpaidUsers[_user.name] = exceeded[u]; + unpaidUsers[_user.name].email = _user.email; + // TODO: set user lastAlerted to new Date(); + // TODO: send out correct alert email + // TODO: + } + if (callbacks === 0) { + console.log('Need to contact', Object.keys(unpaidUsers).length); + console.log(unpaidUsers); + fs.writeFileSync(__dirname + '/../contact-billing.json', JSON.stringify(unpaidUsers, true, 2)); + process.exit(); + } + }) + } +} -// todo: add bitcoin option module['exports'] = billing; \ No newline at end of file diff --git a/lib/resources/broadcast.js b/lib/resources/broadcast.js new file mode 100644 index 00000000..457f9135 --- /dev/null +++ b/lib/resources/broadcast.js @@ -0,0 +1,27 @@ +var broadcast = {}; + +var WebSocket = require('ws'); +var broadcastRemote = new WebSocket('ws://0.0.0.0:9998'); +broadcastRemote.on('open', function open () { + console.log('open'); +}); + +broadcastRemote.on('error', function error (err) { + console.log('error', err); +}); + +broadcastRemote.on('close', function close (c) { + console.log('close', c); +}); + +broadcast.message = function broadcastMessage (msg) { + // TODO: broadcast message to instance of websocket client + console.log('sending message to connected clients?', msg); + broadcastRemote.send(JSON.stringify(msg)); +}; + +broadcast.clients = function () { + // gets list of connected clients waiting for data, show their stats +}; + +module.exports = broadcast; \ No newline at end of file diff --git a/lib/resources/cache.js b/lib/resources/cache.js new file mode 100644 index 00000000..01ef84c6 --- /dev/null +++ b/lib/resources/cache.js @@ -0,0 +1,93 @@ +/* simple caching resource that uses redis */ +var config = require('../../config'); + +var redis = require("redis"), + client = redis.createClient(config.redisCache.port, config.redisCache.host); + +if (config.redisCache.password !== null) { + client.auth(config.redisCache.password); +} + +// TODO: better error handling and client setup/teardown +client.on("error", function (err) { + console.log("Redis Error, will not throw: " + err.message); +}); + +var cache = {}; + +cache._store = {}; +var store = cache._store; + +cache.get = function (key, cb) { + // TODO: consider HMSET / HMGET and not using of serialization here + client.get(key, function(err, reply){ + if (reply !== null) { + try { + reply = JSON.parse(reply); + } catch (err) { + reply = 'invalid'; + } + } + return cb(err, reply); + }); +}; + +cache.set = function (key, data, cb) { + // TODO: consider HMSET instead of seralization here + client.set(key, JSON.stringify(data), function(err, result){ + return cb(err, result); + }); +}; + +cache.del = function (key, cb) { + client.del(key, function(err, result){ + return cb(err, result); + }); +}; + +cache.sadd = function (key, value, cb) { + client.sadd(key, JSON.stringify(value), function (err, result) { + return cb(err, result); + }); +}; + +cache.srem = function (set, key, cb) { + client.srem(set, JSON.stringify(key), function (err, result) { + return cb(err, result); + }); +} + +cache.spop = function (set, key, cb) { + client.spop(set, key, function (err, result) { + return cb(err, result); + }); +} + +cache.smembers = function (key, cb) { + client.smembers(key, function(err, results) { + results = results.map(function(result){ + try { + result = JSON.parse(result); + } catch (err) { + return cb(err); + } + return result; + }); + return cb(err, results); + }); +}; + +cache.lrange = function (key, start, end, cb) { + client.lrange(key, 0, -1, function(err, result){ + return cb(err, result); + }); +}; + +cache.lpush = function (key, value, cb) { + client.lpush(key, JSON.stringify(value), function(err, result){ + return cb(err, result); + }); +}; + + +module['exports'] = cache; \ No newline at end of file diff --git a/lib/resources/cron/cron.js b/lib/resources/cron/cron.js new file mode 100644 index 00000000..e88c21cd --- /dev/null +++ b/lib/resources/cron/cron.js @@ -0,0 +1,320 @@ +var resource = require('resource'); +var colors = require('colors'); +var cache = require('../cache'); +var cron = resource.define('cron'); +var config = require('../../../config'); +var parser = require('cron-parser'); +var util = require('util'); +var servicePlan = require('../servicePlan'); + +var redis = require('redis'), + client = redis.createClient(config.redis.port, config.redis.host); + +if (config.redis.password !== null) { + client.auth(config.redis.password); +} + +// TODO: better error handling and client setup/teardown +client.on('error', function (err) { + console.log('Error ' + err); +}); + +cron.timestamps(); + +cron.property('name', { + 'type': 'string', + 'default': 'my-key-name', + 'required': true, + 'minLength': 1, + 'maxLength': 50 +}); + +cron.property('owner', { + 'type': 'string', + 'default': 'anonymous', + 'required': true, + 'minLength': 1, + 'maxLength': 50 +}); + +cron.property('cronExpression', { + 'type': 'string', + 'default': '*/60 * * * *', // defaults to once every 60 minutes + 'required': true, + 'minLength': 1, + 'maxLength': 50 +}); + +cron.property('nextExecutionUnixTime', { + 'type': 'number' +}); + +cron.property('nextExecutionDate', { + 'type': 'string' +}); + +cron.property('lastExecutionUnixTime', { + 'type': 'number' +}); + +cron.property('lastExecutionDate', { + 'type': 'string' +}); + +cron.property('uri', { + 'type': 'string', + 'required': true, + 'minLength': 1, + 'maxLength': 50 +}); + +cron.property('method', { + 'type': 'string', + 'required': true, + 'default': 'GET', + 'enum': ['GET', 'POST', 'HEAD', 'PUT', 'PATCH', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE'] +}); + +cron.property('params', { + 'type': 'object', + 'required': false, + 'description': 'the payload data which should be sent via the http request ( could be query string / json / form data )' +}); + +cron.property('status', { + 'type': 'string', + 'default': 'paused', + 'required': true, + 'enum': ['paused', 'running', 'error'] +}); + +cron.getStatus = function getStatus (cb) { + var now = new Date().getTime() - 60000; + cron.getKey('/lastCronBatch', function (err, lastCronBatch) { + cb(err, { + lastCronBatch: lastCronBatch + }); + }); +}; + +cron.run = require('./process'); + +function onlyUniqueCronNames (data, next) { + // TODO: move this code to resource library as composite key + return cron.find({ + owner: data.owner, + name: data.name + }, function (err, results) { + if (err) { + return next(err); + } + if (results.length > 0 && results[0].id !== data.id) { + var msg = 'Cron already exists ' + '/' + data.owner + "/" + data.name; + return next(new Error(msg)); + } else { + next(null, data); + } + }); +} + +function limitMinimumCronInterval (data, next) { + if (typeof data.cronExpression === 'undefined') { + return next(null, data); + } + var limit = servicePlan[data.servicePlan].cronMinimalInterval; + var expression = data.cronExpression; + try { + var interval = parser.parseExpression(expression); + // calculate the next date + var nextTime = interval.next()._date.format('x'); + // calculate the previous date + var prevTime = interval.prev()._date.format('x'); + var diff = nextTime - prevTime; + if (diff < limit) { + return next(new Error('Interval is too frequent per service plan ' + (diff / 1000) + " is not enough seconds")) + } + return next(null, data); + } catch (err) { + return next(new Error('Invalid cron expression: ' + data.cronExpression)); + } +} + +cron.before('update', limitMinimumCronInterval); +cron.before('create', limitMinimumCronInterval); + +cron.before('update', onlyUniqueCronNames); +cron.before('create', onlyUniqueCronNames); + +cron.after('update', updateCronCache); +cron.before('update', setNextExecution); + +cron.calculateNextExecution = function calculateNextExecution (last, cronExpression) { + var nextExecutionDate; + var options = { + currentDate: last + }; + var interval = parser.parseExpression(cronExpression, options); + nextExecutionDate = interval.next(); + /* + var error = false, next; + try { + } catch (err) { + console.log('Error: ' + err.message); + error = true; + // ignore errors, keep going + // TODO: mark hook as inactive / disabled due to error? + } + */ + return nextExecutionDate; +}; + +function setNextExecution (data, next) { + try { + var nextExecutionDate = cron.calculateNextExecution(data.lastExecutionUnixTime || new Date(), data.cronExpression); + data.nextExecutionUnixTime = nextExecutionDate.getTime(); + data.nextExecutionDate = nextExecutionDate; + next(null, data); + } catch (err) { + next(new Error('Invalid cron expression: ' + data.cronExpression)); + } +} + +cron.setCronCache = function (data, next) { + // compute next estimate date time + var nextDate = data.nextExecutionUnixTime; + var now = new Date(); + data.lastExecutionUnixTime = now.getTime(); + data.lastExecutionDate = now; + var key = '/' + data.owner + '/' + data.name; + // add the next computed time into the sorted set of all pending crons + // ZADD crons NEXT_COMPUTED_DATE_TIME "/cron/marak/cron-test" + // add the serialized cron item itself into the redis cache + cache.set('/cron' + key, data, function (err, result) { + if (err) { + console.log('error in cache.set crons', err); + } + // we can update the cached version without issue, but the sorted set of pending crons must be cleared and re-added everytime + // failure to remove and re-add this item could cause multiple executions of the cron, or existing active crons toggling to disabled + cron.zrem('crons', key, function (err, re) { + // only update the cache and add new items to be processed if the cron is actually active + if (data.status === 'active') { + cron.zadd('crons', nextDate, key, function (err, re) { + if (err) { + console.log('error in zadd crons', err); + } + next(null, data); + }); + } else { + next(null, data); + } + }); + }); +}; + +function updateCronCache (data, next) { + cron.setCronCache(data, next); +} + +/* resource for keeping metrics on hooks and site usage using redis */ + +cron.sadd = function (key, value, cb) { + client.sadd(key, JSON.stringify(value), function (err, result) { + return cb(err, result); + }); +}; + +cron.zadd = function (zset, member, value, cb) { + client.zadd(zset, member, value, function (err, result) { + return cb(err, result); + }); +}; + +cron.zrevrangebyscore = function (zset, max, min, cb) { + var args = [zset, max, min]; + client.zrevrangebyscore(args, function (err, response) { + cb(err, response); + }); +}; + +cron.srem = function (set, key, cb) { + client.srem(set, JSON.stringify(key), function (err, result) { + return cb(err, result); + }); +}; + +cron.zrem = function (set, key, cb) { + client.zrem(set, key, function (err, result) { + return cb(err, result); + }); +}; + +cron.spop = function (set, key, cb) { + client.spop(set, key, function (err, result) { + return cb(err, result); + }); +}; + +cron.smembers = function (key, cb) { + client.smembers(key, function(err, results) { + results = results.map(function(result){ + try { + result = JSON.parse(result); + } catch (err) { + return cb(err); + } + return result; + }); + return cb(err, results); + }); +}; + +cron.getKey = function (key, cb) { + var _key = '/cron' + key; + client.get(_key, function (err, reply) { + return cb(err, JSON.parse(reply)); + }); +}; + +cron.mget = function (keys, cb) { + client.mget(keys, function (err, result) { + return cb(err, result); + }); +}; + +cron.batchGet = function (keys, cb) { + var _keys = []; + var result = {}; + // keep metrics keyed into namespace ( so calling method does not need to know /metrics root ) + // this allows /metrics to be configurable in future + _keys = keys.map(function(k){ + return '/cron' + k; + }); + client.mget(_keys, function (err, reply) { + // merge results back into object hash containing keys + if (reply) { + reply.forEach(function(k, i){ + result[keys[Object.keys(keys)[i]]] = JSON.parse(k); + }); + } + return cb(err, result); + }); +}; + +cron.get = util.promisify(cron.get); + +cron.set = function (key, data, cb) { + // TODO: consider HMSET instead of seralization here + client.set('/cron' + key, JSON.stringify(data), function (err, result) { + return cb(err, result); + }); +}; + +cron.del = function (key, cb) { + client.del('/cron' + key, function (err, result) { + return cb(err, result); + }); +}; + +cron.client = client; + +module['exports'] = cron; \ No newline at end of file diff --git a/lib/resources/cron/index.js b/lib/resources/cron/index.js deleted file mode 100644 index ff8f2f4b..00000000 --- a/lib/resources/cron/index.js +++ /dev/null @@ -1,81 +0,0 @@ -var resource = require('resource'); -var hook = require('../hook'); -var cron = resource.define('cron'); -var config = require('../../../config'); -var request = require('hyperquest'); -var parser = require('cron-parser'); - -hook.persist(config.couch); - -cron.method('processAll', function(cb){ - - // get all the hooks with active crons - - hook.find({ cronActive: true }, function(err, results){ - - if (err) { - throw err; - } - - if(results.length === 0) { - console.log("No cron jobs found!"); - return cb(); - } - - // TODO: replace this forEach loop with a basic async iterator, - // to ensure that crons are run in batches ( instead of all at once like they are now ) - var callbacks = results.length; - results.forEach(function(h){ - if (typeof h.cron === "undefined" || h.cron.length < 8) { - return; - } - // h.cron = "*/1 * * * *"; - var now = new Date(); - h.lastCron = h.lastCron || now; - var last = new Date(h.lastCron); - var options = { - currentDate: last - }; - console.log(h.owner + '/' + h.name, h.cron) - console.log('last', last) - console.log('now', now) - try { - var interval = parser.parseExpression(h.cron.toString(), options); - var next = interval.next(); - } catch (err) { - console.log('Error: ' + err.message); - return cb(); - } - h.lastCron = now; - console.log('next', next) - - // if the next time the cron is suppose to run is before now ( minus a few ticks ) - if (next.getTime() < now.getTime() - 10) { - var _url = 'http://hook.io/' + h.owner + "/" + h.name + "?ranFromCron=true&run=true"; - console.log("EXECUTE THE CRON", _url); - var stream = request(_url) - stream.on('error', function(err){ - console.log("UNCAUGHT ERROR MAKING REQUEST", err); - }); - stream.on('end', function(){ - if (callbacks === 1) { - return cb(); - } - }); - // pipe response to STDOUT ( for now ) - // TODO: do something meaningful with cron output - // stream.pipe(process.stdout) - } - console.log("_______"); - h.save(function(err){ - callbacks--; - if (err) { - // TODO: do something meaningful with errors on save - console.log("ERROR SAVING HOOK, THIS SHOULD NOT HAPPEN.", err) - } - }) - }); - }); -}); - -module['exports'] = cron; \ No newline at end of file diff --git a/lib/resources/cron/process.js b/lib/resources/cron/process.js new file mode 100644 index 00000000..22b70de1 --- /dev/null +++ b/lib/resources/cron/process.js @@ -0,0 +1,85 @@ +/* + + resources/cron/process.js + + High level overview of how Crons are stored and processed: + + # Whenever a cron is created or saved, a new entry has been added to the sorted set "crons" where key is the next calculated Unix Time the cron should run + # Note: Only store the key and time of the cron, not any of the meta data ( as the meta-data is stored in a separate hash in the cache ) + ZADD crons NEXT_COMPUTED_DATE_TIME "/cron/marak/cron-test" + + # Next, a copy of the cron itself should be added to the cache + SET /cron/marak/cron-test {data...} + + # Later, to get back all crons which should be executed. get a range by scores using range of -inf to current time + # this should return all crons jobs which are expired ( ready to run ) + ( CURRENT_TIME is actually new Date().getTime() - 60000) + ZREVRANGEBYSCORE crons CURRENT_TIME -inf + + # Now can mget the cached versions using the results ( keys array ) from the zscore query + + # After each cron is processed, delete that cron from the sorted set and then recaculate + # at this point if the request is a success, we should delete entry + ZREM crons /marak/cron-test + + # for most crons we'll want to estimate the next excution and add it back to the crons redis + ZADD crons NEXT_COMPUTED_DATE_TIME "/cron/marak/cron-test" +*/ + +var async = require('async'); +var request = require('hyperquest'); +var metric = require('../metric'); + +module.exports = function processCron (cb) { + var cron = require('./cron'); + cron.set('/lastCronBatch', new Date(), function (err, re) { + cron.zrevrangebyscore('crons', new Date().getTime(), '-inf', function (err, results) { + results = results.map(function (key){ + return '/cron' + key; + }); + // for each batch, grab all crons from the cache + if (!results || results.length === 0) { + console.log('no results to process'); + return cb(); + } + cron.mget(results, function (err, crons) { + // now iterate through all cached cron results with a fixed concurrency + async.eachLimit(crons, 2, runCron, finish); + }); + }); + }); + + function runCron (item, next) { + try { + item = JSON.parse(item); + } catch (err) { + return next(err); + } + var _request = { + method: item.method.toLowerCase() || 'get', + uri: item.uri, + params: item.params || {} + }; + console.log('processing cron', item, _request); + var stream = request[_request.method](_request.uri, function (err, res) { + if (err) { + console.log('error running cron service', err.message); + } + // remove item sorted set + cron.zrem('crons', '/' + item.owner + '/' + item.name, function (err, re) { + // recalculate the next execution time and save it again + var nextTime = cron.calculateNextExecution(new Date(), item.cronExpression); + item.nextExecutionUnixTime = nextTime.getTime(); + item.nextExecutionDate = nextTime; + metric.incr('/cron/' + item.owner + '/' + item.name, function () { + cron.setCronCache(item, next); + }); + }); + }); + } + + function finish (err, res) { + console.log('completed cron', new Date(), err, res); + cb(err, res); + } +}; \ No newline at end of file diff --git a/lib/resources/datastore.js b/lib/resources/datastore.js new file mode 100644 index 00000000..42088ba9 --- /dev/null +++ b/lib/resources/datastore.js @@ -0,0 +1,14 @@ +// datastore.js resource - provides a key / value store interface for hooks to perist data +// see: http://github.com/bigcompany/hook.io-datastore + +var config = require('../../config'); +var datastore = require("hook.io-datastore"); +// TODO: Datastore needs to have EE pattern with role checks +// currently Datastore role logic is handled in /view/datastore/* ( not ideal ) +datastore.start({ + port: config.redis.port, + host: config.redis.host, + password: config.redis.password +}); + +module['exports'] = datastore; \ No newline at end of file diff --git a/lib/resources/domain.js b/lib/resources/domain.js index a16e5381..1d9026e5 100644 --- a/lib/resources/domain.js +++ b/lib/resources/domain.js @@ -1,20 +1,42 @@ -var resource = require('resource'); -var domain = resource.define('domain'); +var http = require('resource-http'); + +var domain = http.domain; + +domain.schema.properties.name.default = ""; + var config = require('../../config'); +var checkRoleAccess = require('../server/routeHandlers/checkRoleAccess'); -domain.timestamps(); -domain.property('name', { - "type": "string", - "default": "marak.com", - "required": true, - "minLength": 1, - "maxLength": 50 -}); +// Foreign Key to user.name domain.property('owner', { "type": "string", "required": true }); +domain.before('all', function(data, next){ + next(null, data); +}); + +domain.before('find', function(data, next){ + // check auth role + var self = this; + checkRoleAccess({ req: self.req, res: self.res, role: "domain::find" }, function (err, hasPermission) { + if (!hasPermission) { + next(new Error(config.messages.unauthorizedRoleAccess(self.req, "domain::find")), data); + //return res.end(config.messages.unauthorizedRoleAccess(req)); + } else { + next(null, data); + } + }); +}); + +domain.before('create', function(data, next){ + // TODO: perform role check + // Note: view already has role check in-place to make API secure, we'd remove that in favor for this + // console.log(this, data) + next(null, data); +}); + module['exports'] = domain; \ No newline at end of file diff --git a/lib/resources/events.js b/lib/resources/events.js new file mode 100644 index 00000000..8187412d --- /dev/null +++ b/lib/resources/events.js @@ -0,0 +1,299 @@ +var config = require('../../config'); +var resource = require('resource'); + +var events = resource.define('events'); + +module['exports'] = events; + +var eventsSubcriberClient; +var MAX_EVENTS_PER_USER = 50; +var redis = require("redis"); +var client; +require('colors'); + +/* + +TODO + +// datastore role access currently being handled in view, this is wrong, and should be moved to datastore.js resource methods +datastore::get +datastore::set +datastore::del +datastore::exists +datastore::recent + +domain::create +domain::find +domain::update +domain::destroy + + +Done + + ✓ hook::created + ✓ hook::destroy + ✓ hook::update + + ✓ hook::package::read + ✓ hook::presenter::read + ✓ hook::resource::read + ✓ hook::run + ✓ hook::source::read + ✓ hook::view::read + + ✓ env::read + ✓ env::write + +Don't track + + events::read + events::write + + hook::logs::read + hook::logs::write + + +*/ + +// TODO: need to fix resource-forms in /keys page +// keys::create +// keys::destroy + +resource.on('keys::created', function(data){ + events.push('/' + data.owner, { + "type": "keys::created", + "time": new Date(), + "data": data + }); +}); + +resource.on('cron::created', function(data){ + events.push('/' + data.owner, { + "type": "cron::created", + "time": new Date(), + "data": data + }); +}); + +resource.on('cron::updated', function (data) { + console.log('cron::updated', data) + events.push('/' + data.owner, { + "type": "cron::updated", + "time": new Date(), + "data": data + }); +}); + +resource.on('cron::destroyed', function(data){ + events.push('/' + data.owner, { + "type": "cron::destroyed", + "time": new Date(), + "data": data + }); +}); + +resource.on('hook::created', function(data){ + events.push('/' + data.owner, { + "type": "hook::created", + "time": new Date(), + "ip": data.ip + }); +}); + +resource.on('hook::updated', function(data){ + events.push('/' + data.owner, { + "type": "hook::updated", + "time": new Date(), + "ip": data.ip + }); +}); + +resource.on('hook::destroyed', function(data){ + events.push('/' + data.owner, { + "type": "hook::destroyed", + "time": new Date(), + "ip": data.ip + }); +}); + +resource.on('hook::run', function (data) { + events.push('/' + data.owner, { + "type": "hook::run", + "time": new Date(), + "ip": data.ip, + "url": data.url + }); +}); + +resource.on('hook::source::read', function (data) { + events.push('/' + data.owner, { + "type": "hook::source::read", + "time": new Date(), + "ip": data.ip, + "url": data.url + }); +}); + +resource.on('hook::presenter::read', function (data) { + events.push('/' + data.owner, { + "type": "hook::presenter::read", + "time": new Date(), + "ip": data.ip, + "url": data.url + }); +}); + +resource.on('hook::view::read', function (data) { + events.push('/' + data.owner, { + "type": "hook::view::read", + "time": new Date(), + "ip": data.ip, + "url": data.url + }); +}); + +resource.on('hook::package::read', function (data) { + events.push('/' + data.owner, { + "type": "hook::package::read", + "time": new Date(), + "ip": data.ip, + "url": data.url + }); +}); + +resource.on('hook::resource::read', function (data) { + events.push('/' + data.owner, { + "type": "hook::resource::read", + "time": new Date(), + "ip": data.ip, + "url": data.url + }); +}); + +resource.on('keys::authAttempt', function (data) { + events.push('/' + data.owner, { + "type": "keys::authAttempt", + "name": data.name, + "time": new Date(), + "ip": data.ip, + "owner": data.owner, + "hasAccess": data.hasAccess + }); +}); + +resource.on('env::write', function (data) { + events.push('/' + data.owner, { + "type": "env::write", + "time": new Date(), + "ip": data.ip + }); +}); + +resource.on('env::read', function (data) { + events.push('/' + data.owner, { + "type": "env::read", + "time": new Date(), + "ip": data.ip + }); +}); + +events.start = function (opts) { + + client = redis.createClient(opts.port, opts.host); + + if (config.redis.password !== null) { + client.auth(config.redis.password); + } + + eventsSubcriberClient = redis.createClient(opts.port, opts.host); + + if (config.redis.password !== null) { + eventsSubcriberClient.auth(config.redis.password); + } + + // TODO: better error handling and client setup/teardown + client.on("error", function (err) { + console.log("Error " + err); + }); + + // TODO: better error handling and client setup/teardown + eventsSubcriberClient.on("error", function (err) { + console.log("Error " + err); + }); +} + +events.flush = function (endpoint, cb) { + // TODO: removes all events from the endpoint + // TODO: run this every day or so to remove old events + +}; + +events.recent = function (endpoint, cb) { + // gets the most recent events for endpoint + client.lrange("/user" + endpoint + "/events", 0, MAX_EVENTS_PER_USER, function(err, results){ + // show events in reverse order + var parsed = []; + results.forEach(function(item){ + parsed.push(JSON.parse(item)); + }); + + parsed = parsed.reverse(); + return cb(err, parsed); + }); +}; + +events.stream = function (outputStream) { + // tails the most recent events entries for user +}; + +events.push = function push (endpoint, entry, cb) { + // Before adding a new entry we must check if endpoint has exceeded MAX_LOGS_PER_HOOK + // if so, then pop the last item from the list before adding a new item + events._count("/user" + endpoint + "/events", function(err, res){ + if (err) { + return cb(err); + } + if (res >= MAX_EVENTS_PER_USER) { + // console.log("Max events entries hit!"); + return removeLastEntry(); + } else { + return addEntry(); + } + }); + + function addEntry () { + // add entry to set + client.rpush("/user" + endpoint + "/events", JSON.stringify(entry), function (err, res){ + if (err) { + return cb(err); + } + //console.log('pushing to', "/user" + endpoint + "/events") + eventsSubcriberClient.publish("/user" + endpoint + "/events", JSON.stringify(entry)); + }); + }; + + function removeLastEntry () { + client.lpop("/user" + endpoint + "/events", function (err, result){ + if (err) { + return cb(err); + } + addEntry(); + }); + }; + +}; + +// gets the amount of events currently keyed to endpoint +events._count = function (endpoint, cb) { + client.llen(endpoint, function (err, res){ + if (err) { + return cb(err); + } + cb(null, res); + }) +}; + +events.start({ + port: config.redis.port, + host: config.redis.host +}); \ No newline at end of file diff --git a/lib/resources/features.js b/lib/resources/features.js new file mode 100644 index 00000000..42e49a78 --- /dev/null +++ b/lib/resources/features.js @@ -0,0 +1,15 @@ +/* +var config = require('../../config'); +var resource = require('resource'); +var feature = resource.define('feature'); +*/ +var feature = {}; + +module['exports'] = feature; + +feature.features = { + "apiKeyLimit" : "number", + "customTimeouts" : "boolean", + "customRoleChecks" : "boolean", + "privateServices" : "boolean" +}; \ No newline at end of file diff --git a/lib/resources/gist.js b/lib/resources/gist.js new file mode 100644 index 00000000..ed0da625 --- /dev/null +++ b/lib/resources/gist.js @@ -0,0 +1,49 @@ +var gist = {}; + +gist.create = function (options, cb) { + + options = options || {}; + options.name = options.name || "testHook.js"; + options.description = options.description || 'My first hook.io microservice'; + options.source = options.source || ""; + + if (typeof options.accessToken === 'undefined') { + return cb(new Error('Github Access Token is required!')); + } + + var GitHubApi = require('github'); + var github = new GitHubApi({ + // required + version: "3.0.0", + // optional + debug: false, + protocol: "https", + host: "api.github.com", + requestFormat: "json", + timeout: 5000 + }); + + github.authenticate({ + type: "oauth", + token: options.accessToken + }); + + var msg = { + description: options.description, + public: true, + files: { + "testHook.js": { + content: options.source + } + } + }; + return github.gists.create(msg, function(err, rest) { + if (err) { + return cb(err); + } + return cb(null, rest); + }); +}; + + +module['exports'] = gist; \ No newline at end of file diff --git a/lib/resources/hook/ReadMe.md b/lib/resources/hook/ReadMe.md new file mode 100644 index 00000000..021c3a0f --- /dev/null +++ b/lib/resources/hook/ReadMe.md @@ -0,0 +1,5 @@ +# TODO + + - Move `hook` into separate repository + - Decouple server / worker logic + - Decouple stream transformation logic ( should be pipeable ) diff --git a/lib/resources/hook/attemptToRequireUntrustedHook.js b/lib/resources/hook/attemptToRequireUntrustedHook.js deleted file mode 100644 index badb02ac..00000000 --- a/lib/resources/hook/attemptToRequireUntrustedHook.js +++ /dev/null @@ -1,39 +0,0 @@ -var config = require('../../../config'); - -var fs = require("fs"); - -module['exports'] = function attemptToRequireUntrustedHook (opts, callback) { - - var username = opts.username, - script = opts.script; - - var untrustedHook; - var isStreamingHook; - var untrustedTemplate; - var err = null; - // At this stage, the hook source code is untrusted ( and should be validated ) - try { - var _script = require.resolve(__dirname + '/../../../temp/' + username + "/" + script + '.js'); - delete require.cache[_script]; - untrustedHook = require(_script); - opts.req.hook = opts.req.hook || {}; - untrustedHook.schema = untrustedHook.schema || {}; - untrustedHook.theme = opts.req.hook.theme || untrustedHook.theme || config.defaultTheme; - untrustedHook.presenter = opts.req.hook.presenter || untrustedHook.presenter || config.defaultPresenter; - } catch (e) { - err = e; - } - - if (err) { - return fs.readFile(__dirname + '/../../../temp/' + username + "/" + script + '.js', function(_err, _source){ - if (_err) { - throw _err; - return callback(_err); - } - // unable to require Hook as commonjs module, - // the Hook is invalid - return callback(err, _source); - }); - } - return callback(null, untrustedHook) -}; diff --git a/lib/resources/hook/determineRequestFormat.js b/lib/resources/hook/determineRequestFormat.js deleted file mode 100644 index d83b0b55..00000000 --- a/lib/resources/hook/determineRequestFormat.js +++ /dev/null @@ -1,44 +0,0 @@ -var mergeParams = require('../../../view/mergeParams'); - -var view = require('view'); -var _view; - view.create({ path: __dirname + "/../../../view"}, function (err, v){ - if (err) { - throw err; - } - _view = v; -}); - -module['exports'] = function determineRequestFormat (req, res, next) { - - var hook = require('./'); - var types = []; - if (req.headers && req.headers.accept) { - types = req.headers.accept.split(','); - } - req.resource.params.format = req.resource.params.format || "friendly"; - if (types.indexOf('text/html') !== -1 && req.resource.params.format === "friendly") { - // console.log('RENDERING FRIENDLY RESPONSE') - mergeParams(req, res, function(){ - _view['hook'].present({ - gist: req.hook.gist, - request: req, - response: res - }, function(err, html){ - res.end(html); - }); - }); - } else { - // - // If the response should be rendered raw, write the response as the Hook dictates - // - // console.log('RENDERING RAW RESPONSE'); - // override format - mergeParams(req, res, function(){ - //console.log('resource parsed', req.resource.params) - req.resource.params.format = "raw"; - return next(req, res); - }); - } - -} \ No newline at end of file diff --git a/lib/resources/hook/fetchHookPresenter.js b/lib/resources/hook/fetchHookPresenter.js deleted file mode 100644 index b46af1c8..00000000 --- a/lib/resources/hook/fetchHookPresenter.js +++ /dev/null @@ -1,49 +0,0 @@ -var hook = require('./'); - -var themeCache = false; - -var request = require('hyperquest'); -var mkdirp = require('mkdirp'); -var fs = require('fs'); -var loadPresenter = require('./loadPresenter'); - - -module['exports'] = function fetchHookPresenter (url, callback) { - request.get(url, function (err, res) { - if (err) { - return callback(err); - } - var body = ''; - res.on('data', function (c){ - body += c.toString(); - }); - res.on('end', function () { - var filePath, - dirPath; - - dirPath = url.replace(/\//g, "\\"); - dirPath = dirPath.replace('index.js', ''); - filePath = "index.js"; - - var path = __dirname + '/../../../temp/' + dirPath + "/" + filePath; - - if (themeCache === true) { - loadPresenter(path, callback); - } else { - mkdirp(__dirname + '/../../../temp/' + dirPath, function(err){ - if (err) { - return callback(err); - } - // write the code to a temporary file - fs.writeFile(__dirname + '/../../../temp/' + dirPath + "/" + filePath, body, function(err){ - if (err) { - return callback(err); - } - loadPresenter(path, callback); - }); - }); - } - }) - - }) -}; diff --git a/lib/resources/hook/fetchHookSourceCode.js b/lib/resources/hook/fetchHookSourceCode.js new file mode 100644 index 00000000..b61cf7b3 --- /dev/null +++ b/lib/resources/hook/fetchHookSourceCode.js @@ -0,0 +1,165 @@ +var request = require('hyperquest'); +var config = require('../../../config'); +var path = require('path'); + +module['exports'] = function fetchHookSourceCode (opts, callback) { + + var req = opts.req, + res = opts.res, + hook = opts.req.hook; + + var cache = false; + var source = "gist"; + var username, script, userHome, userFile; + + if (hook.mode === "Production") { + cache = true; + } + + // console.log('what is hook', hook) + + // the user's temporary home directory + // will contain cached versions of Hook and Hook related assets + userHome = path.resolve(process.cwd() + '/' + config.tempDirectory + hook.owner + "/" + hook.name + "/"); + userName = hook.owner; + + /* + if (typeof req.hook.source !== "undefined" && req.hook.source.length > 5) { + source = "hook"; + script = "index"; + //return res.end('custom source' + req.hook.source); + } + */ + + userFile = userHome + "/" + script + '.js'; + opts.script = script; + + function readSource (cb) { + // As per https://github.com/bigcompany/hook.io/issues/136, as FS access is removed from worker + // Instead of loading the code from an additional async source, we now assume that the + // source has already been loaded on the couchdb document + if (typeof hook.source === "undefined" || hook.source.length === 0) { + return fetchAndSaveSource(cb); + } else { + // console.log('using cached source', hook.source); + hook.originalSource = hook.source; + return callback(null, hook.source); + } + }; + + function saveSourceToDisk (code, cb) { + // TODO: save to couchdb now? + // req.saveHook = true; + hook.source = code; + hook.originalSource = code; + return cb(null, code); + } + + function fetchRemoteGistSource (cb) { + + var gist = opts.gist; // || "https://gist.github.com/Marak/357645b8a17daeb17458"; + var userScript = gist.replace('https://gist.github.com/', ''); + userScript = userScript.replace('/', ' '); + var parts = userScript.split(" "); + var username = parts[0], + script = parts[1]; + + var gistAPI = "https://api.github.com/gists/" + script; + + // Indicate that a change has happened to the hook and it should be saved + // req.saveHook = true; + /* + fetches the latest version of source code from remote Github Gist Source + requires a valid and correctly formatted link to a github gist URL + */ + var options = { + headers: { + "User-Agent": "hook.io source code agent" + } + }; + // must validate request. github API limits requests per hour. non-authorizated HTTP requests cap out quickly. + // warning: in the future, we might have to ask github to increase our API limit / create several access tokens + if (config.github.accessName && config.github.accessName.length && config.github.accessToken && config.github.accessToken.length) { + options.headers['Authorization'] = "Basic " + new Buffer(config.github.accessName + ":" + config.github.accessToken, "utf8").toString("base64") + } + // console.log('getting gistAPI', gistAPI) + request.get(gistAPI, options, function (err, apiRes) { + if (err) { + return res.end(err.message); + } + var apiReply = ''; + apiRes.on('data', function (c) { + apiReply += c.toString(); + }); + apiRes.on('error', function(err){ + return callback(err); + }); + apiRes.on('end', function(){ + var gistJSON = {}, files, keys; + try { + gistJSON = JSON.parse(apiReply.toString()); + files = gistJSON.files; + keys = Object.keys(files); + } catch (err) { + return callback(new Error('Not valid JSON: ' + apiReply)); + } + // assume first file is the source code + requestSource(gistJSON.files[keys[0]].raw_url); + }); + function requestSource (source) { + // console.log('getting new hook source', source) + request.get(source, function (err, res) { + if (err) { + return opts.res.end(err.message); + } + var body = ''; + res.on('data', function(c){ + body += c.toString(); + }); + res.on('end', function(){ + var code = body.toString(); + req.hook.source = code; + // console.log('fetchHookSourceCode setting hook.source', code); + cb(null, code); + }); + }); + } + }); + } + + function fetchSource (cb) { + if (typeof hook.sourceType === "undefined") { + hook.sourceType = "code"; + if (typeof hook.source === "undefined" || hook.source.length === 0) { + if (typeof hook.gist !== "undefined" && hook.gist.length > 0) { + hook.sourceType = "gist"; + } + } + } + if (hook.sourceType === "gist") { + fetchRemoteGistSource(cb); + } else { + cb(null, hook.source); + } + }; + + function fetchAndSaveSource (cb) { + fetchSource(function (err, code) { + if (typeof code === "undefined") { + // TODO: better error / fetch for github source when doing /source + return res.end('no source code was found for this service yet.'); + // return res.end('fetchandsave Unable to fetch hook source. We made a mistake. Please contact support'); + } + saveSourceToDisk(code, cb); + }); + } + + if (cache) { + // if cache is enabled for Hook, attempt to read the temporary source file + readSource(callback); + } else { + // if no cache is enabled, fetch the source code and save it as a local temporary file + fetchAndSaveSource(callback); + } + +}; diff --git a/lib/resources/hook/fetchHookSourceCodeFromGithub.js b/lib/resources/hook/fetchHookSourceCodeFromGithub.js deleted file mode 100644 index 869e22c9..00000000 --- a/lib/resources/hook/fetchHookSourceCodeFromGithub.js +++ /dev/null @@ -1,49 +0,0 @@ -var cache = false; - -var request = require('hyperquest'); -var mkdirp = require('mkdirp'); -var fs = require('fs'); - -module['exports'] = function fetchHookSourceCodeFromGithub (opts, callback) { - var gist = opts.gist || "https://gist.github.com/Marak/357645b8a17daeb17458"; - var userScript = gist.replace('https://gist.github.com/', ''); - userScript = userScript.replace('/', ' '); - var parts = userScript.split(" "); - var username = parts[0], - script = parts[1]; - var source = gist.replace('https://gist.github.com/', 'https://gist.githubusercontent.com/') + '/raw/'; - opts.username = username; - opts.script = script; - opts.source = source; - if (opts.req.resource && opts.req.resource.params) { - opts.req.resource.params.gist = gist; - } - request.get(source, function(err, res){ - if (err) { - return opts.res.end(err.message); - } - var body = ''; - res.on('data', function(c){ - body += c.toString(); - }); - res.on('end', function(){ - var code = body.toString(); - if (cache) { - callback(); - } else { - mkdirp(__dirname + '/../../../temp/' + opts.username, function(err){ - if (err) { - return callback(err); - } - // write the code to a temporary file - fs.writeFile(__dirname + '/../../../temp/' + opts.username + "/" + opts.script + '.js', code, function(err){ - if (err) { - return callback(err); - } - callback(null, code); - }); - }); - } - }); - }); -}; diff --git a/lib/resources/hook/fetchHookTheme.js b/lib/resources/hook/fetchHookTheme.js deleted file mode 100644 index da9950e2..00000000 --- a/lib/resources/hook/fetchHookTheme.js +++ /dev/null @@ -1,17 +0,0 @@ -var request = require('hyperquest'); - -module['exports'] = function fetchHookTheme (url, callback) { - request.get(url, function(err, res){ - if (err) { - // no theme, do nothing - return callback(err); - } - var themeHtml = ''; - res.on('data', function(c){ - themeHtml += c.toString(); - }); - res.on('end', function(){ - callback(null, themeHtml); - }); - }); -} diff --git a/lib/resources/hook/fork.js b/lib/resources/hook/fork.js index dbc75a42..0310a2e5 100644 --- a/lib/resources/hook/fork.js +++ b/lib/resources/hook/fork.js @@ -1,26 +1,29 @@ var GitHubApi = require('github'); -var github = new GitHubApi({ - // required - version: "3.0.0", - // optional - debug: false, - protocol: "https", - host: "api.github.com", - requestFormat: "json", - timeout: 5000 -}); +var config = require('../../../config'); module['exports'] = function forkHook (req, res) { + + var github = new GitHubApi({ + // required + version: "3.0.0", + // optional + debug: false, + protocol: "https", + host: "api.github.com", + requestFormat: "json", + timeout: 5000 + }); + var opts; var hook = require('./'); if (!req.isAuthenticated()) { - req.session.redirectTo = "/" + req.params.username + "/" + req.params.hook + "?fork=true"; + req.session.redirectTo = "/" + req.params.owner + "/" + req.params.hook + "/fork"; return res.redirect(302, '/login'); } - var query = { owner: req.params.username, name: req.params.hook }; + var query = { owner: req.params.owner, name: req.params.hook }; return hook.find(query, function(err, result){ - if(err) { + if (err) { return res.end(err.message); } if (result.length === 0) { @@ -30,77 +33,145 @@ module['exports'] = function forkHook (req, res) { var h = result[0]; req.hook = result[0]; - var userScript = h.gist.replace('https://gist.github.com/', ''); - userScript = userScript.replace('/', ' '); - var parts = userScript.split(" "); - var username = parts[0], - script = parts[1]; + var sourceType = "gist"; - if (req.user.username === username) { - return res.end('You cannot fork your own Hooks. Try creating a new Hook instead?'); + // TODO: allow forking of non-github gists! easy. + if (typeof h.gist === "undefined" || h.gist.length === 0) { + sourceType = "hook"; + // return res.end('Sorry, this hook is not backed by a Github Gist. Forking is not currently available for non-Github based Hooks.'); } + + if (req.session.user === h.owner) { + return res.end('You are already the owner of this service, so you cannot fork it.\nShare this link with other developers so they may fork it.'); + } + // before we fork, check if hook already exists by that name, if return error - hook.find({ owner: req.user.username, name: h.name }, function(err, result){ + hook.find({ owner: req.session.user, name: h.name }, function (err, result) { if (err) { return res.end(err.message); } if(result.length > 0) { - return res.end("You've already forked this Hook at " + "http://hook.io/" + req.user.username + "/" + h.name); + var r = { + error: true, + message: "You've already forked this Hook at " + config.app.url + "/" + req.session.user + "/" + h.name + }; + if (req.jsonResponse) { + return res.json(r); + } else { + return res.end(r.message); + } + } + forkScript(h); + }); + // TODO: better forking support / choose where to fork to + // add UI screen for forking + /* + if (sourceType === "gist") { + var userScript = h.gist.replace('https://gist.github.com/', ''); + userScript = userScript.replace('/', ' '); + var parts = userScript.split(" "); + var username = parts[0], + script = parts[1]; + + if (req.session.user === username) { + return res.end('You cannot fork your own Hooks. Try creating a new Hook instead? https://hook.io/new'); + } + + // before we fork, check if hook already exists by that name, if return error + hook.find({ owner: req.session.user, name: h.name }, function(err, result){ + if (err) { + return res.end(err.message); + } + if(result.length > 0) { + return res.end("You've already forked this Hook at " + config.app.url + "/" + req.session.user + "/" + h.name); + } + forkGithubScript(); + }); + } else { + + if (req.session.user === username) { + return res.end('You cannot fork your own Hooks. Try creating a new Hook instead? https://hook.io/new'); } - forkScript(); - }) - - function forkScript () { + + // before we fork, check if hook already exists by that name, if return error + hook.find({ owner: req.session.user, name: h.name }, function (err, result) { + if (err) { + return res.end(err.message); + } + if(result.length > 0) { + return res.end("You've already forked this Hook at " + config.app.url + "/" + req.session.user + "/" + h.name); + } + forkScript(h); + }); + } + */ + function forkScript (h) { + var _hook = { + owner: req.session.user, + name: h.name, + source: h.source, + language: h.language, + mschema: h.mschema, + mschemaStatus: h.mschemaStatus, + themeSource: h.themeSource, + presenterSource: h.presenterSource, + themeStatus: h.themeStatus, + schemaStatus: h.schemaStatus + }; + return hook.create(_hook, function(err, result){ + if (err) { + return callback(err.message); + } + var h = result; + //console.log('performing redirect', req.session.user); + if (req.jsonResponse) { + var r = { + status: 'created' + }; + return res.json(r); + } else { + return res.redirect('/admin?owner=' + _hook.owner + "&name=" + _hook.name + "&status=forked"); + } + }); + }; + + function forkGithubScript () { // authenticate github API - github.authenticate({ - type: "oauth", - token: req.user.accessToken - }); - - var _script = script + ".js"; - return github.gists.fork({ - id: script, - }, function(err, rest) { + // TODO: update reference? + if (typeof req.user.accessToken === "undefined") { + // redirect to github login with fork role ability + return res.redirect(301, '/login/github/gist'); + } + + github.authenticate({ + type: "oauth", + token: req.user.accessToken + }); + + var _script = script + ".js"; + return github.gists.fork({ + id: script, + }, function(err, rest) { + if (err) { + return res.end(err.message); + } + var _hook = { + owner: req.session.user, + name: h.name, + gist: rest.owner.html_url.replace('https://github.com/', 'https://gist.github.com/') + "/" + rest.id + }; + return hook.create(_hook, function(err, result){ if (err) { - return res.end(err.message); + return callback(err.message); } - var _hook = { - owner: req.user.username, - name: h.name, - gist: rest.owner.html_url.replace('https://github.com/', 'https://gist.github.com/') + "/" + rest.id - }; - return hook.create(_hook, function(err, result){ - if (err) { - return callback(null, err.message); - } - var h = result; - opts = opts || {}; - opts.gist = h.gist; - opts.req = req; - opts.res = res; - hook.fetchHookSourceCodeFromGithub(opts, function(err, code){ - if (err) { - return opts.res.end(err.message); - } - hook.attemptToRequireUntrustedHook(opts, function(err, _module){ - if (err) { - return opts.res.end(hook.formatError(err)) - } - h.mschema = _module.schema; - h.theme = _module.theme; - h.presenter = _module.presenter; - h.save(function(){ - // redirect to new fork friendly page - return res.redirect('/' + h.owner + "/" + h.name + "?forked=true"); - }); - - }); - - }); - - }); + var h = result; + opts = opts || {}; + opts.gist = h.gist; + opts.req = req; + opts.res = res; + return res.redirect('/' + h.owner + "/" + h.name + "?forked=true"); + }); }); }; - }); }; \ No newline at end of file diff --git a/lib/resources/hook/formatError.js b/lib/resources/hook/formatError.js index 9d9c929f..ef0e0a6a 100644 --- a/lib/resources/hook/formatError.js +++ b/lib/resources/hook/formatError.js @@ -1,6 +1,22 @@ -module['exports'] = function formatError (type, err) { - if (typeof err === "undefined") { - err = type; +module['exports'] = function formatError (err, opts) { + // TODO: 500 errors + // opts.res.writeHead(500); + var message = 'An error occurred \n'; + /* + if(err.stack) { + console.log(err.stack) + var stack = err.stack.split('\n'); + message += ' ' + stack[1] + '\n'; + message += ' ' + stack[2] + '\n\n'; + console.log(stack) } - return (err.stack); + */ + if (typeof opts !== "undefined" && typeof opts.tag !== "undefined") { + message += 'Error: ' + opts.tag; + } else { + message += 'Error: ' + err.message; + } + err.message = message; + err.message += '\n\n' + err.stack; + return err; }; \ No newline at end of file diff --git a/lib/resources/hook/gateway.js b/lib/resources/hook/gateway.js new file mode 100644 index 00000000..66a3277c --- /dev/null +++ b/lib/resources/hook/gateway.js @@ -0,0 +1,185 @@ +// used for running hot-code gateway services +// these are services which are never saved and are only run in real-time +// Note: a lot of this logic already exists in runHook.js and run.js files, +// we have replicated the logic here and created a new code path in the hopes of possible, +// refactoring / replacing runHook.js and run.js logic with this gateway.js +var config = require('../../../config'); +var microcule = require('microcule'); +var events = require('../events'); +var metric = require("../metric"); +var log = require("../log"); +var user = require("../user"); +var hook = require('./'); +var psr = require('parse-service-request'); +var resource = require('resource'); + +// use bash version 4 by spawned bash services by default +microcule.config.bash.version = 4; + +module.exports = function gateway (req, res, next) { + + // gateway execution will automatically parse body everytime + psr(req, res, function(req, res){ + + var params = req.resource.params; + + var service = { + owner: "anonymous", + name: "gateway", + language: params.language, + code: params.code + }; + + if (typeof params.presenter !== "undefined" && params.presenter.length > 0) { + service.presenter = params.presenter; + service.view = ""; + } + + if (typeof params.schema === "string" && params.schema.length > 0) { + // Remark: Should this parsing logic be inside stack library? + try { + params.schema = JSON.parse(params.schema) + } catch (err) { + res.end('invalid schema property. cannot parse as valid JSON'); + } + } + + if (typeof params.schema === "object" && Object.keys(params.schema).length > 0) { + service.schema = params.schema; + } + + if (typeof params.view !== "undefined" && params.view.length > 0) { + service.view = params.view; + } + + if (typeof params.language === "undefined") { + return res.end('language parameter is required!'); + } + + // legacy API + params.code = params.source || params.code; + + if (typeof params.code === "undefined") { + return res.end('code parameter is required!'); + } + + if (typeof req.headers === "object" && typeof req.headers["x-hookio-user-session-name"] === "string" && req.headers["x-hookio-user-session-name"].length > 0) { + var _name = req.headers["x-hookio-user-session-name"]; + if (_name === "anonymous") { + service.owner = "anonymous"; + return _spawn(); + } + user.find({ name: _name }, function (err, _user){ + if (err) { + return res.end(err.message); + } + if (_user.length === 0) { + return res.end('Could not find user ' + _name + '. Please contact support.'); + } + var u = _user[0]; + service.owner = _name; + req.env = u.env || {}; + req.env.hookAccessKey = u.hookAccessKey; + // TODO: refactor out key sorting into resource library + var keys = Object.keys(req.env).sort(); + var __env = {}; + keys.forEach(function(k){ + __env[k] = req.env[k]; + }); + req.env = __env; + return _spawn(); + }) + } else { + service.owner = "anonymous"; + return _spawn(); + } + + function _spawn () { + + var customLogger = new log.Logger({ + ip: req.connection.remoteAddress, + service: { + owner: service.owner, + name: service.name, + } + }); + + service.log = customLogger.log.bind(customLogger); // Imporant: Must bind logger instance or it will lose `self` scope + + // only populate the service we are about to run with data specificed in params.data + // this means that incoming parameters such as `code` and `language` will not be automically included in the spawned service + // this seems like the least suprising way to handle parameters + req.resource = { + params: {} + }; + + for (var k in params.data) { + req.resource.params[k] = params.data[k]; + } + + // Note: Rate limiting code has already been applied before reaching this code in worker + // emit hook::run event, so we can scope gateway executions somewhere + resource.emit('hook::run', { + name: 'gateway', + owner: service.owner, + // ip: req.connection.remoteAddress, + url: req.url + }); + + service.isHookio = true; + if (config.worker.useNSJAIL) { + // TODO: move nsJAIL configuration to config file + service.jail = "nsjail"; + service.jailArgs = config.worker.nsJailArgs; + service.home = "/"; + } + + // indicates that service should output stderr to stdout on non-zero exit codes + // this is useful for users developing and debugging services + // its the least surprising behavior for all use-cases + // the only potential down-side is situations where you don't want to expose stderr data to the client + // we could expose this option to the API and allow users to toggle it to hide errors to the client + service.redirectStderrToStdout = true; + + // indicates that service should not close response when it exits + // this is required for services that may be chains + // for non-chained services, the final _res.end(); should end the response + service.endResponseOnExit = false; + + microcule.plugins.mschema(service.schema)(req, res, function (err, re) { + if (err) { + return res.end(err.message); + } + // console.log("gateway".green, service.view) + if (service.view) { + microcule.viewPresenter({ + view: service.view, + presenter: service.presenter + }, req, res, function (err, req, output) { + if (err) { + return next(err); + } + microcule.plugins.spawn(service)(req, output, function(){ + if (err) { + return res.end(err.message); + } + // if next() was fired, assume we need to end the request here + output.end(); + }); + }); + } else { + microcule.plugins.spawn(service)(req, res, function completed (err, r){ + // decrease currently running count by 1 ( required for tracking concurrency of running services ) + if (err) { + return res.end(err.message); + } + // if next() was fired, assume we need to end the request + res.end(); + }); + } + }); + } + + }); + +}; \ No newline at end of file diff --git a/lib/resources/hook/index.js b/lib/resources/hook/index.js index 6fae7d5e..8d5e6e1e 100644 --- a/lib/resources/hook/index.js +++ b/lib/resources/hook/index.js @@ -1,7 +1,35 @@ var resource = require('resource'); var hook = resource.define('hook'); +var microcule = require('microcule'); +var RateLimiter = microcule.plugins.RateLimiter; +var user = require('../user'); +var metric = require('../metric'); +var cache = require('../cache'); var config = require('../../../config'); var request = require('hyperquest'); +var slug = require('slug'); +var rateLimiter = new RateLimiter({ + provider: metric +}); + +hook.languages = require('../programmingLanguage').languages; + +/* +[ + "bash", + "coffee-script", + "javascript", + "lua", + "perl", + "php", + "python", + "python3", + "ruby", + "scheme", + "smalltalk", + "tcl" +]; +*/ hook.timestamps(); @@ -10,12 +38,99 @@ hook.property('name', { "default": "my-hook", "required": true, "minLength": 1, - "maxLength": 50 + "maxLength": 50, + "description": "The name of the Hook. This will be part of the url to access to the Hook." }); -hook.property('gist', { +hook.property('path', { + "type": "string", + "default": "/:id", + "required": false, + "maxLength": 100, + "description": "Optional path of the Hook. This allows for url route parameter style routing." +}); + +hook.property('description', { "type": "string", + "default": "", + "required": false, + "description": "A brief description of what the Hook does" +}); + +// npm package for hook +hook.property('pkg', 'object'); + +hook.property('language', { + "type": "string", + "default": "bash", "required": true, + "minLength": 1, + "maxLength": 50, + "description": "The programming language of the Hook." +}); + +hook.property('isPublic', { + "type": "boolean", + "default": true +}); + +hook.property('customTimeout', { + "type": "number", + "default": config.UNTRUSTED_HOOK_TIMEOUT, + "min": 1000, + "max": 300000, + "description": "Custom timeout variable for services" +}); + +hook.property('themeName', { + "type": "string", + "default": "form", + "required": false, + "description": "The name of the Theme, such as 'form'" +}); + +hook.property('themeStatus', { + "description": "the current status of the theme", + "enum": ["enabled", "disabled", "error"], + "default": "disabled", + "required": false +}); + +hook.property('mschemaStatus', { + "description": "the current status of the schema for the service", + "enum": ["enabled", "disabled", "error"], + "default": "disabled", + "required": false +}); + +hook.property('status', { + "type": "string", + "enum": ["active", "disabled"], + "default": "active" +}); + +hook.property('gist', { + "type": "string", + "required": false, + "description": "source of the Hook provided as a Github Gist Url" +}); + +hook.property('githubRepo', { + "type": "string", + "required": false, + "description": "github repo of the Hook source code" +}); + +hook.property('githubBranch', { + "type": "string", + "required": false, + "description": "github repo branch of the Hook source code" +}); + +hook.property('mainEntry', { + "type": "string", + "required": false, + "description": "main entry point of service" }); hook.property('ran', { @@ -23,6 +138,24 @@ hook.property('ran', { "default": 0 }); +// Array of services as strings which act as pre-facing middlewares +hook.property('inputs', { + "type": "array", + "default": [] +}); + +// TODO: implement after middlewares ( not yet available ) +hook.property('outputs', { + "type": "array", + "default": [] +}); + +hook.property('forked', { + "type": "number", + "default": 0, + "description": "The amount of times the Hook has been forked" +}); + hook.property('owner', { "type": "string", "required": true @@ -30,18 +163,14 @@ hook.property('owner', { hook.property('theme', { "type": "string", - "required": true, - "default": config.defaultTheme + "required": false, + "default": "" }); -hook.property('cron', 'string'); -hook.property('lastCron', 'string'); -hook.property('cronActive', 'boolean') - hook.property('presenter', { "type": "string", - "required": true, - "default": config.defaultPresenter + "required": false, + "default": "" }); // holds the schema of the hook @@ -50,26 +179,226 @@ hook.property('mschema', { "type": "object" }); -hook.property('isStreaming', { +// TODO: should auto-create or update this on uploads / saves / deploys +hook.property('packageJSON', { + "type": "object" +}); + +hook.property('hookType', { + "type": "string", + "description": "additional ( internal ) type classification for hooks. currently used to help manage hot-code gateways", + "enum": ["service", "gateway"], + "default": "service", + "required": false +}); + +hook.property('sourceType', { + "type": "string", + "description": "the active type of source for the hook", + "enum": ["code", "gist", "githubRepo"], + "default": "code", + "required": false +}); + +hook.property('source', { + "type": "string", + "description": "source code of Hook", + "required": false, + "default": 'echo "hello world"' +}); + +hook.property('themeSource', { + "type": "string", + "description": "source code of Hook's view", + "required": false +}); + +hook.property('presenterSource', { + "type": "string", + "description": "source code of Hook's presenter", + "required": false +}); + +hook.property('mode', { + "type": "string", + "enum": ["Production", "Development"], + "required": true, + "default": "Development" +}); + +// cache settings +hook.property('cacheSourceCode', { + "type": "boolean", + "default": false, + "required": true +}); + +hook.property('cacheThemeView', { + "type": "boolean", + "default": false, + "required": true +}); + +hook.property('cacheThemePresenter', { "type": "boolean", "default": false, "required": true }); +hook.property('isPromoted', { + "type": "boolean", + "description": "Promoted hooks are top-level Hooks show-cased on https://hook.io", + "default": false +}); + +hook.property('isPrivate', { + "type": "boolean", + "description": "Private hooks require access keys", + "default": false +}); + +var checkRoleAccess = require('../../server/routeHandlers/checkRoleAccess'); + +hook.before('create', function (data, next) { + // check auth role + var self = this; + checkRoleAccess({ req: self.req, res: self.res, role: "hook::create" }, function (err, hasPermission) { + if (!hasPermission) { + next(new Error(config.messages.unauthorizedRoleAccess(self.req, "hook::create")), data); + //return res.end(config.messages.unauthorizedRoleAccess(req)); + } else { + next(null, data); + } + }); +}); + +function onlyUniqueHookNames (data, next) { + // TODO: move this code to resource library as composite key + return hook.find({ + owner: data.owner, + name: data.name + }, function (err, results) { + if (err) { + return next(err); + } + if (results.length > 0 && results[0].id !== data.id) { + var msg = 'Hook already exists ' + '/' + data.owner + "/" + data.name; + return next(new Error(msg)); + } else { + next(null, data); + } + }); +} + +function sourceContainsTelegram (str) { + var contains = false; + str = str || ''; + if (str.search(/telegram/) !== -1) { + contains = true; + } + return contains; +}; + +hook.before('create', function (data, next) { + // slugify name + data.name = slug(data.name); + next(null, data); +}); + +hook.before('create', onlyUniqueHookNames); +hook.before('update', onlyUniqueHookNames); + +hook.before('create', onlyPaidAccountsCanUseTelegram); +hook.before('update', onlyPaidAccountsCanUseTelegram); + +hook.after('destroy', function clearMetrics (data, next) { + rateLimiter.removeService({ owner: data.owner, name: data.name }, next) +}); + +// There have been to many international Telegram users who want free hosting for bots with no intention to ever pay +// These additional non-paying users were skewing resource distribution ratios which was in turn causing problem for other users +// Now we only allow paid accounts to save or update source code which contains Telegram +function onlyPaidAccountsCanUseTelegram (data, next) { + var self = this; + if (!sourceContainsTelegram(data.source)) { + return next(null, data); + } + // The source contains Telegram code, check to see if account is paid + user.findOne({ name: data.owner }, function (err, _user) { + if (err) { + return next(err); + } + if (_user.paidStatus === "paid" || (self.req && self.req.session && self.req.session.paidStatus === "paid")) { + next(null, data); + } else { + var msg = { + error: true, + message: "Only paid accounts can use Telegram", + type: "paid-account-required-telegram" + }; + next(new Error(JSON.stringify(msg))) + } + }); +} + +function onlyPaidAccountsCanCreatePrivateServices (data, next) { + var self = this; + if (data.isPrivate) { + user.findOne({ name: data.owner }, function (err, _user) { + if (err) { + return next(err); + } + if (_user.paidStatus === "paid" || (self.req && self.req.session && self.req.session.paidStatus === "paid")) { + next(null, data); + } else { + var msg = { + error: true, + message: "Only paid accounts can create private Hook Services!", + type: "paid-account-required" + }; + next(new Error(JSON.stringify(msg))) + } + }); + } else { + next(null, data); + } +} + +// check that non-paid account is not trying to create private hook +hook.before('create', onlyPaidAccountsCanCreatePrivateServices); +// check that non-paid account is not trying to create private hook +hook.before('update', onlyPaidAccountsCanCreatePrivateServices); + + +// having a metric report for the service is now required as of 4/12/2019 +// this is so we use the non-existence of a metric to indicate the load balance should +// immediately 404 the service request instead of routing it to the worker pool trigger a couchdb / redis lookup.. +hook.after('create', function (data, next) { + rateLimiter.registerService({ owner: data.owner, name: data.name }, function () { + next(null, data); + }); +}); +hook.after('update', function (data, next) { + rateLimiter.registerService({ owner: data.owner, name: data.name }, function () { + next(null, data); + }); +}); + +hook.after('create', function (data, next) { + // updates metrics for total hook count after creating + metric.incr('/hook/count'); + next(null, data); +}); + hook.fork = require('./fork'); hook.run = require('./run'); -hook.runRemote = require('./runRemote'); +hook.gateway = require('./gateway'); hook.runHook = require('./runHook'); -hook.determineRequestFormat = require('./determineRequestFormat'); -hook.parseRequestBody = require('./parseRequestBody'); hook.formatError = require('./formatError'); -hook.fetchHookSourceCodeFromGithub = require('./fetchHookSourceCodeFromGithub'); -hook.fetchHookTheme = require('./fetchHookTheme'); -hook.fetchHookPresenter = require('./fetchHookPresenter'); -hook.loadPresenter = require('./loadPresenter'); -hook.attemptToRequireUntrustedHook = require('./attemptToRequireUntrustedHook'); +hook.fetchHookSourceCode = require('./fetchHookSourceCode'); hook.preprocessHook = require('./preprocessHook'); -hook.runUntrustedHook = require('./runUntrustedHook'); -hook.postprocessHook = require('./postprocessHook'); +hook.viewPresenter = require('microcule').viewPresenter; +hook.runUntrustedService = require('run-service'); +hook.runRemote = require('run-remote-service'); module['exports'] = hook; \ No newline at end of file diff --git a/lib/resources/hook/loadPresenter.js b/lib/resources/hook/loadPresenter.js deleted file mode 100644 index 3b463daa..00000000 --- a/lib/resources/hook/loadPresenter.js +++ /dev/null @@ -1,10 +0,0 @@ -module['exports'] = function loadPresenter (path, callback) { - var _presenter, - err = null; - try { - _presenter = require(path); - } catch (e) { - err = e; - } - callback(err, _presenter); -}; diff --git a/lib/resources/hook/parseRequestBody.js b/lib/resources/hook/parseRequestBody.js deleted file mode 100644 index cbe8b1a3..00000000 --- a/lib/resources/hook/parseRequestBody.js +++ /dev/null @@ -1,76 +0,0 @@ -var mergeParams = require('../../../view/mergeParams'); - -module['exports'] = function parseRequestBody (req, res, next) { - var hook = require('./'); - if (req.method === "GET") { - return mergeParams(req, res, function(){ - return next(req, res); - }); - } else if (req.method === "POST") { - - // only attempt to parse body if its multipart form or urlencoded form, if not - // do not parse as we don't want to interrupt req - var contentTypes = []; - if (req.headers && req.headers['content-type']) { - contentTypes = req.headers['content-type'].split(';'); - } - // - // If a content-type of multipart/form-data or application/x-www-form-urlencoded is detected, - // use busboy to parse the incoming form. - // - // For multipart file uploads: - // Each file is added to the request.resource.params as a pipe, - // this file upload pipe can later be processed by the hook - // - - if (contentTypes.indexOf("multipart/form-data") !== -1 || - contentTypes.indexOf("application/x-www-form-urlencoded") !== -1) { - var Busboy = require('busboy'); - var inspect = require('util').inspect; - - // create two busboy instances - // one for parsing multipart form files, another for parsing urlencoded form fields - var busboyFiles = new Busboy({ headers: req.headers }); - var busboyFields = new Busboy({ headers: req.headers }); - - // a multipart file upload was detected, add this upload pipe to the resource params - busboyFiles.on('file', function(fieldname, file, filename, encoding, mimetype) { - req.resource.params[fieldname] = file; - }); - - // a urlencoded form field was detected, add it's value to resource params - busboyFields.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) { - req.resource.params[fieldname] = val; - }); - - // when all fields have been parsed, merge the resource params and continue - busboyFields.on('finish', function() { - mergeParams(req, res, function(){ - next(req, res); - }) - }); - - // when all multipart files have been uploaded, do nothing - // these upload file pipes should be handled by the user-defined Hook - busboyFiles.on('finish', function() { - // do nothing - }); - - // pipe the incoming request to busboy for processing - req.pipe(busboyFiles); - req.pipe(busboyFields); - - //return next(req, res); - - - } else if (contentTypes.indexOf("application/json") !== -1 ) { - jsonParser(req, res, function(){ - next(req, res); - }); - } else { - // Incoming request was a POST, but did not contain content types of multipart or urlencoded form - // This means we should treat the incoming request is a streaming request - next(req, res); - } - } -} diff --git a/lib/resources/hook/postprocessHook.js b/lib/resources/hook/postprocessHook.js deleted file mode 100644 index f35057d5..00000000 --- a/lib/resources/hook/postprocessHook.js +++ /dev/null @@ -1,40 +0,0 @@ - -module['exports'] = function postprocessHook (err, opts, _hook) { - var hook = require('./'); - - var req = opts.req, - res = opts.res; - // after the hook is run fire and forget to update the hook count - // and hook schema - var query = { name: req.params.hook, owner: req.params.username }; - hook.find(query, function (err, results) { - if (err) { - throw err; - // do nothing - // return _res.end(err.message); - } - if (results.length > 0) { - var _h = results[0]; - - if (typeof err !== null) { - _h.ran = _h.ran + 1; - } - - // in addition, save the schema onto the document ( for the UI ) - _h.mschema = _hook.schema; - _h.theme = _hook.theme; - _h.presenter = _hook.presenter; - _h.save(function(err) { - if (err) { - console.log("UNABLE TO SAVE HOOK"); - console.log(err); - // do nothing - } - }); - - } else { - // do nothing - } - - }); -} diff --git a/lib/resources/hook/preprocessHook.js b/lib/resources/hook/preprocessHook.js index de0496ea..5ca173eb 100644 --- a/lib/resources/hook/preprocessHook.js +++ b/lib/resources/hook/preprocessHook.js @@ -1,6 +1,14 @@ +// TODO: remove this file +// move env code to new module +// move theme presenter code to appropiate modules + var hook = require('./'); var user = require("../user"); +var keys = require("../keys"); +var cache = require("../cache"); var config = require('../../../config'); +var Route = require('route-parser'); +var role = require('../role'); module['exports'] = function preprocessHook (opts, userModule, callback) { var req = opts.req, @@ -11,18 +19,125 @@ module['exports'] = function preprocessHook (opts, userModule, callback) { // console.log('running', opts.req.params, opts.params, opts.req.resource.params) // console.log('about to hook it', userModule, userModule.theme, userModule.presenter) - if (typeof req.params.username === "undefined") { - req.params.username = "Marak"; + if (typeof req.params.owner === "undefined") { + req.params.owner = "marak"; } // load up the user who owns this hook, // so we can load Hook Enviroment variables - user.find({ name: req.params.username }, function (err, _user){ + // TODO: remove this code and replace with user.getOrSetCache + var key = '/user/' + req.params.owner; + cache.get(key, function (err, _user) { + if (_user === null) { + findUser(function(err, u){ + if (err) { + return callback(err); + } + cache.set(key, u[0], function(){ + finish(err, u[0]); + }); + }); + } else { + finish(null, _user); + } + }); + + // if a Hook.path is defined and there is a wildcard route present + if (typeof req.hook.path !== "undefined" && req.hook.path.length > 0 + && typeof req.params["0"] !== "undefined" && req.params["0"].length > 0) { + // attempt to match wildcard route ( recieved after /:owner/:hook/* ) against Hook.path + var route, routeParams; + try { + route = new Route(req.hook.path); + routeParams = route.match("/" + req.params["0"]); + } catch (err) { + return res.end('Issue with route params ' + err.message) + } + // if no route match is found, 404 with a friendly error + if (routeParams === false) { + res.writeHead(404); + // TODO: make error customizable + res.end('Invalid path: ' + req.params["0"] + ' does not match ' + req.hook.path); + return; + } + // route matches, map route parameters to resource scope + for (var p in routeParams) { + req.resource.params[p] = routeParams[p]; + } + } + + function findUser (cb) { + + user.find({ name: req.params.owner }, function (err, _user) { + + if (err) { + return res.end(err.message); + } + + if (_user.length === 0) { + return res.end('Error: Could not find user. Please contact support.') + } + + var r = _user[0]; + if (typeof r.hookAccessKey === "undefined" || r.hookAccessKey.length === 0) { + keys.create({ + name: "api-access-key", + owner: r.name, + key_type: "internal", + roles: Object.keys(role.roles) + }, function (err, k) { + if (err) { + console.log('Error: Issue with creating internal key', err.message); + // If for some reason the key name exists but it missing from user, re-attach it + // Note: This shouldn't happen often outside of testing or manually adjusting accounts + if (err.message === "Key name api-access-key already exists!") { + return keys.findOne({ + name: "api-access-key", + owner: r.name, + }, function (err, k) { + return saveKeyToUser(k); + }); + } + return cb(err); + } + saveKeyToUser(k); + function saveKeyToUser (k) { + var _update = { + id: r.id, + hookAccessKey: k.hook_private_key + }; + // console.log('attemping to update user', _update) + user.update(_update, function(err, re){ + if (err) { + console.log('Error: ', err.message); + } + cb(err, re); + }); + } + }); + } + else { + cb(err, _user); + } + + }); + }; + + function finish (err, _user) { + var _env = {}; + _user = _user || {}; + // since we probably have a user document for the owner the service, + // might as well attach a copy of it to the request for later usage + req._user = { + name: _user.name, + hookAccessKey: _user.hookAccessKey, + githubAccessToken: _user.githubAccessToken + }; if (err) { // do nothing, set env to empty _env = {}; } else { - _env = _user[0].env || {}; + _env = _user.env || {}; var keys = Object.keys(_env).sort(); var __env = {}; keys.forEach(function(k){ @@ -31,19 +146,25 @@ module['exports'] = function preprocessHook (opts, userModule, callback) { _env = __env; } req.env = _env; - // loadTheme + + // determine if there are any plugins that need to be run + // console.log('which plugins are available', req.hook.inputs) if (userModule.theme && userModule.theme.length > 0) { var theme, presenter; - theme = userModule.theme || config.defaultTheme; - presenter = userModule.presenter || config.defaultPresenter + // theme = userModule.theme || config.defaultTheme; + // presenter = userModule.presenter || config.defaultPresenter + + theme = userModule.theme; + presenter = userModule.presenter; + // if a theme was set ( not the default debug theme, and no presenter was given, use simple.js ) if (typeof theme === "undefined" || theme.length === 0) { theme = config.defaultTheme; if (typeof presenter === "undefined" || typeof presenter !== "function") { - presenter = "http://hook.io/themes/simple/index.js"; + presenter = "https://hook.io/themes/simple/index.js"; } } @@ -54,7 +175,8 @@ module['exports'] = function preprocessHook (opts, userModule, callback) { userModule.presenter = presenter; return callback(null, userModule); } else { - return callback(null, opts, userModule); + return callback(null, userModule); } - }); + }; + } diff --git a/lib/resources/hook/run.js b/lib/resources/hook/run.js index e066d686..0738c09c 100644 --- a/lib/resources/hook/run.js +++ b/lib/resources/hook/run.js @@ -1,5 +1,10 @@ -var mergeParams = require('../../../view/mergeParams'); +var mergeParams = require('merge-params'); +var cache = require('../cache'); +var metric = require('../metric'); +var config = require('../../../config'); +var async = require('async'); +/* var view = require('view'); var _view; view.create({ path: __dirname + "/../../../view"}, function (err, v){ @@ -8,47 +13,248 @@ var _view; } _view = v; }); +*/ -function handle404(req, res) { - _view['404'].present({ - request: req, - response: res +function handle404 (req, res) { + res.status(404); + return res.end('404 not found /' + req.params.owner + '/' + req.params.hook); + // TODO: actually use 404 handler scoped to app.view ( missing from this file ) + app.view['404'].present({ + req: req, + res: res }, function (err, html){ res.writeHead(404); res.end(html); }) }; +var themes = require('../themes'); + module['exports'] = function run (req, res, next) { - var hook = require('./'); - if (typeof req.params.subhook !== "undefined" && req.params.subhook.length) { - req.params.hook = req.params.hook + "/" + req.params.subhook; - } + var hook = require('./'); mergeParams(req, res, function(){ - var query = { owner: req.params.username, name: req.params.hook }; - hook.find(query, function (err, result) { - if (err) { - return res.end(err.message); - } - if (result.length === 0) { - return handle404(req, res); + // check to see if user has hit rate limit for account + // console.log('running hook'.yellow, req.url) + // TODO: since cache is here, its tricky for auth, move auth...trying elsewhere + var key = '/hook/' + req.params.owner + "/" + req.params.hook; + + if (typeof req.resource.params._rev === "string") { + return findHook(function(err, h){ + if (err) { + return res.end(err.message); + } + var nanoConfig = 'http://' + config.couch.username + ":" + config.couch.password + "@" + config.couch.host + ":" + config.couch.port; + var nano = require('nano')(nanoConfig); + // Note: possible issue with running from cache? + // console.log('looking up rev', h[0].id, req.resource.params._rev) + nano.request({ + db: 'hook', + doc: h[0].id, + method: 'get', + qs: { rev: req.resource.params._rev } + }, function (err, rev) { + if (err) { + return res.end(err.message); + } + return finish(err, rev); + }); + }); + } + + // TODO: if params.invalidate has been sent, attempt to remove item from cache if it exists + // if (typeof req.params.invalidate !== 'undefined') {} + cache.get(key, function(err, _hook){ + if (_hook === null) { + findHook(function(err, h){ + cache.set(key, h[0], function(){ + finish(err, h[0]) + }); + }); + } else { + finish(null, _hook); } + }) + + function findHook (cb) { + // find hook based on owner and id + var query = { owner: req.params.owner, name: req.params.hook }; + hook.find.call({"req": req }, query, function (err, result) { + if (err) { + return res.end('Error communicating with couchdb: \n\n' + err.message); + } + + if (result.length === 0) { + // could not find hook, reduce concurrency + // no need to reduce concurrency here? as it hasn't incremented? + // metric.zincrby(['running', -1, req.params.owner]); + return handle404(req, res); + } + cb(null, result); + }); + }; + + function finish (err, result) { + // attach found hook onto request scope - req.hook = result[0]; - - req.params.gist = result[0].gist; - req.resource.params.gist = result[0].gist; - hook.parseRequestBody(req, res, function(){ - hook.determineRequestFormat(req , res, function(req, res){ - hook.runHook({ req: req, res: res }, function(err, res){ - // do nothing - console.log('ran the hook', err, res) + // console.log('attaching hook'.yellow, result) + req.hook = result; + + // override theme if set in url + var params = req.resource.params; + if (typeof params.theme !== 'undefined') { + + if (typeof themes[params.theme] === 'undefined') { + return res.end('Aborting Request! We we unable to find any themes on file called: ' + params.theme); + } + + req._themeOverride = true; + req._theme = themes[params.theme].theme; + req._presenter = themes[params.theme].presenter; + } else { + req._themeOverride = false; + } + // Removed as legacy 4/23/16 + // req.params.gist = result.gist; + // req.resource.params.gist = result.gist; + // console.log('way before running hook', req.hook) + // req.processedInputs = true; + // console.log('found the following hook'.blue, req.hook); + var inputs = req.hook.inputs || []; + // console.log('found inputs'.red, inputs, req.level) + + // Determine if this a chained service, if so, we need to execute the chain of services using async + if (inputs.length > 0 && !req.processedInputs) { + // For every chained service, we need to re-call hook.run and execute these services as a middleware chain + + // re-run hook with modified req/res url? is that okay? + var key = req.params.owner + "/" + req.params.hook; + var ogOwner = req.params.owner, ogHook = req.params.hook; + + if (typeof req.level === 'undefined') { + // Keeps track of the levels of service execution in the chain + // Each level can be considered the straight chain of services + // If the services which are chained call additional chains, that will count as an additional level ( and so on ) + req.level = 0; + req.levels = [key]; + } else { + // Since inputs were detected on this hook, increase the level of the chain + req.level ++; + req.levels.push(req.params.owner + '/' + req.params.hook) + } + + // Note: Default max levels is 20. This should cover most cases ( for now ) + // In theory, we shouldn't ever this code path due to ciricular references ( since they should be validated before being saved ) + if (req.level > 20) { + res.write("We've stopped execution since the chain is now 20 levels deep.\n"); + res.write("This usually indicates an uncaught circular reference, which our systems are suppose to catch before executions.\n"); + res.end("If this is an error or you require a longer chain, please contact support.\n"); + return; + } + + // For each input in the inputs array, run the service in a serial async chain + // If any errors occur during the execution of the chain, the response will end that that error message + async.eachSeries(inputs, function iterate(item, cb){ + var input = item.split('/'); + if (input.length < 2) { + return res.end('invalid service name ' + input.toString()) + } + req.params.owner = input[0].toLowerCase(); + req.params.hook = input[1].toLowerCase(); + // console.log('LEVELS'.yellow, req.levels, input) + + // Do not allow services to reference themself ( circular reference ), + // this would cause the request to never end, which is not good + if (req.levels.indexOf(req.params.owner + '/' + req.params.hook) !== -1) { + res.write('Cannot chain service to itself: ' + req.params.owner + '/' + req.params.hook + '\n') + return res.end('Circular chain detected...aborting'); + } + + // console.log('found input'.blue, input, req.params) + run(req, res, function(err){ + // console.log('service completed'.magenta, req.params.owner, req.params.hook) + if (err) { + // There is some irregulairty to how errors are being handled + // Inside the execution chain for running hooks, sometimes errors may be sent directly + // to res.end(err.message) instead of being propigated down the chain to this callback + // It would be better to have all errors be passed forward and handled here instead of letting + // the chain directly calling res.end() + // see: https://github.com/bigcompany/hook.io/issues/53 + console.log('hook had an issue running ' + err.message); + return res.end(err.message); + } + // console.log('ended current chain'.blue, req.url, cb.toString()) + cb(); + }); + }, function completed (err) { + req.params.owner = ogOwner; + req.params.hook = ogHook; + // TODO: allow for multi-level chains, right now we can only chain once ( at the top level ) + // Any attempts to include sub-chains will not be executed + req.processedInputs = true; + // console.log('made it to final hook'.blue, ogOwner, ogHook) + run(req, res, function (err, response) { + if (err) { + // There is some irregulairty to how errors are being handled + // Inside the execution chain for running hooks, sometimes errors may be sent directly + // to res.end(err.message) instead of being propigated down the chain to this callback + // It would be better to have all errors be passed forward and handled here instead of letting + // the chain directly calling res.end() + // see: https://github.com/bigcompany/hook.io/issues/53 + console.log('hook had an issue running ' + err.message); + return res.end(err.message); + } + // console.log('service completed'.magenta, response) + // Continue to next() middleware ( if the none of the services have called end ) + return next(); + // return res.end(); + /* + // Can this block now be removed? + if (typeof req.level !== 'undefined') { // req.hook.inputs + // if the callback made it this far and levels have been defined, it's possible we have additional middlewares to process, + // do not end response here + // Continue to next() middleware ( if the none of the services have called end ) + next(); + } else { + } + */ + }); }); - }); - }); + } else { + hook.runHook({ req: req, res: res }, function (err, response) { + if (err) { + // There is some irregulairty to how errors are being handled + // Inside the execution chain for running hooks, sometimes errors may be sent directly + // to res.end(err.message) instead of being propigated down the chain to this callback + // It would be better to have all errors be passed forward and handled here instead of letting + // the chain directly calling res.end() + // see: https://github.com/bigcompany/hook.io/issues/53 + console.log('hook had an issue running ' + err.message); + if (req.jsonResponse) { + var r = { + error: true, + message: err.message + }; + return res.json(r); + } else { + return res.end(err.message); + } + } + // console.log('service completed'.green, response) + // Continue to next() middleware ( if the none of the services have called end ) + next(); + }); + } + + // TODO: check and run for outputs + function _outputs () { + + } + + }; + }); }; \ No newline at end of file diff --git a/lib/resources/hook/runHook.js b/lib/resources/hook/runHook.js index b1cc0cb5..274bd16e 100644 --- a/lib/resources/hook/runHook.js +++ b/lib/resources/hook/runHook.js @@ -1,55 +1,406 @@ +var request = require('hyperquest'); +var log = require("../log"); +var user = require("../user"); +var cache = require("../cache"); +var metric = require("../metric"); +var events = require('../events'); +var formatError = require("./formatError"); +var themes = require('../themes'); +var config = require('../../../config'); +var checkRoleAccess = require('../../server/routeHandlers/checkRoleAccess'); +var stack = require('microcule'); +// use bash version 4 by spawned bash services by default +stack.config.bash.version = 4; +var resource = require('resource'); module['exports'] = function runHook (opts, callback) { + // console.log('running the hook'.yellow, req.params.owner, req.params.hook) var hook = require('./'); - opts = opts || {}; var req = opts.req; var res = opts.res; - var gist = req.resource.params.gist; var params = req.resource.params; opts.gist = params.gist; opts.params = params; + var h = req.hook; + var untrustedService = {}; + untrustedService.name = h.name; + untrustedService.owner = h.owner; + untrustedService.gist = h.gist; + untrustedService.schema = {}; + untrustedService.hookType = h.hookType || "service"; + untrustedService.customTimeout = h.customTimeout || config.UNTRUSTED_HOOK_TIMEOUT; // TODO: clean up execution chain, normalize error handlers, // put entire sequence into async iterator + /* + function loadViewPresenter (untrustedService, _theme, _presenter, cb) { + // console.log('pre fetchHookTheme', _theme); + hook.fetchHookTheme(opts, _theme, function(err, _theme){ + // console.log('post fetchHookTheme', err); + if (err) { + return opts.res.end(hook.formatError(err, { tag: 'fetchHookTheme' }).message) + } + hook.fetchHookPresenter(opts, _presenter, function(err, _presenter){ + if (err) { + return opts.res.end(hook.formatError(err, { tag: 'fetchHookPresenter' }).message) + } + untrustedService.presenterSource = _presenter; + // TODO: replace with request scope? + //req.view = _theme; + //req.presenter = _presenter; + cb(null, untrustedService) + }); + }); + }; + */ + + function validateAndRunHook (opts, cb) { + + if (req.hook.mschemaStatus === "disabled") { + delete req.hook.mschema; + } + + // TOOD: remove manual mschema call here, should use input plugin instead + // TODO: only perform validation if schema exists + stack.plugins.mschema(req.hook.mschema)(req, res, function (err) { + //console.log('post validateServiceInput', err); + if (err) { + // changes format of validation errors as of 8/25/16 + // return opts.res.end(hook.formatError(err).message) + return cb(err); + } + + _runHook(req, res); + + // Remark: Theme override code has been disabled here? + /* + if (h.themeStatus === "enabled" || opts.req._themeOverride === true) { // TODO: better none theme detection, actually don't use any theme at all + console.log('rendering with view'.yellow, untrustedService) + hook.viewPresenter(untrustedService, req, res, function (err, input, output){ + _runHook(req, output); + }); + } else { + // console.log('rendering without view', untrustedService) + } + */ + + function _runHook () { + + resource.emit('hook::run', { + name: req.hook.name, + owner: req.hook.owner, + // ip: req.connection.remoteAddress, // why was this removed? we do need it. if anything, filter out this property in /:username/events logs for public services + url: req.url + }); + + function _errorHandler (err) { + err = formatError(err); + return res.end(err.message); + } + + var customLogger = new log.Logger({ + ip: req.connection.remoteAddress, + service: { + owner: untrustedService.owner, + name: untrustedService.name, + } + }); + + var debug = customLogger.log.bind(customLogger); // Imporant: Must bind logger instance or it will lose `self` scope + + // TODO: this block should probably be removed / and or reviewed + // appears to be theme and view-presenter related code + if (req.saveHook === true) { + delete req.hook._rev; + req.hook.source = req.hook.originalSource; + //delete req.hook.originalSource; + //console.log('updating and saving', req.hook) + hook.update(req.hook, function (err, result) { + // TODO: update cache? + if (err) { + // ignore document update error messages for now + // TODO: fix this + // return res.end(err.message); + console.log('could not save hook', err.message); + console.log('error spawn') + return _spawn(); + } + var key = '/hook/' + req.hook.owner + "/" + req.hook.name; + cache.set(key, result, function(){ + _spawn(); + }); + }); + } else { + _spawn(); + } + + // spawn hook service as child process in chroot jail + function _spawn () { + + req.env.hookAccessKey = req._user.hookAccessKey; + return _finishSpawn(); + + function _finishSpawn () { + // console.log("CALLED _finishSpawn") + + /* + if (typeof untrustedService.code === "undefined" && typeof untrustedService.evalSource === "string") { + untrustedService.code = untrustedService.evalSource; + } + */ + + // Remark: Build new config object with only untrusted configuration values + var _config = { + SERVICE_MAX_TIMEOUT: config.UNTRUSTED_HOOK_TIMEOUT, + messages: config.messages + } + untrustedService.config = _config; + untrustedService.isHookio = true; + untrustedService.log = debug; + + if (untrustedService.themeStatus !== "disabled" && untrustedService.view) { + stack.viewPresenter(untrustedService, req, res, function (err, req, output){ + if (err) { + return callback(err); + } + _spawnService(req, output); + }) + } else { + _spawnService (req, res); + } + + function _spawnService (_req, _res) { + if (config.worker.useNSJAIL) { + untrustedService.jail = "nsjail"; + untrustedService.jailArgs = config.worker.nsJailArgs; + untrustedService.home = "/"; + } + console.log(new Date(), 'spawning', req.method, req.url); + // untrustedService.home = __dirname + "/../../../"; + + // indicates that service should output stderr to stdout on non-zero exit codes + // this is useful for users developing and debugging services + // its the least surprising behavior for all use-cases + // the only potential down-side is situations where you don't want to expose stderr data to the client + // we could expose this option to the API and allow users to toggle it to hide errors to the client + untrustedService.redirectStderrToStdout = true; + + // indicates that service should not close response when it exits + // this is required for services that may be chains + // for non-chained services, the final _res.end(); should end the response + untrustedService.endResponseOnExit = false; + + stack.plugins.spawn(untrustedService)(_req, _res, function _serviceEnded (err, result) { + // console.log('service has ended'.red, req.params.owner, req.params.hook, err, result); + // + // This is the callback for when the service has actually completed ( output has ended / next has been called from stack.spawn ) + // + // console.log('inputs:levels', req.hook.inputs, req.level) + if (typeof req.level !== 'undefined') { // req.hook.inputs + // if the callback made it this far and levels have been defined, it's possible we have additional middlewares to process, + // do not end response here + callback(); + } else { + _res.end(); + } + }); + } + } + } + } + }); + } + + function _execute2 () { + + hook.preprocessHook(opts, untrustedService, function (err, untrustedService) { + // console.log("_execute2 untrustedService", untrustedService) + if (err) { + // opts.res.writeHead(500); + return opts.res.end(hook.formatError(err).message) + } + untrustedService.language = req.hook.language || "javascript"; + + untrustedService.view = req.hook.themeSource; + untrustedService.themeStatus = req.hook.themeStatus; + untrustedService.presenter = req.hook.presenterSource || req.hook.presenter || ""; + + // check if source of gist is remote, if so use the corresponding remote source code middleware + if (req.hook.sourceType === "gist" && typeof untrustedService.gist === "string" && untrustedService.gist.length > 0) { + var gistID = untrustedService.gist.split('/'); + gistID = gistID[gistID.length - 1]; + if (typeof req._user.githubAccessToken === "undefined") { + // TODO: move to config.messages + // TODO: fallback to config. marak access token so previous services dont fail + // fallback to legacy gist api + return fetchRemoteGistSource({ + req: req, + res: res, + gist: untrustedService.gist + }, function (err, code) { + if (err) { + // opts.res.writeHead(500); + return opts.res.end(hook.formatError(err).message) + } + req.code = code; + untrustedService.source = req.code || h.source; + untrustedService.code = req.code || h.source; + validateAndRunHook(opts, callback); + }); + // return callback(new Error('Could not load Github oauth token!\n\nAs of 12/24/2017, a valid Github OAuth token is now required for all Github code sources.\nTo fix this, simply visit https://hook.io/login/github in your browser to generate a valid Github OAuth access token.')); + } + stack.plugins.sourceGithubGist({ + gistID: gistID, + main: req.hook.mainEntry, + token: req._user.githubAccessToken + })(req, res, function (){ + if (err) { + return next(err) + } + untrustedService.source = req.code || h.source; + untrustedService.code = req.code || h.source; + validateAndRunHook(opts, callback); + }); + } + else if (req.hook.sourceType === "githubRepo" && typeof req.hook.githubRepo === "string" && req.hook.githubRepo.length > 0) { + if (typeof req._user.githubAccessToken === "undefined") { + // TODO: move to config.messages + // TODO: fallback to config. marak access token so previous services dont fail + if (config.env === 'prod') { + return callback(new Error('Could not load Github oauth token!\n\nAs of 12/24/2016, a valid Github OAuth token is now required for all Github code sources.\nTo fix this, simply visit https://hook.io/login/github in your browser to generate a valid Github OAuth access token.')); + } else { + // This is needed for tests + req._user.githubAccessToken = config.github.accessToken; + } + } + stack.plugins.sourceGithubRepo({ + repo: req.hook.githubRepo, + branch: req.hook.githubBranch, + main: req.hook.mainEntry, + token: req._user.githubAccessToken, + errorHandler: function (err, next) { + var newErr; + if (err.message === "Bad credentials") { + newErr = new Error('Invalid Github oauth key: ' + token + '\nThis is most likely a hook.io problem. Please contact support.'); + } + if (err.message === "Not Found") { + newErr = new Error('Could not load: ' + req.hook.githubRepo + "@" + req.hook.githubBranch + ":" + req.hook.mainEntry + '\nIs this information correct?\nIf ' + req.hook.githubRepo + ' is a private repo, please make sure to login at ' + config.app.url + '/login/github/private-repos in order to grant hook.io access'); + } + return next(newErr); + } + })(req, res, function (err) { + if (err) { + return callback(err); + } + untrustedService.source = req.code || h.source; + untrustedService.code = req.code || h.source; + validateAndRunHook(opts, callback); + }); + } else { + untrustedService.source = req.code || h.source; + untrustedService.code = req.code || h.source; + validateAndRunHook(opts, callback); + } + }); + } + + // TODO: move jsonResponse check to a more generalized location + var acceptTypes = []; + + if (req.headers && req.headers.accept) { + acceptTypes = req.headers.accept.split(','); + } + if (acceptTypes.indexOf('text/html') === -1) { + req.jsonResponse = true; + } + + checkRoleAccess({ req: req, res: res, role: "hook::run" }, function (err, hasPermission) { + // only protect running of private hooks + if (h.isPrivate !== true) { + hasPermission = true; + } + if (!hasPermission) { + //runHook(); + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::run")); + } else { + // legacy _execute() method removed 10/13/16 in favor of stackvana plugins + // _execute(); + _execute2(); + } + }); + +} + +function fetchRemoteGistSource (opts, cb) { + + var gist = opts.gist; // || "https://gist.github.com/Marak/357645b8a17daeb17458"; + var userScript = gist.replace('https://gist.github.com/', ''); + userScript = userScript.replace('/', ' '); + var parts = userScript.split(" "); + var username = parts[0], + script = parts[1]; + + var req = opts.req, res = opts.res; + var gistAPI = "https://api.github.com/gists/" + script; - // execute hook - hook.fetchHookSourceCodeFromGithub(opts, function(err, code) { - if (err) { - throw err; - } - // console.log('fetchHookSourceCodeFromGithub',req.params) - hook.attemptToRequireUntrustedHook(opts, function(err, untrustedModule){ - if (err) { - opts.res.writeHead(500); - return opts.res.end(hook.formatError(err)) - } - // console.log('attemptToRequireUntrustedHook',req.params) - hook.preprocessHook(opts, untrustedModule, function(err, userModule) { - hook.fetchHookTheme(userModule.theme, function(err, _theme){ - if (err) { - throw err; - } - // console.log('fetchHookTheme',req.params) - hook.fetchHookPresenter(userModule.presenter, function(err, _presenter){ - // console.log('fetch presenter') - if (err) { - return opts.res.end(hook.formatError(err)); - } - // console.log('fetchHookPresenter',req.params) - opts.theme = _theme; - opts.presenter = _presenter; - hook.runUntrustedHook(opts, userModule, function(err, result){ - if (err) { - return opts.res.end(hook.formatError(err)); - } - callback(err, result); - - hook.postprocessHook(err, opts, userModule); - }); - }); - }); - }); - }); - }); + // Indicate that a change has happened to the hook and it should be saved + // req.saveHook = true; + /* + fetches the latest version of source code from remote Github Gist Source + requires a valid and correctly formatted link to a github gist URL + */ + var options = { + headers: { + "User-Agent": "hook.io source code agent" + } + }; + // must validate request. github API limits requests per hour. non-authorizated HTTP requests cap out quickly. + // warning: in the future, we might have to ask github to increase our API limit / create several access tokens + if (config.github.accessName && config.github.accessName.length && config.github.accessToken && config.github.accessToken.length) { + options.headers['Authorization'] = "Basic " + new Buffer(config.github.accessName + ":" + config.github.accessToken, "utf8").toString("base64") + } + // console.log('getting gistAPI', gistAPI) + request.get(gistAPI, options, function (err, apiRes) { + if (err) { + return res.end(err.message); + } + var apiReply = ''; + apiRes.on('data', function (c) { + apiReply += c.toString(); + }); + apiRes.on('error', function(err){ + return callback(err); + }); + apiRes.on('end', function(){ + var gistJSON = {}, files, keys; + try { + gistJSON = JSON.parse(apiReply.toString()); + files = gistJSON.files; + keys = Object.keys(files); + } catch (err) { + return cb(new Error('Not valid JSON: ' + apiReply)); + } + // assume first file is the source code + requestSource(gistJSON.files[keys[0]].raw_url); + }); + function requestSource (source) { + // console.log('getting new hook source', source) + request.get(source, function (err, res) { + if (err) { + return opts.res.end(err.message); + } + var body = ''; + res.on('data', function(c){ + body += c.toString(); + }); + res.on('end', function(){ + var code = body.toString(); + req.hook.source = code; + // console.log('fetchHookSourceCode setting hook.source', code); + cb(null, code); + }); + }); + } + }); } \ No newline at end of file diff --git a/lib/resources/hook/runRemote.js b/lib/resources/hook/runRemote.js deleted file mode 100644 index 5d368dc3..00000000 --- a/lib/resources/hook/runRemote.js +++ /dev/null @@ -1,48 +0,0 @@ -var request = require('hyperquest'); - -// ports of worker pool -var pool = ["10000", "10001", "10002", "10003", "10004"]; - -module['exports'] = function runRemote (req, res) { - - // Remark: User sessions are sticky to specific hook workers - // this stickiness is not needed for the app to work ( as workers are stateless ), - // but is useful for possible caching opportunities - // and containing bad users who are repeatedly running bad scripts - // - if (typeof req.session.worker === "undefined") { - var w = pool.pop(); - req.session.worker = w; - pool.unshift(w); - } - var _url = 'http://localhost:' + req.session.worker + req.url; - - console.log('about to use worker', _url); - - var stream = request.post(_url, { - headers: req.headers - }); - - stream.on('error', function(err){ - console.log('WORKER STREAM ERROR', err) - // do nothing; - res.write('Error communicating with worker ' + req.session.worker + '\n\n'); - res.end(err.stack) - }); - - req.pipe(stream).pipe(res); - - stream.on('response', function (response) { - // replay all headers except set-cookie ( to preserve session ) - for (var p in response.headers) { - // Remark: Don't overwrite the passport session on server - // This may cause issues with user hooks which intend to modify cookies - if (p !== "set-cookie") { - res.setHeader(p, response.headers[p]) - } - } - // replay the status code - res.writeHead(response.statusCode); - }); - -}; \ No newline at end of file diff --git a/lib/resources/hook/runUntrustedHook.js b/lib/resources/hook/runUntrustedHook.js deleted file mode 100644 index 5b38ef19..00000000 --- a/lib/resources/hook/runUntrustedHook.js +++ /dev/null @@ -1,170 +0,0 @@ -var request = require("hyperquest"); -var through = require('through2'); -var user = require("../user"); -var mschema = require("mschema"); -var streamBuffers = require('stream-buffers'); -var through = require('through2'); -var Mustache = require("mustache"); -var View = require('view').View; -var trycatch = require('trycatch'); - -module['exports'] = function runUntrustedHook (opts, untrustedHook, cb) { - - var hook = require('./'); - - var req = opts.req, - res = opts.res; - - console.log('running untrusted hook', opts.req.resource.params) - var untrustedSchema = untrustedHook.schema || {}; - var defaults = mschema.validate(opts.req.resource.params, untrustedHook.schema || {}, { strict: false }); - var debugOutput = []; - var open = function open (url) { - return request(url, { - "headers": { - "accept": "*/*" - } - }); - }; - - var post = function post (url) { - return request.post(url, { - "headers": { - "accept": "*/*" - } - }); - }; - - // A simple debug utility for inspecting data from inside a running hook - var debug = function debug (arg) { - debugOutput.push({ - time: new Date(), - data: arg, - ip : opts.req.connection.remoteAddress - }) - }; - - // create a new buffer and output stream for capturing the hook.res.write and hook.res.end calls from inside the hook - // this is used as an intermediary to pipe hook output to other streams ( such as another hook ) - var hookOutput = new streamBuffers.WritableStreamBuffer({ - initialSize: (100 * 1024), // start as 100 kilobytes. - incrementAmount: (10 * 1024) // grow by 10 kilobytes each time buffer overflows. - }); - - var _headers = { - code: null, - headers: {} - }; - - var output = through(function (chunk, enc, callback) { - hookOutput.write(chunk); - callback() - }, function(){ - var content = hookOutput.getContents(); - // Apply basic mustache replacements - var strTheme = opts.theme; - strTheme = Mustache.render(strTheme, { - hook: { - output: content.toString(), - debug: JSON.stringify(debugOutput, true, 2), - params: defaults.instance, - headers: _headers, - schema: untrustedSchema - } - }); - - if (req.resource.params.format === "raw") { - cb(null, { - output: content, //hookOutput, - debug: debugOutput, - params: defaults.instance, - headers: _headers, - hook: { - schema: untrustedSchema - } - }) - } - - var _view = new View({ template: strTheme, presenter: opts.presenter }); - - // give the presenter 3 seconds to render, or else it has failed - var completedTimer = setTimeout(function(){ - if (!completed) { - return cb(new Error('Hook presenter took more than 3 seconds to load. Aborting request. \n\nA delay of this long usually means the presenter never fired it\'s callback. Check the presenter code for error. \n\nIf this is not the case and you require more than 3 seconds to present your view, please contact hookmaster@hook.io')); - } - }, 3000); - var completed = false; - - try { // this will catch user run-time errors in the presenter - _view.present({ - request: req, - response: res, - output: content, - debug: debugOutput, - instance: defaults.instance, - params: opts.req.resource.params, - headers: _headers - }, function(err, rendered){ - completed = true; - completedTimer = clearTimeout(completed); - cb(null, { - output: rendered, - debug: debugOutput, - params: defaults.instance, - headers: _headers, - hook: { - schema: untrustedSchema - } - }); - }); - } catch (err) { - cb(err); - } - - - }); - var _res = res; - - output.writeHead = function (code, headers) { - _headers.code = code; - for(var h in headers) { - _headers.headers[h] = headers[h]; - } - }; - - if (opts.params.format === "friendly") { - _res = output; - } - - if (defaults.valid === false) { - output.writeHead(500); - output.write(JSON.stringify(defaults.errors, true, 2)); - output.end(); - return cb(defaults.errors, opts, untrustedHook) - //return hook.postprocessHook(false, opts, untrustedHook); - //return opts.res.end(JSON.stringify(defaults.errors, true, 2)) - } - - // async try / catch is required for async user errors - trycatch(function() { - var isStreaming = false; - if (req._readableState.buffer && req._readableState.buffer.length) { - isStreaming = true; - } - untrustedHook({ - env: req.env, - debug: debug, - get: open, - open: open, - post: post, - params: defaults.instance, - req: opts.req, - res: _res, - streaming: isStreaming - }); - }, function(err) { - console.log(err.stack); - return cb(err, opts, untrustedHook) - }); - -} diff --git a/lib/resources/hook/stderr/index.js b/lib/resources/hook/stderr/index.js new file mode 100644 index 00000000..e34f80eb --- /dev/null +++ b/lib/resources/hook/stderr/index.js @@ -0,0 +1,163 @@ +var responseMethods = require('./responseMethods'); +var config = require("../../../../config"); + +var stderr = {}; +module['exports'] = stderr; + +// processes incoming stderr buffer +stderr.onData = function onStderrData (data, status, debug, output) { + var messages = data.toString(); + + + // Remark: Ignore special case"\nmodule.js:333", which is module require error + // This is a bit brittle, but is okay for now + if (messages.substr(0, 1) !== "{" && messages.substr(0, 14) !== "\nmodule.js:333") { + // Remark: Encode any non JSON messages as a JSON error message + var message = { "type": "error", "payload": { "error": messages }}; + return handleMessage(message, status, debug, output); + } + messages = messages.split('\n'); + messages.forEach(function(message){ + if (message.length === 0) { + return; + } + // attempt to parse incoming stderr as JSON message + try { + message = JSON.parse(message.toString()); + } catch (err) { + // don't do anything, ignore + // message = { "type": "error", "payload": { "error": message.toString() }}; + } + handleMessage(message, status, debug, output); + }); +}; + +var handleMessage = stderr.handleMessage = function (message, status, debug, output) { + + var request = require('request'); + + /* + stderr message types: + + error: error event from vm, send error stack as plaintext to client. + log: console.log logging event, send log entry to logging system + end: hook.res.end was called inside the vm, call output.end() + untyped: any untyped messages are considered type `error` and will be wrapped as error types + + */ + // check to see if incoming message is a response method ( like res.writeHead ) + if(typeof responseMethods[message.type] === "function") { + responseMethods[message.type](message, output); + return; + } + + // if the incoming message is end event, signal that is time to end the response + if (message.type === "end") { + status.serviceEnded = true; + } + + // send logging messages to the debug function ( redis logs at this point ) + if (message.type === "log") { + debug(message.payload.entry); + return; + } + // if the incoming message is an error + if (message.type === "error") { + // let's do some custom behavior for MODULE_NOT_FOUND errors, + // i.e. require('colors') when colors is not installed + status.erroring = true; + if (message.payload.code === "MODULE_NOT_FOUND") { + var missingModule = message.payload.error.replace("Cannot find module '", ''); + missingModule = missingModule.substr(0, missingModule.length - 1); + // if a module is missing, check to see if it is a valid module, + // ( exists on npm / doesn't attempt to require other files outside root ) + + status.checkingRegistry = true; + // call out to the hpm server to install the module + // TODO: make a proper hpm node.js API client + request({ + uri: "http://localhost:8888/npm/exists", + method: "POST", + form: { + packages: missingModule + } + }, function (err, result){ + if (err) { + if(err.code === "ECONNREFUSED") { + output.write('Unable to communicate with hpm server \n\n'); + output.write(err.message); + } + status.ended = true; + status.checkingRegistry = false; + // console.log('npm error called output.end()'); + output.end(); + return; + } + if (result.body === "true") { + // the missing module exists on the public npm registry, + // let's install it and tell the user it's pending installation + + // message.payload.error = message.payload.error + "\n\n" + "npm installations are currently disabled. They will be back online soon." + message.payload.error = message.payload.error + "\n\n" + "It looks like `" + missingModule + "` is a npm dependency. We are going to try to install it!"; + message.payload.error += '\n' + 'It should be ready in a few moments... \n\n'; + message.payload.error += 'Check https://hook.io/packages/npm/installed for updates.\n'; + message.payload.error += 'Pending installations https://hook.io/packages/npm/pending.\n\n'; + output.write(message.payload.error); + + // call out to the hpm server to install the module + // TODO: make a proper hpm node.js API client + request({ + uri: "http://localhost:8888/npm/install", + method: "POST", + form: { + packages: missingModule, + where: config.worker.npmPath + } + }, function (err, result) { + // console.log(err, result.body); + }); + + status.erroring = false; + status.checkingRegistry = false; + if(!status.ended) { + //console.log('npm found module called output.end()'); + status.ended = true; + output.end(); + } + + } else if (result.body === "false"){ + // we couldn't find the missing module ( for some reason ), + // show the user + var str = 'We were unable to find "' + missingModule + '" in the public npm registry! \n\n'; + str += "Unable to require module. Sorry."; + str += "If you feel this message is an error, please contact support."; + output.write(str); + status.ended = true; + return output.end(); + } else { + output.write(result.body); + status.ended = true; + return output.end(); + } + + }); + + } else { + status.erroring = true; + // the process is erroring and its not MODULE_NOT_FOUND. + // we don't know what happened at this point, or how much more error information is coming + // let's just set a timer to end the request after a few moments + // this ensures that most ( if not the entire ) error stack gets sent to the client + if(!status.ended && output) { + output.write(message.payload.error); + setTimeout(function(){ + if (!status.checkingRegistry) { + status.ended = true; + console.log('erroring timeout called output.end()'); + output.end(); + } + }, 200); + } + } + } +} \ No newline at end of file diff --git a/lib/resources/hook/stderr/responseMethods.js b/lib/resources/hook/stderr/responseMethods.js new file mode 100644 index 00000000..2174b16b --- /dev/null +++ b/lib/resources/hook/stderr/responseMethods.js @@ -0,0 +1,80 @@ +var methods = {}; +module['exports'] = methods; + +/* + TODO: Implement the following methods + see: https://github.com/bigcompany/hook.io/issues/96 + + ✓ hook.res.writeHead + ✓ hook.res.write + ✓ hook.res.end + ✓ hook.res.writeContinue + ✓ hook.res.setTimeout ( missing callback argument ) + ✓ hook.res.statusCode (getter and setter) + ✓ hook.res.statusMessage + ✓ hook.res.setHeader + ✓ hook.res.sendDate + ✓ hook.res.removeHeader + ✓ hook.res.addTrailers + + TODO: + + hook.res.headersSent + hook.res.getHeader + +*/ + +/* + Remark: All methods carry the following signature: + + function (message, res) + + message: hash containing the hook.res.foo method payload + res: the http response object + +*/ + +methods.addTrailers = function (message, res) { + res.addTrailers(message.payload.headers); +}; + +methods.removeHeader = function (message, res) { + res.removeHeader(message.payload.name); +}; + +methods.setHeader = function (message, res) { + try { + res.setHeader(message.payload.name, message.payload.value); + } catch (err) { + // do nothing + } +}; + +methods.setTimeout = function (message, res) { + // TODO: add optional callback argument? + res.setTimeout(message.payload.msecs); +}; + +methods.sendDate = function (message, res) { + res.sendDate = message.payload.value; +}; + +methods.statusMessage = function (message, res) { + res.statusMessage = message.payload.value; +}; + +methods.statusCode = function (message, res) { + res.statusCode = message.payload.value; +}; + +methods.writeContinue = function (message, res) { + res.writeContinue(); +}; + +methods.writeHead = function (message, res) { + try { + res.writeHead(message.payload.code, message.payload.headers); + } catch (err) { + // do nothing + } +}; \ No newline at end of file diff --git a/lib/resources/keys/index.js b/lib/resources/keys/index.js new file mode 100644 index 00000000..2a89b489 --- /dev/null +++ b/lib/resources/keys/index.js @@ -0,0 +1,167 @@ +/* + Basic Key store for public / private crypto-keys + Currently used to manage developer access keys for public / private hooks +*/ +var resource = require('resource'); +var keys = resource.define('keys'); +var cache = require('../cache'); +var config = require('../../../config'); +var uuid = require('node-uuid'); +var servicePlan = require('../servicePlan'); + +var user; + +keys.setUser = function (_u){ + user = _u; +}; + +keys.timestamps(); + +keys.property('name', { + "type": "string", + "default": "my-key-name", + "required": true, + "minLength": 1, + "maxLength": 50 +}); + +keys.property('owner', { + "type": "string", + "default": "anonymous", + "required": true, + "minLength": 1, + "maxLength": 50 +}); + +keys.property('hook_public_key', { + "type": "string", + "default": "public-" + new Date().getTime() +}); + +keys.property('hook_private_key', { + type: 'string', + label: "Private Key", + required: true, + minLength: 1, + maxLength: 255, + size: 40, + private: true +}); + +keys.property('key_type', { + "type": "string", + "enum": ["user", "service", "admin", "internal"], + "default": "user" +}); + +keys.property('status', { + "type": "string", + "enum": ["active", "expired", "revoked"], + "default": "active" +}); + +keys.property('readOnly', { + "type": "boolean", + "default": false, + "description": "determines whether or not the key can be modified or deleted by the owner" +}); + +keys.property('roles', { + "type": "string" + // TODO: make actual array type instead of string + // "type": "array" +}); + +keys.before('update', function(data, next){ + if (data.readOnly === true) { + return next(new Error('Cannot modify this key'), data); + } + return next(null, data); +}); + +keys.before('destroy', function(data, next){ + if (data.readOnly === true) { + return next(new Error('Cannot modify this key'), data); + } + return next(null, data); +}); + +// removes key from cache ( if it exists ) +keys.after('destroy', function (data, next){ + var keyPath = '/keys/' + data.owner + '/' + data.hook_private_key; + cache.del(keyPath, function (err) { + if (err) { + console.log(err.message); + } + return next(null, data); + }); +}); + +// only allow 1 key for non-paid accounts +keys.before('create', function (data, next){ + + var query = { + owner: data.owner + }; + + if (typeof data.hook_private_key === "undefined") { + data.hook_private_key = uuid(); + } + + // TODO: move to more generic paid account check + user.find({ name: data.owner }, function (err, res) { + if (err) { + return next(err, data); + } + if (res.length === 0) { + // it's possible during account creation that the user doesn't exist yet + // allow creation of the key anyway and assume user is coming + console.log('Warning Keys: generating key for non-existent user'); + return next(null, data); + } + var u = res[0]; + var status = u.paidStatus; + keys.find(query, function (err, res) { + if (err) { + return next(err, data); + } + var conflicts = null; + res.forEach(function(i){ + if (i.name === data.name) { + conflicts = true; + } + }); + // Don't allow duplicate key names + if (conflicts === true) { + var msg = "Key name " + data.name + " already exists!"; + return next(new Error(msg), data); + } + + // determine current users service plan limits + // note: do not use session scope since checkRoleAccess() might not have session ( from public API usage ) + var plan = u.servicePlan || 'trial'; + + // free plans are now trial plans + if (plan === 'free') { + plan = 'trial'; + } + + var serviceLimits = servicePlan[plan]; + + // Use session.serviceLimits to only allow n keys per user account based on current subscription plan + if (res.length > serviceLimits.apiKeys) { + var keyNoun = "key"; + if (serviceLimits.apiKeys > 1) { + keyNoun = "keys"; + } + var msg = 'You may not create more than ' + serviceLimits.apiKeys + ' access ' + keyNoun + ' with current account plan: ' + plan + '\n\nPlease see ' + config.app.url + '/pricing for more details.'; + return next(new Error(msg), data); + } + // if all checks pass, continue forward and create key + next(null, data); + }); + }) + +}); + +module['exports'] = keys; \ No newline at end of file diff --git a/lib/resources/log.js b/lib/resources/log.js new file mode 100644 index 00000000..377b4458 --- /dev/null +++ b/lib/resources/log.js @@ -0,0 +1,39 @@ +var config = require('../../config'); + +var logs = require("hook.io-logs"); + +// Remark: This could be moved to `hook.io-logs` library as helper Logger method +var Logger = logs.Logger = function Logger (opts) { + var self = this; + self.ip = opts.ip; + self.owner = opts.service.owner; + self.name = opts.service.name; + return self; + } + +Logger.prototype.log = function () { + var self = this; + var args = []; + for (var a in arguments) { + args.push(arguments[a]); + } + if (args.length === 1) { + args = args[0]; + } + // create log entry + var entry = { + time: new Date(), + data: args, + ip : self.ip + }; + // push entry to log datasource + logs.push("/" + self.owner + "/" + self.name, entry, function(err, res) { + if (err) { + console.log('Error pushing log entry to datasource! ', err.message); + } + }); +}; + +module['exports'] = logs; + +logs.start(config.redis); \ No newline at end of file diff --git a/lib/resources/metric/index.js b/lib/resources/metric/index.js new file mode 100644 index 00000000..1a8285f5 --- /dev/null +++ b/lib/resources/metric/index.js @@ -0,0 +1,168 @@ +/* resource for keeping metrics on hooks and site usage using redis */ +var metric = {}; +var config = require('../../../config'); + +var redis = require("redis"), + client = redis.createClient(config.redis.port, config.redis.host); + + if (config.redis.password !== null) { + client.auth(config.redis.password); + } + +// TODO: better error handling and client setup/teardown +client.on("error", function (err) { + console.log("Error " + err); +}); + +metric.keys = function (query, cb) { + client.keys(query, cb); +}; + +metric.batchGet = function (keys, cb) { + var _keys = []; + var result = {}; + // keep metrics keyed into namespace ( so calling method does not need to know /metrics root ) + // this allows /metrics to be configurable in future + _keys = keys.map(function(k){ + return '/metric' + k; + }) + client.mget(_keys, function (err, reply) { + if (reply) { + // merge results back into object hash containing keys + reply.forEach(function(k, i){ + result[keys[Object.keys(keys)[i]]] = JSON.parse(k); + }); + } + return cb(err, result); + }); +}; + +metric.get = function (key, cb) { + var _key = '/metric' + key; + client.get(_key, function (err, reply){ + return cb(err, reply); + }); +}; +metric.client = client; +metric.set = function (key, data, cb) { + cb = cb || function () {}; + client.set('/metric' + key, JSON.stringify(data), function (err, result) { + return cb(err, result); + }); +}; + +metric.zincrby = function (args, cb) { + /* + + zset, member, value, member, value + + zset: the name of the sorted set we are storing + metrics/running + metrics/hits + + member: the name of the member in the set + examples + marak + david + + value: the value to increment / set + + */ + client.zincrby(["metrics/" + args[0], args[1], args[2]], function (err, reply) { + if (typeof cb === "function") { + cb(err, reply); + } + }) +}; + +metric.llen = function (zset, cb) { + client.llen('metrics/' + zset, cb); +}; + +metric.zlexcount = function (zset, min, max, cb) { + client.zlexcount(["metrics/" + zset, min, max], function (err, reply) { + return cb(err, reply); + }); +}; + +metric.zrangebyscore = function (zset, min, max, cb) { + client.zrangebyscore(["metrics/" + zset, min, max], function (err, reply) { + return cb(err, reply); + }); +}; + +metric.zadd = function (zset, member, incr, cb) { + client.zadd("metrics/" + zset, member, incr, function (err, reply) { + return cb(err, reply); + }); +}; + +metric.zrem = function (zset, member, cb) { + client.zrem("metrics/" + zset, member, function (err, reply) { + return cb(err, reply); + }); +}; + +metric.incr = function (key, cb) { + // TODO: check string if /hits, only apply for that + //client.incr('/metric/hook/totalHits'); + client.incr('/metric' + key, cb); +}; + +metric.top = function (zset, cb) { + var args = [ 'metrics/' + zset, '+inf', '-inf', 'WITHSCORES', 'LIMIT', 0, 100 ]; + client.zrevrangebyscore(args, function (err, response) { + cb(err, response); + }); +}; + +metric.zscore = function (zset, member, cb) { + client.zscore("metrics/" + zset, member, function (err, reply) { + cb(err, reply) + }); +}; + +metric.recent = function (zset, cb) { + // go back for the past 24 hours + let past = new Date().getTime() - 86400000; + // past = new Date().getTime() - 60000; + // let now = new Date().getTime(); + var args = [ 'metrics/' + zset, '+inf', past, 'WITHSCORES', 'LIMIT', 0, 100 ]; + client.zrevrangebyscore(args, function (err, response) { + cb(err, response); + }); +}; + +metric.incrby = function (key, value) { + // TODO: check string if /hits, only apply for that + value = value || 1; + // client.incrby('/metric/hook/totalHits', value); + client.incrby('/metric' + key, value); +}; + +metric.hget = function (key, field, cb) { + client.hget('/metric' + key, field, cb); +}; + +metric.hgetall = function (key, cb) { + client.hgetall('/metric' + key, cb); +}; + +metric.hset = function (key, field, value, cb) { + client.hset('/metric' + key, field, value, cb); +}; + +metric.hincrby = function (key, field, value, cb) { + client.hincrby('/metric' + key, field, value, cb); +}; + +metric.resetRunningServicesCount = function (owner, cb) { + client.zrem("metrics/running", owner, function (err, reply) { + if (err) { + return cb(err); + } + client.hset('/metric/' + owner + '/report', 'running', 0, cb); + }); +}; + +module['exports'] = metric; \ No newline at end of file diff --git a/lib/resources/nodes.js b/lib/resources/nodes.js new file mode 100644 index 00000000..9e9ba677 --- /dev/null +++ b/lib/resources/nodes.js @@ -0,0 +1,112 @@ +var nodes = {}; + +// amount of time we wait after removing server from pool before killing it +// this is used to ensure we don't kill any servers which are still processing open requests +var COOLDOWN_TIMER = 60000; +nodes.rollingUpdate = function (poolName) { + + // assume git pull was already performed + + var cache = require('./cache'); + var async = require('async'); + + function getNodes (poolName, cb) { + cache.smembers(poolName, function (err, _nodes) { + if (err) { + throw err; + process.exit(); + } + _nodes.sort(function(a, b){ + return a.port > b.port; + }); + cb(err, _nodes); + }); + } + + function roundHalf (num) { + // rounds up by default + // i.e. 5 becomes 3, not 2 + return Math.round(num / 2); + } + + //var poolName = 'pools.worker'; + + //rollingUpdate('pools.worker'); + // 'pools.web' + _rollingUpdate(poolName); + + function _rollingUpdate (poolName) { + + var halfDone = false; + + getNodes(poolName, function (err, nodes) { + + removeHalfPool('first'); + + function removeHalfPool (pos) { + pos = pos || 'first'; + console.log('performing rolling update'); + + // split the nodes into two halves + var half = roundHalf(nodes.length); + console.log('taking half of pool', nodes.length, 'which is ', half); + + var _nodes; + if (pos === "first") { + _nodes = nodes.slice(0, half); + } else { + _nodes = nodes.slice(half); + } + + // remove nodes from the pool + // update the pool with smaller node list + console.log('nodes', nodes.length, nodes); + async.forEach(_nodes, function(node, next){ + console.log('removing', node) + cache.srem(poolName, node, next); + }, function complete (err, results) { + if (err) { + console.log('WARNING', err) + } + console.log('completed partial update. waiting 60 seconds for requests to end'); + // wait 60 seconds for all requests to complete...maybe needs more + setTimeout(function(){ + killOldPids(_nodes) + }, COOLDOWN_TIMER) + + }) + } + function killOldPids (nodes) { + // kill the pid of the removed nodes ( pids should already be reported? or query? ) + nodes.forEach(function(node){ + console.log('killing', node, halfDone); + try { + process.kill(node.pid); + } catch (err) { + console.log('WARNING: could not kill pid!', node.pid); + // TODO: should probably perform roll-back to bring capacity back into pool + // at this state we have removed active service from pool, but it's still running somewhere + // Remark: not true, could also mean it died during update + // throw err; + } + // TODO: wait a few moments and perform basic health check of new servers + // TODO: repeat same process for other half of nodes + }); + console.log('performing 20s wait for pids to die...') + setTimeout(function(){ + if (halfDone) { + console.log('completed update!'); + process.exit(); + } + halfDone = true; + removeHalfPool('last'); + }, 20000); + } + + }); + + }; + +} + +module.exports = nodes; \ No newline at end of file diff --git a/lib/resources/packages.js b/lib/resources/packages.js new file mode 100644 index 00000000..986ef064 --- /dev/null +++ b/lib/resources/packages.js @@ -0,0 +1,62 @@ +var packages = {}; +module['exports'] = packages; + +var config = require('../../config'); +var request = require('request'); +var redis = require("redis"), + client = redis.createClient(config.redis.port, config.redis.host); + +if (config.redis.password !== null) { + client.auth(config.redis.password); +} + +var MAX_packagesS_PER_HOOK = 50; + +// TODO: better error handling and client setup/teardown +client.on("error", function (err) { + console.log("Error " + err); +}); + +packages.all = function all (filter, cb) { + client.hkeys('/packages/npm/' + filter.status, function(err, keys){ + cb(err, keys); + }); +}; + +packages.setStatus = function add (status, pkg, cb) { + // add entry to set + client.hset("/packages/npm/" + status, pkg, "", function (err, res){ + if (err) { + throw err; + } + return cb(err, res); + }); +}; + +packages.installed = function installed (pkg, cb) { + client.hget("/packages/npm/installed", pkg, function (err, res){ + if (err) { + throw err; + } + return cb(err, res); + }); +}; + +packages.install = function install (pkg, cb) { + // first check if package exists on the public npm server through hpm.npm.exists + // if it doesn't exist, continue with error + // if it exists, that means we can install it via hpm.npm.install + // call out to the hpm server to install the module + // TODO: make a proper hpm node.js API client + request({ + uri: "http://localhost:8888/npm/install", + method: "POST", + form: { + packages: pkg, + where: config.worker.npmPath + } + }, function (err, result, body) { + return cb(err, body); + // console.log(err, result.body); + }); +} diff --git a/lib/resources/programmingLanguage.js b/lib/resources/programmingLanguage.js new file mode 100644 index 00000000..74c4571e --- /dev/null +++ b/lib/resources/programmingLanguage.js @@ -0,0 +1,83 @@ +var programmingLanguage = {}; +module['exports'] = programmingLanguage; + +programmingLanguage.alpha = ['gcc', 'go', 'ocaml', 'rust', 'r', 'java']; + +programmingLanguage.languages = { + "javascript" : { + "name": "JavaScript", + "title": "JavaScript ( Recommended )" + }, + "coffee-script": { + "name": "Coffee-Script", + "title": "Coffee-Script ( Supported )" + }, + "bash": { + "name": "Bash", + "title": "Bash ( Experimental )" + }, + "clisp": { + "name": "Common Lisp", + "title": "Common Lisp" + }, + "gcc": { + "name": "GCC", + "title": "C ( through gcc )" + }, + "go": { + "name": "GoLang", + "title": "Golang" + }, + "java": { + "name": "Java", + "title": "Java" + }, + "lua": { + "name": "Lua", + "title": "Lua ( Experimental )" + }, + "ocaml": { + "name": "OCaml", + "title": "OCaml" + }, + "perl": { + "name": "Perl", + "title": "Perl ( Experimental )" + }, + "php": { + "name": "PHP", + "title": "PHP ( Experimental )" + }, + "python": { + "name": "Python", + "title": "Python ( Experimental )" + }, + "python3": { + "name": "Python 3", + "title": "Python 3 ( Experimental )" + }, + "ruby": { + "name": "Ruby", + "title": "Ruby ( Experimental )" + }, + "r": { + "name": "R", + "title": "R" + }, + "rust": { + "name": "Rust", + "title": "Rust" + }, + "scheme": { + "name": "Scheme", + "title": "Scheme ( Experimental )" + }, + "smalltalk": { + "name": "SmallTalk", + "title": "SmallTalk ( Experimental )" + }, + "tcl": { + "name": "Tcl", + "title": "Tcl ( Experimental )" + } +}; \ No newline at end of file diff --git a/lib/resources/role.js b/lib/resources/role.js new file mode 100644 index 00000000..92f422a6 --- /dev/null +++ b/lib/resources/role.js @@ -0,0 +1,143 @@ +var config = require('../../config'); +var resource = require('resource'); + +var role = resource.define('role'); + +module['exports'] = role; + +role.roles = { + + // "webadmin::login": {}, // TODO...maybe should just make users + + "hook::run" : { + "description": "run any hook microservice" + }, + "hook::update" : { + "description": "update the properties or sources of any hook microservice" + }, + "hook::create" : { + "description": "create a new hook microservice" + }, + "hook::destroy" : { + "description": "destroy a hook microservice" + }, + "hook::logs::read" : { + "description": "read the logs" + }, + "hook::logs::write" : { + "description": "write to logs" + }, + "hook::find": { + "description": "search or list hooks" + }, + "hook::source::read" : { + "description": "read the source code for hook microservices" + }, + "hook::view::read" : { + "description": "read the View source code for hook microservices" + }, + "hook::presenter::read" : { + "description": "read the Presenter source code for hook microservices" + }, + "hook::resource::read" : { + "description": "read the database document for hook microservices" + }, + "hook::package::read" : { + "description": "read the package.json manifest for hook microservices" + }, + "events::read" : { + "description": "read from the system event log" + }, + "events::write" : { + "description": "write to the system event log" + }, + "files::upload" : { + "description": "upload a new cloud file" + }, + "files::download" : { + "description": "download a cloud file" + }, + "files::readFile" : { + "description": "read a cloud file" + }, + "files::writeFile" : { + "description": "write a new cloud file" + }, + "files::removeFile" : { + "description": "remove an existing cloud file" + }, + "files::readdir" : { + "description": "read a cloud directory" + }, + "files::stat" : { + "description": "perform a stat on a cloud file" + }, + "files::createReadStream" : { + "description": "create a new cloud read file stream" + }, + "files::createWriteStream" : { + "description": "create a new cloud write file stream" + }, + "domain::create": { + "description": "create a new domain or subdomain alias" + }, + "domain::get": { + "description": "get a domain or subdomain alias" + }, + "domain::find": { + "description": "search or list domain and subdomain aliases" + }, + "domain::update": { + "description": "update an existing domain or subdomain alias" + }, + "domain::destroy": { + "description": "destroy an existing domain or subdomain alias" + }, + "datastore::del": { + "description": "destroy a cloud datastore document" + }, + "datastore::get": { + "description": "get a cloud datastore document" + }, + "datastore::recent": { + "description": "get recent cloud datastore documents" + }, + "datastore::set": { + "description": "set a new cloud datastore document" + }, + "env::read": { + "description": "read environment variables" + }, + "env::write": { + "description": "write environment variables" + }, + "keys::create": { + "description": "create a new api key" + }, + "keys::checkAccess": { + "description": "determine if a key has valid role access for account" + }, + "keys::destroy": { + "description": "destroy api keys" + }, + "keys::read": { + "description": "read api keys" + }, + "cron::create": { + "description": "create a new cron job" + }, + "cron::update": { + "description": "update an existing cron job" + }, + "cron::destroy": { + "description": "destroy cron jobs" + }, + "cron::read": { + "description": "read cron jobs" + }, + "cron::resource::read": { + "description": "read the database document for cron services" + } + // TODO: keys::find + +}; diff --git a/lib/resources/servicePlan.js b/lib/resources/servicePlan.js new file mode 100644 index 00000000..e21e14b9 --- /dev/null +++ b/lib/resources/servicePlan.js @@ -0,0 +1,58 @@ +module.exports = { + "free": { + "hits": 1000, + "concurrency": 2, + "apiKeys": 1, + "cost": "$0.00", + "cronMinimalInterval": 3600000, // 60 minutes + "disabled": true // placeholder boolean to indicate we are no longer supporting free accounts + }, + "trial": { + "hits": 1000, + "concurrency": 2, + "apiKeys": 1, + "cost": "$0.00", + "cronMinimalInterval": 3600000, // 60 minutes + "daysInTrial": 60 + }, + "basic": { + "hits": 1000, + "concurrency": 2, + "apiKeys": 1, + "cost": "$2.00", + "cronMinimalInterval": 1800000, // 30 minutes + "stripe_label": 'BASIC_HOSTING_PLAN_2', + }, + "premium": { + "hits": 10000, + "concurrency": 4, + "apiKeys": 5, + "stripe_label": 'BASIC_HOSTING_PLAN_10', + "cronMinimalInterval": 60000, // 1 minute + "cost": "$10.00" + }, + "advanced": { + "hits": 50000, + "concurrency": 16, + "apiKeys": 10, + "stripe_label": 'BASIC_HOSTING_PLAN_25', + "cronMinimalInterval": 60000, // 1 minute + "cost": "$25.00" + }, + "pro": { + "hits": 100000, + "concurrency": 8, + "apiKeys": 20, + "stripe_label": 'BASIC_HOSTING_PLAN_50', + "cronMinimalInterval": 60000, // 1 minute + "cost": "$50.00" + }, + "business": { + "hits": 1000000, + "concurrency": 32, + "apiKeys": 50, + "stripe_label": 'BASIC_HOSTING_PLAN_200', + "cronMinimalInterval": 60000, // 1 minute + "cost": "$200.00" + } +}; \ No newline at end of file diff --git a/lib/resources/themes.js b/lib/resources/themes.js new file mode 100644 index 00000000..e20e3302 --- /dev/null +++ b/lib/resources/themes.js @@ -0,0 +1,34 @@ +var config = require('../../config'); + +module['exports'] = { + "debug": { + "theme": config.app.url + "/themes/debug/index.html", + "presenter": config.app.url + "/themes/debug/index.js" + }, + "minimal": { + "theme": config.app.url + "/themes/minimal/index.html", + "presenter": config.app.url + "/themes/minimal/index.js" + }, + "simple": { + "theme": config.app.url + "/themes/simple/index.html", + "presenter": config.app.url + "/themes/simple/index.js" + }, + "simple-form": { + "theme": config.app.url + "/themes/simple-form/index.html", + "presenter": config.app.url + "/themes/simple-form/index.js", + "description": "a simple form backed by mschema" + }, + "form": { + "theme": config.app.url + "/themes/form/index.html", + "presenter": config.app.url + "/themes/form/index.js", + "description": "auto-generated HTML form based on service schema" + }, + "none": { + "theme": config.app.url + "/themes/none/index.html", + "presenter": config.app.url + "/themes/none/index.js" + }, + "custom": { + "theme": "", + "presenter": "" + } +}; diff --git a/lib/resources/usage.js b/lib/resources/usage.js new file mode 100644 index 00000000..d6719a71 --- /dev/null +++ b/lib/resources/usage.js @@ -0,0 +1,47 @@ +var resource = require('resource'); +var usage = resource.define('usage'); +var metric = require('./metric'); +var email = require('resource-email'); +var config = require('../../config'); + +usage.timestamps(); + +usage.method('reset', function (data, cb){ + var userName = data.account; + // reset the user's account limits + metric.zrem("hits", userName, function (err, status) { + if (err) { + return cb(err); + } + // console.log('metric.' + userName + '.hits'.green, err, accountHits); + // totals.hits = accountHits; + metric.zrem("running", userName, function (err, runningServices) { + if (err) { + return cb(err); + } + cb(null, data) + }); + }); +}); + +resource.on('usage::reset', function (data){ + // send email to admins of what just happened + email.send({ + provider: config.email.provider, + api_user: config.email.api_user, + api_key: config.email.api_key, + to: 'hookmaster@hook.io', + from: 'hookmaster@hook.io', + subject: 'hook.io - usage reset request - ' + data.account, + html: data.reasons + '

' + data.comments + }, function (err){ + if (err) { + console.log(err); + return; + } + console.log('usage reset email sent', data) + }) + +}); + +module['exports'] = usage; \ No newline at end of file diff --git a/lib/resources/user.js b/lib/resources/user.js index aaa6e508..84588fa4 100644 --- a/lib/resources/user.js +++ b/lib/resources/user.js @@ -1,5 +1,17 @@ // default user functionality var user = require('resource-user'); +var cache = require('./cache'); +var metric = require('./metric'); +var keys = require('./keys'); +var role = require('./role'); +var servicePlan = require('./servicePlan'); +var timezone = require('timezone-support'); +var config = require('../../config'); +user.persist(config.couch) +user.property('timezone', { + type: "string", + enum: timezone.listTimeZones() +}); // stores ENV variables for Hooks, these values will map to Hook.env on Hook execution user.property('env', 'object'); @@ -8,11 +20,253 @@ user.property('env', 'object'); user.property('referredBy', 'string'); // user.property('dedicatedSignup', 'object'); +user.schema.properties.name.required = false; +user.schema.properties.email.required = true; +user.schema.properties.password.required = false; user.property('stripeID', 'string'); user.property('domains', ["string"]); +user.property('paidStatus', { + type: "string", + default: "unpaid" +}); + +user.property('servicePlan', { + type: "string", + default: "trial" +}); + +user.property('hostingCredits', 'number'); + +user.property('emailBlasts', ["string"]); +user.property('lastAlerted', 'string'); +user.property('servicePlanMeta', 'object'); + +user.property('githubOauth', "boolean"); +user.property('githubAccessToken', "string"); + +user.property('hookAccessKey', { + "type": "string", + "required": false, + "description": "cached version of the pre-generated admin-access-key used for hook-services" +}); + +// updates metrics for total hook count after creating +user.after('create', function(data, next){ + metric.incr('/user/count'); + next(null, data); +}); + +user.method('login', function (opts, cb) { + var res = opts.res, + req = opts.req; + var u = opts.user; + + if (typeof u.name === 'undefined') { + // if no user name is available, it means the account has been created but no user name is regsitered yet + // login the user and don't perform key creation logic until later ( registering account should re-trigger login event ) + return _login(); + } + // check if internal API key exists + var apiKeyQuery = { + name: "api-access-key", + owner: u.name.toLowerCase() + }; + + // will only create key if it doesn't already exist + keys.findOne(apiKeyQuery, function (err, key) { + // if no key was found, create a new one + if (err) { + apiKeyQuery.id = u.id; + createInternalApiKey(apiKeyQuery, _login); + return; + } + // If key was found but not attached to user, re-attach the key to the user + // Note: This shouldn't happen often outside of testing or manually adjusting user + if (typeof u.hookAccessKey === 'undefined' || u.hookAccessKey.length < 5) { + var _update = { + id: u.id, + hookAccessKey: key.hook_private_key + }; + user.update(_update, function (err, re) { + if (err) { + console.log('Error: ', err.message); + } + _login(); + }); + } else { + _login(); + } + }); + + function _login () { + + // Remark: Filter the user object to reduce session size and, + // also so we don't leak any potentially sensitive information into session redis cache + console.log("who is thi suser", u); + + user.setCache(u, function(){ + var filteredUser = user.filter(u); + + // free accounts are now trial accounts + if (filteredUser.servicePlan === 'free') { + filteredUser.servicePlan = 'trial'; + } + + // perform passport login logic, this will populate req.user scope + req.login(filteredUser, function (err, profile) { + if (err) { + return cb(err); + } + if (typeof u.name === 'string' && u.name.length > 0) { + req.session.user = u.name.toLowerCase(); + } + + req.session.user_ctime = u.ctime; + + req.session.email = u.email; + // is result.hookAccessKey ever defined here? + req.session.hookAccessKey = u.hookAccessKey; + + // set information about current user's current plan and limits + // this is useful for displaying this data without needing to make additional requests to the database later + req.session.paidStatus = u.paidStatus; + req.session.servicePlan = u.servicePlan || 'free'; + req.session.timezone = u.timezone || 'America/New_York'; + req.session.serviceLimits = servicePlan[u.servicePlan]; + + cb(null); + }); + }); + + } + +}); + +// when a user registers an account name +user.on('register', function (data) { + // TODO: create a new subdomain for that user ? + // Remark: Instead of automatically adding subdomains, allow user to register it easily +}); + +user.filter = function (u) { + var _user = {}; + ['name', 'email', 'status', 'paidStatus', 'servicePlan', 'hookAccessKey', 'stripeID'].forEach(function (key) { + _user[key] = u[key]; + }); + return _user; +}; + +function createInternalApiKey (data, cb) { + // console.log('calling createInternalApiKey', data) + keys.create({ + name: "api-access-key", + owner: data.owner, + key_type: "internal", + roles: Object.keys(role.roles) + }, function(err, k) { + if (err) { + console.log('Error: ', err.message); + return; + //return next(err); + } + var _update = { + id: data.id, + hookAccessKey: k.hook_private_key + }; + // console.log('attemping to update user', _update) + user.update(_update, function(err, re){ + if (err) { + console.log('Error: ', err.message); + } + cb(null, re); + }); + }); +} + +user.setCache = function userSetCache (data, next) { + var userKey = '/user/' + data.name; + cache.set(userKey, data, function (err, re) { + next(err, data); + }); +}; + +// returns the cached version if exists, or will set couch version into cache for next time +user.getSetCache = function (opts, finish) { + var key = '/user/' + opts.name; + // TODO: move cache requests inside of resource.before('get') + cache.get(key, function (err, _user) { + if (_user === null) { + getUserFromCouch(function (err, u) { + if (err) { + return finish(err); + } + cache.set(key, u, function () { + postProcessUser(err, u); + }); + }); + } else { + postProcessUser(null, _user); + } + }); + function getUserFromCouch (cb) { + user.findOne({ name: opts.name }, function (err, result) { + if (err) { + return cb(err); + } + return cb(null, result); + }); + } + function postProcessUser (err, re) { + // Perform join of current service plan limits onto user + // console.log(servicePlan, user.servicePlan); + re.servicePlanMeta = servicePlan[re.servicePlan]; + finish(err, re); + } +}; + +user.after('create', function(data, next){ + user.setCache(data, next); +}); + +user.after('update', function(data, next){ + user.setCache(data, next); +}); + + +/* +user.after('create', function (data, next) { + // fire and forget the key creation + next(null, data); + //createInternalApiKey(data); +}); +*/ + +user.before('create', function (_user, next) { + + if(typeof _user.name === "string") { + _user.name = _user.name.toLowerCase(); + } + + // Since we use "anonymous" as the default session name for all non-authenticated users... + // it would cause some trouble if we allowed someone to register a user account with the name "anonymous" + // do not remove... + if (_user.name === "anonymous") { + return next(new Error('Sorry! "anonymous" is a reserved account name.')) + } + + next(null, _user); +}); + +user.before('update', function (_user, next) { + if(typeof _user.name === "string") { + _user.name = _user.name.toLowerCase(); + } + next(null, _user); +}); + // ctime and mtime time stamps user.timestamps(); diff --git a/lib/server.js b/lib/server.js deleted file mode 100644 index 707f05d7..00000000 --- a/lib/server.js +++ /dev/null @@ -1,245 +0,0 @@ -var big = require('big'); - -var request = require("hyperquest"); -var http = require('resource-http'); -var hook = require('./resources/hook'); -var user = require('./resources/user'); -var billing = require('./resources/billing'); -var domain = require('./resources/domain'); -var config = require('../config'); -var mergeParams = require('../view/mergeParams'); -var bodyParser = require('body-parser'); -var fs = require('fs'); - -var server = {}; -module['exports'] = server; - -var jsonParser = bodyParser.json(); - -server.start = function start (opts, cb) { - - hook.persist(config.couch); - user.persist(config.couch); - billing.persist(config.couch); - domain.persist(config.couch); - - var passport = require('passport'), - GitHubStrategy = require('passport-github').Strategy; - - var key = fs.readFileSync(__dirname + "/../ssl/server.key").toString(); - var cert = fs.readFileSync(__dirname + "/../ssl/server.crt").toString(); - var ca = [fs.readFileSync(__dirname + '/../ssl/gd1.crt').toString(), fs.readFileSync(__dirname + '/../ssl/gd2.crt').toString(), fs.readFileSync(__dirname + '/../ssl/gd3.crt').toString()] - big.spawn('website', { - site: { - enableUploads: false, - root: __dirname + '/../public', - view: __dirname + '/../view', - passport: true, - port: config.site.port, - https: config.site.https, - cert: cert, - key: key, - ca: ca - - } - }, function (err, app){ - - var GITHUB_CLIENT_ID = config.github.CLIENT_ID; - var GITHUB_CLIENT_SECRET = config.github.CLIENT_SECRET; - - /* - - // TODO: make mock session configurable - app.all("*", function(req, res, next) { - console.log('using mock middle') - req.isAuthenticated = function() { - return true; - }; - req.user = { - username: "Marak" - }; - next(); - }); - // TODO: END MOCK SESSION CODE - */ - - /* - - TOOD: implement custom domain code - - app.all("*", function(req, res, next) { - // console.log('using domain routing middleware') - // console.log(req.host, req.url.blue) - // - // enables custom domain routing - // EASYTODO: move this into separate module - // - if ( req.host !== "hook.io" && - req.host !== "www.hook.io" && - req.host !== "localhost" && - req.host !== "127.0.0.1" - ) { - // if the req.host doesn't match the main site, assume its a custom domain - // perform domain name lookup - console.log('attempting to find custom domain', req.host) - domain.find({ name: req.host }, function (err, results) { - if (err) { - return res.end(err.stack); - } - if (results.length === 0) { - // domain not found, do nothing - return res.end('cannot find custom domain ' + req.host); - } else { - var result = results[0]; - // TODO: domain found, show the root for that user - return res.end('found custom domain' + result.name); - } - }); - } else { - next(); - } - // - // end custom domain routing - // - }); - */ - - passport.use(new GitHubStrategy({ - clientID: GITHUB_CLIENT_ID, - clientSecret: GITHUB_CLIENT_SECRET, - callbackURL: config.github.OAUTH_CALLBACK - }, - function(accessToken, refreshToken, profile, done) { - process.nextTick(function () { - profile.accessToken = accessToken; - return done(null, profile); - }); - } - )); - - app.get('/login', - passport.authenticate('github', { - scope: ["gist"] - }), - function(req, res){ - // The request will be redirected to GitHub for authentication, so this - // function will not be called. - }); - - app.get('/login/callback', - passport.authenticate('github', { failureRedirect: '/failed' }), - function(req, res) { - var referredBy = req.session.referredBy || ""; - req.session.user = req.user.username; - user.find({ name: req.user.username }, function (err, result){ - if (err) { - return res.end(err.message); - } - if (result.length === 0) { - // TODO: this validation should be handled by mschema - // see: https://github.com/mschema/mschema/issues/9 - // see: https://github.com/mschema/mschema/issues/10 - var mail = ""; - try { - mail = req.user.emails[0].value || ""; - if (mail === null) { - mail = ""; - } - } catch(err) { - // do nothing - } - user.create({ name: req.user.username, email: mail, referredBy: referredBy }, function(err, result){ - if (err) { - return res.end(err.message); - } - return res.redirect(req.session.redirectTo || "/"); - }) - } else { - return res.redirect(req.session.redirectTo || "/"); - } - }); - }); - - app.get('/logout', function(req, res){ - req.session.destroy(); - req.logout(); - res.redirect('/'); - }); - - function ensureAuthenticated(req, res, next) { - if (req.isAuthenticated()) { return next(); } - res.redirect('/login') - } - - function hookHandler (req, res) { - console.log('hookHandler', req.url, req.method) - mergeParams(req, res, function(){ - if (req.resource.params.fork) { - return hook.fork(req, res); - } - // run hook on remote worker - return hook.runRemote(req, res, function(){ - // do nothing with the result - // if the hook has been properly formatted, it should be able to call res.write res.end on it's own - }); - }) - - }; - - app.get('/:username', function (req, res){ - // get all hooks for this user - hook.find({owner: req.params.username }, function (err, result){ - if (err) { - return res.end(err.message); - } - if (result.length === 0) { - return handle404(req, res); - } - app.view.hooks.present({ - hooks: result, - request: req, - response: res - }, function(err, html){ - res.end(html); - }); - }); - }); - - app.post('/:username', function (req, res){ - res.end(req.params.username); - }); - - app.get('/:username/:hook', function (req, res){ - hookHandler(req, res) - }); - - app.post('/:username/:hook', function (req, res){ - hookHandler(req, res); - }); - - app.get('/:username/:hook/:subhook', function (req, res){ - hookHandler(req, res); - }); - - app.post('/:username/:hook/:subhook', function (req, res){ - hookHandler(req, res); - }); - - app.get('/logout', function (req, res) { - delete req.session; - req.logout(); - res.redirect('/'); - }); - function handle404(req, res) { - app.view['404'].present({ - request: req, - response: res - }, function (err, html){ - res.writeHead(404); - res.end(html); - }) - }; - app.use(handle404); - cb(null, app); - }); -}; \ No newline at end of file diff --git a/lib/server/middlewares/mockSession.js b/lib/server/middlewares/mockSession.js new file mode 100644 index 00000000..52a76567 --- /dev/null +++ b/lib/server/middlewares/mockSession.js @@ -0,0 +1,18 @@ +/* + + This module is responsible for inserting a mock user sesssion into Passport + This mock session will bypass any outgoing calls to Github, + allowing the project to work in Offline mode + +*/ + +module['exports'] = function mockSessions (req, res, next) { + console.log('using mock session middleware', req.url); + req.isAuthenticated = function() { + return true; + }; + req.user = { + username: "marak" + }; + next(); +}; diff --git a/lib/server/routeHandlers/checkRoleAccess.js b/lib/server/routeHandlers/checkRoleAccess.js new file mode 100644 index 00000000..e5ce40d8 --- /dev/null +++ b/lib/server/routeHandlers/checkRoleAccess.js @@ -0,0 +1,170 @@ +var keys = require('../../resources/keys'); +var events = require('../../resources/events'); +var cache = require('../../resources/cache'); +var psr = require('parse-service-request'); +module['exports'] = function checkRollAccess (opts, next) { + + // bypass role-access for all non-http requests ( for use in migration scripts ) + if (typeof opts.req === "undefined") { + // TODO: why so many warnings? static assetts? can we bypass? + //console.log('warning: bypassing role access'); + return next(null, true); + } + + var req = opts.req, + res = opts.res; + + opts.role = opts.role || ""; + var requestedRoles = opts.roles || []; + var resource = require('resource'); + // console.log('using roll access'.blue, opts.req.url); + // console.log('roles'.blue, opts.roles, opts.access); + + var ev = opts.role.split('::'); + var evRoot = ev[0]; + + // start out owner as anonymous, or unknown + var owner = "anonymous"; + + // first, assume the owner is the current session user. it's most likely this isn't the case and will be overriden below + if (req.session && req.session.user) { + owner = req.session.user; + } + + // if a resource paramter named 'owner' exists, assume that is the owner of the resource + if (req.resource.params.owner && req.resource.params.owner.length > 0) { + owner = req.resource.params.owner; + } + + // if the :owner route has been defined, assume that's the owner instead + if (req.params.owner && req.params.owner.length > 0) { + owner = req.params.owner; + } + + // if req.resource.owner has been explicityly set somewhere, assume that's the owner instead + if (req.resource.owner) { + // TODO: can we now remove this line? + owner = req.resource.owner + } + + // finally, if we are dealing with a hook which has been bound this request, assume that's the owner instead + if (req.hook && req.hook.owner) { + // TODO: can we now remove this line? + owner = req.hook.owner; + } + + // console.log(req.url, 'owner status', owner, 'session.owner', req.session.user, 'hasAccess', 'unknown') + + psr(req, res, function(){ + _checkRoleAccess(); + }); + + function _checkRoleAccess () { + + var params = req.resource.params; + req.resource.owner = owner; + // console.log('owner', owner, req.session.user) + // parse out `public_key` and `private_key` + var privateKey = null; + // check if the incoming request header has a private key + if (req.headers && req.headers['hookio-private-key'] && req.headers['hookio-private-key'].length > 0) { + privateKey = req.headers['hookio-private-key']; + } + // params override header values (since we may be passing in a key using a key) + // may cause issues with header based key auth? + if (typeof params.hook_private_key !== 'undefined') { + privateKey = params.hook_private_key; + } + // console.log('owner status', owner, 'hasAccess', 'unknown') + + if (privateKey !== null && privateKey.length > 0) { + // Remark: For now, we can key on req.params.owner ( the route parameter ) and everything should be keyed + // Note: For some cases, req.params.owner may be undefined + + // check to see if key was found in cache, if not check couch and or add key to cache + var keyPath = '/keys/' + req.params.owner + '/' + privateKey; + var query = { owner: req.params.owner, hook_private_key: privateKey }; + + // console.log('checking redis for key cache', keyPath) + cache.get(keyPath, function (err, _key) { + if (err) { + return next(null, false); + } + + if (_key === null) { + //console.log('QUERY for role'.yellow, opts.role, query); + keys.findOne(query, function(err, u){ + if (err) { + return next(null, false); + } + // console.log('found u', u) + if (typeof u === 'undefined') { + return next(null, false); + } + cache.set(keyPath, u, function(){ + finish(err, u); + }); + }); + } else { + //console.log('CACHED KEY'.yellow, opts.role, _key); + finish(null, _key); + } + }); + function finish (err, result) { + + // console.log('ROLE ACCESS RESULT',owner, result); + var hasAccess = false; + req.resource.keyName = result.name; + if (owner === "anonymous") { + // if the owner is anonymous ( such as the /env or /datastore endpoints ), + // and we have made it here, assume that key was matched and is valid + owner = result.owner; + hasAccess = true; + } + + if (owner !== result.owner /* && owner !== 'marak' */) { + return next(null, false); + } + req.resource.owner = owner; + if (typeof result.roles === "string") { + result.roles = result.roles.split(','); + } + var roles = result.roles; + if (roles.indexOf(opts.role) === -1) { + hasAccess = false; + } else { + hasAccess = true; + } + if (opts.role === "") { + hasAccess = true; + } + resource.emit('keys::authAttempt', { + ip: req.connection.remoteAddress, + hasAccess: hasAccess, + owner: owner, + url: req.url + }); + next(null, hasAccess, result); + } + + } else if (owner === "anonymous") { + // no-one has permission to do anything to anonymous resources + // Note: Is that right? it use to be opposite. Needs testing. + return next(null, false); + } + else if (req.session && ((owner === req.session.user) || (req.session.user === "marak"))) { + // console.log('owner status', owner, 'session.owner', req.session.user, 'hasAccess', 'unknown') + // users have full access to their own resources ( when logged in with session ), so does "marak" admin account + return next(null, true); + } + else { + resource.emit('keys::authAttempt', { + ip: req.connection.remoteAddress, + hasAccess: false, + owner: owner, + url: req.url + }); + next(null, false); + } + }; +}; \ No newline at end of file diff --git a/lib/server/routeHandlers/cron.js b/lib/server/routeHandlers/cron.js new file mode 100644 index 00000000..fb38c490 --- /dev/null +++ b/lib/server/routeHandlers/cron.js @@ -0,0 +1,11 @@ +module['exports'] = function (req, res, app) { + if (req.params.owner === "anonymous") { + return res.redirect('/login'); + } + app.view.cron.get.present({ + req: req, + res: res + }, function (err, html){ + res.end(html); + }); +}; \ No newline at end of file diff --git a/lib/server/routeHandlers/cronDelete.js b/lib/server/routeHandlers/cronDelete.js new file mode 100644 index 00000000..2a393bf3 --- /dev/null +++ b/lib/server/routeHandlers/cronDelete.js @@ -0,0 +1,66 @@ +var config = require('../../../config'); +var cron = require('../../resources/cron/cron'); +var cache = require('../../resources/cache'); +var resource = require('resource'); +var psr = require('parse-service-request'); + +var checkRoleAccess = require('../../server/routeHandlers/checkRoleAccess'); + +module['exports'] = function handleCronDelete (req, res) { + var params = {}; + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + req.resource.owner = req.params.owner; + req.resource.params = params; + checkRoleAccess({ req: req, res: res, role: 'cron::destroy' }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, 'cron::destroy')); + } else { + deleteCron(); + } + }); + }); + + function deleteCron () { + + return cron.find({ owner: req.resource.owner, name: req.params.name }, function (err, result){ + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + return res.end('Not found'); + } + var h = result[0]; + return h.destroy(function(err){ + if (err) { + return res.end(err.message); + } + // also remove from cache + cache.del('/cron/' + req.params.owner + '/' + req.params.name, function (){ + + resource.emit('cron::destroyed', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + name: req.params.name + }); + + if (req.jsonResponse === true) { + var msg = { + status: 'deleted', + message: 'Cron "' + req.params.name + '" has been deleted!', + owner: req.params.owner, + name: req.params.name + }; + return res.json(msg); + } else { + return res.redirect(config.app.url + '/cron/all'); + } + }); + }); + }); + + } + +}; diff --git a/lib/server/routeHandlers/cronResource.js b/lib/server/routeHandlers/cronResource.js new file mode 100644 index 00000000..ef8bad3e --- /dev/null +++ b/lib/server/routeHandlers/cronResource.js @@ -0,0 +1,50 @@ +var cron = require('../../resources/cron/cron'); +var cache = require('../../resources/cache'); +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); +var resource = require('resource'); + +module['exports'] = function handleCronResource (req, res) { + + var key = '/cron/' + req.params.owner + '/' + req.params.name; + cache.get(key, function(err, _cron){ + if (_cron === null) { + findCron(function(err, c){ + cache.set(key, c, function(){ + finish(err, c); + }); + }); + } else { + finish(null, _cron); + } + }); + + function findCron (cb) { + cron.find({ owner: req.params.owner, name: req.params.name }, function (err, result) { + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + res.statusCode = 404; + return res.end('Not found: ' + req.params.owner + '/' + req.params.name); + } + req.cron = result[0]; + cb(null, result[0]); + }); + } + function finish (err, c) { + req.cron = c; + checkRoleAccess({ req: req, res: res, role: 'cron::resource::read' }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, 'cron::resource::read')); + } else { + resource.emit('cron::resource::read', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + url: req.url + }); + res.end(JSON.stringify(c, true, 2)); + } + }); + } +}; diff --git a/lib/server/routeHandlers/hook.js b/lib/server/routeHandlers/hook.js new file mode 100644 index 00000000..b3e96255 --- /dev/null +++ b/lib/server/routeHandlers/hook.js @@ -0,0 +1,145 @@ +var hook = require('../../resources/hook'); +var metric = require('../../resources/metric'); +var cache = require('../../resources/cache'); +var config = require('../../../config'); +var url = require('url'); +var resource = require('resource'); +var servicePlan = require('../../resources/servicePlan'); +var microcule = require('microcule'); +var RateLimiter = microcule.plugins.RateLimiter; +var rateLimiter = new RateLimiter({ + provider: metric +}); + +// TODO: add plan limits on-top of default config for concurrency values +/* + +var config.MAX_SERVICE_CONCURRENCY = 2, + config.MAX_SERVICE_EXECUTIONS_PER_CYCLE = 1000; +*/ + +// keep track of types of errors which can happen if the rate limiter refuses to route the request +var errors = { + 'redis-cache-error': { + statusCode: 410 + }, + 'missing-user-cache': { + statusCode: 410, + message: 'Will not route service for unregistered user. If you are seeing this in error please try logging back in to your hook.io account.' + } +}; + +function rateLimitErrorHandler ({ req, res, user, err }) { + if (err.code === 'RATE_LIMIT_EXCEEDED') { + res.statusCode = 410; + // TODO: add additional event for warning at 75% rate limit + // This will only fire the rate limit event once per once since currentMonthlyHits will continue to increase after the rate limit is exceeded for the month + if (err.monthlyLimit == err.currentMonthlyHits) { + resource.emit('usage::ratelimit', { + code: 'RATE_LIMIT_EXCEEDED', + monthlyLimit: err.monthlyLimit, + username: user.name, + email: user.email, + servicePlan: user.servicePlan + }); + } + return res.json({ + error: true, + message: 'Rate limited: Max monthly limit hit: ' + err.monthlyLimit + }); + } + if (err.code === 'RATE_CONCURRENCY_EXCEEDED') { + res.statusCode = 410; + resource.emit('usage::ratelimit', { + code: 'RATE_CONCURRENCY_EXCEEDED', + maxConcurrency: err.maxConcurrency, + username: user.name, + email: user.email, + servicePlan: user.servicePlan + }); + return res.json({ + error: true, + message: 'Rate limited: Max concurrency limit hit: ' + err.maxConcurrency + }); + } + return res.end(err.message); +}; + +module['exports'] = function (req, res) { + + cache.get('/user/' + req.params.owner , function (err, u) { + if (err) { + // use 410 ( for now for Telegram ) + res.statusCode = errors['redis-cache-error'].statusCode; + return res.end(err.message); + } + if (u === null) { + // use 410 ( for now for Telegram ) + res.statusCode = errors['missing-user-cache'].statusCode; + return res.json({ + error: true, + message: errors['missing-user-cache'].message + }); + } + u.servicePlanMeta = u.servicePlanMeta || servicePlan[u.servicePlan] || { + hits: 1000, + concurrency: 2 + }; + rateLimiter.middle({ + maxLimit: u.servicePlanMeta.hits, + maxConcurrency: u.servicePlanMeta.concurrency + })(req, res, function (err) { + if (err) { + return rateLimitErrorHandler({ + req: req, + res: res, + user: u, + err: err + }); + } + _runRemote(); + }); + }); + + function _runRemote () { + + var pool = config.pools.worker; + var remoteHandler = hook.runRemote({ + pool: pool, + errorHandler: function (err, req, res) { + // error happened, adjust running metric + metric.zincrby(['running', -1, req.params.owner]); + metric.zincrby(['totalRunning', -1, 'tallies']); + // console.log('error:', req.url, err.message); + res.write('Error communicating with ' + req.url + '\n\n'); + res.write('The streaming connection errored in recieving data.\n\n'); + res.write('Please copy and paste this entire error message to: ' + config.app.adminEmail + '.\n\n'); + // TODO: unified error log event schema + res.write(JSON.stringify({ time: new Date(), ip: req.connection.remoteAddress })+ '.\n\n'); + res.end(err.stack) + } + }); + + // normalize all incoming URLS to lowercase + // this is done to ensure that hook.owner and hook.name will always work regardless of case + // this may still cause some issues with overagressive toLowerCase() + // if so, we can always pull out the exact strings that require toLowerCase() via req.params.owner and req.params.name + var urlObj = url.parse(req.url); + var newUrl = urlObj.pathname; + if (urlObj.search !== null) { + newUrl += urlObj.search; + } + req.url = newUrl; + // run hook on remote worker + // console.log('calling remote handler', req.url); + // req.connection.remoteAddress + //console.log('worker', new Date(), req.method, req.url, req.params); + return remoteHandler(req, res, function () { + // TODO: check if callback makes it here everytime... + // do nothing with the result + // if the hook has been properly formatted, it should be able to call res.write res.end on it's own + }); + } + +}; + diff --git a/lib/server/routeHandlers/hookDelete.js b/lib/server/routeHandlers/hookDelete.js new file mode 100644 index 00000000..375490f0 --- /dev/null +++ b/lib/server/routeHandlers/hookDelete.js @@ -0,0 +1,76 @@ +var config = require('../../../config'); +var hook = require('../../resources/hook'); +var cache = require('../../resources/cache'); +var resource = require('resource'); +var psr = require('parse-service-request'); + +var checkRoleAccess = require('../../server/routeHandlers/checkRoleAccess'); + +module['exports'] = function handleHookResource (req, res) { + // if ?delete=true has been passed to the hook, + // attempt to destroy the hook + var user = req.session.id; + var params = {}; + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + req.resource.owner = req.params.owner; + req.resource.params = params; + + // TODO: move to resource.before hook + checkRoleAccess({ req: req, res: res, role: "hook::destroy" }, function (err, hasPermission) { + /* + console.log('session', req.session.user); + console.log('owner', req.resource.owner); + console.log('checked get', err, hasPermission); + */ + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::destroy")); + } else { + deleteHook(); + } + }); + }); + + function deleteHook () { + + return hook.find({ owner: req.resource.owner, name: req.params.hook }, function (err, result){ + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + return res.end('Not found'); + } + var h = result[0]; + return h.destroy(function(err){ + if (err) { + return res.end(err.message); + } + // also remove from cache + cache.del('/hook/' + req.params.owner + '/' + req.params.hook, function (){ + + resource.emit('hook::destroyed', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + name: req.params.hook + }); + + if (req.jsonResponse === true) { + var msg = { + status: 'deleted', + message: 'Hook "' + req.params.hook + '" has been deleted!', + owner: req.params.owner, + name: req.params.hook + }; + return res.json(msg); + } else { + return res.redirect(config.app.url + "/" + h.owner); + } + }); + }); + }); + + } + +} diff --git a/lib/server/routeHandlers/hookEvents.js b/lib/server/routeHandlers/hookEvents.js new file mode 100644 index 00000000..4f5414b4 --- /dev/null +++ b/lib/server/routeHandlers/hookEvents.js @@ -0,0 +1,124 @@ +var events = require('../../resources/events'); + +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); + +var redis = require("redis"), + client = redis.createClient(config.redis.port, config.redis.host); + +if (config.redis.password !== null) { + client.auth(config.redis.password); +} + +// TODO: better error handling and client setup/teardown +client.on("error", function (err) { + console.log("Error " + err); +}); + +module['exports'] = function handleHookEvents (req, res) { + + var key = "/user/" + req.params.owner + "/events"; // wrong amount of / keys? + + var h = { isPrivate: true, owner: req.params.owner }; + req.hook = h; + + // TODO: move to resource.before hook + checkRoleAccess({ req: req, res: res, role: "events::read" }, function (err, hasPermission) { + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "events::read")); + } + + if (req.jsonResponse) { + res.writeHead(200, { + 'Content-Type': 'text/json' + }); + } else { + res.writeHead(200, { + 'Content-Type': 'text/plain' + }); + } + + + // TODO: browser HTML view should show recent events as static html + // later, we can add an ajax gateway to populate events in real-time using eventSubcriberClient + // TODO: in html view give checkbox for enabling auto-refresh of events + + var isStreaming = false; + var params = req.resource.params; + if (req._readableState && req._readableState.buffer && req._readableState.flowing) { + isStreaming = true; + } + + if (params.streaming === "true" || params.s === "true") { + isStreaming = true; + } + + // default curl header + if (req.headers['accept'] === "*/*") { + isStreaming = true; + } + + /* + if (params.streaming === "false" || params.s === "false") { + isStreaming = false; + } + */ + + if (isStreaming) { + // Remark: for */* user agent ( such as curl or other applications ), + // we use eventSubcriberClient to create a streaming event endpoint + var eventsSubcriberClient = redis.createClient(config.redis.port, config.redis.host); + + if (config.redis.password !== null) { + eventsSubcriberClient.auth(config.redis.password); + } + + eventsSubcriberClient.on("pmessage", function (pattern, channel, message) { + try { + res.write(message + '\n'); + } catch (err) { + console.log('Error: ' + err.message); + } + }) + renderEvents(true); + console.log('subcribing to', key) + eventsSubcriberClient.psubscribe(key); + } else if (req.headers['accept'] === "text/plain") { + renderEvents(false); + } + else { + if (!req.jsonResponse) { + // TODO: create a View class for event rendering + res.write('System Events for ' + req.params.owner + '\n'); + res.write('Streaming System Events can be accessed by running: curl -N https://hook.io/' + req.params.owner + "/events" + '\n\n'); + } + renderEvents(false); + } + + function renderEvents () { + + events.recent("/" + req.params.owner, function(err, entries){ + if (err) { + return res.end(err.message); + } + + if (!isStreaming) { + res.write(JSON.stringify(entries, true, 2)); + } else { + entries.forEach(function(entry){ + var str; + str = JSON.stringify(entry) + '\n'; + res.write(str); + }); + } + + if (!isStreaming) { + res.end(); + } + + }); + }; + }); + +} diff --git a/lib/server/routeHandlers/hookLogs.js b/lib/server/routeHandlers/hookLogs.js new file mode 100644 index 00000000..b49f8cb8 --- /dev/null +++ b/lib/server/routeHandlers/hookLogs.js @@ -0,0 +1,181 @@ +// TODO: move this view to hook.io-logs ? + +var log = require('../../resources/log'); +var hook = require('../../resources/hook'); + +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); +var psr = require('parse-service-request'); +var redis = require("redis"), + client = redis.createClient(config.redis.port, config.redis.host); + + if (config.redis.password !== null) { + client.auth(config.redis.password); + } + +// TODO: better error handling and client setup/teardown +client.on("error", function (err) { + console.log("Error " + err); +}); + +module['exports'] = function handleHookLogs (req, res) { + + psr(req, res, function (req, res) { + var key = "/hook/" + req.params.owner + "/" + req.params.hook + "/logs"; + + // lookup hook based on owner and name, we need to do this in order to check if it's private or not + var h = { isPrivate: true, owner: req.params.owner }; + req.hook = h; + + var params = req.resource.params; + var query = { owner: req.params.owner, name: req.params.hook }; + req.resource.owner = req.params.owner; + + return hook.find(query, function(err, result){ + + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + if (req.url !== '/gateway/logs') { + res.json({ + error: true, + message: 'Hook not found', + hook: req.params.hook, + owner: req.params.owner + }) + return; + } + + req.hook = {}; + if (req.session.user !== 'anonymous') { + req.params.owner = req.session.user; + } + + } else { + req.hook = result[0]; + } + + // TODO: move to resource.before hooks + if (params.flush) { + return checkRoleAccess({ req: req, res: res, role: "hook::logs::write" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::logs::write")); + } + // TODO: add better response codes from flush 1, 0, null, error, etc + return log.flush('/' + req.params.owner + '/' + req.params.hook, function (err, r) { + if (err) { + return res.end(err.message) + } + return res.json(r); + }) + }) + } + + checkRoleAccess({ req: req, res: res, role: "hook::logs::read" }, function (err, hasPermission) { + + // only protect source of private services + if (req.hook.isPrivate !== true) { + hasPermission = true; + } + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::logs::read")); + } + + if (req.jsonResponse) { + res.writeHead(200, { + 'Content-Type': 'text/json' + }); + } else { + res.writeHead(200, { + 'Content-Type': 'text/plain' + }); + } + + if (req.resource.owner === 'anonymous') { + //req.params.owner = "anonymous"; + } + + // TODO: browser HTML view should show recent logs as static html + // later, we can add an ajax gateway to populate logs in real-time using logSubcriberClient + // TODO: in html view give checkbox for enabling auto-refresh of logs + + var isStreaming = false; + var params = req.resource.params; + + if (req._readableState && req._readableState.buffer && req._readableState.flowing) { + isStreaming = true; + } + + if (params.streaming === "true" || params.s === "true") { + isStreaming = true; + } + + // default curl header + if (req.headers['accept'] === "*/*") { + isStreaming = true; + } + + if (isStreaming) { + // Remark: for */* user agent ( such as curl or other applications ), + // we use logSubcriberClient to create a streaming log endpoint + var logSubcriberClient = redis.createClient(config.redis.port, config.redis.host); + + if (config.redis.password !== null) { + logSubcriberClient.auth(config.redis.password); + } + + logSubcriberClient.on("pmessage", function (pattern, channel, message) { + try { + res.write(message + '\n'); + } catch (err) { + console.log('Error: ' + err.message); + } + }) + renderLogs(true); + logSubcriberClient.psubscribe(key); + } else if (req.headers['accept'] === "text/plain") { + renderLogs(false); + } + else { + // TODO: create a View class for log rendering + if (!req.jsonResponse) { + res.write('Logs for ' + req.params.owner + "/" + req.params.hook + '\n'); + res.write('Streaming logs can be accessed by running: curl -N https://hook.io/' + req.params.owner + "/" + req.params.hook + "/logs" + '\n\n'); + } + renderLogs(false); + } + + function renderLogs () { + log.recent("/" + req.params.owner + "/" + req.params.hook, function(err, entries){ + if (err) { + return res.end(err.message); + } + if (!isStreaming) { + res.write(JSON.stringify(entries, true, 2)); + } else { + entries.forEach(function(entry){ + // backwards compatbility with old database schema + if (typeof entry === "string") { + entry = JSON.parse(entry); + } + var str; + if (req.jsonResponse) { + str = JSON.stringify(entry) + '\n'; + } else { + str = new Date(entry.time) + ', ' + entry.data + '\n'; + } + res.write(str); + }); + } + if (!isStreaming) { + res.end(); + } + }); + }; + }); + }); + }); + +} diff --git a/lib/server/routeHandlers/hookPackage.js b/lib/server/routeHandlers/hookPackage.js new file mode 100644 index 00000000..e57877e8 --- /dev/null +++ b/lib/server/routeHandlers/hookPackage.js @@ -0,0 +1,65 @@ +var hook = require('../../resources/hook'); +var resource = require('resource'); +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); + +module['exports'] = function handleHookPackage (req, res) { + + /* + + // TODO: make configurable for private hooks + if (!req.isAuthenticated()) { + req.session.redirectTo = req.url; + return res.redirect('/login'); + } + + if (req.session.user !== req.params.owner && req.session.user !== "marak") { + return res.end(req.session.user + ' does not have permission to view ' + req.params.owner + "/" + req.params.hook); + } + */ + + // fetch the latest version of hook ( non-cached ) + hook.find({ owner: req.params.owner, name: req.params.hook }, function (err, result) { + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + return server.handle404(req, res); + } + var h = result[0]; + + checkRoleAccess({ req: req, res: res, role: "hook::package::read" }, function (err, hasPermission) { + + // only protect logs of private services + if (h.isPrivate !== true) { + hasPermission = true; + } + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::package::read")); + } + + // TODO: better package.json support + var pkg = { + "name": h.name, + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": h.owner, + "license": "MIT" + }; + + resource.emit('hook::package::read', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + url: req.url + }); + + res.end(JSON.stringify(pkg, true, 2)); + }); + }); + +} diff --git a/lib/server/routeHandlers/hookPresenter.js b/lib/server/routeHandlers/hookPresenter.js new file mode 100644 index 00000000..d7080f7f --- /dev/null +++ b/lib/server/routeHandlers/hookPresenter.js @@ -0,0 +1,44 @@ +var hook = require('../../resources/hook'); +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); +var resource = require('resource'); + +module['exports'] = function handleHookPresenter (req, res) { + res.writeHead(200, { + "Content-Type": "text/plain" + }); + return hook.find({owner: req.params.owner, name: req.params.hook }, function (err, result){ + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + return res.end('Not found'); + } + + var h = result[0]; + req.hook = h; + + checkRoleAccess({ req: req, res: res, role: "hook::presenter::read" }, function (err, hasPermission) { + + // only protect presenter of private services + if (h.isPrivate !== true) { + hasPermission = true; + } + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::presenter::read")); + } + + // remove this? + // update: should probably be in checkRoleAccess.js itself + resource.emit('hook::presenter::read', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + url: req.url + }); + + return res.end(h.presenterSource); + + }); + }); +}; \ No newline at end of file diff --git a/lib/server/routeHandlers/hookResource.js b/lib/server/routeHandlers/hookResource.js new file mode 100644 index 00000000..778827e0 --- /dev/null +++ b/lib/server/routeHandlers/hookResource.js @@ -0,0 +1,70 @@ +var hook = require('../../resources/hook'); +var cache = require('../../resources/cache'); +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); +var resource = require('resource'); + +module['exports'] = function handleHookResource (req, res) { + + /* + // TODO: make configurable for private accounts + if (!req.isAuthenticated()) { + req.session.redirectTo = req.url; + return res.redirect('/login'); + } + */ + + var key = '/hook/' + req.params.owner + "/" + req.params.hook; + // TODO: move cache requests inside of resource.before('get') + cache.get(key, function(err, _hook){ + if (_hook === null) { + findHook(function(err, h){ + cache.set(key, h, function(){ + finish(err, h); + }); + }); + } else { + finish(null, _hook); + } + }); + + function findHook(cb) { + // console.log('FINDING THE HOOK'.yellow, req.params) + hook.find({ owner: req.params.owner, name: req.params.hook }, function (err, result) { + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + res.statusCode = 404; + return res.end('Not found: ' + req.params.owner + "/" + req.params.hook); + } + req.hook = result[0]; + result[0]['id'] = undefined; + result[0]['_rev'] = undefined; + cb(null, result[0]); + }); + } + function finish (err, h) { + req.hook = h; + checkRoleAccess({ req: req, res: res, role: "hook::resource::read" }, function (err, hasPermission) { + + // only protect resource of private services + if (h.isPrivate !== true) { + hasPermission = true; + } + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::resource::read")); + } else { + + resource.emit('hook::resource::read', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + url: req.url + }); + + res.end(JSON.stringify(h, true, 2)); + } + }); + } +} diff --git a/lib/server/routeHandlers/hookSource.js b/lib/server/routeHandlers/hookSource.js new file mode 100644 index 00000000..2133c5db --- /dev/null +++ b/lib/server/routeHandlers/hookSource.js @@ -0,0 +1,45 @@ +var hook = require('../../resources/hook'); +var resource = require('resource'); +//var events = require('../../resources/events'); +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); + +module['exports'] = function handleHookSource (req, res) { + res.writeHead(200, { + "Content-Type": "text/plain" + }); + return hook.find({ owner: req.params.owner, name: req.params.hook }, function (err, result){ + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + return res.end('Not found'); + } + var h = result[0]; + req.hook = h; + checkRoleAccess({ req: req, res: res, role: "hook::source::read" }, function (err, hasPermission) { + + // only protect source of private services + if (h.isPrivate !== true) { + hasPermission = true; + } + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::source::read")); + } + + resource.emit('hook::source::read', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + url: req.url + }); + + hook.fetchHookSourceCode({ gist: h.gist, req: req, res: res }, function(err, code){ + if (err) { + return res.end(err.message); + } + return res.end(code); + }); + }); + }); +} diff --git a/lib/server/routeHandlers/hookView.js b/lib/server/routeHandlers/hookView.js new file mode 100644 index 00000000..55f29bdd --- /dev/null +++ b/lib/server/routeHandlers/hookView.js @@ -0,0 +1,41 @@ +var hook = require('../../resources/hook'); +var resource = require('resource'); +var checkRoleAccess = require('./checkRoleAccess'); +var config = require('../../../config'); + +module['exports'] = function handleHookView (req, res) { + res.writeHead(200, { + "Content-Type": "text/plain" + }); + return hook.find({owner: req.params.owner, name: req.params.hook }, function (err, result){ + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + return res.end('Not found'); + } + var h = result[0]; + req.hook = h; + + checkRoleAccess({ req: req, res: res, role: "hook::view::read" }, function (err, hasPermission) { + + // only protect view of private services + if (h.isPrivate !== true) { + hasPermission = true; + } + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::view::read")); + } else { + resource.emit('hook::view::read', { + ip: req.connection.remoteAddress, + owner: req.params.owner, + url: req.url + }); + return res.end(h.themeSource); + } + + }); + + }); +}; \ No newline at end of file diff --git a/lib/server/routeHandlers/loginCallback.js b/lib/server/routeHandlers/loginCallback.js new file mode 100644 index 00000000..bfdf5bae --- /dev/null +++ b/lib/server/routeHandlers/loginCallback.js @@ -0,0 +1,70 @@ +var user = require('../../resources/user'); +var metric = require('../../resources/metric'); + +module['exports'] = function loginCallback (req, res) { + + var referredBy = req.session.referredBy || ""; + var redirectTo = req.session.redirectTo || "/services"; + // destroy req.session.user in case it's already been set + delete req.session.user; + req.session.user = req.user.username.toLowerCase(); + // console.log('assigning sesh', req.user) + user.find({ name: req.session.user.toLowerCase() }, function (err, result) { + if (err) { + return res.end(err.message); + } + // increment total logins metric for user + // TODO: replace with metric.zincrby for logins metric + metric.incr("/user/" + req.session.user + "/logins"); + + if (result.length === 0) { + // user cannot be paid at this point since it's a brand new account from github + req.session.paidStatus = "unpaid"; + // TODO: this validation should be handled by mschema + // see: https://github.com/mschema/mschema/issues/9 + // see: https://github.com/mschema/mschema/issues/10 + var mail = ""; + try { + mail = req.user.emails[0].value || ""; + if (mail === null) { + mail = ""; + } + } catch(err) { + // do nothing + } + + // what happens if we have a conflicting namespace here between github and hook.io? + // i believe it will result in an error due to req.session.user already existing in hook.io data + // todo: figure out a way for github users to register accounts if their github name is already taken by another user on hook.io + user.create({ + name: req.session.user, + email: mail, + referredBy: referredBy, + githubAccessToken: req.githubAccessToken + }, function (err, u) { + if (err) { + return res.end(err.message); + } + user.login({ req: req, res: res, user: u }, function (err) { + req.user.accessToken = req.githubAccessToken; + req.session.githubAccessToken = req.githubAccessToken; + return res.redirect(redirectTo); + }); + }); + } else { + var u = result[0]; + u.githubAccessToken = req.githubAccessToken; + u.save(function (err) { + if (err) { + return res.end(err.message); + } + user.login({ req: req, res: res, user: u }, function (err) { + req.user.accessToken = req.githubAccessToken; + req.session.githubAccessToken = req.githubAccessToken; + return res.redirect(redirectTo); + }); + }); + } + }); + +}; \ No newline at end of file diff --git a/lib/server/routeHandlers/modules.js b/lib/server/routeHandlers/modules.js new file mode 100644 index 00000000..c711efeb --- /dev/null +++ b/lib/server/routeHandlers/modules.js @@ -0,0 +1,25 @@ +var modules = require('../../resources/packages'); + +module['exports'] = function (req, res, app) { + return res.end('not being used, replaced by `hpm`'); + var packages = req.resource.params.packages; + if (typeof packages !== "undefined") { + if(typeof packages === "string") { + packages = [packages]; + } + var arr = []; + packages.forEach(function(p){ + p = p.replace(/\./g, ""); + p = p.replace(/\//g, ""); + arr.push(p); + }); + modules.install(arr[0], function(err, r){ + if (err) { + return res.end(err.message); + } + res.end('Successfully installed deps: ' + JSON.stringify(packages, true, 2)); + }) + } else { + return res.end('packages parameter is required!') + } +}; \ No newline at end of file diff --git a/lib/server/routeHandlers/refresh.js b/lib/server/routeHandlers/refresh.js new file mode 100644 index 00000000..88bd91ab --- /dev/null +++ b/lib/server/routeHandlers/refresh.js @@ -0,0 +1,16 @@ +var config = require('../../../config'); + +module['exports'] = refreshHandler = function (req, res) { + var hook = require("../../resources/hook"); + var _hook = "/admin?owner=" + req.params.owner + '&name=' + req.params.hook + "&status=refreshed"; + return hook.invalidateCache({ + owner: req.params.owner, + name: req.params.hook + }, function(err, r){ + if(err) { + return res.end(err.message); + } + return res.redirect(config.app.url + _hook); + //return res.end('Invalidated cache for ' + _hook); + }); +}; \ No newline at end of file diff --git a/lib/server/routeHandlers/user.js b/lib/server/routeHandlers/user.js new file mode 100644 index 00000000..ad1f5780 --- /dev/null +++ b/lib/server/routeHandlers/user.js @@ -0,0 +1,14 @@ +var hook = require('../../resources/hook'); +var web = require('../../web/web'); + +module['exports'] = function (req, res, app) { + if (req.params.owner === "anonymous") { + return res.redirect('/login'); + } + app.view.services.present({ + req: req, + res: res + }, function (err, html){ + res.end(html); + }); +}; \ No newline at end of file diff --git a/lib/web/web.js b/lib/web/web.js new file mode 100644 index 00000000..0c4fd82b --- /dev/null +++ b/lib/web/web.js @@ -0,0 +1,771 @@ +/* + + web/index.js + + front-facing webserver service for hook.io + responsible for static assetts, views, and API endpoints + +*/ + +var big = require('big'); +// application has a lot of listeners +big.resource.setMaxListeners(999); +process.setMaxListeners(999); +big.mode = "Online"; + +var config = require('../../config'); + +if (process.platform === "darwin") { + config.sslKeyDirectory = __dirname + '/../../ssl/'; + config.chrootDirectory = '/Users/chroot'; + /* + config.web.redis.host = "0.0.0.0"; + config.balancer.redis.host = "0.0.0.0"; + config.broadcast.redis.host = "0.0.0.0"; + config.redis.host = "0.0.0.0"; + config.redisCache.host = "0.0.0.0"; + config.couch.host = "0.0.0.0"; + config.worker.publicIP = "0.0.0.0"; + config.web.host = "0.0.0.0"; + */ +} + +var request = require("hyperquest"); +var rrequest = require('request'); +var http = require('resource-http'); + +var hook = require('../resources/hook'); + +var metric = require('../resources/metric'); +var modules = require('../resources/packages'); + +var cache = require('../resources/cache'); +var alerts = require('../resources/alerts/alerts'); +var user = require('../resources/user'); +var billing = require('../resources/billing'); +var cron = require('../resources/cron/cron'); +var domain = require('../resources/domain'); +var keys = require('../resources/keys'); +var events = require('../resources/events'); + +var checkRoleAccess = require('../server/routeHandlers/checkRoleAccess'); +keys.setUser(user); + +var i18n = require('i18n-2') +var bodyParser = require('body-parser'); +var colors = require('colors'); +var fs = require('fs'); +var pool = config.pools.worker; + +var df = require('dateformat'); +var ms = require('ms'); + +// var trycatch = require('trycatch'); +var server = {}; +module['exports'] = server; +var jsonParser = bodyParser.json(); + +var sslKeyDirectory = config.sslKeyDirectory; + +server.start = function start (opts, cb) { + var GitHubStrategy = require('passport-github').Strategy; + + // sometimes in development you might mix and match a common ssl for projects + // comment this line out for production usage + process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 + initCouchDatabase(function(){ + + keys.persist(config.couch); + cron.persist(config.couch); + hook.persist(config.couch); + user.persist(config.couch); + billing.persist(config.couch); + alerts.persist(config.couch); + domain.persist(config.couch); + + var secretConfig; + + var defaultConfig = { + enableUploads: false, + port: config.web.port, + host: config.web.host, + nodeinfo: true, + // host: config.balancer.host, + //roots: config.balancer.roots, + view: __dirname + "/../../view", + passport: true, + locales: { + locales: ['en', 'de'] + }, + app: { // TODO: make able to white-label easily + name: "hook.io", + // host: "https://hook.io", + url: "http://localhost:9999", + //url: "https://hook.io", + domain: "hook.io", + logo: "http://localhost:9999/img/logo.png", + logoInverse: "http://localhost:9999/img/logo-inverse.png", + adminEmail: "hookmaster@hook.io" + /* + Client ID + c030536e9ff50ac1ce37 + Client Secret + f8976cebe01d8a784d62d7636c7a1d6932c38524 + */ + }, + // port: config.balancer.port, + /* + https: config.balancer.https, + cert: cert, + key: key, + ca: ca, + */ + session: config.web.session, + redis: config.balancer.redis, + cacheView: config.cacheView, + sslRequired: false // will not force ssl connections on custom domains / subdomains + }; + + startServer(); + + /* + // get remote configuration for server from oys + client.secret.get(['dev-web'], function (err, _secrets) { + if (err) { + secrets = config; + console.log('web: remote secrets were not fetched, using default static configuration.'); + } else { + secrets = _secrets; + // TODO: update API so that it is uniform no matter how many secrets are returned, + // this means we will *always* return secrets wrapped with a name property + secretConfig = secrets; + // websPool = secrets['dev-pool-webs']; + console.log('web: found remote secrets', secretConfig); + // merge secretConfig over default config + for (var p in secretConfig) { + defaultConfig[p] = secretConfig[p]; + } + defaultConfig.secrets = secretConfig; + } + startServer(); + }); + */ + + function startServer () { + + http.listen(defaultConfig, function (err, app) { + if (err) { + return cb(err); + } + + var GITHUB_CLIENT_ID = config.github.CLIENT_ID; + var GITHUB_CLIENT_SECRET = config.github.CLIENT_SECRET; + server.app = app; + big.server = server; + + var vfs = require('hook.io-vfs'); + var vfsMiddle = vfs.middle({ + config: config, + prefix: "/files/api/v1/fs", + checkRoleAccess: checkRoleAccess, + parent: app.view, + unauthorizedRoleAccess: config.messages.unauthorizedRoleAccess + }); + app.use('/files', vfsMiddle); + + /* TODO: add api=gateway plugin + var apiGateway = require('hook.io-api-gateway'); + var gatewayMiddle = apiGateway.middle({ + config: config, + prefix: "/api-gateway/api/v1", + checkRoleAccess: checkRoleAccess, + parent: app.view, + unauthorizedRoleAccess: config.messages.unauthorizedRoleAccess + }); + + app.use('/api-gateway', gatewayMiddle); + */ + + /* + app.use('/files', function(req, res){ + console.log(vfsMiddle.toString()) + vfsMiddle(req, res, function(){ + res.end('files 404') + }); + }); + */ + + // TODO: move passport / login callback routes to separate file + var passport = require('passport'); + + passport.use(new GitHubStrategy({ + clientID: GITHUB_CLIENT_ID, + clientSecret: GITHUB_CLIENT_SECRET, + callbackURL: config.github.OAUTH_CALLBACK, + passReqToCallback: true + }, + function(req, accessToken, refreshToken, profile, done) { + process.nextTick(function () { + req.githubAccessToken = accessToken; + profile.accessToken = accessToken; + return done(null, profile); + }); + } + )); + + app.get('/login/github', passport.authenticate('github', { + /* + Remark: gist scope has been removed by default + now use /login/github/gist route for gist role access + */ + }), + function(req, res){ + // The request will be redirected to GitHub for authentication, so this + // function will not be called. + }); + + app.get('/login/github/gist', passport.authenticate('github', { + scope: ["gist"] + }), function(req, res){ + // The request will be redirected to GitHub for authentication, so this + // function will not be called. + }); + + app.get('/login/github/private-repos', passport.authenticate('github', { + scope: ["repo"] + }), function (req, res) { + // The request will be redirected to GitHub for authentication, so this + // function will not be called. + }); + + var loginCallbackHandler = require('../server/routeHandlers/loginCallback'); + app.get('/login/github/callback', + passport.authenticate('github', { failureRedirect: '/failed' }), + function (req, res) { + loginCallbackHandler(req, res); + }); + + app.get('/logout', function(req, res){ + req.logout(); + req.session.user = "anonymous"; + req.session.destroy(); + res.redirect("/"); + }); + + app.use(function (req, res, next) { + console.log(new Date(), app.server.address().address, app.server.address().port, req.method, req.url, req.params); + /* + var opt = { + request: req, + locales: config.locales.locales, + directory: require.resolve('hook.io-i18n').replace('index.js', '/locales') + }; + opt.request = req; + req.i18n = new i18n(opt); + // Express 3 + if (res.locals) { + i18n.registerMethods(res.locals, req) + } + */ + + // hack for proper user session from load-balancer for hook.io + // we could do a proper express session lookup instead + //console.log('performing header check' ,req.headers) + if (typeof req.headers["x-hookio-user-session-name"] !== "undefined") { + // Note: Since this header value is explicity set by the hook.io front-end server *after* the client request is processed ( based on existing auth logic )... + // there should be no issue here with users' being able to overwrite x-hookio-user-session-name maliciously + req.session.user = req.headers["x-hookio-user-session-name"]; + } + + next(); + }); + + var handleUser = require('../server/routeHandlers/user'); + var handleCron = require('../server/routeHandlers/cron'); + + app.all('/cron/:owner/:name', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.name = req.params.name.toLowerCase(); + handleCron(req, res, app) + }); + + var handleDestroyCron = require('../server/routeHandlers/cronDelete'); + app.all('/cron/:owner/:name/destroy', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.name = req.params.name.toLowerCase(); + handleDestroyCron(req, res, app) + }); + + app.all('/cron/:owner/:name/admin', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.name = req.params.name.toLowerCase(); + return res.redirect(config.app.url + '/cron/admin?owner=' + req.params.owner + '&name=' + req.params.name + ''); + }); + + var handleCronResource = require('../server/routeHandlers/cronResource'); + app.all('/cron/:owner/:name/resource', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.name = req.params.name.toLowerCase(); + handleCronResource(req, res, app); + }); + + app.get('/:owner', function (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + handleUser(req, res, app); + }); + + app.post('/:owner', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + handleUser(req, res, app); + }); + + var handleDelete = require('../server/routeHandlers/hookDelete'); + var handleResource = require('../server/routeHandlers/hookResource'); + var handlePackage = require('../server/routeHandlers/hookPackage'); + var handleSource = require('../server/routeHandlers/hookSource'); + + var handleView = require('../server/routeHandlers/hookView'); + var handlePresenter = require('../server/routeHandlers/hookPresenter'); + var handleLogs = require('../server/routeHandlers/hookLogs'); + + var handleEvents = require('../server/routeHandlers/hookEvents'); + // var handleModules = require('../server/routeHandlers/modules'); + + app.get('/:owner/:hook/delete', function (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return handleDelete(req, res); + }); + + app.post('/:owner/:hook/delete', function (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return handleDelete(req, res); + }); + + app.get('/:owner/:hook/resource', function (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return handleResource(req, res); + }); + + app.post('/:owner/:hook/resource', function (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return handleResource(req, res); + }); + + app.get('/:owner/:hook/package', function (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return handlePackage(req, res); + }); + + app.post('/:owner/:hook/package', function (req, res) { + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return handlePackage(req, res); + }); + + app.get('/:owner/:hook/fork', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return hook.fork(req, res); + }); + + app.post('/:owner/:hook/fork', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return hook.fork(req, res); + }); + + app.get('/:owner/:hook/_fork', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return hook.fork(req, res); + }); + + app.post('/:owner/:hook/_fork', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return hook.fork(req, res); + }); + + app.post('/:owner/:hook/admin', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return res.redirect(config.app.url + '/admin?owner=' + req.params.owner + '&name=' + req.params.hook + ''); + }); + + app.get('/:owner/:hook/admin', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return res.redirect(config.app.url + '/admin?owner=' + req.params.owner + '&name=' + req.params.hook + ''); + }); + + app.post('/:owner/:hook/_admin', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return res.redirect(config.app.url + '/admin?owner=' + req.params.owner + '&name=' + req.params.hook + ''); + }); + + app.get('/:owner/:hook/_admin', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + return res.redirect(config.app.url + '/admin?owner=' + req.params.owner + '&name=' + req.params.hook + ''); + }); + + + /* TODO: enable stand-alone editor per service + app.get('/:owner/:hook/editor', function (req, res){ + app.view.editor.index.present({ request: req, response: res }, function(err, html){ + console.log(err) + res.end(html); + }); + }); + */ + + app.get('/:owner/:hook/_rev', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + app.view.hook._rev.present({ req: req, res: res }, function(err, html){ + res.end(html); + }); + }); + + app.get('/:owner/:hook/_rev/:revision', function (req, res) { + var nanoConfig = 'http://' + config.couch.username + ":" + config.couch.password + "@" + config.couch.host + ":" + config.couch.port; + var nano = require('nano')(nanoConfig); + // TODO: run hook based on revision + // Note: possible issue with running from cache? + hook.findOne({ name: req.params.hook, owner: req.params.owner }, function(err, _u) { + if (err) { + return res.end(err.message); + } + nano.request({ db: 'hook', + doc: _u.id, + method: 'get', + qs: { rev: req.params.revision } + }, function(err, rev){ + if (err) { + return res.end(err.message); + } + res.json(rev); + }); + }); + }); + + app.get('/gateway/logs', function (req, res){ + req.params.owner = 'anonymous'; + req.params.hook = 'gateway'; + handleLogs(req, res); + }); + + app.get('/:owner/:hook/logs', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handleLogs(req, res); + }); + + app.post('/:owner/:hook/logs', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handleLogs(req, res); + }); + + app.get('/:owner/events', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + //req.params.hook = req.params.hook.toLowerCase(); + handleEvents(req, res); + }); + + app.post('/:owner/events', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + //req.params.hook = req.params.hook.toLowerCase(); + handleEvents(req, res); + }); + + app.all('/:owner/:hook/_src', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + app.view.hook._src.present({ req: req, res: res }, function (err, html) { + res.end(html); + }); + }); + + // legacy /source property + app.get('/:owner/:hook/source', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handleSource(req, res); + }); + + app.post('/:owner/:hook/source', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handleSource(req, res); + }); + + app.get('/:owner/:hook/view', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handleView(req, res); + }); + + app.post('/:owner/:hook/view', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handleView(req, res); + }); + + app.get('/:owner/:hook/presenter', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handlePresenter(req, res); + }); + + app.post('/:owner/:hook/presenter', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + handlePresenter(req, res); + }); + + app.get('/metrics/hook/:metric', function (req, res){ + metric.zscore(req.params.metric, 'tallies', function (err, result) { + // metric.get('/hook' + "/" + req.params.metric, function(err, result){ + if (result === null || typeof result === "undefined") { + result = "0"; + } + res.end(result.toString()); + }) + }); + + app.get('/metrics/:owner/report', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + metric.hgetall('/' + req.params.owner + '/report', function (err, report) { + if (err) { + return res.end(err.message); + } + if (report === null) { + return res.json({ error: true, message: 'metric not found'}) + } + res.json(report); + }); + }); + + app.get('/metrics/:owner/:metric', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.metric = req.params.metric.toLowerCase(); + metric.zscore(req.params.metric, req.params.owner, function(err, score) { + if (err) { + return res.end(err.message); + } + if (score === null) { + return res.json({ error: true, message: 'metric not found'}) + } + res.end(score); + }); + }); + + app.get('/metrics/:owner/:hook/report', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + metric.hgetall('/' + req.params.owner + "/" + req.params.hook + '/report' , function (err, report) { + if (err) { + return res.end(err.message); + } + // format date times so they are easier to read + try { + report.lastStart = df(new Date(Number(report.lastStart)), "mm/dd/yyyy HH:MM:ss Z"); + } catch (err) { + } + try { + report.lastEnd = df(new Date(Number(report.lastEnd)), "mm/dd/yyyy HH:MM:ss Z"); + } catch (err) { + } + try { + report.lastTime = ms(Number(report.lastTime)); + } catch (err) { + } + res.json(report); + }); + }); + + app.get('/metrics/:owner/:hook/:metric', function (req, res){ + req.params.owner = req.params.owner.toLowerCase(); + req.params.hook = req.params.hook.toLowerCase(); + metric.zscore(req.params.metric, req.params.owner + "/" + req.params.hook , function(err, score){ + if (err) { + return res.end(err.message); + } + res.end(score); + }); + }); + + app.use(server.handle404); + if (config.web.registerWithLoadBalancer === true) { + // app started, register it in config pool + var address = app.server.address(); + var _node = { + host: address.address, // might need to be hard-coded to ip of web pool + port: address.port, + status: 'fresh', + spawned: new Date(), + pid: process.pid + }; + + // Register this web node into the web pool for load-balancer + // Note: Currently taking in entire set into memory, but should be okay since set contains < 100 members ( close to 10 ) + // TODO: move into node resource + + // for each web node in the existing pool + cache.smembers('pools.web', function (err, members) { + if (err) { + return cb(err) + } + var found = null; + // check to see if a match was found based on host and port + members.forEach(function (member) { + if (member.host === _node.host && member.port === _node.port) { + found = member; + } + }); + + // if a matching web node was not found, add it to the pool + if (found === null) { + addNode(); + } else { + // if a matching node was found, remove it from the pool and add a fresh one + // Note: Removing the old node and adding a new one here will update the nodes metadata like pid and spawn time + cache.srem('pools.web', found, function (err, rem) { + if (err) { + console.log(err); + } + addNode(); + }); + } + }); + + function addNode () { + console.log('registering with load balancer. adding to pools.web', _node); + // update web pool with new server instance + cache.sadd('pools.web', _node, function (err, pools) { + if (err) { + return cb(err) + } + cb(err, app); + }); + } + } else { + cb(err, app); + } + }); + } + + }); + + // update the workers table - only used for workers /gateway ( can probably be removed / moved ) + updateWorkersTable(function (err) { + // setTimeout to update the balancing table every 20 seconds + loopUpdates(); + }); + +}; + +function loopUpdates () { + setTimeout(function () { + updateWorkersTable(function(err){ + if (err) { + console.log('error updating balancing table', err) + } + loopUpdates(); + }) + }, 1000); +}; + +// +// Remark: Currently web nodes seem to require access to the worker pool +// This doesn't seem right because the load balancer should be responsible for routing requests to the /gateway +// TODO: See if we can remove this code and move gateway routes to load balancer instead +// +function updateWorkersTable (cb) { + cache.smembers('pools.worker', function (err, workers) { + if (err) { + console.log('cant fetch pools.worker', err); + return cb(err); + } + // console.log('got pools.worker', workers.length); + if (workers !== null && typeof workers === "object") { + // only update workers if they are new, add them to the end of the list + if (config.pools.worker.length === 0) { + config.pools.worker = workers; + return cb(null); + } + + config.pools.worker.forEach(function(oldWorker, i){ + var found = false; + // check to see if it exists in the incoming table + workers.forEach(function(newWorker){ + if (newWorker && oldWorker && oldWorker.host === newWorker.host && oldWorker.port === newWorker.port) { + // we found a matching web already in the load balancer pool + found = true; + } + }); + if (!found) { + // if we didn't find a match, assume it's expired and remove it + console.log('performing worker splice', i) + config.pools.worker.splice(i, 1); + //config.pools.worker.splice(i, 0); + } + }); + // for every incoming worker + workers.forEach(function(newWorker){ + var found = false; + // check against all existing workers + config.pools.worker.forEach(function(oldWorker){ + //console.log(newWorker, oldWorker) + if (oldWorker && newWorker && oldWorker.host === newWorker.host && oldWorker.port === newWorker.port) { + // we found a matching worker already in the load balancer pool + found = true; + } + }); + if (!found) { + // if we didn't find a match, assume it's a new worker and put it in the end + config.pools.worker.unshift(newWorker); + } + }); + } + cb(null); + }); +} + +server.handle404 = function handle404 (req, res) { + server.app.view['404'].present({ + request: req, + response: res, + req: req, + res: res + }, function (err, html){ + res.writeHead(404); + res.end(html); + }); +}; + +// TODO: this should be part of the resource library +// see: https://github.com/bigcompany/resource/issues/33 +function initCouchDatabase (cb) { + var nano = require('nano')('http://' + config.couch.username + ":" + config.couch.password + "@" + config.couch.host + ':' + config.couch.port); + //var db = nano.use(config.couch.database); + nano.db.create(config.couch.database, function (err) { + if (err && err.error) { + if (err.error === "file_exists") { + // CouchDB already exists, do nothing + } else { + console.log('Error: Could not create couchdb', err); + } + } else { + // do nothing + } + cb(null); + }); +} \ No newline at end of file diff --git a/lib/worker.js b/lib/worker.js deleted file mode 100644 index 236c9930..00000000 --- a/lib/worker.js +++ /dev/null @@ -1,25 +0,0 @@ -/* worker agent for running hooks */ - -var http = require('resource-http'); -var debug = require('debug')('big::worker'); -var hook = require('./resources/hook'); -var user = require('./resources/user'); -var config = require('../config'); - -user.persist(config.couch); -hook.persist(config.couch); - -var worker = {}; -module['exports'] = worker; - -worker.start = function () { - // worker is very simple. everything is streaming http. - // http-resource will auto-increment the port if 10000 is not available - // this gives the ability to run this script multiple times to spawn multiple servers - http.listen({ port: 10000 }, function(err, app){ - app.get('/:username/:hook', hook.run); - app.post('/:username/:hook', hook.run) - app.get('/:username/:hook/:subhook', hook.run); - app.post('/:username/:hook/:subhook', hook.run) - }) -}; \ No newline at end of file diff --git a/lib/worker/worker.js b/lib/worker/worker.js new file mode 100644 index 00000000..0444794a --- /dev/null +++ b/lib/worker/worker.js @@ -0,0 +1,179 @@ +process.setMaxListeners(99); +/* worker agent for running hooks */ +var config = require('../../config'); + +if (process.platform === "darwin") { + config.sslKeyDirectory = __dirname + '/../../ssl/'; + config.chrootDirectory = '/Users/worker'; + config.redis.host = "0.0.0.0"; + config.couch.host = "0.0.0.0"; + //config.locales.directory +} + +var secrets = {}; + +var http = require('resource-http'); +var debug = require('debug')('big::worker'); +var hook = require('../resources/hook'); +var user = require('../resources/user'); +var keys = require('../resources/keys'); +keys.setUser(user); +var fs = require('fs'); +var chroot = require('chroot'); +var i18n = require('i18n-2') +var events = require('../resources/events'); +var cache = require('../resources/cache'); + +/* +var oysClient = require('oys/client'); +var client = oysClient.createClient({ + oys_private_key: config.oys_private_key, + host: 'localhost', + port: 4000, + protocol: "http" +}); +*/ + +var worker = {}; +module['exports'] = worker; +// sometimes in development you might mix and match a common ssl for projects +// comment this line out for production usage +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 + +worker.start = function (opts, cb) { + + // override config with passed in options + for (var k in opts) { + config[k] = opts[k]; + } + + // sometimes in development you might mix and match a common ssl for projects + // comment this line out for production usage + process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 + + var sslKeyDirectory = config.sslKeyDirectory; + // worker is very simple. everything is streaming http. + // http-resource will auto-increment the port if 10000 is not available + // this gives the ability to run this script multiple times to spawn multiple servers + + var defaultConfig = { + port: config.worker.startingPort, + noSession: true, + host: config.worker.host, + nodeinfo: true + // https: config.worker.https, + //sslRequired: false, + //onlySSL: false + }; + startServer(); + + function startServer () { + http.listen(defaultConfig, function (err, app) { + // Remark: this app is using an in-memory session store + // That's not ideal, as it will leak over time, but we actually don't need sessions at all for the worker? + // TODO: Look into removing session middleware completely from worker instances + + user.persist(config.couch); + hook.persist(config.couch); + keys.persist(config.couch); + + // `chroot` binary is being deprecated in favor of nsjail + if (config.worker.useChroot === true) { + var posix = require('posix'); + try { + chroot(config.chrootDirectory, config.worker.chrootUser, 1000); + posix.setrlimit('nproc', { + soft: config.worker.nproc.soft, + hard: config.worker.nproc.hard + }); + console.log('changed root to "/Users/chroot" and user to "worker"'); + } catch(err) { + console.error('changing root or user failed', err); + process.exit(1); + } + } + + app.get('/', function serveInfo (req, res){ + res.json({ status: "online" }); + }); + + app.post('/', function serveInfo (req, res){ + res.json({ status: "online" }); + }); + + app.get('/gateway', hook.gateway); + app.post('/gateway', hook.gateway) + + app.all('/:owner/:hook', function(req, res, next){ + hook.run(req, res, function(){ + // If we made it to the end of the middleware-chain, assume it's time to stop the service + // If we don't the request will hang forever + // Note: Most languages don't have explict res.end() function support ( like JavaScript ) + res.end(); + }) + }); + + app.all('/:owner/:hook/*', function(req, res, next){ + hook.run(req, res, function(){ + // If we made it to the end of the middleware-chain, assume it's time to stop the service + // If we don't the request will hang forever + // Note: Most languages don't have explict res.end() function support ( like JavaScript ) + res.end(); + }) + }); + + if (config.worker.registerWithLoadBalancer === true) { + var address = app.server.address(); + var _node = { + host: config.worker.publicIP || address.address, // might need to be hard-coded to ip of web pool + port: address.port, + pid: process.pid, + spawned: new Date(), + status: 'fresh' + }; + + cache.smembers('pools.worker', function (err, members) { + if (err) { + return cb(err) + } + var found = null; + members.forEach(function(member){ + if (member.host === _node.host && member.port === _node.port) { + found = member; + } + }); + + if (found === null) { + addNode(); + } else { + cache.srem('pools.worker', found, function (err, rem) { + if (err) { + console.log(err); + } + addNode(); + }); + } + + function addNode () { + // TODO: do not allow duplicate entries? if so, update entry instead of making duplicate? + console.log('registering with load balancer. adding to pools.worker', _node); + // update pool with new server instance + cache.sadd('pools.worker', _node, function (err, pools) { + if (err) { + return cb(err) + } + return cb(err, app); + }); + } + + }); + + } else { + return cb(err, app); + } + + }); + + } + +}; \ No newline at end of file diff --git a/modules/builds/hunspell/en_US/README.txt b/modules/builds/hunspell/en_US/README.txt new file mode 100644 index 00000000..3f3c2835 --- /dev/null +++ b/modules/builds/hunspell/en_US/README.txt @@ -0,0 +1,68 @@ +OpenOffice.org Hunspell en_US dictionary +2007-08-29 release +-- +This dictionary is based on a subset of the original +English wordlist created by Kevin Atkinson for Pspell +and Aspell and thus is covered by his original +LGPL license. The affix file is a heavily modified +version of the original english.aff file which was +released as part of Geoff Kuenning's Ispell and as +such is covered by his BSD license. + +Thanks to both authors for there wonderful work. + +ChangeLog + +2007-08-29 nemeth AT OOo + +Mozilla 376296 - add "axe" (variant of ax) +Mozilla 386259 - add "JavaScript" +Mozilla 383694 - add "foci" and "octopi" (plurals of focus and octopus) +Issue 73024 - add "gauge" +Issue 75710 - add "foldable" +Issue 75772 - add "GHz" +Mozilla 379527 and Issue 62294 - add "dialogue" +Issue 64246 - add "acknowledgement" as a variant of "acknowledgment" + +- TRY extended with apostrophe and dash for + dont -> don't + alltime -> all-time suggestions +- new REP suggestions: +- REP alot a_lot (alot -> a lot) +for suggestion) +- REP nt n't (dont -> don't) +- REP avengence -> a_vengeance (avengence -> a vengeance) +- REP ninties 1990s +- REP tion ssion: readmition -> readmission + +- add Mozilla words (blog, cafe, inline, online, eBay, PayPal, etc.) +- add cybercaf +- alias compression (saving 15 kB disk space + 0.8 MB memory) + +Mozilla 355178 - add scot-free +Mozilla 374411 - add Scotty +Mozilla 359305 - add archaeology, archeological, archeologist +Mozilla 358754 - add doughnut +Mozilla 254814 - add gauging, canoeing, *canoing, proactively +Issue 71718 - remove *opthalmic, *opthalmology; *opthalmologic -> ophthalmologic +Issue 68550 - *estoppal -> estoppel +Issue 69345 - add tapenade +Issue 67975 - add assistive +Issue 63541 - remove *dessicate +Issue 62599 - add toolbar + +2006-02-07 nemeth AT OOo + +Issue 48060 - add ordinal numbers with COMPOUNDRULE (1st, 11th, 101st etc.) +Issue 29112, 55498 - add NOSUGGEST flags to taboo words +Issue 56755 - add sequitor (non sequitor) +Issue 50616 - add open source words (GNOME, KDE, OOo, OpenOffice.org) +Issue 56389 - add Mozilla words (Mozilla, Firefox, Thunderbird) +Issue 29110 - add okay +Issue 58468 - add advisors +Issue 58708 - add hiragana & katakana +Issue 60240 - add arginine, histidine, monovalent, polymorphism, pyroelectric, pyroelectricity + +2005-11-01 dnaber AT OOo + +Issue 25797 - add proven, advisor, etc. diff --git a/modules/builds/hunspell/en_US/en_US.aff b/modules/builds/hunspell/en_US/en_US.aff new file mode 100644 index 00000000..bb2fcbc4 --- /dev/null +++ b/modules/builds/hunspell/en_US/en_US.aff @@ -0,0 +1,466 @@ +SET ISO8859-1 +KEY qwertyuiop|asdfghjkl|zxcvbnm +TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ'- +NOSUGGEST ! + +# ordinal numbers (1st, 2nd, 3th, 11th) and decads (0s, 10s, 1990s) +COMPOUNDMIN 1 +# only in compounds: 1th, 2th, 3th +ONLYINCOMPOUND c +# compound rules: +# 1. [0-9]*1[0-9]th (10th, 11th, 12th, 56714th, etc.) +# 2. [0-9]*[02-9](1st|2nd|3rd|[4-9]th) (21st, 22nd, 123rd, 1234th, etc.) +COMPOUNDRULE 2 +COMPOUNDRULE n*1t +COMPOUNDRULE n*mp +WORDCHARS 0123456789' + +PFX A Y 1 +PFX A 0 re . + +PFX I Y 1 +PFX I 0 in . + +PFX U Y 1 +PFX U 0 un . + +PFX C Y 1 +PFX C 0 de . + +PFX E Y 1 +PFX E 0 dis . + +PFX F Y 1 +PFX F 0 con . + +PFX K Y 1 +PFX K 0 pro . + +SFX V N 2 +SFX V e ive e +SFX V 0 ive [^e] + +SFX N Y 3 +SFX N e ion e +SFX N y ication y +SFX N 0 en [^ey] + +SFX X Y 3 +SFX X e ions e +SFX X y ications y +SFX X 0 ens [^ey] + +SFX H N 2 +SFX H y ieth y +SFX H 0 th [^y] + +SFX Y Y 1 +SFX Y 0 ly . + +SFX G Y 2 +SFX G e ing e +SFX G 0 ing [^e] + +SFX J Y 2 +SFX J e ings e +SFX J 0 ings [^e] + +SFX D Y 4 +SFX D 0 d e +SFX D y ied [^aeiou]y +SFX D 0 ed [^ey] +SFX D 0 ed [aeiou]y + +SFX T N 4 +SFX T 0 st e +SFX T y iest [^aeiou]y +SFX T 0 est [aeiou]y +SFX T 0 est [^ey] + +SFX R Y 4 +SFX R 0 r e +SFX R y ier [^aeiou]y +SFX R 0 er [aeiou]y +SFX R 0 er [^ey] + +SFX Z Y 4 +SFX Z 0 rs e +SFX Z y iers [^aeiou]y +SFX Z 0 ers [aeiou]y +SFX Z 0 ers [^ey] + +SFX S Y 4 +SFX S y ies [^aeiou]y +SFX S 0 s [aeiou]y +SFX S 0 es [sxzh] +SFX S 0 s [^sxzhy] + +SFX P Y 3 +SFX P y iness [^aeiou]y +SFX P 0 ness [aeiou]y +SFX P 0 ness [^y] + +SFX M Y 1 +SFX M 0 's . + +SFX B Y 3 +SFX B 0 able [^aeiou] +SFX B 0 able ee +SFX B e able [^aeiou]e + +SFX L Y 1 +SFX L 0 ment . + +REP 97 +REP nt n't +REP alot a_lot +REP avengence a_vengeance +REP ninties 1990s +REP teached taught +REP rised rose +REP a ei +REP ei a +REP a ey +REP ey a +REP ai ie +REP ie ai +REP are air +REP are ear +REP are eir +REP air are +REP air ere +REP ere air +REP ere ear +REP ere eir +REP ear are +REP ear air +REP ear ere +REP eir are +REP eir ere +REP ch te +REP te ch +REP ch ti +REP ti ch +REP ch tu +REP tu ch +REP ch s +REP s ch +REP ch k +REP k ch +REP f ph +REP ph f +REP gh f +REP f gh +REP i igh +REP igh i +REP i uy +REP uy i +REP i ee +REP ee i +REP j di +REP di j +REP j gg +REP gg j +REP j ge +REP ge j +REP s ti +REP ti s +REP s ci +REP ci s +REP k cc +REP cc k +REP k qu +REP qu k +REP kw qu +REP o eau +REP eau o +REP o ew +REP ew o +REP oo ew +REP ew oo +REP ew ui +REP ui ew +REP oo ui +REP ui oo +REP ew u +REP u ew +REP oo u +REP u oo +REP u oe +REP oe u +REP u ieu +REP ieu u +REP ue ew +REP ew ue +REP uff ough +REP oo ieu +REP ieu oo +REP ier ear +REP ear ier +REP ear air +REP air ear +REP w qu +REP qu w +REP z ss +REP ss z +REP shun tion +REP shun sion +REP shun cion +REP tion ssion +REP ys ies +REP u ough + +# PHONEtic_english.h - #PHONEtic transformation rules for use with #PHONEtic.c +# Copyright (C) 2000 Bjrn Jacke +# +# This rule set is based on Lawrence Phillips original metaPHONE +# algorithm with modifications made by Michael Kuhn in his +# C implantation, more modifications by Bjrn Jacke when +# converting the algorithm to a rule set and minor +# touch ups by Kevin Atkinson +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License version 2.1 as published by the Free Software Foundation; +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Bjrn Jacke may be reached by email at bjoern.jacke@gmx.de +# +# Changelog: +# +# 2000-01-05 Bjrn Jacke +# - first version with translation rules derived from +# metaPHONE.cc distributed with aspell 0.28.3 +# - "TH" is now representated as "@" because "0" is a +# meta character +# - removed TH(!vowel) --> T; always use TH --> # instead +# - dropped "^AE" -> "E" (redundant) +# - "ing" is transformed to "N", not "NK" +# - "SCH(EO)" transforms to "SK" now +# - added R --> SILENT if (after a vowel) and no (vowel or +# "y" follows) like in "Marcy" or "abort" +# - H is SILENT in RH at beginning of words +# - H is SILENT if vowel leads and "Y" follows +# - some ".OUGH.." --> ...F exceptions added +# - "^V" transforms to "W" +# 2000-01-07 Kevin Atkinson +# Converted from header to data file. +# +# 2007-08-23 László Németh +# Add PHONE header and #PHONE keywords +# +# version 1.1 + +# Documentation: http://aspell.net/man-html/PHONEtic-Code.html + +PHONE 105 +PHONE AH(AEIOUY)-^ *H +PHONE AR(AEIOUY)-^ *R +PHONE A(HR)^ * +PHONE A^ * +PHONE AH(AEIOUY)- H +PHONE AR(AEIOUY)- R +PHONE A(HR) _ +PHONE BB- _ +PHONE B B +PHONE CQ- _ +PHONE CIA X +PHONE CH X +PHONE C(EIY)- S +PHONE CK K +PHONE COUGH^ KF +PHONE CC< C +PHONE C K +PHONE DG(EIY) K +PHONE DD- _ +PHONE D T +PHONE < E +PHONE EH(AEIOUY)-^ *H +PHONE ER(AEIOUY)-^ *R +PHONE E(HR)^ * +PHONE ENOUGH^$ *NF +PHONE E^ * +PHONE EH(AEIOUY)- H +PHONE ER(AEIOUY)- R +PHONE E(HR) _ +PHONE FF- _ +PHONE F F +PHONE GN^ N +PHONE GN$ N +PHONE GNS$ NS +PHONE GNED$ N +PHONE GH(AEIOUY)- K +PHONE GH _ +PHONE GG9 K +PHONE G K +PHONE H H +PHONE IH(AEIOUY)-^ *H +PHONE IR(AEIOUY)-^ *R +PHONE I(HR)^ * +PHONE I^ * +PHONE ING6 N +PHONE IH(AEIOUY)- H +PHONE IR(AEIOUY)- R +PHONE I(HR) _ +PHONE J K +PHONE KN^ N +PHONE KK- _ +PHONE K K +PHONE LAUGH^ LF +PHONE LL- _ +PHONE L L +PHONE MB$ M +PHONE MM M +PHONE M M +PHONE NN- _ +PHONE N N +PHONE OH(AEIOUY)-^ *H +PHONE OR(AEIOUY)-^ *R +PHONE O(HR)^ * +PHONE O^ * +PHONE OH(AEIOUY)- H +PHONE OR(AEIOUY)- R +PHONE O(HR) _ +PHONE PH F +PHONE PN^ N +PHONE PP- _ +PHONE P P +PHONE Q K +PHONE RH^ R +PHONE ROUGH^ RF +PHONE RR- _ +PHONE R R +PHONE SCH(EOU)- SK +PHONE SC(IEY)- S +PHONE SH X +PHONE SI(AO)- X +PHONE SS- _ +PHONE S S +PHONE TI(AO)- X +PHONE TH @ +PHONE TCH-- _ +PHONE TOUGH^ TF +PHONE TT- _ +PHONE T T +PHONE UH(AEIOUY)-^ *H +PHONE UR(AEIOUY)-^ *R +PHONE U(HR)^ * +PHONE U^ * +PHONE UH(AEIOUY)- H +PHONE UR(AEIOUY)- R +PHONE U(HR) _ +PHONE V^ W +PHONE V F +PHONE WR^ R +PHONE WH^ W +PHONE W(AEIOU)- W +PHONE X^ S +PHONE X KS +PHONE Y(AEIOU)- Y +PHONE ZZ- _ +PHONE Z S + +#The rules in a different view: +# +# Exceptions: +# +# Beginning of word: "gn", "kn-", "pn-", "wr-" ----> drop first letter +# "Aebersold", "Gnagy", "Knuth", "Pniewski", "Wright" +# +# Beginning of word: "x" ----> change to "s" +# as in "Deng Xiaopeng" +# +# Beginning of word: "wh-" ----> change to "w" +# as in "Whalen" +# Beginning of word: leading vowels are transformed to "*" +# +# "[crt]ough" and "enough" are handled separately because of "F" sound +# +# +# A --> A at beginning +# _ otherwise +# +# B --> B unless at the end of word after "m", as in "dumb", "McComb" +# +# C --> X (sh) if "-cia-" or "-ch-" +# S if "-ci-", "-ce-", or "-cy-" +# SILENT if "-sci-", "-sce-", or "-scy-", or "-cq-" +# K otherwise, including in "-sch-" +# +# D --> K if in "-dge-", "-dgy-", or "-dgi-" +# T otherwise +# +# E --> A at beginnig +# _ SILENT otherwise +# +# F --> F +# +# G --> SILENT if in "-gh-" and not at end or before a vowel +# in "-gn" or "-gned" or "-gns" +# in "-dge-" etc., as in above rule +# K if before "i", or "e", or "y" if not double "gg" +# +# K otherwise (incl. "GG"!) +# +# H --> SILENT if after vowel and no vowel or "Y" follows +# or after "-ch-", "-sh-", "-ph-", "-th-", "-gh-" +# or after "rh-" at beginning +# H otherwise +# +# I --> A at beginning +# _ SILENT otherwise +# +# J --> K +# +# K --> SILENT if after "c" +# K otherwise +# +# L --> L +# +# M --> M +# +# N --> N +# +# O --> A at beginning +# _ SILENT otherwise +# +# P --> F if before "h" +# P otherwise +# +# Q --> K +# +# R --> SILENT if after vowel and no vowel or "Y" follows +# R otherwise +# +# S --> X (sh) if before "h" or in "-sio-" or "-sia-" +# SK if followed by "ch(eo)" (SCH(EO)) +# S otherwise +# +# T --> X (sh) if "-tia-" or "-tio-" +# 0 (th) if before "h" +# silent if in "-tch-" +# T otherwise +# +# U --> A at beginning +# _ SILENT otherwise +# +# V --> V if first letter of word +# F otherwise +# +# W --> SILENT if not followed by a vowel +# W if followed by a vowel +# +# X --> KS +# +# Y --> SILENT if not followed by a vowel +# Y if followed by a vowel +# +# Z --> S diff --git a/modules/builds/hunspell/en_US/en_US.dic b/modules/builds/hunspell/en_US/en_US.dic new file mode 100644 index 00000000..819d7ac6 --- /dev/null +++ b/modules/builds/hunspell/en_US/en_US.dic @@ -0,0 +1,62130 @@ +62118 +0/nm +1/n1 +2/nm +3/nm +4/nm +5/nm +6/nm +7/nm +8/nm +9/nm +1990s +0th/pt +1st/p +1th/tc +2nd/p +2th/tc +3rd/p +3th/tc +4th/pt +5th/pt +6th/pt +7th/pt +8th/pt +9th/pt +0s/pt +a +A +AA +AAA +Aachen/M +aardvark/SM +Aaren/M +Aarhus/M +Aarika/M +Aaron/M +AB +aback +abacus/SM +abaft +Abagael/M +Abagail/M +abalone/SM +abandoner/M +abandon/LGDRS +abandonment/SM +abase/LGDSR +abasement/S +abaser/M +abashed/UY +abashment/MS +abash/SDLG +abate/DSRLG +abated/U +abatement/MS +abater/M +abattoir/SM +Abba/M +Abbe/M +abb/S +abbess/SM +Abbey/M +abbey/MS +Abbie/M +Abbi/M +Abbot/M +abbot/MS +Abbott/M +abbr +abbrev +abbreviated/UA +abbreviates/A +abbreviate/XDSNG +abbreviating/A +abbreviation/M +Abbye/M +Abby/M +ABC/M +Abdel/M +abdicate/NGDSX +abdication/M +abdomen/SM +abdominal/YS +abduct/DGS +abduction/SM +abductor/SM +Abdul/M +ab/DY +abeam +Abelard/M +Abel/M +Abelson/M +Abe/M +Aberdeen/M +Abernathy/M +aberrant/YS +aberrational +aberration/SM +abet/S +abetted +abetting +abettor/SM +Abeu/M +abeyance/MS +abeyant +Abey/M +abhorred +abhorrence/MS +abhorrent/Y +abhorrer/M +abhorring +abhor/S +abidance/MS +abide/JGSR +abider/M +abiding/Y +Abidjan/M +Abie/M +Abigael/M +Abigail/M +Abigale/M +Abilene/M +ability/IMES +abjection/MS +abjectness/SM +abject/SGPDY +abjuration/SM +abjuratory +abjurer/M +abjure/ZGSRD +ablate/VGNSDX +ablation/M +ablative/SY +ablaze +abler/E +ables/E +ablest +able/U +abloom +ablution/MS +Ab/M +ABM/S +abnegate/NGSDX +abnegation/M +Abner/M +abnormality/SM +abnormal/SY +aboard +abode/GMDS +abolisher/M +abolish/LZRSDG +abolishment/MS +abolitionism/SM +abolitionist/SM +abolition/SM +abominable +abominably +abominate/XSDGN +abomination/M +aboriginal/YS +aborigine/SM +Aborigine/SM +aborning +abortionist/MS +abortion/MS +abortiveness/M +abortive/PY +abort/SRDVG +Abo/SM! +abound/GDS +about/S +aboveboard +aboveground +above/S +abracadabra/S +abrader/M +abrade/SRDG +Abraham/M +Abrahan/M +Abra/M +Abramo/M +Abram/SM +Abramson/M +Abran/M +abrasion/MS +abrasiveness/S +abrasive/SYMP +abreaction/MS +abreast +abridge/DSRG +abridged/U +abridger/M +abridgment/SM +abroad +abrogate/XDSNG +abrogation/M +abrogator/SM +abruptness/SM +abrupt/TRYP +ABS +abscess/GDSM +abscissa/SM +abscission/SM +absconder/M +abscond/SDRZG +abseil/SGDR +absence/SM +absenteeism/SM +absentee/MS +absentia/M +absentmindedness/S +absentminded/PY +absent/SGDRY +absinthe/SM +abs/M +absoluteness/SM +absolute/NPRSYTX +absolution/M +absolutism/MS +absolutist/SM +absolve/GDSR +absolver/M +absorb/ASGD +absorbed/U +absorbency/MS +absorbent/MS +absorber/SM +absorbing/Y +absorption/MS +absorptive +absorptivity/M +abstainer/M +abstain/GSDRZ +abstemiousness/MS +abstemious/YP +abstention/SM +abstinence/MS +abstinent/Y +abstractedness/SM +abstracted/YP +abstracter/M +abstractionism/M +abstractionist/SM +abstraction/SM +abstractness/SM +abstractor/MS +abstract/PTVGRDYS +abstruseness/SM +abstruse/PRYT +absurdity/SM +absurdness/SM +absurd/PRYST +Abuja +abundance/SM +abundant/Y +abused/E +abuse/GVZDSRB +abuser/M +abuses/E +abusing/E +abusiveness/SM +abusive/YP +abut/LS +abutment/SM +abutted +abutter/MS +abutting +abuzz +abysmal/Y +abyssal +Abyssinia/M +Abyssinian +abyss/SM +AC +acacia/SM +academe/MS +academia/SM +academical/Y +academicianship +academician/SM +academic/S +academy/SM +Acadia/M +acanthus/MS +Acapulco/M +accede/SDG +accelerated/U +accelerate/NGSDXV +accelerating/Y +acceleration/M +accelerator/SM +accelerometer/SM +accented/U +accent/SGMD +accentual/Y +accentuate/XNGSD +accentuation/M +acceptability/SM +acceptability's/U +acceptableness/SM +acceptable/P +acceptably/U +acceptance/SM +acceptant +acceptation/SM +accepted/Y +accepter/M +accepting/PY +acceptor/MS +accept/RDBSZVG +accessed/A +accessibility/IMS +accessible/IU +accessibly/I +accession/SMDG +accessors +accessory/SM +access/SDMG +accidence/M +accidentalness/M +accidental/SPY +accident/MS +acclaimer/M +acclaim/SDRG +acclamation/MS +acclimate/XSDGN +acclimation/M +acclimatisation +acclimatise/DG +acclimatization/AMS +acclimatized/U +acclimatize/RSDGZ +acclimatizes/A +acclivity/SM +accolade/GDSM +accommodated/U +accommodate/XVNGSD +accommodating/Y +accommodation/M +accommodativeness/M +accommodative/P +accompanied/U +accompanier/M +accompaniment/MS +accompanist/SM +accompany/DRSG +accomplice/MS +accomplished/U +accomplisher/M +accomplishment/SM +accomplish/SRDLZG +accordance/SM +accordant/Y +accorder/M +according/Y +accordionist/SM +accordion/MS +accord/SZGMRD +accost/SGD +accountability/MS +accountability's/U +accountableness/M +accountable/U +accountably/U +accountancy/SM +accountant/MS +account/BMDSGJ +accounted/U +accounting/M +accouter/GSD +accouterments +accouterment's +accoutrement/M +Accra/M +accreditation/SM +accredited/U +accredit/SGD +accretion/SM +accrual/MS +accrue/SDG +acct +acculturate/XSDVNG +acculturation/M +accumulate/VNGSDX +accumulation/M +accumulativeness/M +accumulative/YP +accumulator/MS +accuracy/IMS +accurate/IY +accurateness/SM +accursedness/SM +accursed/YP +accusal/M +accusation/SM +accusative/S +accusatory +accused/M +accuser/M +accuse/SRDZG +accusing/Y +accustomedness/M +accustomed/P +accustom/SGD +ac/DRG +aced/M +acerbate/DSG +acerbic +acerbically +acerbity/MS +ace/SM +acetaminophen/S +acetate/MS +acetic +acetone/SM +acetonic +acetylene/MS +Acevedo/M +Achaean/M +Achebe/M +ached/A +ache/DSG +achene/SM +Achernar/M +aches/A +Acheson/M +achievable/U +achieved/UA +achieve/LZGRSDB +achievement/SM +achiever/M +Achilles +aching/Y +achoo +achromatic +achy/TR +acidic +acidification/M +acidify/NSDG +acidity/SM +acidness/M +acidoses +acidosis/M +acid/SMYP +acidulous +acing/M +Ackerman/M +acknowledgeable +acknowledgedly +acknowledged/U +acknowledge/GZDRS +acknowledger/M +acknowledgment/SAM +acknowledgement/SAM +ACLU +Ac/M +ACM +acme/SM +acne/MDS +acolyte/MS +Aconcagua/M +aconite/MS +acorn/SM +Acosta/M +acoustical/Y +acoustician/M +acoustic/S +acoustics/M +acquaintance/MS +acquaintanceship/S +acquainted/U +acquaint/GASD +acquiesce/GSD +acquiescence/SM +acquiescent/Y +acquirable +acquire/ASDG +acquirement/SM +acquisition's/A +acquisition/SM +acquisitiveness/MS +acquisitive/PY +acquit/S +acquittal/MS +acquittance/M +acquitted +acquitter/M +acquitting +acreage/MS +acre/MS +acridity/MS +acridness/SM +acrid/TPRY +acrimoniousness/MS +acrimonious/YP +acrimony/MS +acrobatically +acrobatic/S +acrobatics/M +acrobat/SM +acronym/SM +acrophobia/SM +Acropolis/M +acropolis/SM +across +acrostic/SM +Acrux/M +acrylate/M +acrylic/S +ACT +Actaeon/M +Acta/M +ACTH +acting/S +actinic +actinide/SM +actinium/MS +actinometer/MS +action/DMSGB +actions/AI +action's/IA +activate/AXCDSNGI +activated/U +activation/AMCI +activator/SM +active/APY +actively/I +activeness/MS +actives +activism/MS +activist/MS +activities/A +activity/MSI +Acton/M +actor/MAS +actress/SM +act's +Acts +act/SADVG +actuality/SM +actualization/MAS +actualize/GSD +actualizes/A +actual/SY +actuarial/Y +actuary/MS +actuate/GNXSD +actuation/M +actuator/SM +acuity/MS +acumen/SM +acupressure/S +acupuncture/SM +acupuncturist/S +acuteness/MS +acute/YTSRP +acyclic +acyclically +acyclovir/S +AD +adage/MS +adagio/S +Adah/M +Adair/M +Adaline/M +Ada/M +adamant/SY +Adamo/M +Adam/SM +Adamson/M +Adana/M +Adan/M +adaptability/MS +adaptable/U +adaptation/MS +adaptedness/M +adapted/P +adapter/M +adapting/A +adaption +adaptively +adaptiveness/M +adaptive/U +adaptivity +adapt/SRDBZVG +Adara/M +ad/AS +ADC +Adda/M +Addams +addenda +addend/SM +addendum/M +adder/M +Addia/M +addiction/MS +addictive/P +addict/SGVD +Addie/M +Addi/M +Addison/M +additional/Y +addition/MS +additive/YMS +additivity +addle/GDS +addressability +addressable/U +addressed/A +addressee/SM +addresser/M +addresses/A +address/MDRSZGB +Addressograph/M +adduce/GRSD +adducer/M +adduct/DGVS +adduction/M +adductor/M +Addy/M +add/ZGBSDR +Adelaida/M +Adelaide/M +Adela/M +Adelbert/M +Adele/M +Adelheid/M +Adelice/M +Adelina/M +Adelind/M +Adeline/M +Adella/M +Adelle/M +Adel/M +Ade/M +Adena/M +Adenauer/M +adenine/SM +Aden/M +adenoidal +adenoid/S +adeptness/MS +adept/RYPTS +adequacy/IMS +adequate/IPY +adequateness's/I +adequateness/SM +Adey/M +Adham/M +Adhara/M +adherence/SM +adherent/YMS +adherer/M +adhere/ZGRSD +adhesion/MS +adhesiveness/MS +adhesive/PYMS +adiabatic +adiabatically +Adiana/M +Adidas/M +adieu/S +Adi/M +Adina/M +adis +adipose/S +Adirondack/SM +adj +adjacency/MS +adjacent/Y +adjectival/Y +adjective/MYS +adjoin/SDG +adjoint/M +adjourn/DGLS +adjournment/SM +adjudge/DSG +adjudicate/VNGXSD +adjudication/M +adjudicator/SM +adjudicatory +adjunct/VSYM +adjuration/SM +adjure/GSD +adjustable/U +adjustably +adjust/DRALGSB +adjusted/U +adjuster's/A +adjuster/SM +adjustive +adjustment/MAS +adjustor's +adjutant/SM +Adkins/M +Adlai/M +Adler/M +adman/M +admen +administer/GDJS +administrable +administrate/XSDVNG +administration/M +administrative/Y +administrator/MS +administratrix/M +admirableness/M +admirable/P +admirably +admiral/SM +admiralty/MS +Admiralty/S +admiration/MS +admirer/M +admire/RSDZBG +admiring/Y +admissibility/ISM +admissible/I +admissibly +admission/AMS +admit/AS +admittance/MS +admitted/A +admittedly +admitting/A +admix/SDG +admixture/SM +Adm/M +Ad/MN +admonisher/M +admonish/GLSRD +admonishing/Y +admonishment/SM +admonition/MS +admonitory +adobe/MS +adolescence/MS +adolescent/SYM +Adolf/M +Adolfo/M +Adolphe/M +Adolph/M +Adolpho/M +Adolphus/M +Ado/M +ado/MS +Adonis/SM +adopted/AU +adopter/M +adoption/MS +adoptive/Y +adopt/RDSBZVG +adopts/A +adorableness/SM +adorable/P +adorably +Adora/M +adoration/SM +adore/DSRGZB +Adoree/M +Adore/M +adorer/M +adoring/Y +adorned/U +Adorne/M +adornment/SM +adorn/SGLD +ADP +Adrea/M +adrenalin +adrenaline/MS +Adrenalin/MS +adrenal/YS +Adria/MX +Adriana/M +Adriane/M +Adrian/M +Adrianna/M +Adrianne/M +Adriano/M +Adriatic +Adriena/M +Adrien/M +Adrienne/M +adrift +adroitness/MS +adroit/RTYP +ads +ad's +adsorbate/M +adsorbent/S +adsorb/GSD +adsorption/MS +adsorptive/Y +adulate/GNDSX +adulation/M +adulator/SM +adulatory +adulterant/SM +adulterated/U +adulterate/NGSDX +adulteration/M +adulterer/SM +adulteress/MS +adulterous/Y +adultery/SM +adulthood/MS +adult/MYPS +adultness/M +adumbrate/XSDVGN +adumbration/M +adumbrative/Y +adv +advance/DSRLZG +advancement/MS +advancer/M +advantage/GMEDS +advantageous/EY +advantageousness/M +Adventist/M +adventist/S +adventitiousness/M +adventitious/PY +adventive/Y +Advent/SM +advent/SVM +adventurer/M +adventuresome +adventure/SRDGMZ +adventuress/SM +adventurousness/SM +adventurous/YP +adverbial/MYS +adverb/SM +adversarial +adversary/SM +adverse/DSRPYTG +adverseness/MS +adversity/SM +advert/GSD +advertised/U +advertise/JGZSRDL +advertisement/SM +advertiser/M +advertising/M +advertorial/S +advice/SM +Advil/M +advisability/SIM +advisable/I +advisableness/M +advisably +advisedly/I +advised/YU +advisee/MS +advisement/MS +adviser/M +advise/ZRSDGLB +advisor/S +advisor's +advisory/S +advocacy/SM +advocate/NGVDS +advocation/M +advt +adze's +adz/MDSG +Aegean +aegis/SM +Aelfric/M +Aeneas +Aeneid/M +aeolian +Aeolus/M +aeon's +aerate/XNGSD +aeration/M +aerator/MS +aerialist/MS +aerial/SMY +Aeriela/M +Aeriell/M +Aeriel/M +aerie/SRMT +aeroacoustic +aerobatic/S +aerobically +aerobic/S +aerodrome/SM +aerodynamically +aerodynamic/S +aerodynamics/M +aeronautical/Y +aeronautic/S +aeronautics/M +aerosolize/D +aerosol/MS +aerospace/SM +Aeschylus/M +Aesculapius/M +Aesop/M +aesthete/S +aesthetically +aestheticism/MS +aesthetics/M +aesthetic/U +aether/M +aetiology/M +AF +AFAIK +afar/S +AFB +AFC +AFDC +affability/MS +affable/TR +affably +affair/SM +affectation/MS +affectedness/EM +affected/UEYP +affect/EGSD +affecter/M +affecting/Y +affectionate/UY +affectioned +affection/EMS +affectioning +affective/MY +afferent/YS +affiance/GDS +affidavit/SM +affiliated/U +affiliate/EXSDNG +affiliation/EM +affine +affinity/SM +affirm/ASDG +affirmation/SAM +affirmative/SY +affix/SDG +afflatus/MS +afflict/GVDS +affliction/SM +afflictive/Y +affluence/SM +affluent/YS +afford/DSBG +afforest/A +afforestation/SM +afforested +afforesting +afforests +affray/MDSG +affricate/VNMS +affrication/M +affricative/M +affright +affront/GSDM +Afghani/SM +Afghanistan/M +afghan/MS +Afghan/SM +aficionado/MS +afield +afire +aflame +afloat +aflutter +afoot +afore +aforementioned +aforesaid +aforethought/S +afoul +Afr +afraid/U +afresh +Africa/M +African/MS +Afrikaans/M +Afrikaner/SM +afro +Afrocentric +Afrocentrism/S +Afro/MS +afterbirth/M +afterbirths +afterburner/MS +aftercare/SM +aftereffect/MS +afterglow/MS +afterimage/MS +afterlife/M +afterlives +aftermath/M +aftermaths +aftermost +afternoon/SM +aftershave/S +aftershock/SM +afters/M +aftertaste/SM +afterthought/MS +afterward/S +afterworld/MS +Afton/M +aft/ZR +Agace/M +again +against +Agamemnon/M +agapae +agape/S +agar/MS +Agassiz/M +Agata/M +agate/SM +Agatha/M +Agathe/M +agave/SM +agedness/M +aged/PY +age/GJDRSMZ +ageism/S +ageist/S +agelessness/MS +ageless/YP +agency/SM +agenda/MS +agent/AMS +agented +agenting +agentive +ageratum/M +Aggie/M +Aggi/M +agglomerate/XNGVDS +agglomeration/M +agglutinate/VNGXSD +agglutination/M +agglutinin/MS +aggrandize/LDSG +aggrandizement/SM +aggravate/SDNGX +aggravating/Y +aggravation/M +aggregated/U +aggregate/EGNVD +aggregately +aggregateness/M +aggregates +aggregation/SM +aggregative/Y +aggression/SM +aggressively +aggressiveness/S +aggressive/U +aggressor/MS +aggrieved/Y +aggrieve/GDS +Aggy/SM +aghast +agile/YTR +agility/MS +agitated/Y +agitate/XVNGSD +agitation/M +agitator/SM +agitprop/MS +Aglaia/M +agleam +aglitter +aglow +Ag/M +Agna/M +Agnella/M +Agnese/M +Agnes/M +Agnesse/M +Agneta/M +Agnew/M +Agni/M +Agnola/M +agnosticism/MS +agnostic/SM +ago +agog +agonizedly/S +agonized/Y +agonize/ZGRSD +agonizing/Y +agony/SM +agoraphobia/MS +agoraphobic/S +Agosto/M +Agra/M +agrarianism/MS +agrarian/S +agreeable/EP +agreeableness/SME +agreeably/E +agreeing/E +agree/LEBDS +agreement/ESM +agreer/S +Agretha/M +agribusiness/SM +Agricola/M +agriculturalist/S +agricultural/Y +agriculture/MS +agriculturist/SM +Agrippa/M +Agrippina/M +agrochemicals +agronomic/S +agronomist/SM +agronomy/MS +aground +Aguascalientes/M +ague/MS +Aguie/M +Aguilar/M +Aguinaldo/M +Aguirre/M +Aguistin/M +Aguste/M +Agustin/M +ah +Ahab/M +Aharon/M +aha/S +ahead +ahem/S +Ahmadabad +Ahmad/M +Ahmed/M +ahoy/S +Ahriman/M +AI +Aida/M +Aidan/M +aided/U +aide/MS +aider/M +AIDS +aid/ZGDRS +Aigneis/M +aigrette/SM +Aiken/M +Aila/M +Ailbert/M +Ailee/M +Aileen/M +Aile/M +Ailene/M +aileron/MS +Ailey/M +Ailina/M +Aili/SM +ail/LSDG +ailment/SM +Ailsun/M +Ailyn/M +Aimee/M +Aime/M +aimer/M +Aimil/M +aimlessness/MS +aimless/YP +aim/ZSGDR +Aindrea/M +Ainslee/M +Ainsley/M +Ainslie/M +ain't +Ainu/M +airbag/MS +airbase/S +airborne +airbrush/SDMG +Airbus/M +airbus/SM +aircraft/MS +aircrew/M +airdrop/MS +airdropped +airdropping +Airedale/SM +Aires +airfare/S +airfield/MS +airflow/SM +airfoil/MS +airframe/MS +airfreight/SGD +airhead/MS +airily +airiness/MS +airing/M +airlessness/S +airless/P +airlift/MDSG +airliner/M +airline/SRMZ +airlock/MS +airmail/DSG +airman/M +airmass +air/MDRTZGJS +airmen +airpark +airplane/SM +airplay/S +airport/MS +airship/MS +airsickness/SM +airsick/P +airspace/SM +airspeed/SM +airstrip/MS +airtightness/M +airtight/P +airtime +airwaves +airway/SM +airworthiness/SM +airworthy/PTR +airy/PRT +Aisha/M +aisle/DSGM +aitch/MS +ajar +Ajax/M +Ajay/M +AK +aka +Akbar/M +Akihito/M +akimbo +Akim/M +akin +Akita/M +Akkad/M +Akron/M +Aksel/M +AL +Alabama/M +Alabaman/S +Alabamian/MS +alabaster/MS +alack/S +alacrity/SM +Aladdin/M +Alaine/M +Alain/M +Alair/M +Alameda/M +Alamogordo/M +Alamo/SM +ala/MS +Ala/MS +Alanah/M +Alana/M +Aland/M +Alane/M +alanine/M +Alan/M +Alanna/M +Alano/M +Alanson/M +Alard/M +Alaric/M +Alar/M +alarming/Y +alarmist/MS +alarm/SDG +Alasdair/M +Alaska/M +Alaskan/S +alas/S +Alastair/M +Alasteir/M +Alaster/M +Alayne/M +albacore/SM +alba/M +Alba/M +Albania/M +Albanian/SM +Albany/M +albatross/SM +albedo/M +Albee/M +albeit +Alberich/M +Alberik/M +Alberio/M +Alberta/M +Albertan/S +Albertina/M +Albertine/M +Albert/M +Alberto/M +Albie/M +Albigensian +Albina/M +albinism/SM +albino/MS +Albion/M +Albireo/M +alb/MS +Albrecht/M +albumen/M +albumin/MS +albuminous +album/MNXS +Albuquerque/M +Alcatraz/M +Alcestis/M +alchemical +alchemist/SM +alchemy/MS +Alcibiades/M +Alcmena/M +Alcoa/M +alcoholically +alcoholic/MS +alcoholism/SM +alcohol/MS +Alcott/M +alcove/MSD +Alcuin/M +Alcyone/M +Aldan/M +Aldebaran/M +aldehyde/M +Alden/M +Alderamin/M +alderman/M +aldermen +alder/SM +alderwoman +alderwomen +Aldin/M +Aldis/M +Aldo/M +Aldon/M +Aldous/M +Aldrich/M +Aldric/M +Aldridge/M +Aldrin/M +Aldus/M +Aldwin/M +aleatory +Alecia/M +Aleck/M +Alec/M +Aleda/M +alee +Aleece/M +Aleen/M +alehouse/MS +Aleichem/M +Alejandra/M +Alejandrina/M +Alejandro/M +Alejoa/M +Aleksandr/M +Alembert/M +alembic/SM +ale/MVS +Alena/M +Alene/M +aleph/M +Aleppo/M +Aler/M +alerted/Y +alertness/MS +alert/STZGPRDY +Alessandra/M +Alessandro/M +Aleta/M +Alethea/M +Aleutian/S +Aleut/SM +alewife/M +alewives +Alexa/M +Alexander/SM +Alexandra/M +Alexandre/M +Alexandria/M +Alexandrian/S +Alexandrina/M +Alexandr/M +Alexandro/MS +Alexei/M +Alexia/M +Alexina/M +Alexine/M +Alexio/M +Alexi/SM +Alex/M +alfalfa/MS +Alfa/M +Alfie/M +Alfi/M +Alf/M +Alfonse/M +Alfons/M +Alfonso/M +Alfonzo/M +Alford/M +Alfreda/M +Alfred/M +Alfredo/M +alfresco +Alfy/M +algae +algaecide +algal +alga/M +algebraic +algebraical/Y +algebraist/M +algebra/MS +Algenib/M +Algeria/M +Algerian/MS +Alger/M +Algernon/M +Algieba/M +Algiers/M +alginate/SM +ALGOL +Algol/M +Algonquian/SM +Algonquin/SM +algorithmic +algorithmically +algorithm/MS +Alhambra/M +Alhena/M +Alia/M +alias/GSD +alibi/MDSG +Alica/M +Alicea/M +Alice/M +Alicia/M +Alick/M +Alic/M +Alida/M +Alidia/M +Alie/M +alienable/IU +alienate/SDNGX +alienation/M +alienist/MS +alien/RDGMBS +Alighieri/M +alight/DSG +aligned/U +aligner/SM +align/LASDG +alignment/SAM +Alika/M +Alikee/M +alikeness/M +alike/U +alimentary +aliment/SDMG +alimony/MS +Ali/MS +Alina/M +Aline/M +alinement's +Alioth/M +aliquot/S +Alisa/M +Alisander/M +Alisha/M +Alison/M +Alissa/M +Alistair/M +Alister/M +Alisun/M +aliveness/MS +alive/P +Alix/M +aliyah/M +aliyahs +Aliza/M +Alkaid/M +alkalies +alkali/M +alkaline +alkalinity/MS +alkalize/SDG +alkaloid/MS +alkyd/S +alkyl/M +all-time +Allahabad/M +Allah/M +Alla/M +Allan/M +Allard/M +allay/GDS +Allayne/M +Alleen/M +allegation/SM +alleged/Y +allege/SDG +Allegheny/MS +allegiance/SM +allegiant +allegoric +allegoricalness/M +allegorical/YP +allegorist/MS +allegory/SM +Allegra/M +allegretto/MS +allegri +allegro/MS +allele/SM +alleluia/S +allemande/M +Allendale/M +Allende/M +Allene/M +Allen/M +Allentown/M +allergenic +allergen/MS +allergic +allergically +allergist/MS +allergy/MS +alleviate/SDVGNX +alleviation/M +alleviator/MS +Alley/M +alley/MS +Alleyn/M +alleyway/MS +Allhallows +alliance/MS +Allianora/M +Allie/M +allier +allies/M +alligator/DMGS +Alli/MS +Allina/M +Allin/M +Allison/M +Allissa/M +Allister/M +Allistir/M +alliterate/XVNGSD +alliteration/M +alliterative/Y +Allix/M +allocable/U +allocatable +allocate/ACSDNGX +allocated/U +allocation/AMC +allocative +allocator/AMS +allophone/MS +allophonic +allotment/MS +allotments/A +allotrope/M +allotropic +allots/A +allot/SDL +allotted/A +allotter/M +allotting/A +allover/S +allowableness/M +allowable/P +allowably +allowance/GSDM +allowed/Y +allowing/E +allow/SBGD +allows/E +alloyed/U +alloy/SGMD +all/S +allspice/MS +Allstate/M +Allsun/M +allude/GSD +allure/GLSD +allurement/SM +alluring/Y +allusion/MS +allusiveness/MS +allusive/PY +alluvial/S +alluvions +alluvium/MS +Allx/M +ally/ASDG +Allyce/M +Ally/MS +Allyn/M +Allys +Allyson/M +alma +Almach/M +Almaden/M +almagest +Alma/M +almanac/MS +Almaty/M +Almeda/M +Almeria/M +Almeta/M +almightiness/M +Almighty/M +almighty/P +Almira/M +Almire/M +almond/SM +almoner/MS +almost +Al/MRY +alms/A +almshouse/SM +almsman/M +alnico +Alnilam/M +Alnitak/M +aloe/MS +aloft +aloha/SM +Aloin/M +Aloise/M +Aloisia/M +aloneness/M +alone/P +along +alongshore +alongside +Alon/M +Alonso/M +Alonzo/M +aloofness/MS +aloof/YP +aloud +Aloysia/M +Aloysius/M +alpaca/SM +Alpert/M +alphabetical/Y +alphabetic/S +alphabetization/SM +alphabetizer/M +alphabetize/SRDGZ +alphabet/SGDM +alpha/MS +alphanumerical/Y +alphanumeric/S +Alphard/M +Alphecca/M +Alpheratz/M +Alphonse/M +Alphonso/M +Alpine +alpine/S +alp/MS +Alps +already +Alric/M +alright +Alsace/M +Alsatian/MS +also +Alsop/M +Alston/M +Altaic/M +Altai/M +Altair/M +Alta/M +altar/MS +altarpiece/SM +alterable/UI +alteration/MS +altercate/NX +altercation/M +altered/U +alternate/SDVGNYX +alternation/M +alternativeness/M +alternative/YMSP +alternator/MS +alter/RDZBG +Althea/M +although +altimeter/SM +Altiplano/M +altitude/SM +altogether/S +Alton/M +alto/SM +Altos/M +altruism/SM +altruistic +altruistically +altruist/SM +alt/RZS +ALU +Aludra/M +Aluin/M +Aluino/M +alumina/SM +aluminum/MS +alumnae +alumna/M +alumni +alumnus/MS +alum/SM +alundum +Alva/M +Alvan/M +Alvarado/M +Alvarez/M +Alvaro/M +alveolar/Y +alveoli +alveolus/M +Alvera/M +Alverta/M +Alvie/M +Alvina/M +Alvinia/M +Alvin/M +Alvira/M +Alvis/M +Alvy/M +alway/S +Alwin/M +Alwyn/M +Alyce/M +Alyda/M +Alyosha/M +Alysa/M +Alyse/M +Alysia/M +Alys/M +Alyson/M +Alyss +Alyssa/M +Alzheimer/M +AM +AMA +Amabelle/M +Amabel/M +Amadeus/M +Amado/M +amain +Amalea/M +Amalee/M +Amaleta/M +amalgamate/VNGXSD +amalgamation/M +amalgam/MS +Amalia/M +Amalie/M +Amalita/M +Amalle/M +Amanda/M +Amandie/M +Amandi/M +Amandy/M +amanuenses +amanuensis/M +Amara/M +amaranth/M +amaranths +amaretto/S +Amargo/M +Amarillo/M +amaryllis/MS +am/AS +amasser/M +amass/GRSD +Amata/M +amateurishness/MS +amateurish/YP +amateurism/MS +amateur/SM +Amati/M +amatory +amazed/Y +amaze/LDSRGZ +amazement/MS +amazing/Y +amazonian +Amazonian +amazon/MS +Amazon/SM +ambassadorial +ambassador/MS +ambassadorship/MS +ambassadress/SM +ambergris/SM +Amberly/M +amber/MS +Amber/YM +ambiance/MS +ambidexterity/MS +ambidextrous/Y +ambience's +ambient/S +ambiguity/MS +ambiguously/U +ambiguousness/M +ambiguous/YP +ambition/GMDS +ambitiousness/MS +ambitious/PY +ambit/M +ambivalence/SM +ambivalent/Y +amble/GZDSR +Amble/M +ambler/M +ambrose +Ambrose/M +ambrosial/Y +ambrosia/SM +Ambrosi/M +Ambrosio/M +Ambrosius/M +Ambros/M +ambulance/MS +ambulant/S +ambulate/DSNGX +ambulation/M +ambulatory/S +Ambur/M +ambuscade/MGSRD +ambuscader/M +ambusher/M +ambush/MZRSDG +Amby/M +Amdahl/M +ameba's +Amelia/M +Amelie/M +Amelina/M +Ameline/M +ameliorate/XVGNSD +amelioration/M +Amelita/M +amenability/SM +amenably +amended/U +amender/M +amendment/SM +amen/DRGTSB +amend/SBRDGL +amends/M +Amenhotep/M +amenity/MS +amenorrhea/M +Amerada/M +Amerasian/S +amercement/MS +amerce/SDLG +Americana/M +Americanism/SM +Americanization/SM +americanized +Americanize/SDG +American/MS +America/SM +americium/MS +Amerigo/M +Amerindian/MS +Amerind/MS +Amer/M +Amery/M +Ameslan/M +Ame/SM +amethystine +amethyst/MS +Amharic/M +Amherst/M +amiability/MS +amiableness/M +amiable/RPT +amiably +amicability/SM +amicableness/M +amicable/P +amicably +amide/SM +amid/S +amidships +amidst +Amie/M +Amiga/M +amigo/MS +Amii/M +Amil/M +Ami/M +amines +aminobenzoic +amino/M +amir's +Amish +amiss +Amitie/M +Amity/M +amity/SM +Ammamaria/M +Amman/M +Ammerman/M +ammeter/MS +ammo/MS +ammoniac +ammonia/MS +ammonium/M +Am/MR +ammunition/MS +amnesiac/MS +amnesia/SM +amnesic/S +amnesty/GMSD +amniocenteses +amniocentesis/M +amnion/SM +amniotic +Amoco/M +amoeba/SM +amoebic +amoeboid +amok/MS +among +amongst +Amontillado/M +amontillado/MS +amorality/MS +amoral/Y +amorousness/SM +amorous/PY +amorphousness/MS +amorphous/PY +amortization/SUM +amortized/U +amortize/SDG +Amory/M +Amos +amount/SMRDZG +amour/MS +Amparo/M +amperage/SM +Ampere/M +ampere/MS +ampersand/MS +Ampex/M +amphetamine/MS +amphibian/SM +amphibiousness/M +amphibious/PY +amphibology/M +amphitheater/SM +amphorae +amphora/M +ampleness/M +ample/PTR +amplification/M +amplifier/M +amplify/DRSXGNZ +amplitude/MS +ampoule's +amp/SGMDY +ampule/SM +amputate/DSNGX +amputation/M +amputee/SM +Amritsar/M +ams +Amsterdam/M +amt +Amtrak/M +amuck's +amulet/SM +Amundsen/M +Amur/M +amused/Y +amuse/LDSRGVZ +amusement/SM +amuser/M +amusingness/M +amusing/YP +Amway/M +Amye/M +amylase/MS +amyl/M +Amy/M +Anabal/M +Anabaptist/SM +Anabella/M +Anabelle/M +Anabel/M +anabolic +anabolism/MS +anachronism/SM +anachronistic +anachronistically +Anacin/M +anaconda/MS +Anacreon/M +anaerobe/SM +anaerobic +anaerobically +anaglyph/M +anagrammatic +anagrammatically +anagrammed +anagramming +anagram/MS +Anaheim/M +Analects/M +analgesia/MS +analgesic/S +Analiese/M +Analise/M +Anallese/M +Anallise/M +analogical/Y +analogize/SDG +analogousness/MS +analogous/YP +analog/SM +analogue/SM +analogy/MS +anal/Y +analysand/MS +analyses +analysis/AM +analyst/SM +analytical/Y +analyticity/S +analytic/S +analytics/M +analyzable/U +analyze/DRSZGA +analyzed/U +analyzer/M +Ana/M +anamorphic +Ananias/M +anapaest's +anapestic/S +anapest/SM +anaphora/M +anaphoric +anaphorically +anaplasmosis/M +anarchic +anarchical/Y +anarchism/MS +anarchistic +anarchist/MS +anarchy/MS +Anastasia/M +Anastasie/M +Anastassia/M +anastigmatic +anastomoses +anastomosis/M +anastomotic +anathema/MS +anathematize/GSD +Anatola/M +Anatole/M +Anatolia/M +Anatolian +Anatollo/M +Anatol/M +anatomic +anatomical/YS +anatomist/MS +anatomize/GSD +anatomy/MS +Anaxagoras/M +Ancell/M +ancestor/SMDG +ancestral/Y +ancestress/SM +ancestry/SM +Anchorage/M +anchorage/SM +anchored/U +anchorite/MS +anchoritism/M +anchorman/M +anchormen +anchorpeople +anchorperson/S +anchor/SGDM +anchorwoman +anchorwomen +anchovy/MS +ancientness/MS +ancient/SRYTP +ancillary/S +an/CS +Andalusia/M +Andalusian +Andaman +andante/S +and/DZGS +Andean/M +Andeee/M +Andee/M +Anderea/M +Andersen/M +Anders/N +Anderson/M +Andes +Andie/M +Andi/M +andiron/MS +Andonis/M +Andorra/M +Andover/M +Andra/SM +Andrea/MS +Andreana/M +Andree/M +Andrei/M +Andrej/M +Andre/SM +Andrew/MS +Andrey/M +Andria/M +Andriana/M +Andriette/M +Andris +androgenic +androgen/SM +androgynous +androgyny/SM +android/MS +Andromache/M +Andromeda/M +Andropov/M +Andros/M +Andrus/M +Andy/M +anecdotal/Y +anecdote/SM +anechoic +anemia/SM +anemically +anemic/S +anemometer/MS +anemometry/M +anemone/SM +anent +aneroid +Anestassia/M +anesthesia/MS +anesthesiologist/MS +anesthesiology/SM +anesthetically +anesthetic/SM +anesthetist/MS +anesthetization/SM +anesthetizer/M +anesthetize/ZSRDG +Anet/M +Anetta/M +Anette/M +Anett/M +aneurysm/MS +anew +Angara/M +Angela/M +Angeleno/SM +Angele/SM +angelfish/SM +Angelia/M +angelic +angelical/Y +Angelica/M +angelica/MS +Angelico/M +Angelika/M +Angeli/M +Angelina/M +Angeline/M +Angelique/M +Angelita/M +Angelle/M +Angel/M +angel/MDSG +Angelo/M +Angelou/M +Ange/M +anger/GDMS +Angevin/M +Angie/M +Angil/M +angina/MS +angiography +angioplasty/S +angiosperm/MS +Angkor/M +angle/GMZDSRJ +angler/M +Angles +angleworm/MS +Anglia/M +Anglicanism/MS +Anglican/MS +Anglicism/SM +Anglicization/MS +anglicize/SDG +Anglicize/SDG +angling/M +Anglo/MS +Anglophile/SM +Anglophilia/M +Anglophobe/MS +Anglophobia/M +Angola/M +Angolan/S +angora/MS +Angora/MS +angrily +angriness/M +angry/RTP +angst/MS +ngstrm/M +angstrom/MS +Anguilla/M +anguish/DSMG +angularity/MS +angular/Y +Angus/M +Angy/M +Anheuser/M +anhydride/M +anhydrite/M +anhydrous/Y +Aniakchak/M +Ania/M +Anibal/M +Anica/M +aniline/SM +animadversion/SM +animadvert/DSG +animalcule/MS +animal/MYPS +animated/A +animatedly +animately/I +animateness/MI +animates/A +animate/YNGXDSP +animating/A +animation/AMS +animator/SM +animism/SM +animistic +animist/S +animized +animosity/MS +animus/SM +anionic/S +anion/MS +aniseed/MS +aniseikonic +anise/MS +anisette/SM +anisotropic +anisotropy/MS +Anissa/M +Anita/M +Anitra/M +Anjanette/M +Anjela/M +Ankara/M +ankh/M +ankhs +anklebone/SM +ankle/GMDS +anklet/MS +Annabal/M +Annabela/M +Annabella/M +Annabelle/M +Annabell/M +Annabel/M +Annadiana/M +Annadiane/M +Annalee/M +Annaliese/M +Annalise/M +annalist/MS +annal/MNS +Anna/M +Annamaria/M +Annamarie/M +Annapolis/M +Annapurna/M +anneal/DRSZG +annealer/M +Annecorinne/M +annelid/MS +Anneliese/M +Annelise/M +Anne/M +Annemarie/M +Annetta/M +Annette/M +annexation/SM +annexe/M +annex/GSD +Annice/M +Annie/M +annihilate/XSDVGN +annihilation/M +annihilator/MS +Anni/MS +Annissa/M +anniversary/MS +Ann/M +Annmaria/M +Annmarie/M +Annnora/M +Annora/M +annotated/U +annotate/VNGXSD +annotation/M +annotator/MS +announced/U +announcement/SM +announcer/M +announce/ZGLRSD +annoyance/MS +annoyer/M +annoying/Y +annoy/ZGSRD +annualized +annual/YS +annuitant/MS +annuity/MS +annular/YS +annuli +annulled +annulling +annulment/MS +annul/SL +annulus/M +annum +annunciate/XNGSD +annunciation/M +Annunciation/S +annunciator/SM +Anny/M +anode/SM +anodic +anodize/GDS +anodyne/SM +anoint/DRLGS +anointer/M +anointment/SM +anomalousness/M +anomalous/YP +anomaly/MS +anomic +anomie/M +anon/S +anonymity/MS +anonymousness/M +anonymous/YP +anopheles/M +anorak/SM +anorectic/S +anorexia/SM +anorexic/S +another/M +Anouilh/M +Ansell/M +Ansel/M +Anselma/M +Anselm/M +Anselmo/M +Anshan/M +ANSI/M +Ansley/M +ans/M +Anson/M +Anstice/M +answerable/U +answered/U +answerer/M +answer/MZGBSDR +antacid/MS +Antaeus/M +antagonism/MS +antagonistic +antagonistically +antagonist/MS +antagonized/U +antagonize/GZRSD +antagonizing/U +Antananarivo/M +antarctic +Antarctica/M +Antarctic/M +Antares +anteater/MS +antebellum +antecedence/MS +antecedent/SMY +antechamber/SM +antedate/GDS +antediluvian/S +anteing +antelope/MS +ante/MS +antenatal +antennae +antenna/MS +anterior/SY +anteroom/SM +ant/GSMD +Anthea/M +Anthe/M +anthem/MGDS +anther/MS +Anthia/M +Anthiathia/M +anthill/S +anthologist/MS +anthologize/GDS +anthology/SM +Anthony/M +anthraces +anthracite/MS +anthrax/M +anthropic +anthropocentric +anthropogenic +anthropoid/S +anthropological/Y +anthropologist/MS +anthropology/SM +anthropometric/S +anthropometry/M +anthropomorphic +anthropomorphically +anthropomorphism/SM +anthropomorphizing +anthropomorphous +antiabortion +antiabortionist/S +antiaircraft +antibacterial/S +antibiotic/SM +antibody/MS +anticancer +Antichrist/MS +anticipated/U +anticipate/XVGNSD +anticipation/M +anticipative/Y +anticipatory +anticked +anticking +anticlerical/S +anticlimactic +anticlimactically +anticlimax/SM +anticline/SM +anticlockwise +antic/MS +anticoagulant/S +anticoagulation/M +anticommunism/SM +anticommunist/SM +anticompetitive +anticyclone/MS +anticyclonic +antidemocratic +antidepressant/SM +antidisestablishmentarianism/M +antidote/DSMG +Antietam/M +antifascist/SM +antiformant +antifreeze/SM +antifundamentalist/M +antigenic +antigenicity/SM +antigen/MS +antigone +Antigone/M +Antigua/M +antiheroes +antihero/M +antihistamine/MS +antihistorical +antiknock/MS +antilabor +Antillean +Antilles +antilogarithm/SM +antilogs +antimacassar/SM +antimalarial/S +antimatter/SM +antimicrobial/S +antimissile/S +antimony/SM +anting/M +Antin/M +antinomian +antinomy/M +antinuclear +Antioch/M +antioxidant/MS +antiparticle/SM +Antipas/M +antipasti +antipasto/MS +antipathetic +antipathy/SM +antipersonnel +antiperspirant/MS +antiphonal/SY +antiphon/SM +antipodal/S +antipodean/S +antipode/MS +Antipodes +antipollution/S +antipoverty +antiquarianism/MS +antiquarian/MS +antiquary/SM +antiquate/NGSD +antiquation/M +antique/MGDS +antiquity/SM +antiredeposition +antiresonance/M +antiresonator +anti/S +antisemitic +antisemitism/M +antisepses +antisepsis/M +antiseptically +antiseptic/S +antiserum/SM +antislavery/S +antisocial/Y +antispasmodic/S +antisubmarine +antisymmetric +antisymmetry +antitank +antitheses +antithesis/M +antithetic +antithetical/Y +antithyroid +antitoxin/MS +antitrust/MR +antivenin/MS +antiviral/S +antivivisectionist/S +antiwar +antler/SDM +Antofagasta/M +Antoine/M +Antoinette/M +Antonella/M +Antone/M +Antonetta/M +Antonia/M +Antonie/M +Antonietta/M +Antoni/M +Antonina/M +Antonin/M +Antonino/M +Antoninus/M +Antonio/M +Antonius/M +Anton/MS +Antonovics/M +Antony/M +antonymous +antonym/SM +antral +antsy/RT +Antwan/M +Antwerp/M +Anubis/M +anus/SM +anvil/MDSG +anxiety/MS +anxiousness/SM +anxious/PY +any +Anya/M +anybody/S +anyhow +Any/M +anymore +anyone/MS +anyplace +anything/S +anytime +anyway/S +anywhere/S +anywise +AOL/M +aorta/MS +aortic +AP +apace +apache/MS +Apache/MS +Apalachicola/M +apartheid/SM +apart/LP +apartment/MS +apartness/M +apathetic +apathetically +apathy/SM +apatite/MS +APB +aped/A +apelike +ape/MDRSG +Apennines +aper/A +aperiodic +aperiodically +aperiodicity/M +aperitif/S +aperture/MDS +apex/MS +aphasia/SM +aphasic/S +aphelia +aphelion/SM +aphid/MS +aphonic +aphorism/MS +aphoristic +aphoristically +aphrodisiac/SM +Aphrodite/M +Apia/M +apiarist/SM +apiary/SM +apical/YS +apices's +apiece +apishness/M +apish/YP +aplenty +aplomb/SM +APO +Apocalypse/M +apocalypse/MS +apocalyptic +apocryphalness/M +apocryphal/YP +apocrypha/M +Apocrypha/M +apogee/MS +apolar +apolitical/Y +Apollinaire/M +Apollonian +Apollo/SM +apologetically/U +apologetic/S +apologetics/M +apologia/SM +apologist/MS +apologize/GZSRD +apologizer/M +apologizes/A +apologizing/U +apology/MS +apoplectic +apoplexy/SM +apostasy/SM +apostate/SM +apostatize/DSG +apostleship/SM +apostle/SM +apostolic +apostrophe/SM +apostrophized +apothecary/MS +apothegm/MS +apotheoses +apotheosis/M +apotheosized +apotheosizes +apotheosizing +Appalachia/M +Appalachian/MS +appalling/Y +appall/SDG +Appaloosa/MS +appaloosa/S +appanage/M +apparatus/SM +apparel/SGMD +apparency +apparently/I +apparentness/M +apparent/U +apparition/SM +appealer/M +appealing/UY +appeal/SGMDRZ +appear/AEGDS +appearance/AMES +appearer/S +appease/DSRGZL +appeased/U +appeasement/MS +appeaser/M +appellant/MS +appellate/VNX +appellation/M +appellative/MY +appendage/MS +appendectomy/SM +appendices +appendicitis/SM +appendix/SM +append/SGZDR +appertain/DSG +appetite/MVS +appetizer/SM +appetizing/YU +Appia/M +Appian/M +applauder/M +applaud/ZGSDR +applause/MS +applecart/M +applejack/MS +Apple/M +apple/MS +applesauce/SM +Appleseed/M +Appleton/M +applet/S +appliance/SM +applicabilities +applicability/IM +applicable/I +applicably +applicant/MS +applicate/V +application/MA +applicative/Y +applicator/MS +applier/SM +appliqud +appliqu/MSG +apply/AGSDXN +appointee/SM +appoint/ELSADG +appointer/MS +appointive +appointment/ASEM +Appolonia/M +Appomattox/M +apportion/GADLS +apportionment/SAM +appose/SDG +appositeness/MS +apposite/XYNVP +apposition/M +appositive/SY +appraisal/SAM +appraised/A +appraisees +appraiser/M +appraises/A +appraise/ZGDRS +appraising/Y +appreciable/I +appreciably/I +appreciated/U +appreciate/XDSNGV +appreciation/M +appreciativeness/MI +appreciative/PIY +appreciator/MS +appreciatory +apprehend/DRSG +apprehender/M +apprehensible +apprehension/SM +apprehensiveness/SM +apprehensive/YP +apprentice/DSGM +apprenticeship/SM +apprise/DSG +apprizer/SM +apprizingly +apprizings +approachability/UM +approachable/UI +approach/BRSDZG +approacher/M +approbate/NX +approbation/EMS +appropriable +appropriated/U +appropriately/I +appropriateness/SMI +appropriate/XDSGNVYTP +appropriation/M +appropriator/SM +approval/ESM +approve/DSREG +approved/U +approver's/E +approver/SM +approving/YE +approx +approximate/XGNVYDS +approximation/M +approximative/Y +appurtenance/MS +appurtenant/S +APR +apricot/MS +Aprilette/M +April/MS +Apr/M +apron/SDMG +apropos +apse/MS +apsis/M +apter +aptest +aptitude/SM +aptness/SMI +aptness's/U +apt/UPYI +Apuleius/M +aquaculture/MS +aqualung/SM +aquamarine/SM +aquanaut/SM +aquaplane/GSDM +aquarium/MS +Aquarius/MS +aqua/SM +aquatically +aquatic/S +aquavit/SM +aqueduct/MS +aqueous/Y +aquiculture's +aquifer/SM +Aquila/M +aquiline +Aquinas/M +Aquino/M +Aquitaine/M +AR +Arabela/M +Arabele/M +Arabella/M +Arabelle/M +Arabel/M +arabesque/SM +Arabia/M +Arabian/MS +Arabic/M +arability/MS +Arabist/MS +arable/S +Arab/MS +Araby/M +Araceli/M +arachnid/MS +arachnoid/M +arachnophobia +Arafat/M +Araguaya/M +Araldo/M +Aral/M +Ara/M +Aramaic/M +Aramco/M +Arapahoes +Arapahoe's +Arapaho/MS +Ararat/M +Araucanian/M +Arawakan/M +Arawak/M +arbiter/MS +arbitrage/GMZRSD +arbitrager/M +arbitrageur/S +arbitrament/MS +arbitrarily +arbitrariness/MS +arbitrary/P +arbitrate/SDXVNG +arbitration/M +arbitrator/SM +arbor/DMS +arboreal/Y +arbores +arboretum/MS +arborvitae/MS +arbutus/SM +ARC +arcade/SDMG +Arcadia/M +Arcadian +arcana/M +arcane/P +arc/DSGM +archaeological/Y +archaeologist/SM +archaically +archaic/P +Archaimbaud/M +archaism/SM +archaist/MS +archaize/GDRSZ +archaizer/M +Archambault/M +archangel/SM +archbishopric/SM +archbishop/SM +archdeacon/MS +archdiocesan +archdiocese/SM +archduchess/MS +archduke/MS +Archean +archenemy/SM +archeologist's +archeology/MS +archer/M +Archer/M +archery/MS +archetypal +archetype/SM +archfiend/SM +archfool +Archibald/M +Archibaldo/M +Archibold/M +Archie/M +archiepiscopal +Archimedes/M +arching/M +archipelago/SM +architect/MS +architectonic/S +architectonics/M +architectural/Y +architecture/SM +architrave/MS +archival +archive/DRSGMZ +archived/U +archivist/MS +Arch/MR +archness/MS +arch/PGVZTMYDSR +archway/SM +Archy/M +arclike +ARCO/M +arcsine +arctangent +Arctic/M +arctic/S +Arcturus/M +Ardabil +Arda/MH +Ardath/M +Ardeen/M +Ardelia/M +Ardelis/M +Ardella/M +Ardelle/M +ardency/M +Ardene/M +Ardenia/M +Arden/M +ardent/Y +Ardine/M +Ardisj/M +Ardis/M +Ardith/M +ardor/SM +Ardra/M +arduousness/SM +arduous/YP +Ardyce/M +Ardys +Ardyth/M +areal +area/SM +areawide +are/BS +Arel/M +arenaceous +arena/SM +aren't +Arequipa/M +Ares +Aretha/M +Argentina/M +Argentinean/S +Argentine/SM +Argentinian/S +argent/MS +arginine/MS +Argonaut/MS +argonaut/S +argon/MS +Argonne/M +Argo/SM +argosy/SM +argot/SM +arguable/IU +arguably/IU +argue/DSRGZ +arguer/M +argumentation/SM +argumentativeness/MS +argumentative/YP +argument/SM +Argus/M +argyle/S +Ariadne/M +Ariana/M +Arianism/M +Arianist/SM +aria/SM +Aridatha/M +aridity/SM +aridness/M +arid/TYRP +Ariela/M +Ariella/M +Arielle/M +Ariel/M +Arie/SM +Aries/S +aright +Ari/M +Arin/M +Ario/M +Ariosto/M +arise/GJSR +arisen +Aristarchus/M +Aristides +aristocracy/SM +aristocratic +aristocratically +aristocrat/MS +Aristophanes/M +Aristotelean +Aristotelian/M +Aristotle/M +arithmetical/Y +arithmetician/SM +arithmetic/MS +arithmetize/SD +Arius/M +Ariz/M +Arizona/M +Arizonan/S +Arizonian/S +Arjuna/M +Arkansan/MS +Arkansas/M +Arkhangelsk/M +Ark/M +ark/MS +Arkwright/M +Arlana/M +Arlan/M +Arlee/M +Arleen/M +Arlena/M +Arlene/M +Arlen/M +Arleta/M +Arlette/M +Arley/M +Arleyne/M +Arlie/M +Arliene/M +Arlina/M +Arlinda/M +Arline/M +Arlington/M +Arlin/M +Arluene/M +Arly/M +Arlyne/M +Arlyn/M +Armada/M +armada/SM +armadillo/MS +Armageddon/SM +Armagnac/M +armament/EAS +armament's/E +Armand/M +Armando/M +Arman/M +arm/ASEDG +Armata/M +armature/MGSD +armband/SM +armchair/MS +Armco/M +armed/U +Armenia/M +Armenian/MS +armer/MES +armful/SM +armhole/MS +arming/M +Arminius/M +Armin/M +armistice/MS +armless +armlet/SM +armload/M +Armonk/M +armored/U +armorer/M +armorial/S +armory/DSM +armor/ZRDMGS +Armour/M +armpit/MS +armrest/MS +arm's +Armstrong/M +Ar/MY +army/SM +Arnaldo/M +Arneb/M +Arne/M +Arney/M +Arnhem/M +Arnie/M +Arni/M +Arnold/M +Arnoldo/M +Arno/M +Arnuad/M +Arnulfo/M +Arny/M +aroma/SM +aromatherapist/S +aromatherapy/S +aromatically +aromaticity/M +aromaticness/M +aromatic/SP +Aron/M +arose +around +arousal/MS +aroused/U +arouse/GSD +ARPA/M +Arpanet/M +ARPANET/M +arpeggio/SM +arrack/M +Arragon/M +arraignment/MS +arraign/SDGL +arrangeable/A +arranged/EA +arrangement/AMSE +arranger/M +arranges/EA +arrange/ZDSRLG +arranging/EA +arrant/Y +arras/SM +arrayer +array/ESGMD +arrear/SM +arrest/ADSG +arrestee/MS +arrester/MS +arresting/Y +arrestor/MS +Arrhenius/M +arrhythmia/SM +arrhythmic +arrhythmical +Arri/M +arrival/MS +arriver/M +arrive/SRDG +arrogance/MS +arrogant/Y +arrogate/XNGDS +arrogation/M +Arron/M +arrowhead/SM +arrowroot/MS +arrow/SDMG +arroyo/MS +arr/TV +arsenal/MS +arsenate/M +arsenic/MS +arsenide/M +arsine/MS +arsonist/MS +arson/SM +Artair/M +Artaxerxes/M +artefact's +Arte/M +Artemas +Artemis/M +Artemus/M +arterial/SY +arteriolar +arteriole/SM +arterioscleroses +arteriosclerosis/M +artery/SM +artesian +artfulness/SM +artful/YP +Arther/M +arthritic/S +arthritides +arthritis/M +arthrogram/MS +arthropod/SM +arthroscope/S +arthroscopic +Arthurian +Arthur/M +artichoke/SM +article/GMDS +articulable/I +articular +articulated/EU +articulately/I +articulateness/IMS +articulates/I +articulate/VGNYXPSD +articulation/M +articulator/SM +articulatory +Artie/M +artifact/MS +artificer/M +artifice/ZRSM +artificiality/MS +artificialness/M +artificial/PY +artillerist +artilleryman/M +artillerymen +artillery/SM +artiness/MS +artisan/SM +artiste/SM +artistically/I +artistic/I +artist/MS +artistry/SM +artlessness/MS +artless/YP +Art/M +art/SM +artsy/RT +Artur/M +Arturo/M +Artus/M +artwork/MS +Arty/M +arty/TPR +Aruba/M +arum/MS +Arvie/M +Arvin/M +Arv/M +Arvy/M +Aryan/MS +Aryn/M +as +As +A's +Asa/M +Asama/M +asap +ASAP +asbestos/MS +Ascella/M +ascend/ADGS +ascendancy/MS +ascendant/SY +ascender/SM +Ascension/M +ascension/SM +ascent/SM +ascertain/DSBLG +ascertainment/MS +ascetically +asceticism/MS +ascetic/SM +ASCII +ascot/MS +ascribe/GSDB +ascription/MS +ascriptive +Ase/M +aseptically +aseptic/S +asexuality/MS +asexual/Y +Asgard/M +ashame/D +ashamed/UY +Ashanti/M +Ashbey/M +Ashby/M +ashcan/SM +Ashely/M +Asher/M +Asheville/M +Ashia/M +Ashien/M +Ashil/M +Ashkenazim +Ashkhabad/M +Ashla/M +Ashland/M +Ashlan/M +ashlar/GSDM +Ashlee/M +Ashleigh/M +Ashlen/M +Ashley/M +Ashlie/M +Ashli/M +Ashlin/M +Ashly/M +ashman/M +ash/MNDRSG +Ashmolean/M +Ash/MRY +ashore +ashram/SM +Ashton/M +ashtray/MS +Ashurbanipal/M +ashy/RT +Asia/M +Asian/MS +Asiatic/SM +aside/S +Asilomar/M +Asimov +asinine/Y +asininity/MS +askance +ask/DRZGS +asked/U +asker/M +askew/P +ASL +aslant +asleep +Asmara/M +asocial/S +Asoka/M +asparagus/MS +aspartame/S +ASPCA +aspect/SM +Aspell/M +aspen/M +Aspen/M +asperity/SM +asper/M +aspersion/SM +asphalt/MDRSG +asphodel/MS +asphyxia/MS +asphyxiate/GNXSD +asphyxiation/M +aspic/MS +Aspidiske/M +aspidistra/MS +aspirant/MS +aspirate/NGDSX +aspirational +aspiration/M +aspirator/SM +aspire/GSRD +aspirer/M +aspirin/SM +asplenium +asp/MNRXS +Asquith/M +Assad/M +assailable/U +assailant/SM +assail/BGDS +Assamese/M +Assam/M +assassinate/DSGNX +assassination/M +assassin/MS +assaulter/M +assaultive/YP +assault/SGVMDR +assayer/M +assay/SZGRD +assemblage/MS +assemble/ADSREG +assembled/U +assembler/EMS +assemblies/A +assembly/EAM +assemblyman/M +assemblymen +Assembly/MS +assemblywoman +assemblywomen +assent/SGMRD +assert/ADGS +asserter/MS +assertional +assertion/AMS +assertiveness/SM +assertive/PY +assess/BLSDG +assessed/A +assesses/A +assessment/SAM +assessor/MS +asset/SM +asseverate/XSDNG +asseveration/M +asshole/MS! +assiduity/SM +assiduousness/SM +assiduous/PY +assign/ALBSGD +assignation/MS +assigned/U +assignee/MS +assigner/MS +assignment/MAS +assignor/MS +assigns/CU +assimilate/VNGXSD +assimilationist/M +assimilation/M +Assisi/M +assistance/SM +assistantship/SM +assistant/SM +assisted/U +assister/M +assist/RDGS +assize/MGSD +ass/MNS +assn +assoc +associable +associated/U +associate/SDEXNG +associateship +associational +association/ME +associative/Y +associativity/S +associator/MS +assonance/SM +assonant/S +assorter/M +assort/LRDSG +assortment/SM +asst +assuaged/U +assuage/SDG +assumability +assumer/M +assume/SRDBJG +assuming/UA +assumption/SM +assumptive +assurance/AMS +assure/AGSD +assuredness/M +assured/PYS +assurer/SM +assuring/YA +Assyria/M +Assyrian/SM +Assyriology/M +Astaire/SM +Astarte/M +astatine/MS +aster/ESM +asteria +asterisked/U +asterisk/SGMD +astern +asteroidal +asteroid/SM +asthma/MS +asthmatic/S +astigmatic/S +astigmatism/SM +astir +astonish/GSDL +astonishing/Y +astonishment/SM +Aston/M +Astoria/M +Astor/M +astounding/Y +astound/SDG +astraddle +Astrakhan/M +astrakhan/SM +astral/SY +Astra/M +astray +astride +Astrid/M +astringency/SM +astringent/YS +Astrix/M +astrolabe/MS +astrologer/MS +astrological/Y +astrologist/M +astrology/SM +astronautical +astronautic/S +astronautics/M +astronaut/SM +astronomer/MS +astronomic +astronomical/Y +astronomy/SM +astrophysical +astrophysicist/SM +astrophysics/M +Astroturf/M +AstroTurf/S +Asturias/M +astuteness/MS +astute/RTYP +Asuncin/M +asunder +Aswan/M +asylum/MS +asymmetric +asymmetrical/Y +asymmetry/MS +asymptomatic +asymptomatically +asymptote/MS +asymptotically +asymptotic/Y +asynchronism/M +asynchronous/Y +asynchrony +at +Atacama/M +Atahualpa/M +Atalanta/M +Atari/M +Atatrk/M +atavism/MS +atavistic +atavist/MS +ataxia/MS +ataxic/S +atelier/SM +atemporal +ate/S +Athabasca/M +Athabascan's +Athabaskan/MS +Athabaska's +atheism/SM +atheistic +atheist/SM +Athena/M +Athene/M +Athenian/SM +Athens/M +atheroscleroses +atherosclerosis/M +athirst +athlete/MS +athletically +athleticism/M +athletic/S +athletics/M +athwart +atilt +Atkins/M +Atkinson/M +Atlanta/M +Atlante/MS +atlantes +Atlantic/M +Atlantis/M +atlas/SM +Atlas/SM +At/M +Atman +ATM/M +atmosphere/DSM +atmospherically +atmospheric/S +atoll/MS +atomically +atomicity/M +atomic/S +atomics/M +atomistic +atomization/SM +atomize/GZDRS +atomizer/M +atom/SM +atonality/MS +atonal/Y +atone/LDSG +atonement/SM +atop +ATP +Atreus/M +atria +atrial +Atria/M +atrium/M +atrociousness/SM +atrocious/YP +atrocity/SM +atrophic +atrophy/DSGM +atropine/SM +Atropos/M +Ats +attach/BLGZMDRS +attached/UA +attacher/M +attach/S +attachment/ASM +attacker/M +attack/GBZSDR +attainabilities +attainability/UM +attainableness/M +attainable/U +attainably/U +attain/AGSD +attainder/MS +attained/U +attainer/MS +attainment/MS +attar/MS +attempt/ADSG +attempter/MS +attendance/MS +attendant/SM +attended/U +attendee/SM +attender/M +attend/SGZDR +attentional +attentionality +attention/IMS +attentiveness/IMS +attentive/YIP +attenuated/U +attenuate/SDXGN +attenuation/M +attenuator/MS +attestation/SM +attested/U +attester/M +attest/GSDR +Attic +Attica/M +attic/MS +Attila/M +attire/SDG +attitude/MS +attitudinal/Y +attitudinize/SDG +Attlee/M +attn +Attn +attorney/SM +attractant/SM +attract/BSDGV +attraction/MS +attractivenesses +attractiveness/UM +attractive/UYP +attractor/MS +attributable/U +attribute/BVNGRSDX +attributed/U +attributer/M +attributional +attribution/M +attributive/SY +attrition/MS +Attucks +attune/SDG +atty +ATV/S +atwitter +Atwood/M +atypical/Y +Aube/M +Auberge/M +aubergine/MS +Auberon/M +Auberta/M +Aubert/M +Aubine/M +Aubree/M +Aubrette/M +Aubrey/M +Aubrie/M +Aubry/M +auburn/SM +Auckland/M +auctioneer/SDMG +auction/MDSG +audaciousness/SM +audacious/PY +audacity/MS +Auden/M +audibility/MSI +audible/I +audibles +audibly/I +Audie/M +audience/MS +Audi/M +audiogram/SM +audiological +audiologist/MS +audiology/SM +audiometer/MS +audiometric +audiometry/M +audiophile/SM +audio/SM +audiotape/S +audiovisual/S +audited/U +audition/MDSG +auditorium/MS +auditor/MS +auditory/S +audit/SMDVG +Audra/M +Audre/M +Audrey/M +Audrie/M +Audrye/M +Audry/M +Audubon/M +Audy/M +Auerbach/M +Augean +auger/SM +aught/S +Augie/M +Aug/M +augmentation/SM +augmentative/S +augment/DRZGS +augmenter/M +augur/GDMS +augury/SM +Augusta/M +Augustan/S +Auguste/M +Augustina/M +Augustine/M +Augustinian/S +Augustin/M +augustness/SM +Augusto/M +August/SM +august/STPYR +Augustus/M +Augy/M +auk/MS +Au/M +Aundrea/M +auntie/MS +aunt/MYS +aunty's +aural/Y +Aura/M +aura/SM +Aurea/M +Aurelea/M +Aurelia/M +Aurelie/M +Aurelio/M +Aurelius/M +Aurel/M +aureole/GMSD +aureomycin +Aureomycin/M +Auria/M +auric +auricle/SM +auricular +Aurie/M +Auriga/M +Aurilia/M +Aurlie/M +Auroora/M +auroral +Aurora/M +aurora/SM +Aurore/M +Aurthur/M +Auschwitz/M +auscultate/XDSNG +auscultation/M +auspice/SM +auspicious/IPY +auspiciousnesses +auspiciousness/IM +Aussie/MS +Austen/M +austereness/M +austere/TYRP +austerity/SM +Austina/M +Austine/M +Austin/SM +austral +Australasia/M +Australasian/S +australes +Australia/M +Australian/MS +Australis/M +australites +Australoid +Australopithecus/M +Austria/M +Austrian/SM +Austronesian +authentically +authenticated/U +authenticate/GNDSX +authentication/M +authenticator/MS +authenticity/MS +authentic/UI +author/DMGS +authoress/S +authorial +authoritarianism/MS +authoritarian/S +authoritativeness/SM +authoritative/PY +authority/SM +authorization/MAS +authorize/AGDS +authorized/U +authorizer/SM +authorizes/U +authorship/MS +autism/MS +autistic/S +autobahn/MS +autobiographer/MS +autobiographic +autobiographical/Y +autobiography/MS +autoclave/SDGM +autocollimator/M +autocorrelate/GNSDX +autocorrelation/M +autocracy/SM +autocratic +autocratically +autocrat/SM +autodial/R +autodidact/MS +autofluorescence +autograph/MDG +autographs +autoignition/M +autoimmune +autoimmunity/S +autoloader +automaker/S +automata's +automate/NGDSX +automatically +automatic/S +automation/M +automatism/SM +automatize/DSG +automaton/SM +automobile/GDSM +automorphism/SM +automotive +autonavigator/SM +autonomic/S +autonomous/Y +autonomy/MS +autopilot/SM +autopsy/MDSG +autoregressive +autorepeat/GS +auto/SDMG +autostart +autosuggestibility/M +autotransformer/M +autoworker/S +autumnal/Y +Autumn/M +autumn/MS +aux +auxiliary/S +auxin/MS +AV +availability/USM +availableness/M +available/U +availably +avail/BSZGRD +availing/U +avalanche/MGSD +Avalon/M +Ava/M +avant +avarice/SM +avariciousness/M +avaricious/PY +avast/S +avatar/MS +avaunt/S +avdp +Aveline/M +Ave/MS +avenged/U +avenger/M +avenge/ZGSRD +Aventine/M +Aventino/M +avenue/MS +average/DSPGYM +Averell/M +Averill/M +Averil/M +Avernus/M +averred +averrer +averring +Averroes/M +averseness/M +averse/YNXP +aversion/M +avers/V +avert/GSD +Averyl/M +Avery/M +ave/S +aves/C +Avesta/M +avg +avian/S +aviary/SM +aviate/NX +aviation/M +aviator/SM +aviatrices +aviatrix/SM +Avicenna/M +Avictor/M +avidity/MS +avid/TPYR +Avie/M +Avigdor/M +Avignon/M +Avila/M +avionic/S +avionics/M +Avior/M +Avis +avitaminoses +avitaminosis/M +Avivah/M +Aviva/M +Aviv/M +avocado/MS +avocational +avocation/SM +Avogadro/M +avoidable/U +avoidably/U +avoidance/SM +avoider/M +avoid/ZRDBGS +avoirdupois/MS +Avon/M +avouch/GDS +avowal/EMS +avowed/Y +avower/M +avow/GEDS +Avram/M +Avril/M +Avrit/M +Avrom/M +avuncular +av/ZR +AWACS +await/SDG +awake/GS +awakened/U +awakener/M +awakening/S +awaken/SADG +awarder/M +award/RDSZG +awareness/MSU +aware/TRP +awash +away/PS +aweigh +awe/SM +awesomeness/SM +awesome/PY +awestruck +awfuller +awfullest +awfulness/SM +awful/YP +aw/GD +awhile/S +awkwardness/MS +awkward/PRYT +awl/MS +awning/DM +awn/MDJGS +awoke +awoken +AWOL +awry/RT +ax/DRSZGM +axehead/S +Axel/M +Axe/M +axeman +axial/Y +axillary +axiological/Y +axiology/M +axiomatically +axiomatic/S +axiomatization/MS +axiomatize/GDS +axiom/SM +axion/SM +axis/SM +axle/MS +axletree/MS +Ax/M +axolotl/SM +axon/SM +ayah/M +ayahs +Ayala/M +ayatollah +ayatollahs +aye/MZRS +Ayers +Aylmar/M +Aylmer/M +Aymara/M +Aymer/M +Ayn/M +AZ +azalea/SM +Azania/M +Azazel/M +Azerbaijan/M +azimuthal/Y +azimuth/M +azimuths +Azores +Azov/M +AZT +Aztecan +Aztec/MS +azure/MS +BA +Baal/SM +baa/SDG +Babara/M +Babar's +Babbage/M +Babbette/M +Babbie/M +babbitt/GDS +Babbitt/M +babbler/M +babble/RSDGZ +Babb/M +Babcock/M +Babel/MS +babel/S +babe/SM +Babette/M +Babita/M +Babka/M +baboon/MS +Bab/SM +babushka/MS +babyhood/MS +babyish +Babylonia/M +Babylonian/SM +Babylon/MS +babysat +babysit/S +babysitter/S +babysitting +baby/TDSRMG +Bacall/M +Bacardi/M +baccalaureate/MS +baccarat/SM +bacchanalia +Bacchanalia/M +bacchanalian/S +bacchanal/SM +Bacchic +Bacchus/M +bachelorhood/SM +bachelor/SM +Bach/M +bacillary +bacilli +bacillus/MS +backache/SM +backarrow +backbencher/M +backbench/ZR +backbiter/M +backbite/S +backbitten +backbit/ZGJR +backboard/SM +backbone/SM +backbreaking +backchaining +backcloth/M +backdate/GDS +backdrop/MS +backdropped +backdropping +backed/U +backer/M +backfield/SM +backfill/SDG +backfire/GDS +backgammon/MS +background/SDRMZG +back/GZDRMSJ +backhanded/Y +backhander/M +backhand/RDMSZG +backhoe/S +backing/M +backlash/GRSDM +backless +backlogged +backlogging +backlog/MS +backorder +backpacker/M +backpack/ZGSMRD +backpedal/DGS +backplane/MS +backplate/SM +backrest/MS +backscatter/SMDG +backseat/S +backside/SM +backslapper/MS +backslapping/M +backslash/DSG +backslider/M +backslide/S +backslid/RZG +backspace/GSD +backspin/SM +backstabber/M +backstabbing +backstage +backstair/S +backstitch/GDSM +backstop/MS +backstopped +backstopping +backstreet/M +backstretch/SM +backstroke/GMDS +backtalk/S +backtrack/SDRGZ +backup/SM +Backus/M +backwardness/MS +backward/YSP +backwash/SDMG +backwater/SM +backwood/S +backwoodsman/M +backwoodsmen +backyard/MS +baconer/M +Bacon/M +bacon/SRM +bacterial/Y +bacteria/MS +bactericidal +bactericide/SM +bacteriologic +bacteriological +bacteriologist/MS +bacteriology/SM +bacterium/M +Bactria/M +badder +baddest +baddie/MS +bade +Baden/M +badge/DSRGMZ +badger/DMG +badinage/DSMG +badland/S +Badlands/M +badman/M +badmen +badminton/MS +badmouth/DG +badmouths +badness/SM +bad/PSNY +Baedeker/SM +Baez/M +Baffin/M +bafflement/MS +baffler/M +baffle/RSDGZL +baffling/Y +bagatelle/MS +bagel/SM +bagful/MS +baggageman +baggagemen +baggage/SM +bagged/M +bagger/SM +baggily +bagginess/MS +bagging/M +baggy/PRST +Baghdad/M +bagpiper/M +bagpipe/RSMZ +Bagrodia/MS +bag/SM +baguette/SM +Baguio/M +bah +Baha'i +Bahama/MS +Bahamanian/S +Bahamian/MS +Baha'ullah +Bahia/M +Bahrain/M +bahs +Baikal/M +Bailey/SM +bail/GSMYDRB +Bailie/M +bailiff/SM +bailiwick/MS +Baillie/M +Bail/M +bailout/MS +bailsman/M +bailsmen +Baily/M +Baird/M +bairn/SM +baiter/M +bait/GSMDR +baize/GMDS +Baja/M +baked/U +bakehouse/M +Bakelite/M +baker/M +Baker/M +Bakersfield/M +bakery/SM +bakeshop/S +bake/ZGJDRS +baking/M +baklava/M +baksheesh/SM +Baku/M +Bakunin/M +balaclava/MS +balalaika/MS +balanced/A +balancedness +balancer/MS +balance's +balance/USDG +Balanchine/M +Balboa/M +balboa/SM +balcony/MSD +balderdash/MS +Balder/M +baldfaced +Bald/MR +baldness/MS +bald/PYDRGST +baldric/SM +Balduin/M +Baldwin/M +baldy +Balearic/M +baleen/MS +balefuller +balefullest +balefulness/MS +baleful/YP +Bale/M +bale/MZGDRS +baler/M +Balfour/M +Bali/M +Balinese +balkanization +balkanize/DG +Balkan/SM +balker/M +balk/GDRS +Balkhash/M +balkiness/M +balky/PRT +balladeer/MS +ballade/MS +balladry/MS +ballad/SM +Ballard/SM +ballast/SGMD +ballcock/S +ballerina/MS +baller/M +balletic +ballet/MS +ballfields +ballgame/S +ball/GZMSDR +ballistic/S +ballistics/M +Ball/M +balloonist/S +balloon/RDMZGS +balloter/M +ballot/MRDGS +ballpark/SM +ballplayer/SM +ballpoint/SM +ballroom/SM +ballsy/TR +ballyhoo/SGMD +balminess/SM +balm/MS +balmy/PRT +baloney/SM +balsam/GMDS +balsamic +balsa/MS +Balthazar/M +Baltic/M +Baltimore/M +Baluchistan/M +baluster/MS +balustrade/SM +Balzac/M +Ba/M +Bamako/M +Bamberger/M +Bambie/M +Bambi/M +bamboo/SM +bamboozle/GSD +Bamby/M +Banach/M +banality/MS +banal/TYR +banana/SM +Bancroft/M +bandager/M +bandage/RSDMG +bandanna/SM +bandbox/MS +bandeau/M +bandeaux +band/EDGS +bander/M +banding/M +bandit/MS +banditry/MS +bandmaster/MS +bandoleer/SM +bandpass +band's +bandsman/M +bandsmen +bandstand/SM +bandstop +Bandung/M +bandwagon/MS +bandwidth/M +bandwidths +bandy/TGRSD +banefuller +banefullest +baneful/Y +bane/MS +Bangalore/M +banger/M +bang/GDRZMS +bangkok +Bangkok/M +Bangladeshi/S +Bangladesh/M +bangle/MS +Bangor/M +Bangui/M +bani +banisher/M +banishment/MS +banish/RSDGL +banister/MS +Banjarmasin/M +banjoist/SM +banjo/MS +Banjul/M +bankbook/SM +bankcard/S +banker/M +bank/GZJDRMBS +banking/M +Bank/MS +banknote/S +bankroll/DMSG +bankruptcy/MS +bankrupt/DMGS +Banky/M +Ban/M +banned/U +Banneker/M +banner/SDMG +banning/U +Bannister/M +bannister's +bannock/SM +banns +banqueter/M +banquet/SZGJMRD +banquette/MS +ban/SGMD +banshee/MS +bans/U +bantam/MS +bantamweight/MS +banterer/M +bantering/Y +banter/RDSG +Banting/M +Bantu/SM +banyan/MS +banzai/S +baobab/SM +Baotou/M +baptismal/Y +baptism/SM +Baptiste/M +baptistery/MS +baptist/MS +Baptist/MS +baptistry's +baptized/U +baptizer/M +baptize/SRDZG +baptizes/U +Barabbas/M +Barbabas/M +Barbabra/M +Barbadian/S +Barbados/M +Barbaraanne/M +Barbara/M +Barbarella/M +barbarianism/MS +barbarian/MS +barbaric +barbarically +barbarism/MS +barbarity/SM +barbarize/SDG +Barbarossa/M +barbarousness/M +barbarous/PY +Barbary/M +barb/DRMSGZ +barbecue/DRSMG +barbed/P +Barbee/M +barbell/SM +barbel/MS +Barbe/M +barbeque's +barber/DMG +barbered/U +Barber/M +barberry/MS +barbershop/MS +Barbette/M +Barbey/M +Barbie/M +Barbi/M +barbital/M +barbiturate/MS +Barbour/M +Barbra/M +Barb/RM +Barbuda/M +barbwire/SM +Barby/M +barcarole/SM +Barcelona/M +Barclay/M +Bardeen/M +Barde/M +bardic +Bard/M +bard/MDSG +bareback/D +barefacedness/M +barefaced/YP +barefoot/D +barehanded +bareheaded +barelegged +bareness/MS +Barents/M +bare/YSP +barfly/SM +barf/YDSG +bargainer/M +bargain/ZGSDRM +barge/DSGM +bargeman/M +bargemen +bargepole/M +barhopped +barhopping +barhop/S +Bari/M +baritone/MS +barium/MS +barked/C +barkeeper/M +barkeep/SRZ +barker/M +Barker/M +bark/GZDRMS +Barkley/M +barks/C +barleycorn/MS +barley/MS +Barlow/M +barmaid/SM +barman/M +barmen +Bar/MH +Barnabas +Barnabe/M +Barnaby/M +barnacle/MDS +Barnard/M +Barnaul/M +Barnebas/M +Barnes +Barnett/M +Barney/M +barnful +barn/GDSM +Barnhard/M +Barnie/M +Barn/M +barnsful +barnstorm/DRGZS +barnstormer/M +Barnum/M +barnyard/MS +Barny/M +Baroda/M +barometer/MS +barometric +barometrically +baronage/MS +baroness/MS +baronetcy/SM +baronet/MS +baronial +Baron/M +baron/SM +barony/SM +baroque/SPMY +barque's +Barquisimeto/M +barracker/M +barrack/SDRG +barracuda/MS +barrage/MGSD +Barranquilla/M +barred/ECU +barre/GMDSJ +barrel/SGMD +barrenness/SM +barren/SPRT +Barrera/M +Barret/M +barrette/SM +Barrett/M +barricade/SDMG +Barrie/M +barrier/MS +barring/R +barrio/SM +Barri/SM +barrister/MS +Barr/M +Barron/M +barroom/SM +barrow/MS +Barry/M +Barrymore/MS +bars/ECU +barstool/SM +Barstow/M +Bartel/M +bartender/M +bartend/ZR +barterer/M +barter/SRDZG +bar/TGMDRS +Barthel/M +Barth/M +Bartholdi/M +Bartholemy/M +Bartholomeo/M +Bartholomeus/M +Bartholomew/M +Bartie/M +Bartlet/M +Bartlett/M +Bart/M +Bartk/M +Bartolemo/M +Bartolomeo/M +Barton/M +Bartram/M +Barty/M +barycenter +barycentre's +barycentric +Bary/M +baryon/SM +Baryram/M +Baryshnikov/M +basaltic +basalt/SM +basal/Y +Bascom/M +bas/DRSTG +baseball/MS +baseband +baseboard/MS +base/CGRSDL +baseless +baseline/SM +Basel/M +basely +Base/M +baseman/M +basemen +basement/CSM +baseness/MS +baseplate/M +base's +basetting +bashfulness/MS +bashful/PY +bash/JGDSR +Basho/M +Basia/M +BASIC +basically +basic/S +Basie/M +basilar +Basile/M +basilica/SM +Basilio/M +basilisk/SM +Basilius/M +Basil/M +basil/MS +basin/DMS +basinful/S +basis/M +basketball/MS +basketry/MS +basket/SM +basketwork/SM +bask/GSD +basophilic +Basque/SM +Basra/M +Basseterre/M +basset/GMDS +Bassett/M +bassinet/SM +bassist/MS +Bass/M +basso/MS +bassoonist/MS +bassoon/MS +bass/SM +basswood/SM +bastardization/MS +bastardized/U +bastardize/SDG +bastard/MYS +bastardy/MS +baste/NXS +baster/M +Bastian/M +Bastien/M +Bastille/M +basting/M +bastion/DM +bast/SGZMDR +Basutoland/M +Bataan/M +Batavia/M +batch/MRSDG +bated/U +bate/KGSADC +bater/AC +Bates +bathe +bather/M +bathetic +bathhouse/SM +bath/JMDSRGZ +bathmat/S +Batholomew/M +bathos/SM +bathrobe/MS +bathroom/SDM +baths +Bathsheba/M +bathtub/MS +bathwater +bathyscaphe's +bathysphere/MS +batik/DMSG +Batista/M +batiste/SM +Bat/M +batman/M +Batman/M +batmen +baton/SM +Batsheva/M +batsman/M +bat/SMDRG +batsmen +battalion/MS +batted +batten/SDMG +batter/SRDZG +battery/MS +batting/MS +battledore/MS +battledress +battlefield/SM +battlefront/SM +battle/GMZRSDL +battleground/SM +Battle/M +battlement/SMD +battler/M +battleship/MS +batty/RT +Batu/M +batwings +bauble/SM +Baudelaire/M +baud/M +Baudoin/M +Baudouin/M +Bauer/M +Bauhaus/M +baulk/GSDM +Bausch/M +bauxite/SM +Bavaria/M +Bavarian/S +bawdily +bawdiness/MS +bawd/SM +bawdy/PRST +bawler/M +bawl/SGDR +Baxie/M +Bax/M +Baxter/M +Baxy/M +Bayamon +Bayard/M +bayberry/MS +Bayda/M +Bayer/M +Bayes +Bayesian +bay/GSMDY +Baylor/M +Bay/MR +bayonet/SGMD +Bayonne/M +bayou/MS +Bayreuth/M +bazaar/MS +bazillion/S +bazooka/MS +BB +BBB +BBC +bbl +BBQ +BBS +BC +BCD +bdrm +beachcomber/SM +beachhead/SM +Beach/M +beach/MSDG +beachwear/M +beacon/DMSG +beading/M +Beadle/M +beadle/SM +bead/SJGMD +beadsman/M +beadworker +beady/TR +beagle/SDGM +beaker/M +beak/ZSDRM +Beale/M +Bealle/M +Bea/M +beam/MDRSGZ +beanbag/SM +bean/DRMGZS +beanie/SM +Bean/M +beanpole/MS +beanstalk/SM +bearable/U +bearably/U +beard/DSGM +bearded/P +beardless +Beard/M +Beardmore/M +Beardsley/M +bearer/M +bearing/M +bearishness/SM +bearish/PY +bearlike +Bear/M +Bearnaise/M +Bearnard/M +bearskin/MS +bear/ZBRSJG +Beasley/M +beasties +beastings/M +beastliness/MS +beastly/PTR +beast/SJMY +beatable/U +beatably/U +beaten/U +beater/M +beatific +beatifically +beatification/M +beatify/GNXDS +beating/M +beatitude/MS +Beatlemania/M +Beatles/M +beatnik/SM +beat/NRGSBZJ +Beatrice/M +Beatrisa/M +Beatrix/M +Beatriz/M +Beauchamps +Beaufort/M +Beaujolais/M +Beau/M +Beaumarchais/M +Beaumont/M +beau/MS +Beauregard/M +beauteousness/M +beauteous/YP +beautician/MS +beautification/M +beautifier/M +beautifully/U +beautifulness/M +beautiful/PTYR +beautify/SRDNGXZ +beaut/SM +beauty/SM +Beauvoir/M +beaux's +beaver/DMSG +Beaverton/M +Bebe/M +bebop/MS +becalm/GDS +became +because +Becca/M +Bechtel/M +Becka/M +Becker/M +Becket/M +Beckett/M +beck/GSDM +Beckie/M +Becki/M +beckon/SDG +Beck/RM +Becky/M +becloud/SGD +become/GJS +becoming/UY +Becquerel/M +bedaub/GDS +bedazzle/GLDS +bedazzlement/SM +bedbug/SM +bedchamber/M +bedclothes +bedded +bedder/MS +bedding/MS +bedeck/DGS +Bede/M +bedevil/DGLS +bedevilment/SM +bedfast +bedfellow/MS +Bedford/M +bedimmed +bedimming +bedim/S +bedizen/DGS +bedlam/MS +bedlinen +bedmaker/SM +bedmate/MS +bed/MS +Bedouin/SM +bedpan/SM +bedpost/SM +bedraggle/GSD +bedridden +bedrock/SM +bedroll/SM +bedroom/DMS +bedsheets +bedside/MS +bedsit +bedsitter/M +bedsore/MS +bedspread/SM +bedspring/SM +bedstead/SM +bedstraw/M +bedtime/SM +Beebe/M +beebread/MS +Beecher/M +beech/MRSN +beechnut/MS +beechwood +beefburger/SM +beefcake/MS +beef/GZSDRM +beefiness/MS +beefsteak/MS +beefy/TRP +beehive/MS +beekeeper/MS +beekeeping/SM +beeline/MGSD +Beelzebub/M +Bee/M +bee/MZGJRS +been/S +beeper/M +beep/GZSMDR +Beerbohm/M +beer/M +beermat/S +beery/TR +beeswax/DSMG +Beethoven/M +beetle/GMRSD +Beeton/M +beetroot/M +beet/SM +beeves/M +befall/SGN +befell +befit/SM +befitted +befitting/Y +befogged +befogging +befog/S +before +beforehand +befoul/GSD +befriend/DGS +befuddle/GLDS +befuddlement/SM +began +beget/S +begetting +beggar/DYMSG +beggarliness/M +beggarly/P +beggary/MS +begged +begging +Begin/M +beginner/MS +beginning/MS +begin/S +begone/S +begonia/SM +begot +begotten +begrime/SDG +begrudge/GDRS +begrudging/Y +beg/S +beguilement/SM +beguiler/M +beguile/RSDLZG +beguiling/Y +beguine/SM +begum/MS +begun +behalf/M +behalves +Behan/M +behave/GRSD +behavioral/Y +behaviorism/MS +behavioristic/S +behaviorist/S +behavior/SMD +behead/GSD +beheld +behemoth/M +behemoths +behest/SM +behindhand +behind/S +beholder/M +behold/ZGRNS +behoofs +behoove/SDJMG +behooving/YM +Behring/M +Beiderbecke/M +beige/MS +Beijing +Beilul/M +being/M +Beirut/M +Beitris/M +bejewel/SDG +Bekesy/M +Bekki/M +be/KS +belabor/MDSG +Bela/M +Belarus +belate/D +belatedness/M +belated/PY +Belau/M +belay/GSD +belch/GSD +beleaguer/GDS +Belem/M +Belfast/M +belfry/SM +Belgian/MS +Belgium/M +Belg/M +Belgrade/M +Belia/M +Belicia/M +belie +belief/ESUM +belier/M +believability's +believability/U +believable/U +believably/U +believed/U +believe/EZGDRS +believer/MUSE +believing/U +Belinda/M +Belita/M +belittlement/MS +belittler/M +belittle/RSDGL +Belize/M +belladonna/MS +Bella/M +Bellamy/M +Bellanca/M +Bellatrix/M +bellboy/MS +belled/A +Belle/M +belle/MS +belletristic +belletrist/SM +Belleville/M +bellflower/M +bell/GSMD +bellhop/MS +bellicoseness/M +bellicose/YP +bellicosity/MS +belligerence/SM +belligerency/MS +belligerent/SMY +Bellina/M +belling/A +Bellini/M +Bell/M +bellman/M +bellmen +Bellovin/M +bellow/DGS +Bellow/M +bellows/M +bells/A +bellwether/MS +Bellwood/M +bellyacher/M +bellyache/SRDGM +bellybutton/MS +bellyfull +bellyful/MS +belly/SDGM +Bel/M +Belmont/M +Belmopan/M +Beloit/M +belong/DGJS +belonging/MP +Belorussian/S +Belorussia's +belove/D +beloved/S +below/S +Belshazzar/M +belted/U +belt/GSMD +belting/M +Belton/M +Beltran/M +Beltsville/M +beltway/SM +beluga/SM +Belushi/M +Belva/M +belvedere/M +Belvia/M +bely/DSRG +beman +Be/MH +bemire/SDG +bemoan/GDS +bemused/Y +bemuse/GSDL +bemusement/SM +Benacerraf/M +Benares's +bencher/M +benchmark/GDMS +bench/MRSDG +bend/BUSG +bended +Bender/M +bender/MS +Bendick/M +Bendicty/M +Bendite/M +Bendix/M +beneath +Benedetta/M +Benedetto/M +Benedick/M +Benedicta/M +Benedictine/MS +benediction/MS +Benedict/M +Benedicto/M +benedictory +Benedikta/M +Benedikt/M +benefaction/MS +benefactor/MS +benefactress/S +benefice/MGSD +beneficence/SM +beneficent/Y +beneficialness/M +beneficial/PY +beneficiary/MS +benefiter/M +benefit/SRDMZG +Benelux/M +Benet/M +Benetta/M +Benetton/M +benevolence/SM +benevolentness/M +benevolent/YP +Bengali/M +Bengal/SM +Benghazi/M +Bengt/M +Beniamino/M +benightedness/M +benighted/YP +benignant +benignity/MS +benign/Y +Beninese +Benin/M +Benita/M +Benito/M +Benjamen/M +Benjamin/M +Benjie/M +Benji/M +Benjy/M +Ben/M +Bennett/M +Bennie/M +Benni/M +Bennington/M +Benn/M +Benny/M +Benoite/M +Benoit/M +Benson/M +Bentham/M +Bentlee/M +Bentley/MS +Bent/M +Benton/M +bents +bent/U +bentwood/SM +benumb/SGD +Benyamin/M +Benzedrine/M +benzene/MS +benzine/SM +Benz/M +Beograd's +Beowulf/M +bequeath/GSD +bequeaths +bequest/MS +berate/GSD +Berber/MS +bereave/GLSD +bereavement/MS +bereft +Berenice/M +Beret/M +beret/SM +Bergen/M +Bergerac/M +Berger/M +Berget/M +Berglund/M +Bergman/M +Berg/NRM +berg/NRSM +Bergson/M +Bergsten/M +Bergstrom/M +beribbon/D +beriberi/SM +Beringer/M +Bering/RM +Berkeley/M +berkelium/SM +Berke/M +Berkie/M +Berkley/M +Berkly/M +Berkowitz/M +Berkshire/SM +Berky/M +Berk/YM +Berle/M +Berliner/M +Berlin/SZRM +Berlioz/M +Berlitz/M +Berman/M +Ber/MG +berm/SM +Bermuda/MS +Bermudan/S +Bermudian/S +Bernadene/M +Bernadette/M +Bernadina/M +Bernadine/M +Berna/M +Bernardina/M +Bernardine/M +Bernardino/M +Bernard/M +Bernardo/M +Bernarr/M +Bernays/M +Bernbach/M +Bernelle/M +Berne's +Bernese +Bernete/M +Bernetta/M +Bernette/M +Bernhard/M +Bernhardt/M +Bernice/M +Berniece/M +Bernie/M +Berni/M +Bernini/M +Bernita/M +Bern/M +Bernoulli/M +Bernstein/M +Berny/M +Berra/M +Berrie/M +Berri/M +berrylike +Berry/M +berry/SDMG +berserker/M +berserk/SR +Berta/M +Berte/M +Bertha/M +Berthe/M +berth/MDGJ +berths +Bertie/M +Bertillon/M +Berti/M +Bertina/M +Bertine/M +Bert/M +Berton/M +Bertram/M +Bertrand/M +Bertrando/M +Berty/M +Beryle/M +beryllium/MS +Beryl/M +beryl/SM +Berzelius/M +bes +beseecher/M +beseeching/Y +beseech/RSJZG +beseem/GDS +beset/S +besetting +beside/S +besieger/M +besiege/SRDZG +besmear/GSD +besmirch/GSD +besom/GMDS +besot/S +besotted +besotting +besought +bespangle/GSD +bespatter/SGD +bespeak/SG +bespectacled +bespoke +bespoken +Bess +Bessel/M +Bessemer/M +Bessie/M +Bessy/M +best/DRSG +bestiality/MS +bestial/Y +bestiary/MS +bestirred +bestirring +bestir/S +Best/M +bestowal/SM +bestow/SGD +bestrew/DGS +bestrewn +bestridden +bestride/SG +bestrode +bestseller/MS +bestselling +bestubble/D +betaken +betake/SG +beta/SM +betatron/M +betcha +Betelgeuse/M +betel/MS +Bethanne/M +Bethany/M +bethel/M +Bethe/M +Bethena/M +Bethesda/M +Bethina/M +bethink/GS +Bethlehem/M +beth/M +Beth/M +bethought +Bethune +betide/GSD +betimes +bet/MS +betoken/GSD +betook +betrayal/SM +betrayer/M +betray/SRDZG +betrothal/SM +betrothed/U +betroth/GD +betroths +Betsey/M +Betsy/M +Betta/M +Betteanne/M +Betteann/M +Bette/M +betterment/MS +better/SDLG +Bettie/M +Betti/M +Bettina/M +Bettine/M +betting +bettor/SM +Bettye/M +Betty/SM +betweenness/M +between/SP +betwixt +Beulah/M +Bevan/M +bevel/SJGMRD +beverage/MS +Beverie/M +Beverlee/M +Beverley/M +Beverlie/M +Beverly/M +Bevin/M +Bevon/M +Bev's +Bevvy/M +bevy/SM +bewail/GDS +beware/GSD +bewhisker/D +bewigged +bewildered/PY +bewildering/Y +bewilder/LDSG +bewilderment/SM +bewitching/Y +bewitch/LGDS +bewitchment/SM +bey/MS +beyond/S +bezel/MS +bf +B/GT +Bhopal/M +Bhutanese +Bhutan/M +Bhutto/M +Bialystok/M +Bianca/M +Bianco/M +Bianka/M +biannual/Y +bias/DSMPG +biased/U +biathlon/MS +biaxial/Y +bibbed +Bibbie/M +bibbing +Bibbye/M +Bibby/M +Bibi/M +bible/MS +Bible/MS +biblical/Y +biblicists +bibliographer/MS +bibliographical/Y +bibliographic/S +bibliography/MS +bibliophile/MS +Bib/M +bib/MS +bibulous +bicameral +bicameralism/MS +bicarb/MS +bicarbonate/MS +bicentenary/S +bicentennial/S +bicep/S +biceps/M +bichromate/DM +bickerer/M +bickering/M +bicker/SRDZG +biconcave +biconnected +biconvex +bicuspid/S +bicycler/M +bicycle/RSDMZG +bicyclist/SM +biddable +bidden/U +bidder/MS +Biddie/M +bidding/MS +Biddle/M +Biddy/M +biddy/SM +bider/M +bide/S +bidet/SM +Bidget/M +bid/GMRS +bidiagonal +bidirectional/Y +bids/A +biennial/SY +biennium/SM +Bienville/M +Bierce/M +bier/M +bifocal/S +bifurcate/SDXGNY +bifurcation/M +bigamist/SM +bigamous +bigamy/SM +Bigelow/M +Bigfoot +bigged +bigger +biggest +biggie/SM +bigging +biggish +bighead/MS +bigheartedness/S +bighearted/P +bighorn/MS +bight/SMDG +bigmouth/M +bigmouths +bigness/SM +bigoted/Y +bigot/MDSG +bigotry/MS +big/PYS +bigwig/MS +biharmonic +bijection/MS +bijective/Y +bijou/M +bijoux +bike/MZGDRS +biker/M +bikini/SMD +Biko/M +bilabial/S +bilateralness/M +bilateral/PY +bilayer/S +Bilbao/M +bilberry/MS +Bilbo/M +bile/SM +bilge/GMDS +biliary +Bili/M +bilinear +bilingualism/SM +bilingual/SY +biliousness/SM +bilious/P +bilker/M +bilk/GZSDR +billboard/MDGS +biller/M +billet/MDGS +billfold/MS +billiard/SM +Billie/M +Billi/M +billing/M +billingsgate/SM +Billings/M +billionaire/MS +billion/SHM +billionths +bill/JGZSBMDR +Bill/JM +billow/DMGS +billowy/RT +billposters +Billye/M +Billy/M +billy/SM +Bil/MY +bi/M +Bi/M +bimbo/MS +bimetallic/S +bimetallism/MS +Bimini/M +bimodal +bimolecular/Y +bimonthly/S +binary/S +binaural/Y +binder/M +bindery/MS +binding/MPY +bindingness/M +bind/JDRGZS +bindle/M +binds/AU +bindweed/MS +binge/MS +bing/GNDM +Bingham/M +Binghamton/M +Bing/M +bingo/MS +Bini/M +Bink/M +Binky/M +binnacle/MS +binned +Binnie/M +Binni/M +binning +Binny/M +binocular/SY +binodal +binomial/SYM +bin/SM +binuclear +biochemical/SY +biochemist/MS +biochemistry/MS +biodegradability/S +biodegradable +biodiversity/S +bioengineering/M +bioethics +biofeedback/SM +biographer/M +biographic +biographical/Y +biograph/RZ +biography/MS +biog/S +Bioko/M +biol +biological/SY +biologic/S +biologist/SM +biology/MS +biomass/SM +biomedical +biomedicine/M +biometric/S +biometrics/M +biometry/M +biomolecule/S +biomorph +bionically +bionic/S +bionics/M +biophysical/Y +biophysicist/SM +biophysic/S +biophysics/M +biopic/S +biopsy/SDGM +biorhythm/S +BIOS +bioscience/S +biosphere/MS +biostatistic/S +biosynthesized +biotechnological +biotechnologist +biotechnology/SM +biotic +biotin/SM +bipartisan +bipartisanship/MS +bipartite/YN +bipartition/M +bipedal +biped/MS +biplane/MS +bipolar +bipolarity/MS +biracial +Birch/M +birch/MRSDNG +birdbath/M +birdbaths +birdbrain/SDM +birdcage/SM +birder/M +birdhouse/MS +birdieing +Birdie/M +birdie/MSD +birdlike +birdlime/MGDS +Bird/M +birdseed/MS +Birdseye/M +bird/SMDRGZ +birdsong +birdtables +birdwatch/GZR +birefringence/M +birefringent +biretta/SM +Birgit/M +Birgitta/M +Birkenstock/M +Birk/M +Birmingham/M +Biro/M +Biron/M +birthday/SM +birthmark/MS +birth/MDG +birthplace/SM +birthrate/MS +birthright/MS +birth's/A +births/A +birthstone/SM +bis +Biscay/M +Biscayne/M +biscuit/MS +bisect/DSG +bisection/MS +bisector/MS +biserial +bisexuality/MS +bisexual/YMS +Bishkek +bishop/DGSM +Bishop/M +bishopric/SM +Bismarck/M +Bismark/M +bismuth/M +bismuths +bison/M +bisque/SM +Bissau/M +bistable +bistate +bistro/SM +bisyllabic +bitblt/S +bitchily +bitchiness/MS +bitch/MSDG +bitchy/PTR +biter/M +bite/S +biting/Y +bitmap/SM +bit/MRJSZG +BITNET/M +bit's/C +bits/C +bitser/M +bitted +bitten +bitterness/SM +bittern/SM +bitternut/M +bitter/PSRDYTG +bitterroot/M +bittersweet/YMSP +bitting +bitty/PRT +bitumen/MS +bituminous +bitwise +bivalent/S +bivalve/MSD +bivariate +bivouacked +bivouacking +bivouac/MS +biweekly/S +biyearly +bizarreness/M +bizarre/YSP +Bizet/M +biz/M +bizzes +Bjorn/M +bk +b/KGD +Bk/M +blabbed +blabber/GMDS +blabbermouth/M +blabbermouths +blabbing +blab/S +blackamoor/SM +blackball/SDMG +blackberry/GMS +blackbirder/M +blackbird/SGDRM +blackboard/SM +blackbody/S +Blackburn/M +blackcurrant/M +blackener/M +blacken/GDR +Blackfeet +Blackfoot/M +blackguard/MDSG +blackhead/SM +blacking/M +blackish +blackjack/SGMD +blackleg/M +blacklist/DRMSG +blackmail/DRMGZS +blackmailer/M +Blackman/M +Blackmer/M +blackness/MS +blackout/SM +Blackpool/M +Black's +black/SJTXPYRDNG +blacksmith/MG +blacksmiths +blacksnake/MS +blackspot +Blackstone/M +blackthorn/MS +blacktop/MS +blacktopped +blacktopping +Blackwell/MS +bladder/MS +bladdernut/M +bladderwort/M +blade/DSGM +blah/MDG +blahs +Blaine/M +Blaire/M +Blair/M +Blakelee/M +Blakeley/M +Blake/M +Blakey/M +blame/DSRBGMZ +blamelessness/SM +blameless/YP +blamer/M +blameworthiness/SM +blameworthy/P +Blanca/M +Blancha/M +Blanchard/M +blanch/DRSG +Blanche/M +blancher/M +Blanch/M +blanc/M +blancmange/SM +blandishment/MS +blandish/SDGL +blandness/MS +bland/PYRT +Blane/M +Blankenship/M +blanketing/M +blanket/SDRMZG +blankness/MS +blank/SPGTYRD +Blanton/M +Blantyre/M +blare/DSG +blarney/DMGS +blas +blasphemer/M +blaspheme/RSDZG +blasphemousness/M +blasphemous/PY +blasphemy/SM +blaster/M +blasting/M +blastoff/SM +blast/SMRDGZ +blatancy/SM +blatant/YP +blather/DRGS +blatting +Blatz/M +Blavatsky/M +Blayne/M +blaze/DSRGMZ +blazer/M +blazing/Y +blazoner/M +blazon/SGDR +bl/D +bldg +bleach/DRSZG +bleached/U +bleacher/M +bleakness/MS +bleak/TPYRS +blear/GDS +blearily +bleariness/SM +bleary/PRT +bleater/M +bleat/RDGS +bleeder/M +bleed/ZRJSG +Bleeker/M +bleep/GMRDZS +blemish/DSMG +blemished/U +blench/DSG +blender/M +blend/GZRDS +Blenheim/M +blessedness/MS +blessed/PRYT +blessing/M +bless/JGSD +Blevins/M +blew +Bligh/M +blighter/M +blight/GSMDR +blimey/S +blimp/MS +blinded/U +blinder/M +blindfold/SDG +blinding/MY +blind/JGTZPYRDS +blindness/MS +blindside/SDG +blinker/MDG +blinking/U +blink/RDGSZ +blinks/M +Blinnie/M +Blinni/M +Blinny/M +blintze/M +blintz/SM +blip/MS +blipped +blipping +Blisse/M +blissfulness/MS +blissful/PY +Bliss/M +bliss/SDMG +blistering/Y +blister/SMDG +blistery +Blithe/M +blitheness/SM +blither/G +blithesome +blithe/TYPR +blitz/GSDM +blitzkrieg/SM +blizzard/MS +bloater/M +bloat/SRDGZ +blobbed +blobbing +blob/MS +Bloch/M +blockader/M +blockade/ZMGRSD +blockage/MS +blockbuster/SM +blockbusting/MS +blocker/MS +blockhead/MS +blockhouse/SM +block's +block/USDG +blocky/R +bloc/MS +Bloemfontein/M +bloke/SM +Blomberg/M +Blomquist/M +Blondelle/M +Blondell/M +blonde's +Blondie/M +blondish +blondness/MS +blond/SPMRT +Blondy/M +bloodbath +bloodbaths +bloodcurdling +bloodhound/SM +bloodied/U +bloodiness/MS +bloodlessness/SM +bloodless/PY +bloodletting/MS +bloodline/SM +bloodmobile/MS +bloodroot/M +bloodshed/SM +bloodshot +blood/SMDG +bloodsport/S +bloodstain/MDS +bloodstock/SM +bloodstone/M +bloodstream/SM +bloodsucker/SM +bloodsucking/S +bloodthirstily +bloodthirstiness/MS +bloodthirsty/RTP +bloodworm/M +bloodymindedness +bloody/TPGDRS +bloomer/M +Bloomer/M +Bloomfield/M +Bloomington/M +Bloom/MR +bloom/SMRDGZ +blooper/M +bloop/GSZRD +blossom/DMGS +blossomy +blotch/GMDS +blotchy/RT +blot/MS +blotted +blotter/MS +blotting +blotto +blouse/GMSD +blower/M +blowfish/M +blowfly/MS +blowgun/SM +blow/GZRS +blowing/M +blown/U +blowout/MS +blowpipe/SM +blowtorch/SM +blowup/MS +blowy/RST +blowzy/RT +BLT +blubber/GSDR +blubbery +Blucher/M +bludgeon/GSMD +blueback +Bluebeard/M +bluebell/MS +blueberry/SM +bluebill/M +bluebird/MS +bluebonnet/SM +bluebook/M +bluebottle/MS +bluebush +bluefish/SM +bluegill/SM +bluegrass/MS +blueing's +blueish +bluejacket/MS +bluejeans +blue/JMYTGDRSP +blueness/MS +bluenose/MS +bluepoint/SM +blueprint/GDMS +bluer/M +bluest/M +bluestocking/SM +bluesy/TR +bluet/MS +bluffer/M +bluffness/MS +bluff/SPGTZYRD +bluing/M +bluishness/M +bluish/P +Blumenthal/M +Blum/M +blunderbuss/MS +blunderer/M +blunder/GSMDRJZ +blundering/Y +bluntness/MS +blunt/PSGTYRD +blurb/GSDM +blur/MS +blurred/Y +blurriness/S +blurring/Y +blurry/RPT +blurt/GSRD +blusher/M +blushing/UY +blush/RSDGZ +blusterer/M +blustering/Y +blusterous +bluster/SDRZG +blustery +blvd +Blvd +Blythe/M +BM +BMW/M +BO +boarded +boarder/SM +boardgames +boardinghouse/SM +boarding/SM +board/IS +boardroom/MS +board's +boardwalk/SM +boar/MS +boa/SM +boaster/M +boastfulness/MS +boastful/YP +boast/SJRDGZ +boatclubs +boater/M +boathouse/SM +boating/M +boatload/SM +boatman/M +boat/MDRGZJS +boatmen +boatswain/SM +boatyard/SM +bobbed +Bobbee/M +Bobbe/M +Bobbette/M +Bobbie/M +Bobbi/M +bobbing/M +bobbin/MS +Bobbitt/M +bobble/SDGM +Bobbsey/M +Bobbye/M +Bobby/M +bobby/SM +bobbysoxer's +bobcat/MS +Bobette/M +Bobina/M +Bobine/M +Bobinette/M +Bob/M +bobolink/SM +Bobrow/M +bobsledded +bobsledder/MS +bobsledding/M +bobsled/MS +bobsleigh/M +bobsleighs +bobs/M +bob/SM +bobtail/SGDM +bobwhite/SM +Boca/M +Boccaccio/M +boccie/SM +bock/GDS +bockwurst +bodega/MS +Bodenheim/M +bode/S +Bodhidharma/M +bodhisattva +Bodhisattva/M +bodice/SM +bodied/M +bodiless +bodily +boding/M +bodkin/SM +bod/SGMD +bodybuilder/SM +bodybuilding/S +body/DSMG +bodyguard/MS +bodying/M +bodysuit/S +bodyweight +bodywork/SM +Boeing/M +Boeotia/M +Boeotian +Boer/M +Bogartian/M +Bogart/M +Bogey/M +bogeyman/M +bogeymen +bogey/SGMD +bogged +bogging +boggle/SDG +boggling/Y +boggy/RT +bogie's +bog/MS +Bogot/M +bogus +bogyman +bogymen +bogy's +Boheme/M +bohemianism/S +bohemian/S +Bohemian/SM +Bohemia/SM +Bohr/M +Boigie/M +boiled/AU +boiler/M +boilermaker/MS +boilerplate/SM +boil/JSGZDR +boils/A +Boise/M +Bois/M +boisterousness/MS +boisterous/YP +bola/SM +boldface/SDMG +boldness/MS +bold/YRPST +bole/MS +bolero/MS +Boleyn/M +bolivares +Bolivar/M +bolivar/MS +Bolivia/M +Bolivian/S +bollard/SM +bollix/GSD +boll/MDSG +Bologna/M +bologna/MS +bolometer/MS +bolo/MS +boloney's +Bolshevik/MS +Bolshevism/MS +Bolshevistic/M +Bolshevist/MS +Bolshoi/M +bolsterer/M +bolster/SRDG +bolted/U +bolter/M +bolt/MDRGS +Bolton/M +bolts/U +Boltzmann/M +bolus/SM +bombardier/MS +bombard/LDSG +bombardment/SM +bombastic +bombastically +bombast/RMS +Bombay/M +bomber/M +bombproof +bomb/SGZDRJ +bombshell/SM +Bo/MRZ +bona +bonanza/MS +Bonaparte/M +Bonaventure/M +bonbon/SM +bondage/SM +bonder/M +bondholder/SM +Bondie/M +bond/JMDRSGZ +Bond/M +bondman/M +bondmen +Bondon/M +bonds/A +bondsman/M +bondsmen +bondwoman/M +bondwomen +Bondy/M +boned/U +bonehead/SDM +boneless +Bone/M +bone/MZDRSG +boner/M +bonfire/MS +bong/GDMS +bongo/MS +Bonham/M +bonhomie/MS +Boniface/M +boniness/MS +Bonita/M +bonito/MS +bonjour +bonkers +Bonnee/M +Bonner/M +bonneted/U +bonnet/SGMD +Bonneville/M +Bonnibelle/M +bonnie +Bonnie/M +Bonni/M +Bonn/RM +Bonny/M +bonny/RT +bonsai/SM +Bontempo/M +bonus/SM +bony/RTP +bonzes +boob/DMSG +booby/SM +boodle/GMSD +boogeyman's +boogieing +boogie/SD +boo/GSDH +boohoo/GDS +bookbinder/M +bookbindery/SM +bookbinding/M +bookbind/JRGZ +bookcase/MS +booked/U +bookend/SGD +Booker/M +book/GZDRMJSB +bookie/SM +booking/M +bookishness/M +bookish/PY +bookkeeper/M +bookkeep/GZJR +bookkeeping/M +booklet/MS +bookmaker/MS +bookmaking/MS +bookmark/MDGS +bookmobile/MS +bookplate/SM +bookseller/SM +bookshelf/M +bookshelves +bookshop/MS +bookstall/MS +bookstore/SM +bookwork/M +bookworm/MS +Boolean +boolean/S +Boole/M +boom/DRGJS +boomerang/MDSG +boomer/M +boomtown/S +boondocks +boondoggle/DRSGZ +boondoggler/M +Boone/M +Boonie/M +boonies +boon/MS +Boony/M +boorishness/SM +boorish/PY +boor/MS +boosterism +booster/M +boost/SGZMRD +boot/AGDS +bootblack/MS +bootee/MS +Boote/M +Botes +Boothe/M +booth/M +Booth/M +booths +bootie's +bootlaces +bootlegged/M +bootlegger/SM +bootlegging/M +bootleg/S +Bootle/M +bootless +Boot/M +bootprints +boot's +bootstrapped +bootstrapping +bootstrap/SM +booty/SM +booze/DSRGMZ +boozer/M +boozy/TR +bopped +bopping +bop/S +borate/MSD +borax/MS +Bordeaux/M +bordello/MS +Borden/M +borderer/M +border/JRDMGS +borderland/SM +borderline/MS +Bordie/M +Bord/MN +Bordon/M +Bordy/M +Borealis/M +Boreas/M +boredom/MS +boreholes +borer/M +bore/ZGJDRS +Borges +Borgia/M +Borg/M +boric +boring/YMP +Boris +Bork/M +born/AIU +Borneo/M +borne/U +Born/M +Borodin/M +boron/SM +borosilicate/M +borough/M +boroughs +Borroughs/M +borrower/M +borrowing/M +borrow/JZRDGBS +borscht/SM +borstal/MS +Boru/M +borzoi/MS +Bosch/M +Bose/M +bosh/MS +Bosnia/M +Bosnian/S +bosom's +bosom/SGUD +bosomy/RT +boson/SM +Bosporus/M +boss/DSRMG +bossily +bossiness/MS +bossism/MS +bossy/PTSR +Bostitch/M +Bostonian/SM +Boston/MS +bosun's +Boswell/MS +botanical/SY +botanic/S +botanist/SM +botany/SM +botcher/M +botch/SRDGZ +botfly/M +bother/DG +bothersome +bothy/M +both/ZR +bot/S +Botswana/M +Botticelli/M +bottle/GMZSRD +bottleneck/GSDM +bottler/M +bottomlessness/M +bottomless/YP +bottommost +bottom/SMRDG +botulin/M +botulinus/M +botulism/SM +Boucher/M +boudoir/MS +bouffant/S +bougainvillea/SM +bough/MD +boughs +bought/N +bouillabaisse/MS +bouillon/MS +boulder/GMDS +Boulder/M +boulevard/MS +bouncer/M +bounce/SRDGZ +bouncily +bouncing/Y +bouncy/TRP +boundary/MS +bound/AUDI +boundedness/MU +bounded/UP +bounden +bounder/AM +bounders +bounding +boundlessness/SM +boundless/YP +bounds/IA +bounteousness/MS +bounteous/PY +bountifulness/SM +bountiful/PY +bounty/SDM +bouquet/SM +Bourbaki/M +bourbon/SM +Bourbon/SM +bourgeoisie/SM +bourgeois/M +Bourke/M +Bourne/M +Bournemouth/M +boutique/MS +bout/MS +boutonnire/MS +Bouvier +Bovary/M +bovine/YS +Bowditch/M +bowdlerization/MS +bowdlerize/GRSD +bowed/U +bowel/GMDS +Bowell/M +Bowen/M +bower/DMG +Bowers +Bowery/M +Bowes +bowie +Bowie/M +bowing/M +bowlder's +bowlegged +bowleg/SM +bowler/M +bowlful/S +bowl/GZSMDR +bowline/MS +bowling/M +bowman/M +Bowman/M +bowmen +bowser/M +bowsprit/SM +bows/R +bowstring/GSMD +bow/SZGNDR +bowwow/DMGS +boxcar/SM +box/DRSJZGM +boxer/M +boxful/M +boxing/M +boxlike +boxtops +boxwood/SM +boxy/TPR +Boyce/M +Boycey/M +Boycie/M +boycotter/M +boycott/RDGS +Boyd/M +Boyer/M +boyfriend/MS +boyhood/SM +boyishness/MS +boyish/PY +Boyle/M +Boy/MR +boy/MRS +boyscout +boysenberry/SM +bozo/SM +bpi +bps +BR +brace/DSRJGM +braced/U +bracelet/MS +bracer/M +brachia +brachium/M +bracken/SM +bracketed/U +bracketing/M +bracket/SGMD +brackishness/SM +brackish/P +bract/SM +Bradan/M +bradawl/M +Bradbury/M +Bradburys +bradded +bradding +Braddock/M +Brade/M +Braden/M +Bradford/M +Bradley/M +Bradly/M +Brad/MYN +Bradney/M +Bradshaw/M +brad/SM +Bradstreet/M +Brady/M +brae/SM +braggadocio/SM +braggart/SM +bragged +bragger/MS +braggest +bragging +Bragg/M +brag/S +Brahe/M +Brahma/MS +Brahmanism/MS +Brahman/SM +Brahmaputra/M +Brahmin's +Brahms +braider/M +braiding/M +braid/RDSJG +braille/DSG +Braille/GDSM +Brainard/SM +braincell/S +brainchild/M +brainchildren +brain/GSDM +braininess/MS +brainlessness/M +brainless/YP +Brain/M +brainpower/M +brainstorm/DRMGJS +brainstorming/M +brainteaser/S +brainteasing +brainwasher/M +brainwashing/M +brainwash/JGRSD +brainwave/S +brainy/RPT +braise/SDG +brake/DSGM +brakeman/M +brakemen/M +bramble/DSGM +brambling/M +brambly/RT +Bram/M +Brampton/M +bra/MS +Brana/M +branched/U +branching/M +branchlike +Branch/M +branch/MDSJG +Branchville/M +Brandais/M +Brandea/M +branded/U +Brandeis/M +Brandel/M +Brande/M +Brandenburg/M +Branden/M +brander/GDM +Brander/M +Brandice/M +Brandie/M +Brandi/M +Brandise/M +brandish/GSD +Brand/MRN +Brando/M +Brandon/M +brand/SMRDGZ +Brandt/M +Brandtr/M +brandy/GDSM +Brandy/M +Brandyn/M +brandywine +Braniff/M +Bran/M +branned +branning +Brannon/M +bran/SM +Brantley/M +Brant/M +Braque/M +brashness/MS +brash/PYSRT +Brasilia +brasserie/SM +brass/GSDM +brassiere/MS +brassily +brassiness/SM +brassy/RSPT +Bratislava/M +brat/SM +Brattain/M +bratty/RT +bratwurst/MS +Braun/M +bravadoes +bravado/M +brave/DSRGYTP +braveness/MS +bravery/MS +bravest/M +bravo/SDG +bravura/SM +brawler/M +brawl/MRDSGZ +brawniness/SM +brawn/MS +brawny/TRP +brayer/M +Bray/M +bray/SDRG +braze/GZDSR +brazenness/MS +brazen/PYDSG +brazer/M +brazier/SM +Brazilian/MS +Brazil/M +Brazos/M +Brazzaville/M +breacher/M +breach/MDRSGZ +breadbasket/SM +breadboard/SMDG +breadbox/S +breadcrumb/S +breadfruit/MS +breadline/MS +bread/SMDHG +breadth/M +breadths +breadwinner/MS +breakables +breakable/U +breakage/MS +breakaway/MS +breakdown/MS +breaker/M +breakfaster/M +breakfast/RDMGZS +breakfront/S +breaking/M +breakneck +breakout/MS +breakpoint/SMDG +break/SZRBG +breakthroughs +breakthrough/SM +breakup/SM +breakwater/SM +bream/SDG +Breanne/M +Brear/M +breastbone/MS +breastfed +breastfeed/G +breasting/M +breast/MDSG +breastplate/SM +breaststroke/SM +breastwork/MS +breathable/U +breathalyser/S +Breathalyzer/SM +breathe +breather/M +breathing/M +breathlessness/SM +breathless/PY +breaths +breathtaking/Y +breathy/TR +breath/ZBJMDRSG +Brecht/M +Breckenridge/M +bred/DG +bredes +breeching/M +breech/MDSG +breeder/I +breeder's +breeding/IM +breeds/I +breed/SZJRG +Bree/M +Breena/M +breeze/GMSD +breezeway/SM +breezily +breeziness/SM +breezy/RPT +Bremen/M +bremsstrahlung/M +Brena/M +Brenda/M +Brendan/M +Brenden/M +Brendin/M +Brendis/M +Brendon/M +Bren/M +Brenna/M +Brennan/M +Brennen/M +Brenner/M +Brenn/RNM +Brent/M +Brenton/M +Bresenham/M +Brest/M +brethren +Bret/M +Breton +Brett/M +breve/SM +brevet/MS +brevetted +brevetting +breviary/SM +brevity/MS +brew/DRGZS +brewer/M +Brewer/M +brewery/MS +brewing/M +brewpub/S +Brew/RM +Brewster/M +Brezhnev/M +Bria/M +Briana/M +Brian/M +Brianna/M +Brianne/M +Briano/M +Briant/M +briar's +bribe/GZDSR +briber/M +bribery/MS +Brice/M +brickbat/SM +brick/GRDSM +bricklayer/MS +bricklaying/SM +brickmason/S +brickwork/SM +brickyard/M +bridal/S +Bridalveil/M +bridegroom/MS +Bride/M +bride/MS +bridesmaid/MS +Bridewell/M +bridgeable/U +bridged/U +bridgehead/MS +Bridgeport/M +Bridger/M +Bridges +bridge/SDGM +Bridget/M +Bridgetown/M +Bridgette/M +Bridgett/M +Bridgewater/M +bridgework/MS +bridging/M +Bridgman/M +Bridie/M +bridled/U +bridle/SDGM +bridleway/S +briefcase/SM +briefed/C +briefing/M +briefness/MS +briefs/C +brief/YRDJPGTS +Brien/M +Brier/M +brier/MS +Brie/RSM +Brietta/M +brigade/GDSM +brigadier/MS +Brigadoon +brigandage/MS +brigand/MS +brigantine/MS +Brigg/MS +Brigham/M +brightener/M +brighten/RDZG +bright/GXTPSYNR +Bright/M +brightness/SM +Brighton/M +Brigida/M +Brigid/M +Brigit/M +Brigitta/M +Brigitte/M +Brig/M +brig/SM +brilliance/MS +brilliancy/MS +brilliantine/MS +brilliantness/M +brilliant/PSY +Brillo +Brillouin/M +brimful +brimless +brimmed +brimming +brim/SM +brimstone/MS +Brina/M +Brindisi/M +brindle/DSM +brine/GMDSR +briner/M +Briney/M +bringer/M +bring/RGZS +brininess/MS +Brinkley/M +brinkmanship/SM +brink/MS +Brinna/M +Brinn/M +Briny/M +briny/PTSR +brioche/SM +Brion/M +briquet's +briquette/MGSD +Brisbane/M +brisket/SM +briskness/MS +brisk/YRDPGTS +bristle/DSGM +bristly/TR +Bristol/M +bristol/S +Britain/M +Brita/M +Britannia/M +Britannic +Britannica/M +britches +Briticism/MS +Britisher/M +Britishly/M +British/RYZ +Brit/MS +Britney/M +Britni/M +Briton/MS +Britta/M +Brittaney/M +Brittani/M +Brittan/M +Brittany/MS +Britte/M +Britten/M +Britteny/M +brittleness/MS +brittle/YTPDRSG +Britt/MN +Brittne/M +Brittney/M +Brittni/M +Brnaba/M +Brnaby/M +Brno/M +broach/DRSG +broacher/M +broadband +broadcaster/M +broadcast/RSGZJ +broadcasts/A +broadcloth/M +broadcloths +broaden/JGRDZ +broadleaved +broadloom/SM +broadminded/P +broadness/S +broadsheet/MS +broadside/SDGM +broadsword/MS +broad/TXSYRNP +Broadway/SM +Brobdingnagian +Brobdingnag/M +brocade/DSGM +broccoli/MS +brochette/SM +brochure/SM +Brockie/M +Brock/M +Brocky/M +Broddie/M +Broddy/M +Broderick/M +Broderic/M +Brodie/M +Brod/M +Brody/M +brogan/MS +Broglie/M +brogue/MS +broiler/M +broil/RDSGZ +brokenhearted/Y +brokenness/MS +broken/YP +brokerage/MS +broker/DMG +broke/RGZ +Brok/M +bromide/MS +bromidic +bromine/MS +bronchial +bronchi/M +bronchiolar +bronchiole/MS +bronchiolitis +bronchitic/S +bronchitis/MS +broncho's +bronchus/M +broncobuster/SM +bronco/SM +bronc/S +Bron/M +Bronnie/M +Bronny/M +Bronson/M +Bronte +brontosaur/SM +brontosaurus/SM +Bronx/M +bronzed/M +bronze/SRDGM +bronzing/M +brooch/MS +brooder/M +broodiness/M +brooding/Y +broodmare/SM +brood/SMRDGZ +broody/PTR +Brookdale/M +Brooke/M +Brookfield/M +Brookhaven/M +brooklet/MS +Brooklyn/M +Brookmont/M +brook/SGDM +brookside +Brook/SM +broom/SMDG +broomstick/MS +Bros +Brose/M +bro/SH +bros/S +brothel/MS +brother/DYMG +brotherhood/SM +brotherliness/MS +brotherly/P +broths +broth/ZMR +brougham/MS +brought +brouhaha/MS +browbeat/NSG +brow/MS +Brownell/M +Browne/M +Brownian/M +Brownie/MS +brownie/MTRS +browning/M +Browning/M +brownish +Brown/MG +brownness/MS +brownout/MS +brownstone/MS +Brownsville/M +brown/YRDMSJGTP +browse +browser/M +brows/SRDGZ +brr +Br/TMN +Brubeck/M +brucellosis/M +Bruce/M +Brucie/M +Bruckner/M +Bruegel/M +Brueghel's +bruin/MS +bruised/U +bruise/JGSRDZ +bruiser/M +Bruis/M +bruit/DSG +Brumidi/M +Brummel/M +brunch/MDSG +Brunei/M +Brunelleschi/M +brunet/S +brunette/SM +Brunhilda/M +Brunhilde/M +Bruno/M +Brunswick/M +brunt/GSMD +brusher/M +brushfire/MS +brushlike +brush/MSRDG +brushoff/S +brushwood/SM +brushwork/MS +brushy/R +brusqueness/MS +brusque/PYTR +Brussels +brutality/SM +brutalization/SM +brutalized/U +brutalizes/AU +brutalize/SDG +brutal/Y +brute/DSRGM +brutishness/SM +brutish/YP +Brutus/M +Bruxelles/M +Bryana/M +Bryan/M +Bryant/M +Bryanty/M +Bryce/M +Bryna/M +Bryn/M +Brynna/M +Brynne/M +Brynner/M +Brynn/RM +Bryon/M +Brzezinski/M +B's +BS +BSA +BSD +Btu +BTU +BTW +bu +bubblegum/S +bubbler/M +bubble/RSDGM +bubbly/TRS +Buber/M +bub/MS +buboes +bubo/M +bubonic +buccaneer/GMDS +Buchanan/M +Bucharest/M +Buchenwald/M +Buchwald/M +buckaroo/SM +buckboard/SM +bucker/M +bucketful/MS +bucket/SGMD +buckeye/SM +buck/GSDRM +buckhorn/M +Buckie/M +Buckingham/M +buckled/U +buckler/MDG +buckle/RSDGMZ +buckles/U +Buckley/M +buckling's +buckling/U +Buck/M +Buckner/M +buckram/GSDM +bucksaw/SM +buckshot/MS +buckskin/SM +buckteeth +bucktooth/DM +buckwheat/SM +Bucky/M +bucolically +bucolic/S +Budapest/M +budded +Buddha/MS +Buddhism/SM +Buddhist/SM +Buddie/M +budding/S +Budd/M +buddy/GSDM +Buddy/M +budge/GDS +budgerigar/MS +budgetary +budgeter/M +budget/GMRDZS +budgie/MS +budging/U +Bud/M +bud/MS +Budweiser/MS +Buehring/M +Buena/M +buffaloes +Buffalo/M +buffalo/MDG +buff/ASGD +buffered/U +bufferer/M +buffer/RDMSGZ +buffet/GMDJS +bufflehead/M +buffoonery/MS +buffoonish +buffoon/SM +buff's +Buffy/M +Buford/M +bugaboo/SM +Bugatti/M +bugbear/SM +bug/CS +bugeyed +bugged/C +buggered +buggering +bugger/SCM! +buggery/M +bugging/C +buggy/RSMT +bugle/GMDSRZ +bugler/M +bug's +Buick/M +builder/SM +building/SM +build/SAG +buildup/MS +built/AUI +Buiron/M +Bujumbura/M +Bukhara/M +Bukharin/M +Bulawayo/M +Bulba/M +bulb/DMGS +bulblet +bulbous +Bulfinch/M +Bulganin/M +Bulgaria/M +Bulgarian/S +bulge/DSGM +bulgy/RT +bulimarexia/S +bulimia/MS +bulimic/S +bulk/GDRMS +bulkhead/SDM +bulkiness/SM +bulky/RPT +bulldogged +bulldogger +bulldogging +bulldog/SM +bulldoze/GRSDZ +bulldozer/M +bullet/GMDS +bulletin/SGMD +bulletproof/SGD +bullfighter/M +bullfighting/M +bullfight/SJGZMR +bullfinch/MS +bullfrog/SM +bullhead/DMS +bullheadedness/SM +bullheaded/YP +bullhide +bullhorn/SM +bullied/M +bullion/SM +bullishness/SM +bullish/PY +bull/MDGS +Bullock/M +bullock/MS +bullpen/MS +bullring/SM +bullseye +bullshit/MS! +bullshitted/! +bullshitter/S! +bullshitting/! +bullwhackers +Bullwinkle/M +bullyboy/MS +bullying/M +bully/TRSDGM +bulrush/SM +Bultmann/M +bulwark/GMDS +bumblebee/MS +bumble/JGZRSD +bumbler/M +bumbling/Y +Bumbry/M +bummed/M +bummer/MS +bummest +bumming/M +bumper/DMG +bump/GZDRS +bumpiness/MS +bumpkin/MS +Bumppo/M +bumptiousness/SM +bumptious/PY +bumpy/PRT +bum/SM +Bunche/M +bunch/MSDG +bunchy/RT +buncombe's +bunco's +Bundestag/M +bundled/U +bundle/GMRSD +bundler/M +Bundy/M +bungalow/MS +bungee/SM +bung/GDMS +bunghole/MS +bungle/GZRSD +bungler/M +bungling/Y +Bunin/M +bunion/SM +bunk/CSGDR +Bunker/M +bunker's/C +bunker/SDMG +bunkhouse/SM +bunkmate/MS +bunko's +bunk's +bunkum/SM +Bunnie/M +Bunni/M +Bunny/M +bunny/SM +Bunsen/SM +bun/SM +bunt/GJZDRS +bunting/M +Buuel/M +Bunyan/M +buoyancy/MS +buoyant/Y +buoy/SMDG +Burbank/M +burbler/M +burble/RSDG +burbs +Burch/M +burden's +burdensomeness/M +burdensome/PY +burden/UGDS +burdock/SM +bureaucracy/MS +bureaucratically +bureaucratic/U +bureaucratization/MS +bureaucratize/SDG +bureaucrat/MS +bureau/MS +burgeon/GDS +burger/M +Burger/M +Burgess/M +burgess/MS +burgher/M +burgh/MRZ +burghs +burglarize/GDS +burglarproof/DGS +burglar/SM +burglary/MS +burgle/SDG +burgomaster/SM +Burgoyne/M +Burg/RM +burg/SZRM +Burgundian/S +Burgundy/MS +burgundy/S +burial/ASM +buried/U +burier/M +Burke/M +Burk/SM +burlap/MS +burler/M +burlesquer/M +burlesque/SRDMYG +burley/M +Burlie/M +burliness/SM +Burlingame/M +Burlington/M +Burl/M +burl/SMDRG +burly/PRT +Burma/M +Burmese +bur/MYS +burnable/S +Burnaby/M +Burnard/M +burned/U +Burne/MS +burner/M +Burnett/M +burn/GZSDRBJ +burning/Y +burnisher/M +burnish/GDRSZ +burnoose/MS +burnout/MS +Burns +Burnside/MS +burnt/YP +burp/SGMD +burr/GSDRM +Burris/M +burrito/S +Burr/M +burro/SM +Burroughs/M +burrower/M +burrow/GRDMZS +bursae +bursa/M +Bursa/M +bursar/MS +bursary/MS +bursitis/MS +burster/M +burst/SRG +Burtie/M +Burt/M +Burton/M +Burty/M +Burundian/S +Burundi/M +bury/ASDG +busboy/MS +busby/SM +Busch/M +buses/A +busgirl/S +bus/GMDSJ +bushel/MDJSG +Bushido/M +bushiness/MS +bushing/M +bush/JMDSRG +bushland +Bush/M +bushman/M +bushmaster/SM +bushmen +Bushnell/M +bushwhacker/M +bushwhacking/M +bushwhack/RDGSZ +bushy/PTR +busily +businesslike +businessman/M +businessmen +business/MS +businesspeople +businessperson/S +businesswoman/M +businesswomen +busker/M +busk/GRM +buskin/SM +bus's/A +buss/D +bustard/MS +buster/M +bustle/GSD +bustling/Y +bust/MSDRGZ +busty/RT +busybody/MS +busy/DSRPTG +busyness/MS +busywork/SM +but/ACS +butane/MS +butcherer/M +butcher/MDRYG +butchery/MS +Butch/M +butch/RSZ +butene/M +Butler/M +butler/SDMG +butted/A +butte/MS +butterball/MS +buttercup/SM +buttered/U +butterfat/MS +Butterfield/M +butterfingered +butterfingers/M +butterfly/MGSD +buttermilk/MS +butternut/MS +butter/RDMGZ +butterscotch/SM +buttery/TRS +butting/M +buttock/SGMD +buttoner/M +buttonhole/GMRSD +buttonholer/M +button's +button/SUDG +buttonweed +buttonwood/SM +buttress/MSDG +butt/SGZMDR +butyl/M +butyrate/M +buxomness/M +buxom/TPYR +Buxtehude/M +buyback/S +buyer/M +buyout/S +buy/ZGRS +buzzard/MS +buzz/DSRMGZ +buzzer/M +buzzword/SM +buzzy +bx +bxs +byelaw's +Byelorussia's +bye/MZS +Byers/M +bygone/S +bylaw/SM +byliner/M +byline/RSDGM +BYOB +bypass/GSDM +bypath/M +bypaths +byplay/S +byproduct/SM +Byram/M +Byran/M +Byrann/M +Byrd/M +byre/SM +Byrle/M +Byrne/M +byroad/MS +Byrom/M +Byronic +Byronism/M +Byron/M +bystander/SM +byte/SM +byway/SM +byword/SM +byzantine +Byzantine/S +Byzantium/M +by/ZR +C +ca +CA +cabala/MS +caballed +caballero/SM +caballing +cabal/SM +cabana/MS +cabaret/SM +cabbage/MGSD +cabbed +cabbing +cabby's +cabdriver/SM +caber/M +Cabernet/M +cabinetmaker/SM +cabinetmaking/MS +cabinet/MS +cabinetry/SM +cabinetwork/MS +cabin/GDMS +cablecast/SG +cable/GMDS +cablegram/SM +cabochon/MS +caboodle/SM +caboose/MS +Cabot/M +Cabrera/M +Cabrini/M +cabriolet/MS +cab/SMR +cabstand/MS +cacao/SM +cacciatore +cache/DSRGM +cachepot/MS +cachet/MDGS +Cacilia/M +Cacilie/M +cackler/M +cackle/RSDGZ +cackly +CACM +cacophonist +cacophonous +cacophony/SM +cacti +cactus/M +CAD +cadaverous/Y +cadaver/SM +caddishness/SM +caddish/PY +Caddric/M +caddy/GSDM +cadence/CSM +cadenced +cadencing +cadent/C +cadenza/MS +cadet/SM +Cadette/S +cadge/DSRGZ +cadger/M +Cadillac/MS +Cadiz/M +Cad/M +cadmium/MS +cadre/SM +cad/SM +caducei +caduceus/M +Caedmon/M +Caesar/MS +caesura/SM +caf/MS +cafeteria/SM +caffeine/SM +caftan/SM +caged/U +Cage/M +cage/MZGDRS +cager/M +cagey/P +cagier +cagiest +cagily +caginess/MS +Cagney/M +Cahokia/M +cahoot/MS +Cahra/M +CAI +Caiaphas/M +caiman's +Caine/M +Cain/MS +Cairistiona/M +cairn/SDM +Cairo/M +caisson/SM +caitiff/MS +Caitlin/M +Caitrin/M +cajole/LGZRSD +cajolement/MS +cajoler/M +cajolery/SM +Cajun/MS +cake/MGDS +cakewalk/SMDG +calabash/SM +calaboose/MS +Calais/M +calamari/S +calamine/GSDM +calamitousness/M +calamitous/YP +calamity/MS +cal/C +calcareousness/M +calcareous/PY +calciferous +calcification/M +calcify/XGNSD +calcimine/GMSD +calcine/SDG +calcite/SM +calcium/SM +Calcomp/M +CalComp/M +CALCOMP/M +calculability/IM +calculable/IP +calculate/AXNGDS +calculated/PY +calculatingly +calculating/U +calculation/AM +calculative +calculator/SM +calculi +calculus/M +Calcutta/M +caldera/SM +Calder/M +Calderon/M +caldron's +Caldwell/M +Caleb/M +Caledonia/M +Cale/M +calendar/MDGS +calender/MDGS +calf/M +calfskin/SM +Calgary/M +Calhoun/M +Caliban/M +caliber/SM +calibrated/U +calibrater's +calibrate/XNGSD +calibrating/A +calibration/M +calibrator/MS +calicoes +calico/M +Calida/M +Calif/M +California/M +Californian/MS +californium/SM +calif's +Caligula/M +Cali/M +caliper/SDMG +caliphate/SM +caliph/M +caliphs +calisthenic/S +calisthenics/M +Callaghan/M +call/AGRDBS +Callahan/M +calla/MS +Calla/MS +Callao/M +callback/S +Callean/M +called/U +callee/M +caller/MS +Calley/M +Callida/M +Callie/M +calligrapher/M +calligraphic +calligraphist/MS +calligraph/RZ +calligraphy/MS +Calli/M +calling/SM +Calliope/M +calliope/SM +callisthenics's +Callisto/M +callosity/MS +callousness/SM +callous/PGSDY +callowness/MS +callow/RTSP +callus/SDMG +Cally/M +calming/Y +calmness/MS +calm/PGTYDRS +Cal/MY +Caloocan/M +caloric/S +calorie/SM +calorific +calorimeter/MS +calorimetric +calorimetry/M +Caltech/M +Calumet/M +calumet/MS +calumniate/NGSDX +calumniation/M +calumniator/SM +calumnious +calumny/MS +calvary/M +Calvary/M +calve/GDS +Calvert/M +calves/M +Calvinism/MS +Calvinistic +Calvinist/MS +Calvin/M +Calv/M +calyces's +Calypso/M +calypso/SM +calyx/MS +Ca/M +CAM +Camacho/M +Camala/M +camaraderie/SM +camber/DMSG +cambial +cambium/SM +Cambodia/M +Cambodian/S +Cambrian/S +cambric/MS +Cambridge/M +camcorder/S +Camden/M +camelhair's +Camella/M +Camellia/M +camellia/MS +Camel/M +Camelopardalis/M +Camelot/M +camel/SM +Camembert/MS +cameo/GSDM +camerae +cameraman/M +cameramen +camera/MS +camerawoman +camerawomen +Cameron/M +Cameroonian/S +Cameroon/SM +came/N +Camey/M +Camila/M +Camile/M +Camilla/M +Camille/M +Cami/M +Camino/M +camion/M +camisole/MS +Cam/M +cammed +Cammie/M +Cammi/M +cam/MS +Cammy/M +Camoens/M +camomile's +camouflage/DRSGZM +camouflager/M +campaigner/M +campaign/ZMRDSG +campanile/SM +campanological +campanologist/SM +campanology/MS +Campbell/M +Campbellsport/M +camper/SM +campesinos +campest +campfire/SM +campground/MS +camphor/MS +Campinas/M +camping/S +Campos +camp's +camp/SCGD +campsite/MS +campus/GSDM +campy/RT +Camry/M +camshaft/SM +Camus/M +Canaanite/SM +Canaan/M +Canada/M +Canadianism/SM +Canadian/S +Canad/M +Canaletto/M +canalization/MS +canalize/GSD +canal/SGMD +canap/S +canard/MS +Canaries +canary/SM +canasta/SM +Canaveral/M +Canberra/M +cancan/SM +cancelate/D +canceled/U +canceler/M +cancellation/MS +cancel/RDZGS +cancer/MS +Cancer/MS +cancerous/Y +Cancun/M +Candace/M +candelabra/S +candelabrum/M +Candice/M +candidacy/MS +Candida/M +candidate/SM +candidature/S +Candide/M +candidly/U +candidness/SM +candid/TRYPS +Candie/M +Candi/SM +candle/GMZRSD +candlelight/SMR +candlelit +candlepower/SM +candler/M +candlestick/SM +Candlewick/M +candlewick/MS +candor/MS +Candra/M +candy/GSDM +Candy/M +canebrake/SM +caner/M +cane/SM +canine/S +caning/M +Canis/M +canister/SGMD +cankerous +canker/SDMG +Can/M +can/MDRSZGJ +cannabis/MS +canned +cannelloni +canner/SM +cannery/MS +Cannes +cannibalism/MS +cannibalistic +cannibalization/SM +cannibalize/GSD +cannibal/SM +cannily/U +canninesses +canniness/UM +canning/M +cannister/SM +cannonade/SDGM +cannonball/SGDM +Cannon/M +cannon/SDMG +cannot +canny/RPUT +canoe/DSGM +canoeist/SM +Canoga/M +canonic +canonicalization +canonicalize/GSD +canonical/SY +canonist/M +canonization/MS +canonized/U +canonize/SDG +canon/SM +Canopus/M +canopy/GSDM +canst +can't +cantabile/S +Cantabrigian +cantaloupe/MS +cantankerousness/SM +cantankerous/PY +cantata/SM +cant/CZGSRD +canted/IA +canteen/MS +Canterbury/M +canter/CM +cantered +cantering +canticle/SM +cantilever/SDMG +canto/MS +cantonal +Cantonese/M +Canton/M +cantonment/SM +canton/MGSLD +Cantor/M +cantor/MS +Cantrell/M +cant's +cants/A +Cantu/M +Canute/M +canvasback/MS +canvas/RSDMG +canvasser/M +canvass/RSDZG +canyon/MS +CAP +capability/ISM +capableness/IM +capable/PI +capabler +capablest +capably/I +capaciousness/MS +capacious/PY +capacitance/SM +capacitate/V +capacitive/Y +capacitor/MS +capacity/IMS +caparison/SDMG +Capek/M +Capella/M +caper/GDM +capeskin/SM +cape/SM +Capet/M +Capetown/M +Caph/M +capillarity/MS +capillary/S +Capistrano/M +capitalism/SM +capitalistic +capitalistically +capitalist/SM +capitalization/SMA +capitalized/AU +capitalizer/M +capitalize/RSDGZ +capitalizes/A +capital/SMY +capita/M +Capitan/M +capitation/CSM +Capitoline/M +Capitol/MS +capitol/SM +capitulate/AXNGSD +capitulation/MA +caplet/S +cap/MDRSZB +Capone/M +capon/SM +capo/SM +Capote/M +capped/UA +capping/M +cappuccino/MS +Cappy/M +Capra/M +Caprice/M +caprice/MS +capriciousness/MS +capricious/PY +Capricorn/MS +Capri/M +caps/AU +capsicum/MS +capsize/SDG +capstan/MS +capstone/MS +capsular +capsule/MGSD +capsulize/GSD +captaincy/MS +captain/SGDM +caption/GSDRM +captiousness/SM +captious/PY +captivate/XGNSD +captivation/M +captivator/SM +captive/MS +captivity/SM +Capt/M +captor/SM +capture/AGSD +capturer/MS +capt/V +Capulet/M +Caputo/M +Caracalla/M +Caracas/M +caracul's +carafe/SM +Caralie/M +Cara/M +caramelize/SDG +caramel/MS +carapace/SM +carapaxes +carat/SM +Caravaggio/M +caravan/DRMGS +caravaner/M +caravansary/MS +caravanserai's +caravel/MS +caraway/MS +carbide/MS +carbine/MS +carbohydrate/MS +carbolic +Carboloy/M +carbonaceous +carbonate/SDXMNG +carbonation/M +Carbondale/M +Carbone/MS +carbonic +carboniferous +Carboniferous +carbonization/SAM +carbonizer/AS +carbonizer's +carbonizes/A +carbonize/ZGRSD +carbon/MS +carbonyl/M +carborundum +Carborundum/MS +carboy/MS +carbuncle/SDM +carbuncular +carburetor/MS +carburetter/S +carburettor/SM +carcase/MS +carcass/SM +Carce/M +carcinogenic +carcinogenicity/MS +carcinogen/SM +carcinoma/SM +cardamom/MS +cardboard/MS +card/EDRSG +Cardenas/M +carder/MS +carder's/E +cardholders +cardiac/S +Cardiff/M +cardigan/SM +cardinality/SM +cardinal/SYM +carding/M +Cardin/M +Cardiod/M +cardiogram/MS +cardiograph/M +cardiographs +cardioid/M +cardiologist/SM +cardiology/MS +cardiomegaly/M +cardiopulmonary +cardiovascular +card's +cardsharp/ZSMR +CARE +cared/U +careen/DSG +careerism/M +careerist/MS +career/SGRDM +carefree +carefuller +carefullest +carefulness/MS +careful/PY +caregiver/S +carelessness/MS +careless/YP +Care/M +Carena/M +Caren/M +carer/M +care/S +Caresa/M +Caressa/M +Caresse/M +caresser/M +caressing/Y +caressive/Y +caress/SRDMVG +caretaker/SM +caret/SM +careworn +Carey/M +carfare/MS +cargoes +cargo/M +carhopped +carhopping +carhop/SM +Caria/M +Caribbean/S +Carib/M +caribou/MS +caricature/GMSD +caricaturisation +caricaturist/MS +caricaturization +Carie/M +caries/M +carillonned +carillonning +carillon/SM +Caril/M +Carilyn/M +Cari/M +Carina/M +Carine/M +caring/U +Carin/M +Cariotta/M +carious +Carissa/M +Carita/M +Caritta/M +carjack/GSJDRZ +Carla/M +Carlee/M +Carleen/M +Carlene/M +Carlen/M +Carletonian/M +Carleton/M +Carley/M +Carlie/M +Carlina/M +Carline/M +Carling/M +Carlin/M +Carlita/M +Carl/MNG +carload/MSG +Carlo/SM +Carlota/M +Carlotta/M +Carlsbad/M +Carlson/M +Carlton/M +Carlye/M +Carlyle/M +Carly/M +Carlyn/M +Carlynne/M +Carlynn/M +Carma/M +Carmela/M +Carmelia/M +Carmelina/M +Carmelita/M +Carmella/M +Carmelle/M +Carmel/M +Carmelo/M +Carmencita/M +Carmen/M +Carmichael/M +Carmina/M +Carmine/M +carmine/MS +Carmita/M +Car/MNY +Carmon/M +carnage/MS +carnality/SM +carnal/Y +Carnap/M +carnation/IMS +Carnegie/M +carnelian/SM +Carney/M +carney's +carnival/MS +carnivore/SM +carnivorousness/MS +carnivorous/YP +Carnot/M +Carny/M +carny/SDG +carob/SM +Carola/M +Carolan/M +Carolann/M +Carolee/M +Carole/M +caroler/M +Carolina/MS +Caroline/M +Carolingian +Carolinian/S +Carolin/M +Caroljean/M +Carol/M +carol/SGZMRD +Carolus/M +Carolyne/M +Carolyn/M +Carolynn/M +Caro/M +carom/GSMD +Caron/M +carotene/MS +carotid/MS +carousal/MS +carousel/MS +carouser/M +carouse/SRDZG +carpal/SM +Carpathian/MS +carpel/SM +carpenter/DSMG +carpentering/M +Carpenter/M +carpentry/MS +carper/M +carpetbagged +carpetbagger/MS +carpetbagging +carpetbag/MS +carpeting/M +carpet/MDJGS +carpi/M +carping/Y +carp/MDRSGZ +carpool/DGS +carport/MS +carpus/M +carrageen/M +Carree/M +carrel/SM +carriage/SM +carriageway/SM +Carrie/M +carrier/M +Carrier/M +Carrillo/M +Carri/M +carrion/SM +Carrissa/M +Carr/M +Carroll/M +Carrol/M +carrot/MS +carroty/RT +carrousel's +carryall/MS +Carry/MR +carryout/S +carryover/S +carry/RSDZG +carsickness/SM +carsick/P +Carson/M +cartage/MS +cartel/SM +carte/M +carter/M +Carter/M +Cartesian +Carthage/M +Carthaginian/S +carthorse/MS +Cartier/M +cartilage/MS +cartilaginous +cartload/MS +cart/MDRGSZ +Cart/MR +cartographer/MS +cartographic +cartography/MS +carton/GSDM +cartoon/GSDM +cartoonist/MS +cartridge/SM +cartwheel/MRDGS +Cartwright/M +Carty/RM +Caruso/M +carve/DSRJGZ +carven +carver/M +Carver/M +carving/M +caryatid/MS +Caryl/M +Cary/M +Caryn/M +car/ZGSMDR +casaba/SM +Casablanca/M +Casals/M +Casandra/M +Casanova/SM +Casar/M +casbah/M +cascade/MSDG +Cascades/M +cascara/MS +casebook/SM +case/DSJMGL +cased/U +caseharden/SGD +casein/SM +caseload/MS +Case/M +casement/SM +caseworker/M +casework/ZMRS +Casey/M +cashbook/SM +cashew/MS +cash/GZMDSR +cashier/SDMG +cashless +Cash/M +cashmere/MS +Casie/M +Casi/M +casing/M +casino/MS +casket/SGMD +cask/GSDM +Caspar/M +Casper/M +Caspian +Cass +Cassandra/SM +Cassandre/M +Cassandry/M +Cassatt/M +Cassaundra/M +cassava/MS +casserole/MGSD +cassette/SM +Cassey/M +cassia/MS +Cassie/M +Cassi/M +cassino's +Cassiopeia/M +Cassite/M +Cassius/M +cassock/SDM +Cassondra/M +cassowary/SM +Cassy/M +Castaneda/M +castanet/SM +castaway/SM +castellated +caste/MHS +caster/M +cast/GZSJMDR +castigate/XGNSD +castigation/M +castigator/SM +Castile's +Castillo/M +casting/M +castle/GMSD +castoff/S +Castor/M +castor's +castrate/DSNGX +castration/M +Castries/M +Castro/M +casts/A +casualness/SM +casual/SYP +casualty/SM +casuistic +casuist/MS +casuistry/SM +cataclysmal +cataclysmic +cataclysm/MS +catacomb/MS +catafalque/SM +Catalan/MS +catalepsy/MS +cataleptic/S +Catalina/M +cataloger/M +catalog/SDRMZG +Catalonia/M +catalpa/SM +catalysis/M +catalyst/SM +catalytic +catalytically +catalyze/DSG +catamaran/MS +catapult/MGSD +cataract/MS +Catarina/M +catarrh/M +catarrhs +catastrophe/SM +catastrophic +catastrophically +catatonia/MS +catatonic/S +Catawba/M +catbird/MS +catboat/SM +catcall/SMDG +catchable/U +catchall/MS +catch/BRSJLGZ +catcher/M +catchment/SM +catchpenny/S +catchphrase/S +catchup/MS +catchword/MS +catchy/TR +catechism/MS +catechist/SM +catechize/SDG +catecholamine/MS +categoric +categorical/Y +categorization/MS +categorized/AU +categorize/RSDGZ +category/MS +Cate/M +catenate/NF +catenation/MF +catercorner +caterer/M +cater/GRDZ +Caterina/M +catering/M +Caterpillar +caterpillar/SM +caterwaul/DSG +catfish/MS +catgut/SM +Catha/M +Catharina/M +Catharine/M +catharses +catharsis/M +cathartic/S +Cathay/M +cathedral/SM +Cathee/M +Catherina/M +Catherine/M +Catherin/M +Cather/M +Cathe/RM +catheterize/GSD +catheter/SM +Cathie/M +Cathi/M +Cathleen/M +Cathlene/M +cathode/MS +cathodic +catholicism +Catholicism/SM +catholicity/MS +catholic/MS +Catholic/S +Cathrine/M +Cathrin/M +Cathryn/M +Cathyleen/M +Cathy/M +Catie/M +Catiline/M +Cati/M +Catina/M +cationic +cation/MS +catkin/SM +Catlaina/M +Catlee/M +catlike +Catlin/M +catnapped +catnapping +catnap/SM +catnip/MS +Cato/M +Catrina/M +Catriona/M +Catskill/SM +cat/SMRZ +catsup's +cattail/SM +catted +cattery/M +cattily +cattiness/SM +catting +cattle/M +cattleman/M +cattlemen +Catt/M +catty/PRST +Catullus/M +CATV +catwalk/MS +Caty/M +Caucasian/S +Caucasoid/S +Caucasus/M +Cauchy/M +caucus/SDMG +caudal/Y +caught/U +cauldron/MS +cauliflower/MS +caulker/M +caulk/JSGZRD +causality/SM +causal/YS +causate/XVN +causation/M +causative/SY +cause/DSRGMZ +caused/U +causeless +causerie/MS +causer/M +causeway/SGDM +caustically +causticity/MS +caustic/YS +cauterization/SM +cauterized/U +cauterize/GSD +cautionary +cautioner/M +caution/GJDRMSZ +cautiousness's/I +cautiousness/SM +cautious/PIY +cavalcade/MS +cavalierness/M +cavalier/SGYDP +cavalryman/M +cavalrymen +cavalry/MS +caveat/SM +caveatted +caveatting +cave/GFRSD +caveman/M +cavemen +Cavendish/M +caver/M +cavern/GSDM +cavernous/Y +cave's +caviar/MS +caviler/M +cavil/SJRDGZ +caving/MS +cavity/MFS +cavort/SDG +Cavour/M +caw/SMDG +Caxton/M +Caye/M +Cayenne/M +cayenne/SM +Cayla/M +Cayman/M +cayman/SM +cay's +cay/SC +Cayuga/M +cayuse/SM +Caz/M +Cazzie/M +c/B +CB +CBC +Cb/M +CBS +cc +Cchaddie/M +CCTV +CCU +CD +CDC/M +Cd/M +CDT +Ce +cease/DSCG +ceasefire/S +ceaselessness/SM +ceaseless/YP +ceasing/U +Ceausescu/M +Cebuano/M +Cebu/M +ceca +cecal +Cecelia/M +Cece/M +Cecile/M +Ceciley/M +Cecilia/M +Cecilio/M +Cecilius/M +Cecilla/M +Cecil/M +Cecily/M +cecum/M +cedar/SM +ceded/A +cede/FRSDG +ceder's/F +ceder/SM +cedes/A +cedilla/SM +ceding/A +Ced/M +Cedric/M +ceilidh/M +ceiling/MDS +Ceil/M +celandine/MS +Celanese/M +Celebes's +celebrant/MS +celebratedness/M +celebrated/P +celebrate/XSDGN +celebration/M +celebrator/MS +celebratory +celebrity/MS +Cele/M +Celene/M +celerity/SM +celery/SM +Celesta/M +celesta/SM +Celeste/M +celestial/YS +Celestia/M +Celestina/M +Celestine/M +Celestyna/M +Celestyn/M +Celia/M +celibacy/MS +celibate/SM +Celie/M +Celina/M +Celinda/M +Celine/M +Celinka/M +Celisse/M +Celka/M +cellarer/M +cellar/RDMGS +Celle/M +cell/GMDS +Cellini/M +cellist/SM +Cello/M +cello/MS +cellophane/SM +cellphone/S +cellular/SY +cellulite/S +celluloid/SM +cellulose/SM +Celsius/S +Celtic/SM +Celt/MS +cementa +cementer/M +cementum/SM +cement/ZGMRDS +cemetery/MS +cenobite/MS +cenobitic +cenotaph/M +cenotaphs +Cenozoic +censer/MS +censored/U +censor/GDMS +censorial +censoriousness/MS +censorious/YP +censorship/MS +censure/BRSDZMG +censurer/M +census/SDMG +centaur/SM +Centaurus/M +centavo/SM +centenarian/MS +centenary/S +centennial/YS +center/AC +centerboard/SM +centered +centerer/S +centerfold/S +centering/SM +centerline/SM +centerpiece/SM +center's +Centigrade +centigrade/S +centigram/SM +centiliter/MS +centime/SM +centimeter/SM +centipede/MS +Centralia/M +centralism/M +centralist/M +centrality/MS +centralization/CAMS +centralize/CGSD +centralizer/SM +centralizes/A +central/STRY +centrefold's +Centrex +CENTREX/M +centric/F +centrifugal/SY +centrifugate/NM +centrifugation/M +centrifuge/GMSD +centripetal/Y +centrist/MS +centroid/MS +cent/SZMR +centurion/MS +century/MS +CEO +cephalic/S +Cepheid +Cepheus/M +ceramicist/S +ceramic/MS +ceramist/MS +cerate/MD +Cerberus/M +cereal/MS +cerebellar +cerebellum/MS +cerebra +cerebral/SY +cerebrate/XSDGN +cerebration/M +cerebrum/MS +cerement/SM +ceremonial/YSP +ceremoniousness/MS +ceremoniousness's/U +ceremonious/YUP +ceremony/MS +Cerenkov/M +Ceres/M +Cerf/M +cerise/SM +cerium/MS +cermet/SM +CERN/M +certainer +certainest +certainty/UMS +certain/UY +cert/FS +certifiable +certifiably +certificate/SDGM +certification/AMC +certified/U +certifier/M +certify/DRSZGNX +certiorari/M +certitude/ISM +cerulean/MS +Cervantes/M +cervical +cervices/M +cervix/M +Cesarean +cesarean/S +Cesare/M +Cesar/M +Cesaro/M +cesium/MS +cessation/SM +cession/FAMSK +Cessna/M +cesspit/M +cesspool/SM +Cesya/M +cetacean/S +cetera/S +Cetus/M +Ceylonese +Ceylon/M +Cezanne/S +cf +CF +CFC +Cf/M +CFO +cg +Chablis/SM +Chaddie/M +Chadd/M +Chaddy/M +Chadian/S +Chad/M +Chadwick/M +chafe/GDSR +chafer/M +chaffer/DRG +chafferer/M +Chaffey/M +chaff/GRDMS +chaffinch/SM +Chagall/M +chagrin/DGMS +Chaim/M +chainlike +chain's +chainsaw/SGD +chain/SGUD +chairlady/M +chairlift/MS +chairman/MDGS +chairmanship/MS +chairmen +chairperson/MS +chair/SGDM +chairwoman/M +chairwomen +chaise/SM +chalcedony/MS +Chaldea/M +Chaldean/M +chalet/SM +chalice/DSM +chalkboard/SM +chalk/DSMG +chalkiness/S +chalkline +chalky/RPT +challenged/U +challenger/M +challenge/ZGSRD +challenging/Y +challis/SM +Chalmers +chamberer/M +Chamberlain/M +chamberlain/MS +chambermaid/MS +chamberpot/S +Chambers/M +chamber/SZGDRM +chambray/MS +chameleon/SM +chamfer/DMGS +chammy's +chamois/DSMG +chamomile/MS +champagne/MS +champaign/M +champ/DGSZ +champion/MDGS +championship/MS +Champlain/M +chanced/M +chance/GMRSD +chancellery/SM +chancellorship/SM +chancellor/SM +Chancellorsville/M +chancel/SM +Chance/M +chancery/SM +Chancey/M +chanciness/S +chancing/M +chancre/SM +chancy/RPT +Chandal/M +Chanda/M +chandelier/SM +Chandigarh/M +Chandler/M +chandler/MS +Chandragupta/M +Chandra/M +Chandrasekhar/M +Chandy/M +Chanel/M +Chane/M +Chaney/M +Changchun/M +changeabilities +changeability/UM +changeableness/SM +changeable/U +changeably/U +changed/U +change/GZRSD +changeless +changeling/M +changeover/SM +changer/M +changing/U +Chang/M +Changsha/M +Chan/M +Channa/M +channeler/M +channeling/M +channelization/SM +channelize/GDS +channellings +channel/MDRZSG +Channing/M +chanson/SM +Chantalle/M +Chantal/M +chanter/M +chanteuse/MS +chantey/SM +chanticleer/SM +Chantilly/M +chantry/MS +chant/SJGZMRD +chanty's +Chanukah's +Chao/M +chaos/SM +chaotic +chaotically +chaparral/MS +chapbook/SM +chapeau/MS +chapel/MS +chaperonage/MS +chaperoned/U +chaperone's +chaperon/GMDS +chaplaincy/MS +chaplain/MS +chaplet/SM +Chaplin/M +Chapman/M +chap/MS +Chappaquiddick/M +chapped +chapping +chapter/SGDM +Chara +charabanc/MS +characterful +characteristically/U +characteristic/SM +characterizable/MS +characterization/MS +characterize/DRSBZG +characterized/U +characterizer/M +characterless +character/MDSG +charade/SM +charbroil/SDG +charcoal/MGSD +Chardonnay +chardonnay/S +chard/SM +chargeableness/M +chargeable/P +charged/U +charge/EGRSDA +charger/AME +chargers +char/GS +Charil/M +charily +chariness/MS +Charin/M +charioteer/GSDM +Chariot/M +chariot/SMDG +Charis +charisma/M +charismata +charismatically +charismatic/S +Charissa/M +Charisse/M +charitablenesses +charitableness/UM +charitable/UP +charitably/U +Charita/M +Charity/M +charity/MS +charlady/M +Charla/M +charlatanism/MS +charlatanry/SM +charlatan/SM +Charlean/M +Charleen/M +Charlemagne/M +Charlena/M +Charlene/M +Charles/M +Charleston/SM +Charley/M +Charlie/M +Charline/M +Charlot/M +Charlotta/M +Charlotte/M +Charlottesville/M +Charlottetown/M +Charlton/M +Charmaine/M +Charmain/M +Charmane/M +charmer/M +Charmian/M +Charmine/M +charming/RYT +Charmin/M +Charmion/M +charmless +charm/SGMZRD +Charolais +Charo/M +Charon/M +charred +charring +charted/U +charter/AGDS +chartered/U +charterer/SM +charter's +chartist/SM +Chartres/M +chartreuse/MS +chartroom/S +chart/SJMRDGBZ +charwoman/M +charwomen +Charybdis/M +Charyl/M +chary/PTR +Chas +chase/DSRGZ +Chase/M +chaser/M +chasing/M +Chasity/M +chasm/SM +chassis/M +chastely +chasteness/SM +chasten/GSD +chaste/UTR +chastisement/SM +chastiser/M +chastise/ZGLDRS +Chastity/M +chastity/SM +chastity's/U +chasuble/SM +Chateaubriand +chteau/M +chateaus +chteaux +chtelaine/SM +chat/MS +Chattahoochee/M +Chattanooga/M +chatted +chattel/MS +chatterbox/MS +chatterer/M +Chatterley/M +chatter/SZGDRY +Chatterton/M +chattily +chattiness/SM +chatting +chatty/RTP +Chaucer/M +chauffeur/GSMD +Chaunce/M +Chauncey/M +Chautauqua/M +chauvinism/MS +chauvinistic +chauvinistically +chauvinist/MS +Chavez/M +chaw +Chayefsky/M +cheapen/DG +cheapish +cheapness/MS +cheapskate/MS +cheap/YRNTXSP +cheater/M +cheat/RDSGZ +Chechen/M +Chechnya/M +checkable/U +checkbook/MS +checked/UA +checkerboard/MS +checker/DMG +check/GZBSRDM +checklist/S +checkmate/MSDG +checkoff/SM +checkout/S +checkpoint/MS +checkroom/MS +check's/A +checks/A +checksummed +checksumming +checksum/SM +checkup/MS +Cheddar/MS +cheddar/S +cheekbone/SM +cheek/DMGS +cheekily +cheekiness/SM +cheeky/PRT +cheep/GMDS +cheerer/M +cheerfuller +cheerfullest +cheerfulness/MS +cheerful/YP +cheerily +cheeriness/SM +cheerio/S +Cheerios/M +cheerleader/SM +cheerlessness/SM +cheerless/PY +cheers/S +cheery/PTR +cheer/YRDGZS +cheeseburger/SM +cheesecake/SM +cheesecloth/M +cheesecloths +cheeseparing/S +cheese/SDGM +cheesiness/SM +cheesy/PRT +cheetah/M +cheetahs +Cheeto/M +Cheever/M +cheffed +cheffing +chef/SM +Chekhov/M +chelate/XDMNG +chelation/M +Chelsae/M +Chelsea/M +Chelsey/M +Chelsie/M +Chelsy/M +Chelyabinsk/M +chem +Che/M +chemic +chemical/SYM +chemiluminescence/M +chemiluminescent +chemise/SM +chemistry/SM +chemist/SM +chemotherapeutic/S +chemotherapy/SM +chemurgy/SM +Chengdu +Cheng/M +chenille/SM +Chen/M +Cheops/M +Chere/M +Cherey/M +Cherianne/M +Cherice/M +Cherida/M +Cherie/M +Cherilyn/M +Cherilynn/M +Cheri/M +Cherin/M +Cherise/M +cherisher/M +cherish/GDRS +Cherish/M +Cheriton/M +Cherlyn/M +Cher/M +Chernenko/M +Chernobyl/M +Cherokee/MS +cheroot/MS +Cherri/M +Cherrita/M +Cherry/M +cherry/SM +chert/MS +cherubic +cherubim/S +cherub/SM +chervil/MS +Cherye/M +Cheryl/M +Chery/M +Chesapeake/M +Cheshire/M +Cheslie/M +chessboard/SM +chessman/M +chessmen +chess/SM +Chesterfield/M +chesterfield/MS +Chester/M +Chesterton/M +chestful/S +chest/MRDS +chestnut/SM +Cheston/M +chesty/TR +Chet/M +Chevalier/M +chevalier/SM +Cheviot/M +cheviot/S +Chev/M +Chevrolet/M +chevron/DMS +Chevy/M +chewer/M +chew/GZSDR +chewiness/S +chewy/RTP +Cheyenne/SM +chg +chge +Chiang/M +chianti/M +Chianti/S +chiaroscuro/SM +Chiarra/M +Chiba/M +Chicagoan/SM +Chicago/M +Chicana/MS +chicane/MGDS +chicanery/MS +Chicano/MS +chichi/RTS +chickadee/SM +Chickasaw/SM +chickenfeed +chicken/GDM +chickenhearted +chickenpox/MS +Chickie/M +Chick/M +chickpea/MS +chickweed/MS +chick/XSNM +Chicky/M +chicle/MS +Chic/M +chicness/S +Chico/M +chicory/MS +chic/SYRPT +chide/GDS +chiding/Y +chiefdom/MS +chieftain/SM +chief/YRMST +chiffonier/MS +chiffon/MS +chigger/MS +chignon/MS +Chihuahua/MS +chihuahua/S +chilblain/MS +childbearing/MS +childbirth/M +childbirths +childcare/S +childes +child/GMYD +childhood/MS +childishness/SM +childish/YP +childlessness/SM +childless/P +childlikeness/M +childlike/P +childminders +childproof/GSD +childrearing +children/M +Chilean/S +Chile/MS +chile's +chilies +chili/M +chiller/M +chilliness/MS +chilling/Y +chilli's +chill/MRDJGTZPS +chillness/MS +chilly/TPRS +Chilton/M +Chi/M +chimaera's +chimaerical +Chimborazo/M +chime/DSRGMZ +Chimera/S +chimera/SM +chimeric +chimerical +chimer/M +Chimiques +chimney/SMD +chimpanzee/SM +chimp/MS +chi/MS +Chimu/M +Ch'in +China/M +Chinaman/M +Chinamen +china/MS +Chinatown/SM +chinchilla/SM +chine/MS +Chinese/M +Ching/M +chink/DMSG +chinless +Chin/M +chinned +chinner/S +chinning +chino/MS +Chinook/MS +chin/SGDM +chinstrap/S +chintz/SM +chintzy/TR +chipboard/M +Chipewyan/M +Chip/M +chipmunk/SM +chipped +Chippendale/M +chipper/DGS +Chippewa/MS +chipping/MS +chip/SM +Chiquia/M +Chiquita/M +chiral +Chirico/M +chirography/SM +chiropodist/SM +chiropody/MS +chiropractic/MS +chiropractor/SM +chirp/GDS +chirpy/RT +chirrup/DGS +chiseler/M +chisel/ZGSJMDR +Chisholm/M +Chisinau/M +chitchat/SM +chitchatted +chitchatting +chitinous +chitin/SM +chit/SM +Chittagong/M +chitterlings +chivalric +chivalrously/U +chivalrousness/MS +chivalrous/YP +chivalry/SM +chive/GMDS +chivvy/D +chivying +chlamydiae +chlamydia/S +Chloe/M +Chloette/M +Chlo/M +chloral/MS +chlorate/M +chlordane/MS +chloride/MS +chlorinated/C +chlorinates/C +chlorinate/XDSGN +chlorination/M +chlorine/MS +Chloris +chlorofluorocarbon/S +chloroform/DMSG +chlorophyll/SM +chloroplast/MS +chloroquine/M +chm +Ch/MGNRS +chockablock +chock/SGRDM +chocoholic/S +chocolate/MS +chocolaty +Choctaw/MS +choiceness/M +choice/RSMTYP +choirboy/MS +choirmaster/SM +choir/SDMG +chokeberry/M +chokecherry/SM +choke/DSRGZ +choker/M +chokes/M +choking/Y +cholera/SM +choleric +choler/SM +cholesterol/SM +choline/M +cholinesterase/M +chomp/DSG +Chomsky/M +Chongqing +choose/GZRS +chooser/M +choosiness/S +choosy/RPT +chophouse/SM +Chopin/M +chopped +chopper/SDMG +choppily +choppiness/MS +chopping +choppy/RPT +chop/S +chopstick/SM +chorale/MS +choral/SY +chordal +chordata +chordate/MS +chording/M +chord/SGMD +chorea/MS +chore/DSGNM +choreographer/M +choreographic +choreographically +choreographs +choreography/MS +choreograph/ZGDR +chorines +chorion/M +chorister/SM +choroid/S +chortler/M +chortle/ZGDRS +chorus/GDSM +chosen/U +chose/S +Chou/M +chowder/SGDM +chow/DGMS +Chretien/M +Chris/M +chrism/SM +chrissake +Chrisse/M +Chrissie/M +Chrissy/M +Christabella/M +Christabel/M +Christalle/M +Christal/M +Christa/M +Christan/M +Christchurch/M +Christean/M +Christel/M +Christendom/MS +christened/U +christening/SM +Christen/M +christen/SAGD +Christensen/M +Christenson/M +Christiana/M +Christiane/M +Christianity/SM +Christianize/GSD +Christian/MS +Christiano/M +Christiansen/M +Christians/N +Christie/SM +Christi/M +Christina/M +Christine/M +Christin/M +Christlike +Christmas/SM +Christmastide/SM +Christmastime/S +Christoffel/M +Christoffer/M +Christoforo/M +Christoper/M +Christophe/M +Christopher/M +Christoph/MR +Christophorus/M +Christos/M +Christ/SMN +Christye/M +Christyna/M +Christy's +Chrisy/M +chroma/M +chromate/M +chromatically +chromaticism/M +chromaticness/M +chromatic/PS +chromatics/M +chromatin/MS +chromatogram/MS +chromatograph +chromatographic +chromatography/M +chrome/GMSD +chromic +chromite/M +chromium/SM +chromosomal +chromosome/MS +chromosphere/M +chronically +chronicled/U +chronicler/M +chronicle/SRDMZG +chronic/S +chronograph/M +chronographs +chronography +chronological/Y +chronologist/MS +chronology/MS +chronometer/MS +chronometric +Chrotoem/M +chrysalids +chrysalis/SM +Chrysa/M +chrysanthemum/MS +Chrysler/M +Chrysostom/M +Chrystal/M +Chrystel/M +Chryste/M +chubbiness/SM +chubby/RTP +chub/MS +Chucho/M +chuck/GSDM +chuckhole/SM +chuckle/DSG +chuckling/Y +Chuck/M +chuff/DM +chugged +chugging +chug/MS +Chukchi/M +chukka/S +Chumash/M +chummed +chummily +chumminess/MS +chumming +chum/MS +chummy/SRTP +chumping/M +chump/MDGS +Chungking's +Chung/M +chunkiness/MS +chunk/SGDM +chunky/RPT +chuntering +churchgoer/SM +churchgoing/SM +Churchillian +Churchill/M +churchliness/M +churchly/P +churchman/M +church/MDSYG +churchmen +Church/MS +churchwarden/SM +churchwoman/M +churchwomen +churchyard/SM +churlishness/SM +churlish/YP +churl/SM +churner/M +churning/M +churn/SGZRDM +chute/DSGM +chutney/MS +chutzpah/M +chutzpahs +chutzpa/SM +Chuvash/M +ch/VT +chyme/SM +Ci +CIA +ciao/S +cicada/MS +cicatrice/S +cicatrix's +Cicely/M +Cicero/M +cicerone/MS +ciceroni +Ciceronian +Cicily/M +CID +cider's/C +cider/SM +Cid/M +Ciel/M +cigarette/MS +cigarillo/MS +cigar/SM +cilantro/S +cilia/M +ciliate/FDS +ciliately +cilium/M +Cilka/M +cinch/MSDG +cinchona/SM +Cincinnati/M +cincture/MGSD +Cinda/M +Cindee/M +Cindelyn/M +cinder/DMGS +Cinderella/MS +Cindie/M +Cindi/M +Cindra/M +Cindy/M +cine/M +cinema/SM +cinematic +cinematographer/MS +cinematographic +cinematography/MS +Cinerama/M +cinnabar/MS +Cinnamon/M +cinnamon/MS +ciphered/C +cipher/MSGD +ciphers/C +cir +circa +circadian +Circe/M +circler/M +circle/RSDGM +circlet/MS +circuital +circuit/GSMD +circuitousness/MS +circuitous/YP +circuitry/SM +circuity/MS +circulant +circularity/SM +circularize/GSD +circularness/M +circular/PSMY +circulate/ASDNG +circulation/MA +circulations +circulative +circulatory +circumcise/DRSXNG +circumcised/U +circumciser/M +circumcision/M +circumference/SM +circumferential/Y +circumflex/MSDG +circumlocution/MS +circumlocutory +circumnavigate/DSNGX +circumnavigational +circumnavigation/M +circumpolar +circumscribe/GSD +circumscription/SM +circumspection/SM +circumspect/Y +circumsphere +circumstance/SDMG +circumstantial/YS +circumvention/MS +circumvent/SBGD +circus/SM +Cirillo/M +Cirilo/M +Ciro/M +cirque/SM +cirrhoses +cirrhosis/M +cirrhotic/S +cirri/M +cirrus/M +Cissiee/M +Cissy/M +cistern/SM +citadel/SM +citations/I +citation/SMA +cit/DSG +cite/ISDAG +Citibank/M +citified +citizenry/SM +citizenship/MS +citizen/SYM +citrate/DM +citric +Citroen/M +citronella/MS +citron/MS +citrus/SM +city/DSM +cityscape/MS +citywide +civet/SM +civic/S +civics/M +civilian/SM +civility/IMS +civilizational/MS +civilization/AMS +civilizedness/M +civilized/PU +civilize/DRSZG +civilizer/M +civilizes/AU +civil/UY +civvies +ck/C +clack/SDG +cladding/SM +clads +clad/U +Claiborne/M +Claiborn/M +claimable +claimant/MS +claim/CDRSKAEGZ +claimed/U +claimer/KMACE +Claire/M +Clair/M +Clairol/M +clairvoyance/MS +clairvoyant/YS +clambake/MS +clamberer/M +clamber/SDRZG +clammed +clammily +clamminess/MS +clamming +clam/MS +clammy/TPR +clamorer/M +clamor/GDRMSZ +clamorousness/UM +clamorous/PUY +clampdown/SM +clamper/M +clamp/MRDGS +clamshell/MS +Clancy/M +clandestineness/M +clandestine/YP +clanger/M +clangor/MDSG +clangorous/Y +clang/SGZRD +clanking/Y +clank/SGDM +clan/MS +clannishness/SM +clannish/PY +clansman/M +clansmen +clapboard/SDGM +Clapeyron/M +clapped +clapper/GMDS +clapping +clap/S +Clapton/M +claptrap/SM +claque/MS +Clarabelle/M +Clara/M +Clarance/M +Clare/M +Claremont/M +Clarence/M +Clarendon/M +Claresta/M +Clareta/M +claret/MDGS +Claretta/M +Clarette/M +Clarey/M +Claribel/M +Clarice/M +Clarie/M +clarification/M +clarifier/M +clarify/NGXDRS +Clari/M +Clarinda/M +Clarine/M +clarinetist/SM +clarinet/SM +clarinettist's +clarion/GSMD +Clarissa/M +Clarisse/M +Clarita/M +clarities +clarity/UM +Clarke/M +Clark/M +Clarridge/M +Clary/M +clasher/M +clash/RSDG +clasped/M +clasper/M +clasp's +clasp/UGSD +classer/M +class/GRSDM +classical/Y +classicism/SM +classicist/SM +classic/S +classics/M +classifiable/U +classification/AMC +classificatory +classified/S +classifier/SM +classify/CNXASDG +classiness/SM +classless/P +classmate/MS +classroom/MS +classwork/M +classy/PRT +clatterer/M +clattering/Y +clatter/SGDR +clattery +Claudelle/M +Claudell/M +Claude/M +Claudetta/M +Claudette/M +Claudia/M +Claudian/M +Claudianus/M +Claudie/M +Claudina/M +Claudine/M +Claudio/M +Claudius/M +clausal +clause/MS +Clausen/M +Clausewitz/M +Clausius/M +Claus/NM +claustrophobia/SM +claustrophobic +clave/RM +clave's/F +clavichord/SM +clavicle/MS +clavier/MS +clawer/M +claw/GDRMS +Clayborne/M +Clayborn/M +Claybourne/M +clayey +clayier +clayiest +Clay/M +clay/MDGS +claymore/MS +Clayson/M +Clayton/M +Clea/M +cleanable +cleaner/MS +cleaning/SM +cleanliness/UMS +cleanly/PRTU +cleanness/MSU +cleanse +cleanser/M +cleans/GDRSZ +cleanup/MS +clean/UYRDPT +clearance/MS +clearcut +clearer/M +clearheadedness/M +clearheaded/PY +clearinghouse/S +clearing/MS +clearly +clearness/MS +clears +clear/UTRD +Clearwater/M +clearway/M +cleat/MDSG +cleavage/MS +cleaver/M +cleave/RSDGZ +Cleavland/M +clef/SM +cleft/MDGS +clematis/MS +clemence +Clemenceau/M +Clemence/M +clemency/ISM +Clemente/M +Clementia/M +Clementina/M +Clementine/M +Clementius/M +clement/IY +Clement/MS +clements +Clemmie/M +Clemmy/M +Clemons +Clemson/M +Clem/XM +clenches +clenching +clench/UD +Cleo/M +Cleon/M +Cleopatra/M +Clerc/M +clerestory/MS +clergyman/M +clergymen +clergy/MS +clergywoman +clergywomen +clericalism/SM +clerical/YS +cleric/SM +Clerissa/M +clerk/SGYDM +clerkship/MS +Cletis +Cletus/M +Cleveland/M +Cleve/M +cleverness/SM +clever/RYPT +Clevey/M +Clevie/M +clevis/SM +clew/DMGS +cl/GJ +Cliburn/M +clichd +clich/SM +clicker/M +click/GZSRDM +clientle/SM +client/SM +cliffhanger/MS +cliffhanging +Cliff/M +Clifford/M +cliff/SM +Clifton/M +climacteric/SM +climactic +climate/MS +climatic +climatically +climatological/Y +climatologist/SM +climatology/MS +climax/MDSG +climbable/U +climb/BGZSJRD +climbdown +climbed/U +climber/M +clime/SM +Clim/M +clinch/DRSZG +clincher/M +clinching/Y +Cline/M +clinger/MS +clinging +cling/U +clingy/TR +clinical/Y +clinician/MS +clinic/MS +clinker/GMD +clink/RDGSZ +clinometer/MIS +Clint/M +Clinton/M +Clio/M +cliometrician/S +cliometric/S +clipboard/SM +clipped/U +clipper/MS +clipping/SM +clip/SM +clique/SDGM +cliquey +cliquier +cliquiest +cliquishness/SM +cliquish/YP +clitoral +clitorides +clitoris/MS +Clive/M +cloacae +cloaca/M +cloakroom/MS +cloak's +cloak/USDG +clobber/DGS +cloche/MS +clocker/M +clockmaker/M +clock/SGZRDMJ +clockwatcher +clockwise +clockwork/MS +clodded +clodding +cloddishness/M +cloddish/P +clodhopper/SM +clod/MS +Cloe/M +clogged/U +clogging/U +clog's +clog/US +cloisonn +cloisonnes +cloister/MDGS +cloistral +Clo/M +clomp/MDSG +clonal +clone/DSRGMZ +clonk/SGD +clopped +clopping +clop/S +Cloris/M +closed/U +close/EDSRG +closefisted +closely +closemouthed +closeness/MS +closeout/MS +closer/EM +closers +closest +closet/MDSG +closeup/S +closing/S +closured +closure/EMS +closure's/I +closuring +clothbound +clothesbrush +clotheshorse/MS +clothesline/SDGM +clothesman +clothesmen +clothespin/MS +clothe/UDSG +cloth/GJMSD +clothier/MS +clothing/M +Clotho/M +cloths +Clotilda/M +clot/MS +clotted +clotting +cloture/MDSG +cloudburst/MS +clouded/U +cloudiness/SM +cloudlessness/M +cloudless/YP +cloudscape/SM +cloud/SGMD +cloudy/TPR +clout/GSMD +cloven +cloverleaf/MS +clover/M +clove/SRMZ +Clovis/M +clown/DMSG +clownishness/SM +clownish/PY +cloy/DSG +cloying/Y +clubbed/M +clubbing/M +clubfeet +clubfoot/DM +clubhouse/SM +club/MS +clubroom/SM +cluck/GSDM +clueless +clue/MGDS +Cluj/M +clump/MDGS +clumpy/RT +clumsily +clumsiness/MS +clumsy/PRT +clung +clunk/SGZRDM +clunky/PRYT +clustered/AU +clusters/A +cluster/SGJMD +clutch/DSG +cluttered/U +clutter/GSD +Cl/VM +Clyde/M +Clydesdale/M +Cly/M +Clytemnestra/M +Clyve/M +Clywd/M +cm +Cm/M +CMOS +cnidarian/MS +CNN +CNS +CO +coacher/M +coachman/M +coachmen +coach/MSRDG +coachwork/M +coadjutor/MS +coagulable +coagulant/SM +coagulate/GNXSD +coagulation/M +coagulator/S +coaler/M +coalesce/GDS +coalescence/SM +coalescent +coalface/SM +coalfield/MS +coalitionist/SM +coalition/MS +coal/MDRGS +coalminers +coarseness/SM +coarsen/SGD +coarse/TYRP +coastal +coaster/M +coastguard/MS +coastline/SM +coast/SMRDGZ +coated/U +Coates/M +coating/M +coat/MDRGZJS +coattail/S +coattest +coauthor/MDGS +coaxer/M +coax/GZDSR +coaxial/Y +coaxing/Y +Cobain/M +cobalt/MS +cobbed +Cobbie/M +cobbing +cobbler/M +cobble/SRDGMZ +cobblestone/MSD +Cobb/M +Cobby/M +coble/M +Cob/M +COBOL +Cobol/M +cobra/MS +cob/SM +cobwebbed +cobwebbing +cobwebby/RT +cobweb/SM +cocaine/MS +coca/MS +cocci/MS +coccus/M +coccyges +coccyx/M +Cochabamba/M +cochineal/SM +Cochin/M +Cochise/M +cochleae +cochlear +cochlea/SM +Cochran/M +cockade/SM +cockamamie +cockatoo/SM +cockatrice/MS +cockcrow/MS +cockerel/MS +cocker/M +cockeye/DM +cockeyed/PY +cockfighting/M +cockfight/MJSG +cock/GDRMS +cockily +cockiness/MS +cocklebur/M +cockle/SDGM +cockleshell/SM +Cockney +cockney/MS +cockpit/MS +cockroach/SM +cockscomb/SM +cockshies +cocksucker/S! +cocksure +cocktail/GDMS +cocky/RPT +cocoa/SM +coco/MS +coconut/SM +cocoon/GDMS +Cocteau/M +COD +coda/SM +codded +codding +coddle/GSRD +coddler/M +codebook/S +codebreak/R +coded/UA +Codee/M +codeine/MS +codename/D +codependency/S +codependent/S +coder/CM +code's +co/DES +codes/A +code/SCZGJRD +codetermine/S +codeword/SM +codex/M +codfish/SM +codger/MS +codices/M +codicil/SM +Codie/M +codification/M +codifier/M +codify/NZXGRSD +Codi/M +coding/M +codling/M +Cod/M +cod/MDRSZGJ +codpiece/MS +Cody/M +coedited +coediting +coeditor/MS +coedits +coed/SM +coeducational +coeducation/SM +coefficient/SYM +coelenterate/MS +coequal/SY +coercer/M +coerce/SRDXVGNZ +coercible/I +coercion/M +coerciveness/M +coercive/PY +coeval/YS +coexistence/MS +coexistent +coexist/GDS +coextensive/Y +cofactor/MS +coffeecake/SM +coffeecup +coffeehouse/SM +coffeemaker/S +coffeepot/MS +coffee/SM +cofferdam/SM +coffer/DMSG +Coffey/M +coffin/DMGS +Coffman/M +cogency/MS +cogent/Y +cogged +cogging +cogitate/DSXNGV +cogitation/M +cogitator/MS +cog/MS +Cognac/M +cognac/SM +cognate/SXYN +cognation/M +cognitional +cognition/SAM +cognitive/SY +cognizable +cognizance/MAI +cognizances/A +cognizant/I +cognomen/SM +cognoscente +cognoscenti +cogwheel/SM +cohabitant/MS +cohabitational +cohabitation/SM +cohabit/SDG +Cohan/M +coheir/MS +Cohen/M +cohere/GSRD +coherence/SIM +coherencies +coherency/I +coherent/IY +coherer/M +cohesion/MS +cohesiveness/SM +cohesive/PY +Cohn/M +cohoes +coho/MS +cohort/SM +coiffed +coiffing +coiffure/MGSD +coif/SM +coil/UGSAD +Coimbatore/M +coinage's/A +coinage/SM +coincide/GSD +coincidence/MS +coincidental/Y +coincident/Y +coined/U +coiner/M +coin/GZSDRM +coinsurance/SM +Cointon/M +cointreau +coital/Y +coitus/SM +coke/MGDS +Coke/MS +COL +COLA +colander/SM +Colan/M +Colas +cola/SM +colatitude/MS +Colbert/M +Colby/M +coldblooded +coldish +coldness/MS +cold/YRPST +Coleen/M +Cole/M +Coleman/M +Colene/M +Coleridge/M +coleslaw/SM +Colet/M +Coletta/M +Colette/M +coleus/SM +Colfax/M +Colgate/M +colicky +colic/SM +coliform +Colin/M +coliseum/SM +colitis/MS +collaborate/VGNXSD +collaboration/M +collaborative/SY +collaborator/SM +collage/MGSD +collagen/M +collapse/SDG +collapsibility/M +collapsible +collarbone/MS +collar/DMGS +collard/SM +collarless +collated/U +collateral/SYM +collate/SDVNGX +collation/M +collator/MS +colleague/SDGM +collectedness/M +collected/PY +collectible/S +collection/AMS +collective/SY +collectivism/SM +collectivist/MS +collectivity/MS +collectivization/MS +collectivize/DSG +collector/MS +collect/SAGD +Colleen/M +colleen/SM +college/SM +collegiality/S +collegian/SM +collegiate/Y +Collen/M +Collete/M +Collette/M +coll/G +collide/SDG +Collie/M +collie/MZSRD +collier/M +Collier/M +colliery/MS +collimate/C +collimated/U +collimates +collimating +collimation/M +collimator/M +collinear +collinearity/M +Colline/M +Collin/MS +collisional +collision/SM +collocate/XSDGN +collocation/M +colloidal/Y +colloid/MS +colloq +colloquialism/MS +colloquial/SY +colloquies +colloquium/SM +colloquy/M +collude/SDG +collusion/SM +collusive +collying +Colly/RM +Colman/M +Col/MY +Cologne/M +cologne/MSD +Colo/M +Colombia/M +Colombian/S +Colombo/M +colonelcy/MS +colonel/MS +colonialism/MS +colonialist/MS +colonial/SPY +colonist/SM +colonization/ACSM +colonize/ACSDG +colonized/U +colonizer/MS +colonizes/U +Colon/M +colonnade/MSD +colon/SM +colony/SM +colophon/SM +Coloradan/S +Coloradoan/S +Colorado/M +colorant/SM +coloration/EMS +coloratura/SM +colorblindness/S +colorblind/P +colored/USE +colorer/M +colorfastness/SM +colorfast/P +colorfulness/MS +colorful/PY +colorimeter/SM +colorimetry +coloring/M +colorization/S +colorize/GSD +colorizing/C +colorlessness/SM +colorless/PY +colors/EA +color/SRDMGZJ +colossal/Y +Colosseum/M +colossi +colossus/M +colostomy/SM +colostrum/SM +col/SD +colter/M +coltishness/M +coltish/PY +Colt/M +colt/MRS +Coltrane/M +Columbia/M +Columbian +Columbine/M +columbine/SM +Columbus/M +columnar +columnist/MS +columnize/GSD +column/SDM +Colver/M +Co/M +comae +comaker/SM +Comanche/MS +coma/SM +comatose +combatant/SM +combativeness/MS +combative/PY +combat/SVGMD +combed/U +comber/M +combinational/A +combination/ASM +combinatorial/Y +combinatoric/S +combinator/SM +combined/AU +combiner/M +combines/A +combine/ZGBRSD +combining/A +combo/MS +comb/SGZDRMJ +Combs/M +combusted +combustibility/SM +combustible/SI +combustion/MS +combustive +Comdex/M +Comdr/M +comeback/SM +comedian/SM +comedic +comedienne/SM +comedown/MS +comedy/SM +come/IZSRGJ +comeliness/SM +comely/TPR +comer/IM +comes/M +comestible/MS +cometary +cometh +comet/SM +comeuppance/SM +comfit's +comfit/SE +comfortability/S +comfortableness/MS +comfortable/U +comfortably/U +comforted/U +comforter/MS +comfort/ESMDG +comforting/YE +comfy/RT +comicality/MS +comical/Y +comic/MS +Cominform/M +comity/SM +com/LJRTZG +comm +Com/M +comma/MS +commandant/MS +commandeer/SDG +commander/M +commanding/Y +commandment/SM +commando/SM +command/SZRDMGL +commemorate/SDVNGX +commemoration/M +commemorative/YS +commemorator/S +commence/ALDSG +commencement/AMS +commencer/M +commendably +commendation/ASM +commendatory/A +commender/AM +commend/GSADRB +commensurable/I +commensurate/IY +commensurates +commensuration/SM +commentary/MS +commentate/GSD +commentator/SM +commenter/M +comment's +comment/SUGD +commerce/MGSD +commercialism/MS +commercialization/SM +commercialize/GSD +commercial/PYS +Commie +commie/SM +commingle/GSD +commiserate/VGNXSD +commiseration/M +commissariat/MS +commissar/MS +commissary/MS +commission/ASCGD +commissioner/SM +commission's/A +commitment/SM +commit/SA +committable +committal/MA +committals +committed/UA +committeeman/M +committeemen +committee/MS +committeewoman/M +committeewomen +committing/A +commode/MS +commodes/IE +commodiousness/MI +commodious/YIP +commodity/MS +commodore/SM +commonality/MS +commonalty/MS +commoner/MS +commonness/MSU +commonplaceness/M +commonplace/SP +common/RYUPT +commonsense +commons/M +Commons/M +commonweal/SHM +commonwealth/M +Commonwealth/M +commonwealths +Commonwealths +commotion/MS +communality/M +communal/Y +commune/XSDNG +communicability/MS +communicable/IU +communicably +communicant/MS +communicate/VNGXSD +communicational +communication/M +communicativeness/M +communicative/PY +communicator/SM +communion/M +Communion/SM +communique/S +communism/MS +Communism/S +communistic +communist/MS +Communist/S +communitarian/M +community/MS +communize/SDG +commutable/I +commutate/XVGNSD +commutation/M +commutative/Y +commutativity +commutator/MS +commute/BZGRSD +commuter/M +Comoros +compaction/M +compactness/MS +compactor/MS +compact/TZGSPRDY +companionableness/M +companionable/P +companionably +companion/GBSMD +companionship/MS +companionway/MS +company/MSDG +Compaq/M +comparabilities +comparability/IM +comparableness/M +comparable/P +comparably/I +comparativeness/M +comparative/PYS +comparator/SM +compare/GRSDB +comparer/M +comparison/MS +compartmental +compartmentalization/SM +compartmentalize/DSG +compartment/SDMG +compassionateness/M +compassionate/PSDGY +compassion/MS +compass/MSDG +compatibility/IMS +compatibleness/M +compatible/SI +compatibly/I +compatriot/SM +compeer/DSGM +compellable +compelled +compelling/YM +compel/S +compendious +compendium/MS +compensable +compensated/U +compensate/XVNGSD +compensation/M +compensator/M +compensatory +compete/GSD +competence/ISM +competency/IS +competency's +competent/IY +competition/SM +competitiveness/SM +competitive/YP +competitor/MS +comp/GSYD +compilable/U +compilation/SAM +compile/ASDCG +compiler/CS +compiler's +complacence/S +complacency/SM +complacent/Y +complainant/MS +complainer/M +complain/GZRDS +complaining/YU +complaint/MS +complaisance/SM +complaisant/Y +complected +complementariness/M +complementarity +complementary/SP +complementation/M +complementer/M +complement/ZSMRDG +complete/BTYVNGPRSDX +completed/U +completely/I +completeness/ISM +completer/M +completion/MI +complexional +complexion/DMS +complexity/MS +complexness/M +complex/TGPRSDY +compliance/SM +compliant/Y +complicatedness/M +complicated/YP +complicate/SDG +complication/M +complicator/SM +complicit +complicity/MS +complier/M +complimentary/U +complimenter/M +compliment/ZSMRDG +comply/ZXRSDNG +component/SM +comport/GLSD +comportment/SM +compose/CGASDE +composedness/M +composed/PY +composer/CM +composers +composite/YSDXNG +compositional/Y +composition/CMA +compositions/C +compositor/MS +compost/DMGS +composure/ESM +compote/MS +compounded/U +compounder/M +compound/RDMBGS +comprehend/DGS +comprehending/U +comprehensibility/SIM +comprehensibleness/IM +comprehensible/PI +comprehensibly/I +comprehension/IMS +comprehensiveness/SM +comprehensive/YPS +compressed/Y +compressibility/IM +compressible/I +compressional +compression/CSM +compressive/Y +compressor/MS +compress/SDUGC +comprise/GSD +compromiser/M +compromise/SRDGMZ +compromising/UY +Compton/M +comptroller/SM +compulsion/SM +compulsiveness/MS +compulsive/PYS +compulsivity +compulsorily +compulsory/S +compunction/MS +Compuserve/M +CompuServe/M +computability/M +computable/UI +computably +computational/Y +computation/SM +computed/A +computerese +computerization/MS +computerize/SDG +computer/M +compute/RSDZBG +computes/A +computing/A +comradely/P +comradeship/MS +comrade/YMS +Comte/M +Conakry/M +Conan/M +Conant/M +concatenate/XSDG +concaveness/MS +concave/YP +conceal/BSZGRDL +concealed/U +concealer/M +concealing/Y +concealment/MS +conceded/Y +conceitedness/SM +conceited/YP +conceit/SGDM +conceivable/IU +conceivably/I +conceive/BGRSD +conceiver/M +concentrate/VNGSDX +concentration/M +concentrator/MS +concentrically +Concepcin/M +conceptional +conception/MS +concept/SVM +conceptuality/M +conceptualization/A +conceptualizations +conceptualization's +conceptualize/DRSG +conceptualizing/A +conceptual/Y +concerned/YU +concern/USGD +concerted/PY +concert/EDSG +concertina/MDGS +concertize/GDS +concertmaster/MS +concerto/SM +concert's +concessionaire/SM +concessional +concessionary +concession/R +Concetta/M +Concettina/M +Conchita/M +conch/MDG +conchs +concierge/SM +conciliar +conciliate/GNVX +conciliation/ASM +conciliator/MS +conciliatory/A +conciseness/SM +concise/TYRNPX +concision/M +conclave/S +concluder/M +conclude/RSDG +conclusion/SM +conclusive/IPY +conclusiveness/ISM +concocter/M +concoction/SM +concoct/RDVGS +concomitant/YS +concordance/MS +concordant/Y +concordat/SM +Concorde/M +Concordia/M +Concord/MS +concourse +concreteness/MS +concrete/NGXRSDPYM +concretion/M +concubinage/SM +concubine/SM +concupiscence/SM +concupiscent +concurrence/MS +concur/S +concussion/MS +concuss/VD +condemnate/XN +condemnation/M +condemnatory +condemner/M +condemn/ZSGRDB +condensate/NMXS +condensation/M +condenser/M +condense/ZGSD +condensible +condescend +condescending/Y +condescension/MS +condign +condiment/SM +condition/AGSJD +conditionals +conditional/UY +conditioned/U +conditioner/MS +conditioning/M +condition's +condole +condolence/MS +condominium/MS +condom/SM +condone/GRSD +condoner/M +Condorcet/M +condor/MS +condo/SM +conduce/VGSD +conduciveness/M +conducive/P +conductance/SM +conductibility/SM +conductible +conduction/MS +conductive/Y +conductivity/MS +conductor/MS +conductress/MS +conduct/V +conduit/MS +coneflower/M +Conestoga +coney's +confabbed +confabbing +confab/MS +confabulate/XSDGN +confabulation/M +confectioner/M +confectionery/SM +confectionist +confection/RDMGZS +confect/S +Confederacy/M +confederacy/MS +confederate/M +Confederate/S +conferee/MS +conference/DSGM +conferrable +conferral/SM +conferred +conferrer/SM +conferring +confer/SB +confessed/Y +confessional/SY +confession/MS +confessor/SM +confetti/M +confidante/SM +confidant/SM +confidence/SM +confidentiality/MS +confidentialness/M +confidential/PY +confident/Y +confider/M +confide/ZGRSD +confiding/PY +configuration/ASM +configure/AGSDB +confined/U +confine/L +confinement/MS +confiner/M +confirm/AGDS +confirmation/ASM +confirmatory +confirmedness/M +confirmed/YP +confiscate/DSGNX +confiscation/M +confiscator/MS +confiscatory +conflagration/MS +conflate/NGSDX +conflation/M +conflicting/Y +conflict/SVGDM +confluence/MS +conformable/U +conformal +conformance/SM +conformational/Y +conform/B +conformer/M +conformism/SM +conformist/SM +conformities +conformity/MUI +confounded/Y +confound/R +confrre/MS +confrontational +confrontation/SM +confronter/M +confront/Z +Confucianism/SM +Confucian/S +Confucius/M +confusedness/M +confused/PY +confuse/RBZ +confusing/Y +confutation/MS +confute/GRSD +confuter/M +conga/MDG +congeal/GSDL +congealment/MS +congeniality/UM +congenial/U +congeries/M +conger/SM +congestion/MS +congest/VGSD +conglomerate/XDSNGVM +conglomeration/M +Cong/M +Congolese +Congo/M +congrats +congratulate/NGXSD +congratulation/M +congratulatory +congregate/DSXGN +congregational +Congregational +congregationalism/MS +congregationalist/MS +Congregationalist/S +congregation/M +congressional/Y +congressman/M +congressmen +Congress/MS +congress/MSDG +congresspeople +congressperson/S +congresswoman/M +congresswomen +Congreve/M +congruence/IM +congruences +congruency/M +congruential +congruent/YI +congruity/MSI +congruousness/IM +congruous/YIP +conicalness/M +conical/PSY +conic/S +conics/M +conifer/MS +coniferous +conjectural/Y +conjecture/GMDRS +conjecturer/M +conjoint +conjugacy +conjugal/Y +conjugate/XVNGYSDP +conjugation/M +conjunct/DSV +conjunctiva/MS +conjunctive/YS +conjunctivitis/SM +conjuration/MS +conjurer/M +conjure/RSDZG +conjuring/M +conker/M +conk/ZDR +Conley/M +Con/M +conman +connect/ADGES +connectedly/E +connectedness/ME +connected/U +connectible +Connecticut/M +connection/AME +connectionless +connections/E +connective/SYM +connectivity/MS +connector/MS +Connelly/M +Conner/M +Connery/M +connexion/MS +Conney/M +conn/GVDR +Connie/M +Conni/M +conniption/MS +connivance/MS +conniver/M +connive/ZGRSD +connoisseur/MS +Connor/SM +connotative/Y +Conn/RM +connubial/Y +Conny/M +conquerable/U +conquered/AU +conqueror/MS +conquer/RDSBZG +conquers/A +conquest/ASM +conquistador/MS +Conrade/M +Conrad/M +Conrado/M +Conrail/M +Conroy/M +Consalve/M +consanguineous/Y +consanguinity/SM +conscienceless +conscientiousness/MS +conscientious/YP +conscionable/U +consciousness/MUS +conscious/UYSP +conscription/SM +consecrated/AU +consecrates/A +consecrate/XDSNGV +consecrating/A +consecration/AMS +consecutiveness/M +consecutive/YP +consensus/SM +consenter/M +consenting/Y +consent/SZGRD +consequence +consequentiality/S +consequential/IY +consequentialness/M +consequently/I +consequent/PSY +conservancy/SM +conservationism +conservationist/SM +conservation/SM +conservatism/SM +conservativeness/M +Conservative/S +conservative/SYP +conservator/MS +conservatory/MS +con/SGM +considerable/I +considerables +considerably/I +considerateness/MSI +considerate/XIPNY +consideration/ASMI +considered/U +considerer/M +consider/GASD +considering/S +consign/ASGD +consignee/SM +consignment/SM +consist/DSG +consistence/S +consistency/IMS +consistent/IY +consistory/MS +consolable/I +Consolata/M +consolation/MS +consolation's/E +consolatory +consoled/U +consoler/M +console/ZBG +consolidated/AU +consolidate/NGDSX +consolidates/A +consolidation/M +consolidator/SM +consoling/Y +consomm/S +consonance/IM +consonances +consonantal +consonant/MYS +consortia +consortium/M +conspectus/MS +conspicuousness/IMS +conspicuous/YIP +conspiracy/MS +conspiratorial/Y +conspirator/SM +constable +Constable/M +constabulary/MS +constance +Constance/M +Constancia/M +constancy/IMS +Constancy/M +Constanta/M +Constantia/M +Constantina/M +Constantine/M +Constantin/M +Constantino/M +Constantinople/M +constant/IY +constants +constellation/SM +consternate/XNGSD +consternation/M +constipate/XDSNG +constipation/M +constituency/MS +constituent/SYM +constituted/A +constitute/NGVXDS +constitutes/A +constituting/A +Constitution +constitutionality's +constitutionality/US +constitutionally/U +constitutional/SY +constitution/AMS +constitutive/Y +constrain +constrainedly +constrained/U +constraint/MS +constriction/MS +constrictor/MS +constrict/SDGV +construable +construct/ASDGV +constructibility +constructible/A +constructional/Y +constructionist/MS +construction/MAS +constructions/C +constructiveness/SM +constructive/YP +constructor/MS +construe/GSD +Consuela/M +Consuelo/M +consular/S +consulate/MS +consul/KMS +consulship/MS +consultancy/S +consultant/MS +consultation/SM +consultative +consulted/A +consulter/M +consult/RDVGS +consumable/S +consumed/Y +consume/JZGSDB +consumerism/MS +consumerist/S +consumer/M +consuming/Y +consummate/DSGVY +consummated/U +consumption/SM +consumptive/YS +cont +contact/BGD +contacted/A +contact's/A +contacts/A +contagion/SM +contagiousness/MS +contagious/YP +containerization/SM +containerize/GSD +container/M +containment/SM +contain/SLZGBRD +contaminant/SM +contaminated/AU +contaminates/A +contaminate/SDCXNG +contaminating/A +contamination/CM +contaminative +contaminator/MS +contd +cont'd +contemn/SGD +contemplate/DVNGX +contemplation/M +contemplativeness/M +contemplative/PSY +contemporaneity/MS +contemporaneousness/M +contemporaneous/PY +contemptibleness/M +contemptible/P +contemptibly +contempt/M +contemptuousness/SM +contemptuous/PY +contentedly/E +contentedness/SM +contented/YP +content/EMDLSG +contention/MS +contentiousness/SM +contentious/PY +contently +contentment/ES +contentment's +conterminous/Y +contestable/I +contestant/SM +contested/U +contextualize/GDS +contiguity/MS +contiguousness/M +contiguous/YP +continence/ISM +Continental/S +continental/SY +continent/IY +Continent/M +continents +continent's +contingency/SM +contingent/SMY +continua +continuable +continual/Y +continuance/ESM +continuant/M +continuation/ESM +continue/ESDG +continuer/M +continuity/SEM +continuousness/M +continuous/YE +continuum/M +contortionist/SM +contortion/MS +contort/VGD +contour +contraband/SM +contrabass/M +contraception/SM +contraceptive/S +contract/DG +contractible +contractile +contractual/Y +contradict/GDS +contradiction/MS +contradictorily +contradictoriness/M +contradictory/PS +contradistinction/MS +contraflow/S +contrail/M +contraindicate/SDVNGX +contraindication/M +contralto/SM +contrapositive/S +contraption/MS +contrapuntal/Y +contrariety/MS +contrarily +contrariness/MS +contrariwise +contrary/PS +contra/S +contrasting/Y +contrastive/Y +contrast/SRDVGZ +contravene/GSRD +contravener/M +contravention/MS +Contreras/M +contretemps/M +contribute/XVNZRD +contribution/M +contributive/Y +contributorily +contributor/SM +contributory/S +contriteness/M +contrite/NXP +contrition/M +contrivance/SM +contriver/M +contrive/ZGRSD +control/CS +controllability/M +controllable/IU +controllably/U +controlled/CU +controller/SM +controlling/C +control's +controversialists +controversial/UY +controversy/MS +controvert/DGS +controvertible/I +contumacious/Y +contumacy/MS +contumelious +contumely/MS +contuse/NGXSD +contusion/M +conundrum/SM +conurbation/MS +convalesce/GDS +convalescence/SM +convalescent/S +convect/DSVG +convectional +convection/MS +convector +convene/ASDG +convener/MS +convenience/ISM +convenient/IY +conventicle/SM +conventionalism/M +conventionalist/M +conventionality/SUM +conventionalize/GDS +conventional/UY +convention/MA +conventions +convergence/MS +convergent +conversant/Y +conversationalist/SM +conversational/Y +conversation/SM +conversazione/M +converse/Y +conversion/AM +conversioning +converted/U +converter/MS +convert/GADS +convertibility's/I +convertibility/SM +convertibleness/M +convertible/PS +convexity/MS +convex/Y +conveyance/DRSGMZ +conveyancer/M +conveyancing/M +convey/BDGS +conveyor/MS +conviction/MS +convict/SVGD +convinced/U +convincer/M +convince/RSDZG +convincingness/M +convincing/PUY +conviviality/MS +convivial/Y +convoke/GSD +convolute/XDNY +convolution/M +convolve/C +convolved +convolves +convolving +convoy/GMDS +convulse/SDXVNG +convulsion/M +convulsiveness/M +convulsive/YP +Conway/M +cony/SM +coo/GSD +cookbook/SM +cooked/AU +Cooke/M +cooker/M +cookery/MS +cook/GZDRMJS +Cookie/M +cookie/SM +cooking/M +Cook/M +cookout/SM +cooks/A +cookware/SM +cooky's +coolant/SM +cooled/U +cooler/M +Cooley/M +coolheaded +Coolidge/M +coolie/MS +coolness/MS +cool/YDRPJGZTS +coon/MS! +coonskin/MS +cooperage/MS +cooperate/VNGXSD +cooperation/M +cooperativeness/SM +cooperative/PSY +cooperator/MS +cooper/GDM +Cooper/M +coop/MDRGZS +Coop/MR +coordinated/U +coordinateness/M +coordinate/XNGVYPDS +coordination/M +coordinator/MS +Coors/M +cootie/SM +coot/MS +copay/S +Copeland/M +Copenhagen/M +coper/M +Copernican +Copernicus/M +cope/S +copied/A +copier/M +copies/A +copilot/SM +coping/M +copiousness/SM +copious/YP +coplanar +Copland/M +Copley/M +copolymer/MS +copora +copped +Copperfield/M +copperhead/MS +copper/MSGD +copperplate/MS +coppersmith/M +coppersmiths +coppery +coppice's +copping +Coppola/M +copra/MS +coprolite/M +coprophagous +copse/M +cops/GDS +cop/SJMDRG +copter/SM +Coptic/M +copula/MS +copulate/XDSNGV +copulation/M +copulative/S +copybook/MS +copycat/SM +copycatted +copycatting +copyist/SM +copy/MZBDSRG +copyrighter/M +copyright/MSRDGZ +copywriter/MS +coquetry/MS +coquette/DSMG +coquettish/Y +Corabella/M +Corabelle/M +Corabel/M +coracle/SM +Coralie/M +Coraline/M +coralline +Coral/M +coral/SM +Coralyn/M +Cora/M +corbel/GMDJS +Corbet/M +Corbett/M +Corbie/M +Corbin/M +Corby/M +cordage/MS +corded/AE +Cordelia/M +Cordelie/M +Cordell/M +corder/AM +Cordey/M +cord/FSAEM +cordiality/MS +cordialness/M +cordial/PYS +Cordie/M +cordillera/MS +Cordilleras +Cordi/M +cording/MA +cordite/MS +cordless +Cord/M +Cordoba +cordon/DMSG +cordovan/SM +Cordula/M +corduroy/GDMS +Cordy/M +cored/A +Coreen/M +Corella/M +core/MZGDRS +Corenda/M +Corene/M +corer/M +corespondent/MS +Coretta/M +Corette/M +Corey/M +Corfu/M +corgi/MS +coriander/SM +Corie/M +Corilla/M +Cori/M +Corina/M +Corine/M +coring/M +Corinna/M +Corinne/M +Corinthian/S +Corinthians/M +Corinth/M +Coriolanus/M +Coriolis/M +Corissa/M +Coriss/M +corked/U +corker/M +cork/GZDRMS +Cork/M +corkscrew/DMGS +corks/U +Corliss/M +Corly/M +Cormack/M +corm/MS +cormorant/MS +Cornall/M +cornball/SM +cornbread/S +corncob/SM +corncrake/M +corneal +cornea/SM +Corneille/M +Cornela/M +Cornelia/M +Cornelius/M +Cornelle/M +Cornell/M +corner/GDM +cornerstone/MS +cornet/SM +Corney/M +cornfield/SM +cornflake/S +cornflour/M +cornflower/SM +corn/GZDRMS +cornice/GSDM +Cornie/M +cornily +corniness/S +Cornish/S +cornmeal/S +cornrow/GDS +cornstalk/MS +cornstarch/SM +cornucopia/MS +Cornwallis/M +Cornwall/M +Corny/M +corny/RPT +corolla/MS +corollary/SM +Coronado/M +coronal/MS +coronary/S +corona/SM +coronate/NX +coronation/M +coroner/MS +coronet/DMS +Corot/M +coroutine/SM +Corp +corporal/SYM +corpora/MS +corporate/INVXS +corporately +corporation/MI +corporatism/M +corporatist +corporeality/MS +corporeal/IY +corporealness/M +corp/S +corpse/M +corpsman/M +corpsmen +corps/SM +corpulence/MS +corpulentness/S +corpulent/YP +corpuscle/SM +corpuscular +corpus/M +corr +corralled +corralling +corral/MS +correctable/U +correct/BPSDRYTGV +corrected/U +correctional +correction/MS +corrective/YPS +correctly/I +correctness/MSI +corrector/MS +Correggio/M +correlated/U +correlate/SDXVNG +correlation/M +correlative/YS +Correna/M +correspond/DSG +correspondence/MS +correspondent/SM +corresponding/Y +Correy/M +Corrianne/M +corridor/SM +Corrie/M +corrigenda +corrigendum/M +corrigible/I +Corri/M +Corrina/M +Corrine/M +Corrinne/M +corroborated/U +corroborate/GNVXDS +corroboration/M +corroborative/Y +corroborator/MS +corroboratory +corrode/SDG +corrodible +corrosion/SM +corrosiveness/M +corrosive/YPS +corrugate/NGXSD +corrugation/M +corrupt/DRYPTSGV +corrupted/U +corrupter/M +corruptibility/SMI +corruptible/I +corruption/IM +corruptions +corruptive/Y +corruptness/MS +Corry/M +corsage/MS +corsair/SM +corset/GMDS +Corsica/M +Corsican/S +cortge/MS +Cortes/S +cortex/M +Cortez's +cortical/Y +cortices +corticosteroid/SM +Cortie/M +cortisone/SM +Cortland/M +Cort/M +Cortney/M +Corty/M +corundum/MS +coruscate/XSDGN +coruscation/M +Corvallis/M +corvette/MS +Corvus/M +Cory/M +Cos +Cosby/M +Cosetta/M +Cosette/M +cos/GDS +cosignatory/MS +cosign/SRDZG +cosily +Cosimo/M +cosine/MS +cosiness/MS +Cosme/M +cosmetically +cosmetician/MS +cosmetic/SM +cosmetologist/MS +cosmetology/MS +cosmic +cosmical/Y +cosmogonist/MS +cosmogony/SM +cosmological/Y +cosmologist/MS +cosmology/SM +Cosmo/M +cosmonaut/MS +cosmopolitanism/MS +cosmopolitan/SM +cosmos/SM +cosponsor/DSG +cossack/S +Cossack/SM +cosset/GDS +Costa/M +Costanza/M +costarred +costarring +costar/S +Costello/M +costiveness/M +costive/PY +costless +costliness/SM +costly/RTP +cost/MYGVJS +Costner/M +costumer/M +costume/ZMGSRD +cotangent/SM +Cote/M +cote/MS +coterie/MS +coterminous/Y +cotillion/SM +Cotonou/M +Cotopaxi/M +cot/SGMD +cottager/M +cottage/ZMGSRD +cottar's +cotted +cotter/SDM +cotton/GSDM +Cotton/M +cottonmouth/M +cottonmouths +cottonseed/MS +cottontail/SM +cottonwood/SM +cottony +cotyledon/MS +couching/M +couch/MSDG +cougar/MS +cougher/M +cough/RDG +coughs +couldn't +could/T +could've +coule/MS +Coulomb/M +coulomb/SM +councilman/M +councilmen +councilor/MS +councilperson/S +council/SM +councilwoman/M +councilwomen +counsel/GSDM +counsellings +counselor/MS +countability/E +countable/U +countably/U +countdown/SM +counted/U +count/EGARDS +countenance/EGDS +countenancer/M +countenance's +counteract/DSVG +counteraction/SM +counterargument/SM +counterattack/DRMGS +counterbalance/MSDG +counterclaim/GSDM +counterclockwise +counterculture/MS +countercyclical +counterespionage/MS +counterexample/S +counterfeiter/M +counterfeit/ZSGRD +counterflow +counterfoil/MS +counterforce/M +counter/GSMD +counterinsurgency/MS +counterintelligence/MS +counterintuitive +countermand/DSG +counterman/M +countermeasure/SM +countermen +counteroffensive/SM +counteroffer/SM +counterpane/SM +counterpart/SM +counterpoint/GSDM +counterpoise/GMSD +counterproductive +counterproposal/M +counterrevolutionary/MS +counterrevolution/MS +counter's/E +counters/E +countersignature/MS +countersign/SDG +countersink/SG +counterspy/MS +counterstrike +countersunk +countertenor/SM +countervail/DSG +counterweight/GMDS +countess/MS +countless/Y +countrify/D +countryman/M +countrymen +country/MS +countryside/MS +countrywide +countrywoman/M +countrywomen +county/SM +coup/ASDG +coupe/MS +Couperin/M +couple/ACU +coupled/CU +coupler/C +couplers +coupler's +couple's +couples/CU +couplet/SM +coupling's/C +coupling/SM +coupon/SM +coup's +courage/MS +courageously +courageousness/MS +courageous/U +courages/E +Courbet/M +courgette/MS +courier/GMDS +course/EGSRDM +courser's/E +courser/SM +course's/AF +courses/FA +coursework +coursing/M +Courtenay/M +courteousness/EM +courteousnesses +courteous/PEY +courtesan/MS +courtesied +courtesy/ESM +courtesying +court/GZMYRDS +courthouse/MS +courtier/SM +courtliness/MS +courtly/RTP +Court/M +Courtnay/M +Courtney/M +courtroom/MS +courtship/SM +courtyard/SM +couscous/MS +cousinly/U +cousin/YMS +Cousteau/M +couture/SM +couturier/SM +covalent/Y +covariance/SM +covariant/S +covariate/SN +covary +cove/DRSMZG +covenanted/U +covenanter/M +covenant/SGRDM +coven/SM +Covent/M +Coventry/MS +coverable/E +cover/AEGUDS +coverage/MS +coverall/DMS +coverer/AME +covering/MS +coverlet/MS +coversheet +covers/M +covertness/SM +covert/YPS +coveter/M +coveting/Y +covetousness/SM +covetous/PY +covet/SGRD +covey/SM +covington +cowardice/MS +cowardliness/MS +cowardly/P +Coward/M +coward/MYS +cowbell/MS +cowbird/MS +cowboy/MS +cowcatcher/SM +cowed/Y +cowering/Y +cower/RDGZ +cowgirl/MS +cowhand/S +cowherd/SM +cowhide/MGSD +Cowley/M +cowlick/MS +cowling/M +cowl/SGMD +cowman/M +cow/MDRSZG +cowmen +coworker/MS +Cowper/M +cowpoke/MS +cowpony +cowpox/MS +cowpuncher/M +cowpunch/RZ +cowrie/SM +cowshed/SM +cowslip/MS +coxcomb/MS +Cox/M +cox/MDSG +coxswain/GSMD +coy/CDSG +coyer +coyest +coyly +Coy/M +coyness/MS +coyote/SM +coypu/SM +cozenage/MS +cozen/SGD +cozily +coziness/MS +Cozmo/M +Cozumel/M +cozy/DSRTPG +CPA +cpd +CPI +cpl +Cpl +CPO +CPR +cps +CPU/SM +crabapple +crabbedness/M +crabbed/YP +Crabbe/M +crabber/MS +crabbily +crabbiness/S +crabbing/M +crabby/PRT +crabgrass/S +crablike +crab/MS +crackable/U +crackdown/MS +crackerjack/S +cracker/M +crackle/GJDS +crackling/M +crackly/RT +crackpot/SM +crackup/S +crack/ZSBYRDG +cradler/M +cradle/SRDGM +cradling/M +craftily +craftiness/SM +Craft/M +craft/MRDSG +craftsman/M +craftsmanship/SM +craftsmen +craftspeople +craftspersons +craftswoman +craftswomen +crafty/TRP +Craggie/M +cragginess/SM +Craggy/M +craggy/RTP +crag/SM +Craig/M +Cramer/M +crammed +crammer/M +cramming +cramper/M +cramp/MRDGS +crampon/SM +cram/S +Cranach/M +cranberry/SM +Crandall/M +crane/DSGM +cranelike +Crane/M +Cranford/M +cranial +cranium/MS +crankcase/MS +crankily +crankiness/MS +crank/SGTRDM +crankshaft/MS +cranky/TRP +Cranmer/M +cranny/DSGM +Cranston/M +crape/SM +crapped +crappie/M +crapping +crappy/RST +crapshooter/SM +crap/SMDG! +crasher/M +crashing/Y +crash/SRDGZ +crassness/MS +crass/TYRP +crate/DSRGMZ +crater/DMG +Crater/M +cravat/SM +cravatted +cravatting +crave/DSRGJ +cravenness/SM +craven/SPYDG +craver/M +craving/M +crawdad/S +crawfish's +Crawford/M +crawler/M +crawl/RDSGZ +crawlspace/S +crawlway +crawly/TRS +craw/SYM +crayfish/GSDM +Crayola/M +crayon/GSDM +Cray/SM +craze/GMDS +crazily +craziness/MS +crazy/SRTP +creakily +creakiness/SM +creak/SDG +creaky/PTR +creamer/M +creamery/MS +creamily +creaminess/SM +cream/SMRDGZ +creamy/TRP +creased/CU +crease/IDRSG +crease's +creases/C +creasing/C +created/U +create/XKVNGADS +creationism/MS +creationist/MS +Creation/M +creation/MAK +creativeness/SM +creative/YP +creativities +creativity/K +creativity's +Creator/M +creator/MS +creatureliness/M +creaturely/P +creature/YMS +crche/SM +credence/MS +credent +credential/SGMD +credenza/SM +credibility/IMS +credible/I +credibly/I +creditability/M +creditableness/M +creditable/P +creditably/E +credited/U +credit/EGBSD +creditor/MS +credit's +creditworthiness +credo/SM +credulity/ISM +credulous/IY +credulousness/SM +creedal +creed/C +creeds +creed's +creekside +creek/SM +Creek/SM +creel/SMDG +Cree/MDS +creeper/M +creepily +creepiness/SM +creep/SGZR +creepy/PRST +Creigh/M +Creight/M +Creighton/M +cremate/XDSNG +cremation/M +crematoria +crematorium/MS +crematory/S +creme/S +crenelate/XGNSD +crenelation/M +Creole/MS +creole/SM +Creon/M +creosote/MGDS +crepe/DSGM +crept +crescendoed +crescendoing +crescendo/SCM +crescent/MS +cress/S +crestfallenness/M +crestfallen/PY +cresting/M +crestless +crest/SGMD +Crestview/M +cretaceous +Cretaceously/M +Cretaceous/Y +Cretan/S +Crete/M +cretinism/MS +cretin/MS +cretinous +cretonne/SM +crevasse/DSMG +crevice/SM +crew/DMGS +crewel/SM +crewelwork/SM +crewman/M +crewmen +cribbage/SM +cribbed +cribber/SM +cribbing/M +crib/SM +Crichton/M +cricketer/M +cricket/SMZRDG +crick/GDSM +Crick/M +cried/C +crier/CM +cries/C +Crimea/M +Crimean +crime/GMDS +criminality/MS +criminalization/C +criminalize/GC +criminal/SYM +criminologist/SM +criminology/MS +crimper/M +crimp/RDGS +crimson/DMSG +cringer/M +cringe/SRDG +crinkle/DSG +crinkly/TRS +Crin/M +crinoline/SM +cripple/GMZDRS +crippler/M +crippling/Y +Crisco/M +crises +crisis/M +Cris/M +crisper/M +crispiness/SM +crispness/MS +crisp/PGTYRDS +crispy/RPT +criss +crisscross/GDS +Crissie/M +Crissy/M +Cristabel/M +Cristal/M +Crista/M +Cristen/M +Cristian/M +Cristiano/M +Cristie/M +Cristi/M +Cristina/M +Cristine/M +Cristin/M +Cristionna/M +Cristobal/M +Cristy/M +criteria +criterion/M +criticality +critically/U +criticalness/M +critical/YP +criticism/MS +criticized/U +criticize/GSRDZ +criticizer/M +criticizes/A +criticizingly/S +criticizing/UY +critic/MS +critique/MGSD +critter/SM +Cr/M +croaker/M +croak/SRDGZ +croaky/RT +Croatia/M +Croatian/S +Croat/SM +Croce/M +crocheter/M +crochet/RDSZJG +crockery/SM +Crockett/M +Crockpot/M +crock/SGRDM +crocodile/MS +crocus/SM +Croesus/SM +crofter/M +croft/MRGZS +croissant/MS +Croix/M +Cromwellian +Cromwell/M +crone/SM +Cronin/M +Cronkite/M +Cronus/M +crony/SM +crookedness/SM +crooked/TPRY +Crookes/M +crookneck/MS +crook/SGDM +crooner/M +croon/SRDGZ +cropland/MS +crop/MS +cropped +cropper/SM +cropping +croquet/MDSG +croquette/SM +Crosby/M +crosier/SM +crossarm +crossbarred +crossbarring +crossbar/SM +crossbeam/MS +crossbones +crossbowman/M +crossbowmen +crossbow/SM +crossbred/S +crossbreed/SG +crosscheck/SGD +crosscurrent/SM +crosscut/SM +crosscutting +crossed/UA +crosses/UA +crossfire/SM +crosshatch/GDS +crossing/M +Cross/M +crossness/MS +crossover/MS +crosspatch/MS +crosspiece/SM +crosspoint +crossproduct/S +crossroad/GSM +crossroads/M +crosstalk/M +crosstown +crosswalk/MS +crossway/M +crosswind/SM +crosswise +crossword/MS +cross/ZTYSRDMPBJG +crotchetiness/M +crotchet/MS +crotchety/P +crotchless +crotch/MDS +crouch/DSG +croupier/M +croup/SMDG +croupy/TZR +croton/MS +crowbait +crowbarred +crowbarring +crowbar/SM +crowdedness/M +crowded/P +crowd/MRDSG +crowfeet +crowfoot/M +crow/GDMS +Crowley/M +crowned/U +crowner/M +crown/RDMSJG +crozier's +CRT/S +crucial/Y +crucible/MS +crucifiable +crucifixion/MS +Crucifixion/MS +crucifix/SM +cruciform/S +crucify/NGDS +crudded +crudding +cruddy/TR +crudeness/MS +crude/YSP +crudits +crudity/MS +crud/STMR +cruelness/MS +cruelty/SM +cruel/YRTSP +cruet/MS +cruft +crufty +Cruikshank/M +cruise/GZSRD +cruiser/M +cruller/SM +crumb/GSYDM +crumble/DSJG +crumbliness/MS +crumbly/PTRS +crumby/RT +crumminess/S +crummy/SRTP +crump +crumpet/SM +crumple/DSG +crunch/DSRGZ +crunchiness/MS +crunchy/TRP +crupper/MS +crusade/GDSRMZ +crusader/M +cruse/MS +crushable/U +crusher/M +crushing/Y +crushproof +crush/SRDBGZ +Crusoe/M +crustacean/MS +crustal +crust/GMDS +crustily +crustiness/SM +crusty/SRTP +crutch/MDSG +Crux/M +crux/MS +Cruz/M +crybaby/MS +cry/JGDRSZ +cryogenic/S +cryogenics/M +cryostat/M +cryosurgery/SM +cryptanalysis/M +cryptanalyst/M +cryptanalytic +crypt/CS +cryptic +cryptically +cryptogram/MS +cryptographer/MS +cryptographic +cryptographically +cryptography/MS +cryptologic +cryptological +cryptologist/M +cryptology/M +Cryptozoic/M +crypt's +crystalline/S +crystallite/SM +crystallization/AMS +crystallized/UA +crystallizes/A +crystallize/SRDZG +crystallizing/A +crystallographer/MS +crystallographic +crystallography/M +Crystal/M +crystal/SM +Crysta/M +Crystie/M +Cs +C's +cs/EA +cs's +CST +ct +CT +Cthrine/M +Ct/M +ctn +ctr +Cuba/M +Cuban/S +cubbed +cubbing +cubbyhole/MS +cuber/M +cube/SM +cubical/Y +cubicle/SM +cubic/YS +cubism/SM +cubist/MS +cubit/MS +cub/MDRSZG +cuboid +Cuchulain/M +cuckold/GSDM +cuckoldry/MS +cuckoo/SGDM +cucumber/MS +cuddle/GSD +cuddly/TRP +cu/DG +cudgel/GSJMD +cud/MS +cue/MS +cuff/GSDM +Cuisinart/M +cuisine/MS +Culbertson/M +culinary +Cullan/M +cull/DRGS +cullender's +Cullen/M +culler/M +Culley/M +Cullie/M +Cullin/M +Cull/MN +Cully/M +culminate/XSDGN +culmination/M +culotte/S +culpability/MS +culpable/I +culpableness/M +culpably +culpa/SM +culprit/SM +cultism/SM +cultist/SM +cultivable +cultivated/U +cultivate/XBSDGN +cultivation/M +cultivator/SM +cult/MS +cultural/Y +cultured/U +culture/SDGM +Culver/MS +culvert/SM +Cu/M +cumber/DSG +Cumberland/M +cumbersomeness/MS +cumbersome/YP +cumbrous +cumin/MS +cummerbund/MS +Cummings +cumquat's +cum/S +cumulate/XVNGSD +cumulation/M +cumulative/Y +cumuli +cumulonimbi +cumulonimbus/M +cumulus/M +Cunard/M +cuneiform/S +cunnilingus/SM +Cunningham/M +cunningness/M +cunning/RYSPT +cunt/SM! +cupboard/SM +cupcake/SM +Cupertino/M +cupful/SM +cupidinously +cupidity/MS +Cupid/M +cupid/S +cup/MS +cupola/MDGS +cupped +cupping/M +cupric +cuprous +curability/MS +curable/IP +curableness/MI +curably/I +Curacao/M +curacy/SM +curare/MS +curate/VGMSD +curative/YS +curatorial +curator/KMS +curbing/M +curbside +curb/SJDMG +curbstone/MS +Curcio/M +curdle/SDG +curd/SMDG +cured/U +cure/KBDRSGZ +curer/MK +curettage/SM +curfew/SM +curfs +curiae +curia/M +cur/IBS +Curie/M +curie/SM +curiosity/SM +curio/SM +curiousness/SM +curious/TPRY +Curitiba/M +curium/MS +curler/SM +curlew/MS +curlicue/MGDS +curliness/SM +curling/M +curl/UDSG +curlycue's +curly/PRT +curmudgeon/MYS +Curran/M +currant/SM +curred/AFI +currency's +currency/SF +current/FSY +currently/A +currentness/M +Currey/M +curricle/M +curricula +curricular +curriculum/M +Currie/M +currier/M +Currier/M +curring/FAI +Curr/M +currycomb/DMGS +Curry/MR +curry/RSDMG +cur's +curs/ASDVG +curse/A +cursedness/M +cursed/YRPT +curse's +cursive/EPYA +cursiveness/EM +cursives +cursor/DMSG +cursorily +cursoriness/SM +cursory/P +curtailer/M +curtail/LSGDR +curtailment/SM +curtain/GSMD +Curtice/M +Curtis/M +Curt/M +curtness/MS +curtsey's +curtsy/SDMG +curt/TYRP +curvaceousness/S +curvaceous/YP +curvature/MS +curved/A +curved's +curve/DSGM +curvilinearity/M +curvilinear/Y +curving/M +curvy/RT +cushion/SMDG +Cushman/M +cushy/TR +cuspid/MS +cuspidor/MS +cusp/MS +cussedness/M +cussed/YP +cuss/EGDSR +cusses/F +cussing/F +cuss's +custard/MS +Custer/M +custodial +custodianship/MS +custodian/SM +custody/MS +customarily +customariness/M +customary/PS +customer/M +customhouse/S +customization/SM +customize/ZGBSRD +custom/SMRZ +cutaneous/Y +cutaway/SM +cutback/SM +cuteness/MS +cute/SPY +cutesy/RT +cuticle/SM +cutlass/MS +cutler/SM +cutlery/MS +cutlet/SM +cut/MRST +cutoff/MS +cutout/SM +cutter/SM +cutthroat/SM +cutting/MYS +cuttlebone/SM +cuttlefish/MS +cuttle/M +cutup/MS +cutworm/MS +Cuvier/M +Cuzco/M +CV +cw +cwt +Cyanamid/M +cyanate/M +cyanic +cyanide/GMSD +cyan/MS +cyanogen/M +Cybele/M +cybernetic/S +cybernetics/M +cyberpunk/S +cyberspace/S +Cybill/M +Cybil/M +Cyb/M +cyborg/S +Cyclades +cyclamen/MS +cycle/ASDG +cycler +cycle's +cycleway/S +cyclic +cyclical/SY +cycling/M +cyclist/MS +cyclohexanol +cycloidal +cycloid/SM +cyclometer/MS +cyclone/SM +cyclonic +cyclopean +cyclopedia/MS +cyclopes +Cyclopes +cyclops +Cyclops/M +cyclotron/MS +cyder/SM +cygnet/MS +Cygnus/M +cylinder/GMDS +cylindric +cylindrical/Y +Cy/M +cymbalist/MS +cymbal/SM +Cymbre/M +Cynde/M +Cyndia/M +Cyndie/M +Cyndi/M +Cyndy/M +cynical/UY +cynicism/MS +cynic/MS +cynosure/SM +Cynthea/M +Cynthia/M +Cynthie/M +Cynthy/M +cypher/MGSD +cypreses +cypress/SM +Cyprian +Cypriot/SM +Cyprus/M +Cyrano/M +Cyrille/M +Cyrillic +Cyrill/M +Cyrillus/M +Cyril/M +Cyrus/M +cystic +cyst/MS +cytochemistry/M +cytochrome/M +cytologist/MS +cytology/MS +cytolysis/M +cytoplasmic +cytoplasm/SM +cytosine/MS +cytotoxic +CZ +czarevitch/M +czarina/SM +czarism/M +czarist/S +czarship +czar/SM +Czech +Czechoslovakia/M +Czechoslovakian/S +Czechoslovak/S +Czechs +Czerniak/M +Czerny/M +D +DA +dabbed +dabber/MS +dabbing +dabbler/M +dabble/RSDZG +dab/S +Dacca's +dace/MS +Dacey/M +dacha/SM +Dachau/M +dachshund/SM +Dacia/M +Dacie/M +Dacron/MS +dactylic/S +dactyl/MS +Dacy/M +Dadaism/M +dadaism/S +Dadaist/M +dadaist/S +Dada/M +daddy/SM +Dade/M +dado/DMG +dadoes +dad/SM +Daedalus/M +Dael/M +daemonic +daemon/SM +Daffie/M +Daffi/M +daffiness/S +daffodil/MS +Daffy/M +daffy/PTR +daftness/MS +daft/TYRP +DAG +dagger/DMSG +Dag/M +Dagmar/M +Dagny/M +Daguerre/M +daguerreotype/MGDS +Dagwood/M +Dahlia/M +dahlia/MS +Dahl/M +Dahomey/M +Daile/M +dailiness/MS +daily/PS +Daimler/M +daintily +daintiness/MS +dainty/TPRS +daiquiri/SM +dairying/M +dairyland +dairymaid/SM +dairyman/M +dairymen +dairy/MJGS +dairywoman/M +dairywomen +Daisey/M +Daisie/M +Daisi/M +dais/SM +Daisy/M +daisy/SM +Dakar/M +Dakotan +Dakota/SM +Dale/M +Dalenna/M +dale/SMH +daleth/M +Daley/M +Dalhousie/M +Dalia/M +Dalian/M +Dalila/M +Dali/SM +Dallas/M +dalliance/SM +dallier/M +Dalli/MS +Dall/M +Dallon/M +dally/ZRSDG +Dal/M +Dalmatia/M +dalmatian/S +Dalmatian/SM +Daloris/M +Dalston/M +Dalt/M +Dalton/M +Daly/M +damageable +damaged/U +damage/MZGRSD +damager/M +damaging/Y +Damara/M +Damaris/M +Damascus/M +damask/DMGS +dame/SM +Dame/SMN +Damian/M +Damiano/M +Damien/M +Damion/M +Damita/M +dam/MDS +dammed +damming +dammit/S +damnably +damnation/MS +damnedest/MS +damned/TR +damn/GSBRD +damning/Y +Damocles/M +Damon/M +damped/U +dampener/M +dampen/RDZG +damper/M +dampness/MS +damp/SGZTXYRDNP +damselfly/MS +damsel/MS +damson/MS +Dana +Dana/M +Danbury/M +dancelike +dancer/M +dance/SRDJGZ +dandelion/MS +dander/DMGS +dandify/SDG +dandily +dandle/GSD +dandruff/MS +dandy/TRSM +Danelaw/M +Danella/M +Danell/M +Dane/SM +Danette/M +danger/DMG +Dangerfield/M +dangerousness/M +dangerous/YP +dangler/M +dangle/ZGRSD +dangling/Y +dang/SGZRD +Danial/M +Dania/M +Danica/M +Danice/M +Daniela/M +Daniele/M +Daniella/M +Danielle/M +Daniel/SM +Danielson/M +Danie/M +Danika/M +Danila/M +Dani/M +Danish +danish/S +Danita/M +Danit/M +dankness/MS +dank/TPYR +Danna/M +Dannel/M +Dannie/M +Danni/M +Dannye/M +Danny/M +danseuse/SM +Dan/SM +Dante/M +Danton/M +Danube/M +Danubian +Danville/M +Danya/M +Danyelle/M +Danyette/M +Danzig/M +Daphene/M +Daphna/M +Daphne/M +dapperness/M +dapper/PSTRY +dapple/SDG +Dara/M +Darbee/M +Darbie/M +Darb/M +Darby/M +Darcee/M +Darcey/M +Darcie/M +Darci/M +D'Arcy +Darcy/M +Darda/M +Dardanelles +daredevil/MS +daredevilry/S +Dareen/M +Darelle/M +Darell/M +Dare/M +Daren/M +darer/M +daresay +dare/ZGDRSJ +d'Arezzo +Daria/M +Darice/M +Darill/M +Dari/M +daringness/M +daring/PY +Darin/M +Dario/M +Darius/M +Darjeeling/M +darkener/M +darken/RDZG +dark/GTXYRDNSP +darkish +darkly/TR +darkness/MS +darkroom/SM +Darla/M +Darleen/M +Darlene/M +Darline/M +Darling/M +darlingness/M +Darlington/M +darling/YMSP +Darlleen/M +Dar/MNH +Darnall/M +darned/TR +Darnell/M +darner/M +darn/GRDZS +darning/M +Darn/M +Daron/M +DARPA/M +Darrelle/M +Darrell/M +Darrel/M +Darren/M +Darrick/M +Darrin/M +Darrow/M +Darryl/M +Darsey/M +Darsie/M +d'art +dartboard/SM +darter/M +Darth/M +Dartmouth/M +dart/MRDGZS +Darvon/M +Darwinian/S +Darwinism/MS +Darwinist/MS +Darwin/M +Darya/M +Daryle/M +Daryl/M +Daryn/M +Dasha/M +dashboard/SM +dasher/M +dash/GZSRD +dashiki/SM +dashing/Y +Dasie/M +Dasi/M +dastardliness/SM +dastardly/P +dastard/MYS +Dasya/M +DAT +database/DSMG +datafile +datagram/MS +data/M +Datamation/M +Datamedia/M +dataset/S +datedly +datedness +date/DRSMZGV +dated/U +dateless +dateline/DSMG +dater/M +Datha/M +dative/S +Datsun/M +datum/MS +dauber/M +daub/RDSGZ +Daugherty/M +daughter/MYS +Daumier/M +Daune/M +daunt/DSG +daunted/U +daunting/Y +dauntlessness/SM +dauntless/PY +dauphin/SM +Davao/M +Daveen/M +Dave/M +Daven/M +Davenport/M +davenport/MS +Daveta/M +Davey/M +Davida/M +Davidde/M +Davide/M +David/SM +Davidson/M +Davie/M +Davina/M +Davine/M +Davinich/M +Davin/M +Davis/M +Davita/M +davit/SM +Dav/MN +Davon/M +Davy/SM +dawdler/M +dawdle/ZGRSD +Dawes/M +Dawna/M +dawn/GSDM +Dawn/M +Dawson/M +daybed/S +daybreak/SM +daycare/S +daydreamer/M +daydream/RDMSZG +Dayle/M +daylight/GSDM +Day/M +Dayna/M +daysack +day/SM +daytime/SM +Dayton/M +dazed/PY +daze/DSG +dazzler/M +dazzle/ZGJRSD +dazzling/Y +db +DB +dbl +dB/M +DBMS +DC +DD +Ddene/M +DDS +DDT +DE +deacon/DSMG +deaconess/MS +deadbeat/SM +deadbolt/S +deadener/M +deadening/MY +deaden/RDG +deadhead/MS +deadline/MGDS +deadliness/SM +deadlock/MGDS +deadly/RPT +deadness/M +deadpanned +deadpanner +deadpanning +deadpan/S +dead/PTXYRN +deadwood/SM +deafening/MY +deafen/JGD +deafness/MS +deaf/TXPYRN +dealer/M +dealership/MS +dealing/M +deallocator +deal/RSGZJ +dealt +Deana/M +dean/DMG +Deandre/M +Deane/M +deanery/MS +Dean/M +Deanna/M +Deanne/M +Deann/M +deanship/SM +Dearborn/M +dearness/MS +dearth/M +dearths +dear/TYRHPS +deary/MS +deassign +deathbed/MS +deathblow/SM +deathless/Y +deathlike +deathly/TR +death/MY +deaths +deathtrap/SM +deathward +deathwatch/MS +debacle/SM +debarkation/SM +debark/G +debar/L +debarment/SM +debarring +debaser/M +debatable/U +debate/BMZ +debater/M +debauchedness/M +debauched/PY +debauchee/SM +debaucher/M +debauchery/SM +debauch/GDRS +Debbie/M +Debbi/M +Debby/M +Debee/M +debenture/MS +Debera/M +debilitate/NGXSD +debilitation/M +debility/MS +Debi/M +debit/DG +deb/MS +Deb/MS +debonairness/SM +debonair/PY +Deborah/M +Debora/M +Debor/M +debouch/DSG +Debra/M +debrief/GJ +debris/M +debtor/SM +debt/SM +Debussy/M +dbutante/SM +debut/MDG +decade/MS +decadency/S +decadent/YS +decaffeinate/DSG +decaf/S +decagon/MS +Decalogue/M +decal/SM +decamp/L +decampment/MS +decapitate/GSD +decapitator/SM +decathlon/SM +Decatur/M +decay/GRD +Decca/M +Deccan/M +decease/M +decedent/MS +deceitfulness/SM +deceitful/PY +deceit/SM +deceived/U +deceiver/M +deceives/U +deceive/ZGRSD +deceivingly +deceiving/U +decelerate/XNGSD +deceleration/M +decelerator/SM +December/SM +decency/ISM +decennial/SY +decent/TIYR +deception/SM +deceptiveness/SM +deceptive/YP +decertify/N +dechlorinate/N +decibel/MS +decidability/U +decidable/U +decidedness/M +decided/PY +decide/GRSDB +deciduousness/M +deciduous/YP +decile/SM +deciliter/SM +decimal/SYM +decimate/XNGDS +decimation/M +decimeter/MS +decipherable/IU +decipher/BRZG +decipherer/M +decisional +decisioned +decisioning +decision/ISM +decisive/IPY +decisiveness/MSI +deckchair +decker/M +Decker/M +deck/GRDMSJ +deckhand/S +decking/M +Deck/RM +declamation/SM +declamatory +declarable +declaration/MS +declaration's/A +declarative/SY +declarator/MS +declaratory +declare/AGSD +declared/U +declarer/MS +declension/SM +declination/MS +decliner/M +decline/ZGRSD +declivity/SM +Dec/M +DEC/M +DECNET +DECnet/M +deco +dcolletage/S +dcollet +decolletes +decolorising +decomposability/M +decomposable/IU +decompose/B +decompress/R +decongestant/S +deconstruction +deconvolution +decorated/AU +decorate/NGVDSX +decorates/A +decorating/A +decoration/ASM +decorativeness/M +decorative/YP +decorator/SM +decorousness/MS +decorousness's/I +decorous/PIY +decor/S +decorticate/GNDS +decortication/M +decorum/MS +decoupage/MGSD +decouple/G +decoy/M +decrease +decreasing/Y +decreeing +decree/RSM +decremental +decrement/DMGS +decrepit +decrepitude/SM +decriminalization/S +decriminalize/DS +decry/G +decrypt/GD +decryption +DECstation/M +DECsystem/M +DECtape/M +decustomised +Dedekind/M +Dede/M +dedicate/AGDS +dedicated/Y +dedication/MS +dedicative +dedicator/MS +dedicatory +Dedie/M +Dedra/M +deduce/RSDG +deducible +deductibility/M +deductible/S +deduction/SM +deductive/Y +deduct/VG +Deeanne/M +Deeann/M +deeded +Deedee/M +deeding +deed/IS +deed's +deejay/MDSG +Dee/M +deem/ADGS +deemphasis +Deena/M +deepen/DG +deepish +deepness/MS +deep/PTXSYRN +Deerdre/M +Deere/M +deerskin/MS +deer/SM +deerstalker/SM +deerstalking/M +Deeyn/M +deface/LZ +defacement/SM +defaecate +defalcate/NGXSD +defalcation/M +defamation/SM +defamatory +defamer/M +defame/ZR +defaulter/M +default/ZR +defeated/U +defeater/M +defeatism/SM +defeatist/SM +defeat/ZGD +defecate/DSNGX +defecation/M +defection/SM +defectiveness/MS +defective/PYS +defect/MDSVG +defector/MS +defendant/SM +defended/U +defenestrate/GSD +defenselessness/MS +defenseless/PY +defenses/U +defense/VGSDM +defensibility/M +defensible/I +defensibly/I +defensiveness/MS +defensive/PSY +deference/MS +deferential/Y +deferent/S +deferrable +deferral/SM +deferred +deferrer/MS +deferring +deffer +defiance/MS +defiant/Y +defibrillator/M +deficiency/MS +deficient/SY +deficit/MS +defier/M +defile/L +defilement/MS +definable/UI +definably/I +define/AGDRS +defined/U +definer/SM +definite/IPY +definiteness/IMS +definitional +definition/ASM +definitiveness/M +definitive/SYP +defis +deflate/XNGRSDB +deflationary +deflation/M +deflect/DSGV +deflected/U +deflection/MS +deflector/MS +defocus +defocussing +Defoe/M +defog +defogger/S +defoliant/SM +defoliator/SM +deformational +deform/B +deformed/U +deformity/SM +defrauder/M +defraud/ZGDR +defrayal/SM +defroster/M +defrost/RZ +deftness/MS +deft/TYRP +defunct/S +defying/Y +defy/RDG +def/Z +deg +Degas/M +degassing +degauss/GD +degeneracy/MS +degenerateness/M +degenerate/PY +degrade/B +degradedness/M +degraded/YP +degrading/Y +degrease +degree/SM +degum +Dehlia/M +dehumanize +dehydrator/MS +deicer/M +deice/ZR +deictic +Deidre/M +deification/M +deify/SDXGN +deign/DGS +Deimos/M +Deina/M +Deirdre/MS +deistic +deist/SM +Deity/M +deity/SM +deja +deject/DSG +dejectedness/M +dejected/PY +dejection/SM +Dejesus/M +DeKalb/M +DeKastere/M +Delacroix/M +Delacruz/M +Delainey/M +Dela/M +Delaney/M +Delano/M +Delawarean/SM +Delaware/MS +delay/D +delayer/G +Delbert/M +Delcina/M +Delcine/M +delectableness/M +delectable/SP +delectably +delectation/MS +delegable +Deleon/M +deleted/U +deleteriousness/M +deleterious/PY +delete/XBRSDNG +deletion/M +delfs +Delft/M +delft/MS +delftware/S +Delgado/M +Delhi/M +Delia/M +deliberateness/SM +deliberate/PVY +deliberativeness/M +deliberative/PY +Delibes/M +delicacy/IMS +delicate/IYP +delicatenesses +delicateness/IM +delicates +delicatessen/MS +deliciousness/MS +delicious/YSP +delicti +delightedness/M +delighted/YP +delightfulness/M +delightful/YP +Delilah/M +Delilahs +Delila/M +Delinda/M +delineate/SDXVNG +delineation/M +delinquency/MS +delinquent/SYM +deliquesce/GSD +deliquescent +deliriousness/MS +delirious/PY +delirium/SM +deli/SM +Delius/M +deliverables +deliverable/U +deliver/AGSD +deliverance/SM +delivered/U +deliverer/SM +delivery/AM +deliverymen/M +Della/M +Dell/M +dell/SM +Dellwood/M +Delly/M +Delmar/M +Delmarva/M +Delmer/M +Delmonico +Delmore/M +Delmor/M +Del/MY +Delora/M +Delores/M +Deloria/M +Deloris/M +Delphic +Delphi/M +Delphine/M +Delphinia/M +delphinium/SM +Delphinus/M +Delta/M +delta/MS +deltoid/SM +deluder/M +delude/RSDG +deluding/Y +deluge/SDG +delusional +delusion/SM +delusiveness/M +delusive/PY +deluxe +delve/GZSRD +delver/M +demagnify/N +demagogic +demagogue/GSDM +demagoguery/SM +demagogy/MS +demander/M +demand/GSRD +demandingly +demanding/U +demarcate/SDNGX +demarcation/M +Demavend/M +demean/GDS +demeanor/SM +dementedness/M +demented/YP +dementia/MS +Demerol/M +demesne/SM +Demeter/M +Demetra/M +Demetre/M +Demetria/M +Demetri/MS +Demetrius/M +demigod/MS +demijohn/MS +demimondaine/SM +demimonde/SM +demineralization/SM +Deming/M +demise/DMG +demit +demitasse/MS +demitted +demitting +Dem/MG +democracy/MS +Democratic +democratically/U +democratic/U +democratization/MS +democratize/DRSG +democratizes/U +Democrat/MS +democrat/SM +Democritus/M +dmod +demo/DMPG +demographer/MS +demographical/Y +demographic/S +demography/MS +demolisher/M +demolish/GSRD +demolition/MS +demonetization/S +demoniacal/Y +demoniac/S +demonic +demonology/M +demon/SM +demonstrable/I +demonstrableness/M +demonstrably/I +demonstrate/XDSNGV +demonstration/M +demonstrativenesses +demonstrativeness/UM +demonstratives +demonstrative/YUP +demonstrator/MS +demoralization/M +demoralizer/M +demoralizing/Y +DeMorgan/M +Demosthenes/M +demote/DGX +demotic/S +Demott/M +demount/B +Dempsey/M +demulcent/S +demultiplex +demureness/SM +demure/YP +demurral/MS +demurred +demurrer/MS +demurring +demur/RTS +demythologization/M +demythologize/R +den +Dena/M +dendrite/MS +Deneb/M +Denebola/M +Deneen/M +Dene/M +Deng/M +dengue/MS +deniable/U +denial/SM +Denice/M +denier/M +denigrate/VNGXSD +denigration/M +denim/SM +Denise/M +Deni/SM +denizen/SMDG +Den/M +De/NM +Denmark/M +Denna/M +denned +Dennet/M +Denney/M +Dennie/M +Denni/MS +denning +Dennison/M +Denny/M +denominate/V +denominational/Y +denote/B +denouement/MS +denounce/LZRSDG +denouncement/SM +denouncer/M +dense/FR +densely +denseness/SM +densitometer/MS +densitometric +densitometry/M +density/MS +dens/RT +dental/YS +dentifrice/SM +dentine's +dentin/SM +dent/ISGD +dentistry/MS +dentist/SM +dentition/MS +dent's +denture/IMS +denuclearize/GSD +denudation/SM +denude/DG +denuder/M +denunciate/VNGSDX +denunciation/M +Denver/M +denying/Y +Deny/M +Denys +Denyse/M +deny/SRDZG +deodorant/SM +deodorization/SM +deodorize/GZSRD +deodorizer/M +Deon/M +Deonne/M +deoxyribonucleic +depart/L +departmentalization/SM +departmentalize/DSG +departmental/Y +department/MS +departure/MS +dependability/MS +dependableness/M +dependable/P +dependably +Dependant/MS +depend/B +dependence/ISM +dependency/MS +dependent/IYS +dependent's +depicted/U +depicter/M +depiction/SM +depict/RDSG +depilatory/S +deplete/VGNSDX +depletion/M +deplorableness/M +deplorable/P +deplorably +deplorer/M +deplore/SRDBG +deploring/Y +deployable +deploy/AGDLS +deployment/SAM +depolarize +deponent/S +deportation/MS +deportee/SM +deport/LG +deportment/MS +depose +deposit/ADGS +depositary/M +deposition/A +depositor/SAM +depository/MS +depravedness/M +depraved/PY +deprave/GSRD +depraver/M +depravity/SM +deprecate/XSDNG +deprecating/Y +deprecation/M +deprecatory +depreciable +depreciate/XDSNGV +depreciating/Y +depreciation/M +depreciative/Y +depressant/S +depressible +depression/MS +depressive/YS +depressor/MS +depress/V +deprive/GSD +depth/M +depths +Dept/M +deputation/SM +depute/SDG +deputize/DSG +deputy/MS +dequeue +derail/L +drailleur/MS +derailment/MS +derange/L +derangement/MS +Derbyshire/M +derby/SM +Derby/SM +dereference/Z +Derek/M +dereliction/SM +derelict/S +Derick/M +deride/D +deriding/Y +derision/SM +derisiveness/MS +derisive/PY +derisory +derivable/U +derivate/XNV +derivation/M +derivativeness/M +derivative/SPYM +derive/B +derived/U +Derk/M +Der/M +dermal +dermatitides +dermatitis/MS +dermatological +dermatologist/MS +dermatology/MS +dermis/SM +Dermot/M +derogate/XDSNGV +derogation/M +derogatorily +derogatory +Derrek/M +Derrick/M +derrick/SMDG +Derrida/M +derrire/S +Derrik/M +Derril/M +derringer/SM +Derron/M +Derry/M +dervish/SM +Derward/M +Derwin/M +Des +desalinate/NGSDX +desalination/M +desalinization/MS +desalinize/GSD +desalt/G +descant/M +Descartes/M +descendant/SM +descended/FU +descendent's +descender/M +descending/F +descends/F +descend/ZGSDR +descent +describable/I +describe/ZB +description/MS +descriptiveness/MS +descriptive/SYP +descriptor/SM +descry/SDG +Desdemona/M +desecrater/M +desecrate/SRDGNX +desecration/M +deserter/M +desertification +desertion/MS +desert/ZGMRDS +deservedness/M +deserved/YU +deserve/J +deserving/Y +dshabill's +desiccant/S +desiccate/XNGSD +desiccation/M +desiccator/SM +desiderata +desideratum/M +designable +design/ADGS +designate/VNGSDX +designational +designation/M +designator/SM +designed/Y +designer/M +designing/U +Desi/M +desirabilia +desirability's +desirability/US +desirableness/SM +desirableness's/U +desirable/UPS +desirably/U +Desirae/M +desire/BR +desired/U +Desiree/M +desirer/M +Desiri/M +desirousness/M +desirous/PY +desist/DSG +desk/SM +desktop/S +Desmond/M +Desmund/M +desolateness/SM +desolate/PXDRSYNG +desolater/M +desolating/Y +desolation/M +desorption/M +despairer/M +despairing/Y +despair/SGDR +desperadoes +desperado/M +desperateness/SM +desperate/YNXP +desperation/M +despicable +despicably +despiser/M +despise/SRDG +despoil/L +despoilment/MS +despond +despondence/S +despondency/MS +despondent/Y +despotic +despotically +despotism/SM +dessert/SM +dessicate/DN +d'Estaing +destinate/NX +destination/M +destine/GSD +destiny/MS +destituteness/M +destitute/NXP +destitution/M +destroy/BZGDRS +destroyer/M +destructibility/SMI +destructible/I +destruction/SM +destructiveness/MS +destructive/YP +destructor/M +destruct/VGSD +desuetude/MS +desultorily +desultoriness/M +desultory/P +detachedness/M +detached/YP +detacher/M +detach/LSRDBG +detachment/SM +detailedness/M +detailed/YP +detainee/S +detainer/M +detain/LGRDS +detainment/MS +d'etat +detectability/U +detectable/U +detectably/U +detect/DBSVG +detected/U +detection/SM +detective/MS +detector/MS +dtente +detentes +detention/SM +detergency/M +detergent/SM +deteriorate/XDSNGV +deterioration/M +determent/SM +determinability/M +determinable/IP +determinableness/IM +determinacy/I +determinant/MS +determinateness/IM +determinate/PYIN +determination/IM +determinativeness/M +determinative/P +determinedly +determinedness/M +determined/U +determine/GASD +determiner/SM +determinism/MS +determinism's/I +deterministically +deterministic/I +deterred/U +deterrence/SM +deterrent/SMY +deterring +detersive/S +deter/SL +deters/V +detestableness/M +detestable/P +detestably +detestation/SM +dethrone/L +dethronement/SM +detonable +detonated/U +detonate/XDSNGV +detonation/M +detonator/MS +detour/G +detoxification/M +detoxify/NXGSD +detox/SDG +detract/GVD +detractive/Y +d'etre +detribalize/GSD +detrimental/SY +detriment/SM +detritus/M +Detroit/M +deuced/Y +deuce/SDGM +deus +deuterium/MS +deuteron/M +Deuteronomy/M +Deutsch/M +Deva/M +Devanagari/M +Devan/M +devastate/XVNGSD +devastating/Y +devastation/M +devastator/SM +develop/ALZSGDR +developed/U +developer/MA +developmental/Y +development/ASM +deviance/MS +deviancy/S +deviant/YMS +deviated/U +deviate/XSDGN +deviating/U +deviation/M +devilishness/MS +devilish/PY +devilment/SM +devilry/MS +devil/SLMDG +deviltry/MS +Devi/M +Devina/M +Devin/M +Devinne/M +deviousness/SM +devious/YP +devise/JR +deviser/M +Devland/M +Devlen/M +Devlin/M +Dev/M +devoice +devolution/MS +devolve/GSD +Devondra/M +Devonian +Devon/M +Devonna/M +Devonne/M +Devonshire/M +Devora/M +devoted/Y +devotee/MS +devote/XN +devotional/YS +devotion/M +devourer/M +devour/SRDZG +devoutness/MS +devout/PRYT +Devy/M +Dewain/M +dewar +Dewar/M +Dewayne/M +dewberry/MS +dewclaw/SM +dewdrop/MS +Dewey/M +Dewie/M +dewiness/MS +Dewitt/M +dewlap/MS +Dew/M +dew/MDGS +dewy/TPR +Dexedrine/M +dexes/I +Dex/M +dexter +dexterity/MS +Dexter/M +dexterousness/MS +dexterous/PY +dextrose/SM +DH +Dhaka +Dhaulagiri/M +dhoti/SM +dhow/MS +DI +diabase/M +diabetes/M +diabetic/S +diabolic +diabolicalness/M +diabolical/YP +diabolism/M +diachronic/P +diacritical/YS +diacritic/MS +diadem/GMDS +diaereses +diaeresis/M +Diaghilev/M +diagnometer/SM +diagnosable/U +diagnose/BGDS +diagnosed/U +diagnosis/M +diagnostically +diagnostician/SM +diagnostic/MS +diagnostics/M +diagonalize/GDSB +diagonal/YS +diagrammable +diagrammatic +diagrammaticality +diagrammatically +diagrammed +diagrammer/SM +diagramming +diagram/MS +Diahann/M +dialectal/Y +dialectical/Y +dialectic/MS +dialect/MS +dialed/A +dialer/M +dialing/M +dial/MRDSGZJ +dialogged +dialogging +dialog/MS +dialogue/DS +dials/A +dialysis/M +dialyzed/U +dialyzes +diam +diamagnetic +diameter/MS +diametric +diametrical/Y +diamondback/SM +diamond/GSMD +Diana/M +Diandra/M +Diane/M +Dianemarie/M +Dian/M +Dianna/M +Dianne/M +Diann/M +Diannne/M +diapason/MS +diaper/SGDM +diaphanousness/M +diaphanous/YP +diaphragmatic +diaphragm/SM +diarist/SM +Diarmid/M +diarrheal +diarrhea/MS +diary/MS +diaspora +Diaspora/SM +diastase/SM +diastole/MS +diastolic +diathermy/SM +diathesis/M +diatomic +diatom/SM +diatonic +diatribe/MS +Diaz's +dibble/SDMG +dibs +DiCaprio/M +dice/GDRS +dicer/M +dicey +dichloride/M +dichotomization/M +dichotomize/DSG +dichotomous/PY +dichotomy/SM +dicier +diciest +dicing/M +Dickensian/S +dickens/M +Dickens/M +dicker/DG +Dickerson/M +dickey/SM +dick/GZXRDMS! +Dickie/M +dickier +dickiest +Dickinson/M +Dickson/M +Dick/XM +Dicky/M +dicky's +dicotyledonous +dicotyledon/SM +dicta/M +Dictaphone/SM +dictate/SDNGX +dictation/M +dictatorialness/M +dictatorial/YP +dictator/MS +dictatorship/SM +dictionary/SM +diction/MS +dictum/M +didactically +didactic/S +didactics/M +did/AU +diddler/M +diddle/ZGRSD +Diderot/M +Didi/M +didn't +didoes +dido/M +Dido/M +didst +die/DS +Diefenbaker/M +Diego/M +dieing +dielectric/MS +diem +Diem/M +Diena/M +Dierdre/M +diereses +dieresis/M +diesel/GMDS +Diesel's +dies's +dies/U +dietary/S +dieter/M +Dieter/M +dietetic/S +dietetics/M +diethylaminoethyl +diethylstilbestrol/M +dietitian/MS +diet/RDGZSM +Dietrich/M +Dietz/M +difference/DSGM +difference's/I +differences/I +differentiability +differentiable +differential/SMY +differentiated/U +differentiate/XSDNG +differentiation/M +differentiator/SM +differentness +different/YI +differ/SZGRD +difficile +difficult/Y +difficulty/SM +diffidence/MS +diffident/Y +diffract/GSD +diffraction/SM +diffractometer/SM +diffuseness/MS +diffuse/PRSDZYVXNG +diffuser/M +diffusible +diffusional +diffusion/M +diffusiveness/M +diffusive/YP +diffusivity/M +digerati +digested/IU +digester/M +digestibility/MS +digestible/I +digestifs +digestion/ISM +digestive/YSP +digest/RDVGS +digger/MS +digging/S +digitalis/M +digitalization/MS +digitalized +digitalizes +digitalizing +digital/SY +digitization/M +digitizer/M +digitize/ZGDRS +digit/SM +dignified/U +dignify/DSG +dignitary/SM +dignity/ISM +digram +digraph/M +digraphs +digress/GVDS +digression/SM +digressiveness/M +digressive/PY +dig/TS +dihedral +Dijkstra/M +Dijon/M +dike/DRSMG +diker/M +diktat/SM +Dilan/M +dilapidate/XGNSD +dilapidation/M +dilatation/SM +dilated/YP +dilate/XVNGSD +dilation/M +dilatoriness/M +dilator/SM +dilatory/P +Dilbert/M +dilemma/MS +dilettante/MS +dilettantish +dilettantism/MS +diligence/SM +diligentness/M +diligent/YP +dilithium +Dillard/M +Dillie/M +Dillinger/M +dilling/R +dillis +Dill/M +Dillon/M +dill/SGMD +dillydally/GSD +Dilly/M +dilly/SM +dilogarithm +diluent +diluted/U +diluteness/M +dilute/RSDPXYVNG +dilution/M +Di/M +DiMaggio/M +dimensionality/M +dimensional/Y +dimensionless +dimension/MDGS +dimer/M +dime/SM +dimethylglyoxime +dimethyl/M +diminished/U +diminish/SDGBJ +diminuendo/SM +diminution/SM +diminutiveness/M +diminutive/SYP +Dimitri/M +Dimitry/M +dimity/MS +dimmed/U +dimmer/MS +dimmest +dimming +dimness/SM +dimorphism/M +dimple/MGSD +dimply/RT +dim/RYPZS +dimwit/MS +dimwitted +Dinah/M +Dina/M +dinar/SM +diner/M +dine/S +dinette/MS +dingbat/MS +ding/GD +dinghy/SM +dingily +dinginess/SM +dingle/MS +dingoes +dingo/MS +dingus/SM +dingy/PRST +dinky/RST +din/MDRZGS +dinned +dinner/SM +dinnertime/S +dinnerware/MS +Dinnie/M +dinning +Dinny/M +Dino/M +dinosaur/MS +dint/SGMD +diocesan/S +diocese/SM +Diocletian/M +diode/SM +Diogenes/M +Dione/M +Dionisio/M +Dionis/M +Dion/M +Dionne/M +Dionysian +Dionysus/M +Diophantine/M +diopter/MS +diorama/SM +Dior/M +dioxalate +dioxide/MS +dioxin/S +diphtheria/SM +diphthong/SM +diplexers +diploid/S +diplomacy/SM +diploma/SMDG +diplomata +diplomatically +diplomatic/S +diplomatics/M +diplomatist/SM +diplomat/MS +dipodic +dipody/M +dipole/MS +dipped +Dipper/M +dipper/SM +dipping/S +dippy/TR +dip/S +dipsomaniac/MS +dipsomania/SM +dipstick/MS +dipterous +diptych/M +diptychs +Dir +Dirac/M +directed/IUA +directionality +directional/SY +direction/MIS +directions/A +directive/SM +directivity/M +directly/I +directness/ISM +director/AMS +directorate/SM +directorial +directorship/SM +directory/SM +direct/RDYPTSVG +directrix/MS +directs/IA +direful/Y +direness/M +dire/YTRP +dirge/GSDM +Dirichlet/M +dirigible/S +dirk/GDMS +Dirk/M +dirndl/MS +dirtily +dirtiness/SM +dirt/MS +dirty/GPRSDT +Dis +disable/LZGD +disablement/MS +disabler/M +disabuse +disadvantaged/P +disagreeable/S +disallow/D +disambiguate/DSGNX +disappointed/Y +disappointing/Y +disarming/Y +disarrange/L +disastrous/Y +disband/L +disbandment/SM +disbar/L +disbarment/MS +disbarring +disbelieving/Y +disbursal/S +disburse/GDRSL +disbursement/MS +disburser/M +discerner/M +discernibility +discernible/I +discernibly +discerning/Y +discernment/MS +discern/SDRGL +disc/GDM +discharged/U +disciple/DSMG +discipleship/SM +disciplinarian/SM +disciplinary +disciplined/U +discipline/IDM +discipliner/M +disciplines +disciplining +disclosed/U +discography/MS +discolored/MP +discoloreds/U +discolor/G +discombobulate/SDGNX +discomfit/DG +discomfiture/MS +disco/MG +discommode/DG +disconcerting/Y +disconnectedness/S +disconnected/P +disconnecter/M +disconnect/R +disconsolate/YN +discordance/SM +discordant/Y +discord/G +discorporate/D +discotheque/MS +discount/B +discourage/LGDR +discouragement/MS +discouraging/Y +discoverable/I +discover/ADGS +discovered/U +discoverer/S +discovery/SAM +discreetly/I +discreetness's/I +discreetness/SM +discreet/TRYP +discrepancy/SM +discrepant/Y +discreteness/SM +discrete/YPNX +discretionary +discretion/IMS +discretization +discretized +discriminable +discriminant/MS +discriminated/U +discriminate/SDVNGX +discriminating/YI +discrimination/MI +discriminator/MS +discriminatory +discursiveness/S +discussant/MS +discussed/UA +discusser/M +discussion/SM +discus/SM +disdainfulness/M +disdainful/YP +disdain/MGSD +disease/G +disembowelment/SM +disembowel/SLGD +disengage/L +disfigure/L +disfigurement/MS +disfranchise/L +disfranchisement/MS +disgorge +disgrace/R +disgracer/M +disgruntle/DSLG +disgruntlement/MS +disguised/UY +disguise/R +disguiser/M +disgust +disgusted/Y +disgustful/Y +disgusting/Y +dishabille/SM +disharmonious +dishcloth/M +dishcloths +dishevel/LDGS +dishevelment/MS +dish/GD +dishonest +dishonored/U +dishpan/MS +dishrag/SM +dishtowel/SM +dishwasher/MS +dishwater/SM +disillusion/LGD +disillusionment/SM +disinfectant/MS +disinherit +disinterestedness/SM +disinterested/P +disinvest/L +disjoin +disjointedness/S +disjunctive/YS +disjunct/VS +disk/D +diskette/S +dislike/G +dislodge/LG +dislodgement/M +dismalness/M +dismal/PSTRY +dismantle/L +dismantlement/SM +dismay/D +dismayed/U +dismaying/Y +dis/MB +dismember/LG +dismemberment/MS +dismissive/Y +dismiss/RZ +Disneyland/M +Disney/M +disoblige/G +disorderedness/M +disordered/YP +disorderliness/M +disorderly/P +disorder/Y +disorganize +disorganized/U +disparagement/MS +disparager/M +disparage/RSDLG +disparaging/Y +disparateness/M +disparate/PSY +dispatch/Z +dispelled +dispelling +dispel/S +dispensable/I +dispensary/MS +dispensate/NX +dispensation/M +dispenser/M +dispense/ZGDRSB +dispersal/MS +dispersant/M +dispersed/Y +disperser/M +disperse/XDRSZLNGV +dispersible +dispersion/M +dispersiveness/M +dispersive/PY +dispirit/DSG +displace/L +display/AGDS +displayed/U +displeased/Y +displease/G +displeasure +disport +disposable/S +disposal/SM +dispose/IGSD +dispositional +disposition/ISM +disproportional +disproportionate/N +disproportionation/M +disprove/B +disputable/I +disputably/I +disputant/SM +disputation/SM +disputatious/Y +disputed/U +disputer/M +dispute/ZBGSRD +disquieting/Y +disquiet/M +disquisition/SM +Disraeli/M +disregardful +disrepair/M +disreputableness/M +disreputable/P +disrepute/M +disrespect +disrupted/U +disrupter/M +disrupt/GVDRS +disruption/MS +disruptive/YP +disruptor/M +dissatisfy +dissect/DG +dissed +dissembler/M +dissemble/ZGRSD +disseminate/XGNSD +dissemination/M +dissension/SM +dissenter/M +dissent/ZGSDR +dissertation/SM +disservice +disses +dissever +dissidence/SM +dissident/MS +dissimilar/S +dissing +dissipatedly +dissipatedness/M +dissipated/U +dissipater/M +dissipate/XRSDVNG +dissipation/M +dissociable/I +dissociate/DSXNGV +dissociated/U +dissociation/M +dissociative/Y +dissoluble/I +dissoluteness/SM +dissolute/PY +dissolve/ASDG +dissolved/U +dissonance/SM +dissonant/Y +dissuade/GDRS +dissuader/M +dissuasive +dist +distaff/SM +distal/Y +distance/DSMG +distantness/M +distant/YP +distaste +distemper +distend +distension +distention/SM +distillate/XNMS +distillation/M +distillery/MS +distincter +distinctest +distinction/MS +distinctiveness/MS +distinctive/YP +distinct/IYVP +distinctness/MSI +distinguishable/I +distinguishably/I +distinguish/BDRSG +distinguished/U +distinguisher/M +distort/BGDR +distorted/U +distorter/M +distortion/MS +distract/DG +distractedness/M +distracted/YP +distracting/Y +distrait +distraught/Y +distress +distressful +distressing/Y +distribute/ADXSVNGB +distributed/U +distributer +distributional +distribution/AM +distributiveness/M +distributive/SPY +distributivity +distributorship/M +distributor/SM +district/GSAD +district's +distrust/G +disturbance/SM +disturbed/U +disturber/M +disturbing/Y +disturb/ZGDRS +disulfide/M +disuse/M +disyllable/M +Dita/M +ditcher/M +ditch/MRSDG +dither/RDZSG +ditsy/TR +ditto/DMGS +ditty/SDGM +Ditzel/M +ditz/S +diuresis/M +diuretic/S +diurnal/SY +divalent/S +diva/MS +divan/SM +dived/M +divergence/SM +divergent/Y +diverge/SDG +diver/M +diverseness/MS +diverse/XYNP +diversification/M +diversifier/M +diversify/GSRDNX +diversionary +diversion/M +diversity/SM +divert/GSD +diverticulitis/SM +divertimento/M +dive/S +divestiture/MS +divest/LDGS +divestment/S +dividable +divide/AGDS +divided/U +dividend/MS +divider/MS +divination/SM +diviner/M +divine/RSDTZYG +divinity/MS +divisibility/IMS +divisible/I +divisional +division/SM +divisiveness/MS +divisive/PY +divisor/SM +divorce/MS +divorce/GSDLM +divorcement/MS +divot/MS +div/TZGJDRS +divulge/GSD +divvy/GSDM +Dixiecrat/MS +dixieland +Dixieland/MS +Dixie/M +Dix/M +Dixon/M +dizzily +dizziness/SM +dizzying/Y +dizzy/PGRSDT +DJ +Djakarta's +djellabah's +djellaba/S +d/JGVX +Djibouti/M +DMD +Dmitri/M +DMZ +DNA +Dnepropetrovsk/M +Dnepr's +Dnieper's +Dniester/M +Dniren/M +DOA +doable +DOB +Dobbin/M +dobbin/MS +Doberman +Dobro/M +docent/SM +docile/Y +docility/MS +docker/M +docket/GSMD +dock/GZSRDM +dockland/MS +dockside/M +dockworker/S +dockyard/SM +doc/MS +Doctor +doctoral +doctorate/SM +doctor/GSDM +Doctorow/M +doctrinaire/S +doctrinal/Y +doctrine/SM +docudrama/S +documentary/MS +documentation/MS +documented/U +document/RDMZGS +DOD +dodder/DGS +dodecahedra +dodecahedral +dodecahedron/M +Dode/M +dodge/GZSRD +Dodge/M +dodgem/S +dodger/M +Dodgson/M +Dodie/M +Dodi/M +Dodington/M +Dodoma/M +dodo/SM +Dodson/M +Dody/M +DOE +Doe/M +doe/MS +doer/MU +does/AU +doeskin/MS +doesn't +d'oeuvre +doff/SGD +dogcart/SM +dogcatcher/MS +dogeared +Doge/M +doge/SM +dogfight/GMS +dogfish/SM +dogfought +doggedness/SM +dogged/PY +doggerel/SM +dogging +doggone/RSDTG +doggy/SRMT +doghouse/SM +dogie/SM +doglegged +doglegging +dogleg/SM +dogma/MS +dogmatically/U +dogmatic/S +dogmatics/M +dogmatism/SM +dogmatist/SM +dogsbody/M +dog/SM +dogtooth/M +Dogtown/M +dogtrot/MS +dogtrotted +dogtrotting +dogwood/SM +dogy's +Doha/M +doh's +doily/SM +doing/MU +Dolby/SM +doldrum/S +doldrums/M +doled/F +dolefuller +dolefullest +dolefulness/MS +doleful/PY +Dole/M +dole/MGDS +doles/F +Dolf/M +doling/F +dollar/SM +Dolley/M +Dollie/M +Dolli/M +Doll/M +doll/MDGS +dollop/GSMD +Dolly/M +dolly/SDMG +dolmen/MS +dolomite/SM +dolomitic +Dolores/M +Dolorita/SM +dolorous/Y +dolor/SM +dolphin/SM +Dolph/M +doltishness/SM +doltish/YP +dolt/MS +domain/MS +dome/DSMG +Domenic/M +Domenico/M +Domeniga/M +Domesday/M +domestically +domesticate/DSXGN +domesticated/U +domestication/M +domesticity/MS +domestic/S +domicile/SDMG +domiciliary +dominance/MS +dominant/YS +dominate/VNGXSD +domination/M +dominator/M +dominatrices +dominatrix +domineer/DSG +domineeringness/M +domineering/YP +Dominga/M +Domingo/M +Dominguez/M +Dominica/M +Dominican/MS +Dominick/M +Dominic/M +Dominik/M +Domini/M +dominion/MS +Dominique/M +dominoes +domino/M +Domitian/M +Dom/M +Donahue/M +Donald/M +Donaldson/M +Donall/M +Donal/M +Donalt/M +Dona/M +dona/MS +Donatello/M +donate/XVGNSD +donation/M +donative/M +Donaugh/M +Donavon/M +done/AUF +Donella/M +Donelle/M +Donetsk/M +Donetta/M +dong/GDMS +dongle/S +Donia/M +Donica/M +Donielle/M +Donizetti/M +donkey/MS +Donna/M +Donnamarie/M +donned +Donnell/M +Donnelly/M +Donne/M +Donner/M +Donnie/M +Donni/M +donning +donnishness/M +donnish/YP +Donn/RM +donnybrook/MS +Donny/M +donor/MS +Donovan/M +don/S +Don/SM +don't +donut/MS +donutted +donutting +doodad/MS +doodlebug/MS +doodler/M +doodle/SRDZG +doohickey/MS +Dooley/M +Doolittle/M +doom/MDGS +doomsday/SM +Doonesbury/M +doorbell/SM +door/GDMS +doorhandles +doorkeeper/M +doorkeep/RZ +doorknob/SM +doorman/M +doormat/SM +doormen +doornail/M +doorplate/SM +doors/I +doorstep/MS +doorstepped +doorstepping +doorstop/MS +doorway/MS +dooryard/SM +dopamine +dopant/M +dopa/SM +dope/DRSMZG +doper/M +dopey +dopier +dopiest +dopiness/S +Doppler/M +Dorado/M +Doralia/M +Doralin/M +Doralyn/M +Doralynne/M +Doralynn/M +Dora/M +Dorcas +Dorchester/M +Doreen/M +Dorelia/M +Dorella/M +Dorelle/M +Dor/M +Dorena/M +Dorene/M +Doretta/M +Dorette/M +Dorey/M +Doria/M +Dorian/M +Doric +Dorice/M +Dorie/M +Dori/MS +Dorine/M +Dorisa/M +Dorise/M +Dorita/M +dork/S +dorky/RT +dormancy/MS +dormant/S +dormer/M +dormice +dormitory/SM +dorm/MRZS +dormouse/M +Dorolice/M +Dorolisa/M +Doro/M +Dorotea/M +Doroteya/M +Dorothea/M +Dorothee/M +Dorothy/M +Dorree/M +Dorrie/M +Dorri/SM +Dorry/M +dorsal/YS +Dorsey/M +Dorthea/M +Dorthy/M +Dortmund/M +Dory/M +dory/SM +DOS +dosage/SM +dose/M +dos/GDS +Dosi/M +dosimeter/MS +dosimetry/M +dossier/MS +dost +Dostoevsky/M +DOT +dotage/SM +dotard/MS +doter/M +dote/S +Doti/M +doting/Y +Dot/M +dot/MDRSJZG +Dotson/M +dotted +Dottie/M +Dotti/M +dottiness/M +dotting +Dotty/M +dotty/PRT +do/TZRHGJ +Douala/M +Douay/M +Doubleday/M +doubled/UA +double/GPSRDZ +doubleheader/MS +doubleness/M +doubler/M +doubles/M +doublespeak/S +doublethink/M +doublet/MS +doubleton/M +doubling/A +doubloon/MS +doubly +doubt/AGSDMB +doubted/U +doubter/SM +doubtfulness/SM +doubtful/YP +doubting/Y +doubtlessness/M +doubtless/YP +douche/GSDM +Dougherty/M +dough/M +doughs +doughty/RT +doughy/RT +Dougie/M +Douglas/M +Douglass +Doug/M +Dougy/M +dourness/MS +Douro/M +dour/TYRP +douser/M +douse/SRDG +dovecote/MS +Dover/M +dove/RSM +dovetail/GSDM +dovish +Dov/MR +dowager/SM +dowdily +dowdiness/MS +dowdy/TPSR +dowel/GMDS +dower/GDMS +Dow/M +downbeat/SM +downcast/S +downdraft/M +downer/M +Downey/M +downfall/NMS +downgrade/GSD +down/GZSRD +downheartedness/MS +downhearted/PY +downhill/RS +downland +download/DGS +downpipes +downplay/GDS +downpour/MS +downrange +downrightness/M +downright/YP +downriver +Downs +downscale/GSD +downside/S +downsize/DSG +downslope +downspout/SM +downstage/S +downstairs +downstate/SR +downstream +downswing/MS +downtime/SM +downtowner/M +downtown/MRS +downtrend/M +downtrodden +downturn/MS +downwardness/M +downward/YPS +downwind +downy/RT +dowry/SM +dowse/GZSRD +dowser/M +doxology/MS +doyenne/SM +doyen/SM +Doyle/M +Doy/M +doze +dozen/GHD +dozenths +dozer/M +doz/XGNDRS +dozy +DP +DPs +dpt +DPT +drabbed +drabber +drabbest +drabbing +drabness/MS +drab/YSP +drachma/MS +Draco/M +draconian +Draconian +Dracula/M +draft/AMDGS +draftee/SM +drafter/MS +draftily +draftiness/SM +drafting/S +draftsman/M +draftsmanship/SM +draftsmen +draftsperson +draftswoman +draftswomen +drafty/PTR +dragged +dragger/M +dragging/Y +draggy/RT +drag/MS +dragnet/MS +dragonfly/SM +dragonhead/M +dragon/SM +dragoon/DMGS +drainage/MS +drainboard/SM +drained/U +drainer/M +drainpipe/MS +drain/SZGRDM +Drake/M +drake/SM +Dramamine/MS +drama/SM +dramatically/U +dramatical/Y +dramatic/S +dramatics/M +dramatist/MS +dramatization/MS +dramatized/U +dramatizer/M +dramatize/SRDZG +dramaturgy/M +Drambuie/M +drammed +dramming +dram/MS +drank +Drano/M +draper/M +drapery/MS +drape/SRDGZ +drastic +drastically +drat/S +dratted +dratting +Dravidian/M +drawable +draw/ASG +drawback/MS +drawbridge/SM +drawer/SM +drawing/SM +drawler/M +drawling/Y +drawl/RDSG +drawly +drawn/AI +drawnly +drawnness +drawstring/MS +dray/SMDG +dreadfulness/SM +dreadful/YPS +dreadlocks +dreadnought/SM +dread/SRDG +dreamboat/SM +dreamed/U +dreamer/M +dreamily +dreaminess/SM +dreaming/Y +dreamland/SM +dreamlessness/M +dreamless/PY +dreamlike +dream/SMRDZG +dreamworld/S +dreamy/PTR +drearily +dreariness/SM +drear/S +dreary/TRSP +Dreddy/M +dredge/MZGSRD +dredger/M +Dredi/M +dreg/MS +Dreiser/M +Dre/M +drencher/M +drench/GDRS +Dresden/M +dress/ADRSG +dressage/MS +dressed/U +dresser/MS +dresser's/A +dresses/U +dressiness/SM +dressing/MS +dressmaker/MS +dressmaking/SM +dressy/PTR +drew/A +Drew/M +Drexel/M +Dreyfus/M +Dreyfuss +dribble/DRSGZ +dribbler/M +driblet/SM +drib/SM +dried/U +drier/M +drifter/M +drifting/Y +drift/RDZSG +driftwood/SM +driller/M +drilling/M +drillmaster/SM +drill/MRDZGS +drinkable/S +drink/BRSZG +drinker/M +dripped +dripping/MS +drippy/RT +drip/SM +driveler/M +drivel/GZDRS +driven/P +driver/M +drive/SRBGZJ +driveway/MS +drizzle/DSGM +drizzling/Y +drizzly/TR +Dr/M +drogue/MS +drollery/SM +drollness/MS +droll/RDSPTG +drolly +dromedary/MS +Drona/M +drone/SRDGM +droning/Y +drool/GSRD +droopiness/MS +drooping/Y +droop/SGD +droopy/PRT +drophead +dropkick/S +droplet/SM +dropout/MS +dropped +dropper/SM +dropping/MS +dropsical +drop/SM +dropsy/MS +drosophila/M +dross/SM +drought/SM +drover/M +drove/SRDGZ +drowner/M +drown/RDSJG +drowse/SDG +drowsily +drowsiness/SM +drowsy/PTR +drubbed +drubber/MS +drubbing/SM +drub/S +Drucie/M +Drucill/M +Druci/M +Drucy/M +drudge/MGSRD +drudger/M +drudgery/SM +drudging/Y +Drud/M +drugged +druggie/SRT +drugging +druggist/SM +Drugi/M +drugless +drug/SM +drugstore/SM +druidism/MS +druid/MS +Druid's +Dru/M +drumbeat/SGM +drumhead/M +drumlin/MS +drummed +drummer/SM +drumming +Drummond/M +drum/SM +drumstick/SM +drunkard/SM +drunkenness/SM +drunken/YP +drunk/SRNYMT +drupe/SM +Drury/M +Drusie/M +Drusilla/M +Drusi/M +Drusy/M +druthers +dryad/MS +Dryden/M +dryer/MS +dry/GYDRSTZ +dryish +dryness/SM +drys +drystone +drywall/GSD +D's +d's/A +Dshubba/M +DST +DTP +dualism/MS +dualistic +dualist/M +duality/MS +dual/YS +Duane/M +Dubai/M +dubbed +dubber/S +dubbing/M +dubbin/MS +Dubcek/M +Dubhe/M +dubiety/MS +dubiousness/SM +dubious/YP +Dublin/M +Dubrovnik/M +dub/S +Dubuque/M +ducal +ducat/SM +duce/CAIKF +duce's +Duchamp/M +duchess/MS +duchy/SM +duckbill/SM +ducker/M +duck/GSRDM +duckling/SM +duckpins +duckpond +duckweed/MS +ducky/RSMT +ducted/CFI +ductile/I +ductility/SM +ducting/F +duct/KMSF +ductless +duct's/A +ducts/CI +ductwork/M +dudder +dude/MS +dudgeon/SM +dud/GMDS +Dudley/M +Dud/M +duelist/MS +duel/MRDGZSJ +dueness/M +duenna/MS +due/PMS +duet/MS +duetted +duetting +duffel/M +duffer/M +duff/GZSRDM +Duffie/M +Duff/M +Duffy/M +Dugald/M +dugout/SM +dug/S +duh +DUI +Duisburg/M +dukedom/SM +duke/DSMG +Duke/M +Dukey/M +Dukie/M +Duky/M +Dulcea/M +Dulce/M +dulcet/SY +Dulcia/M +Dulciana/M +Dulcie/M +dulcify +Dulci/M +dulcimer/MS +Dulcinea/M +Dulcine/M +Dulcy/M +dullard/MS +Dulles/M +dullness/MS +dull/SRDPGT +dully +dulness's +Dulsea/M +Duluth/M +duly/U +Du/M +Dumas +dumbbell/MS +dumbfound/GSDR +dumbness/MS +Dumbo/M +dumb/PSGTYRD +dumbstruck +dumbwaiter/SM +dumdum/MS +dummy/SDMG +Dumont/M +dumper/UM +dumpiness/MS +dumpling/MS +dump/SGZRD +dumpster/S +Dumpster/S +Dumpty/M +dumpy/PRST +Dunant/M +Dunbar/M +Duncan/M +dunce/MS +Dunc/M +Dundee/M +dunderhead/MS +Dunedin/M +dune/SM +dungaree/SM +dungeon/GSMD +dunghill/MS +dung/SGDM +Dunham/M +dunker/M +dunk/GSRD +Dunkirk/M +Dunlap/M +Dun/M +dunned +Dunne/M +dunner +dunnest +dunning +Dunn/M +dunno/M +dun/S +Dunstan/M +duodecimal/S +duodena +duodenal +duodenum/M +duologue/M +duo/MS +duopolist +duopoly/M +dupe/NGDRSMZ +duper/M +dupion/M +duple +duplexer/M +duplex/MSRDG +duplicability/M +duplicable +duplicate/ADSGNX +duplication/AM +duplicative +duplicator/MS +duplicitous +duplicity/SM +Dupont/MS +DuPont/MS +durability/MS +durableness/M +durable/PS +durably +Duracell/M +durance/SM +Durand/M +Duran/M +Durante/M +Durant/M +durational +duration/MS +Durban/M +Drer/M +duress/SM +Durex/M +Durham/MS +during +Durkee/M +Durkheim/M +Dur/M +Durocher/M +durst +durum/MS +Durward/M +Duse/M +Dusenberg/M +Dusenbury/M +Dushanbe/M +dusk/GDMS +duskiness/MS +dusky/RPT +Dsseldorf +dustbin/MS +dustcart/M +dustcover +duster/M +dustily +dustiness/MS +dusting/M +Dustin/M +dustless +dustman/M +dustmen +dust/MRDGZS +dustpan/SM +Dusty/M +dusty/RPT +Dutch/M +Dutchman/M +Dutchmen +dutch/MS +Dutchwoman +Dutchwomen +duteous/Y +dutiable +dutifulness/S +dutiful/UPY +duty/SM +Duvalier/M +duvet/SM +duxes +Dvina/M +Dvork/M +Dwain/M +dwarfish +dwarfism/MS +dwarf/MTGSPRD +Dwayne/M +dweeb/S +dweller/SM +dwell/IGS +dwelling/MS +dwelt/I +DWI +Dwight/M +dwindle/GSD +dyadic +dyad/MS +Dyana/M +Dyane/M +Dyan/M +Dyanna/M +Dyanne/M +Dyann/M +dybbukim +dybbuk/SM +dyed/A +dyeing/M +dye/JDRSMZG +dyer/M +Dyer/M +dyes/A +dyestuff/SM +dying/UA +Dyke/M +dyke's +Dylan/M +Dy/M +Dynah/M +Dyna/M +dynamical/Y +dynamic/S +dynamics/M +dynamism/SM +dynamiter/M +dynamite/RSDZMG +dynamized +dynamo/MS +dynastic +dynasty/MS +dyne/M +dysentery/SM +dysfunctional +dysfunction/MS +dyslectic/S +dyslexia/MS +dyslexically +dyslexic/S +dyspepsia/MS +dyspeptic/S +dysprosium/MS +dystopia/M +dystrophy/M +dz +Dzerzhinsky/M +E +ea +each +Eachelle/M +Eada/M +Eadie/M +Eadith/M +Eadmund/M +eagerness/MS +eager/TSPRYM +eagle/SDGM +eaglet/SM +Eakins/M +Ealasaid/M +Eal/M +Eamon/M +earache/SM +eardrum/SM +earful/MS +ear/GSMDYH +Earhart/M +earing/M +earldom/MS +Earle/M +Earlene/M +Earlie/M +Earline/M +earliness/SM +Earl/M +earl/MS +earlobe/S +Early/M +early/PRST +earmark/DGSJ +earmuff/SM +earned/U +earner/M +Earnestine/M +Earnest/M +earnestness/MS +earnest/PYS +earn/GRDZTSJ +earning/M +earphone/MS +earpieces +earplug/MS +Earp/M +earring/MS +earshot/MS +earsplitting +Eartha/M +earthbound +earthed/U +earthenware/MS +earthiness/SM +earthliness/M +earthling/MS +earthly/TPR +earth/MDNYG +earthmen +earthmover/M +earthmoving +earthquake/SDGM +earthshaking +earths/U +earthward/S +earthwork/MS +earthworm/MS +earthy/PTR +Earvin/M +earwax/MS +earwigged +earwigging +earwig/MS +eased/E +ease/LDRSMG +easel/MS +easement/MS +easer/M +ease's/EU +eases/UE +easies +easily/U +easiness/MSU +easing/M +eastbound +easterly/S +Easter/M +easterner/M +Easterner/M +easternmost +Eastern/RZ +eastern/ZR +easter/Y +east/GSMR +Easthampton/M +easting/M +Eastland/M +Eastman/M +eastward/S +Eastwick/M +Eastwood/M +East/ZSMR +easygoingness/M +easygoing/P +easy/PUTR +eatables +eatable/U +eaten/U +eater/M +eatery/MS +eating/M +Eaton/M +eat/SJZGNRB +eavesdropped +eavesdropper/MS +eavesdropping +eavesdrop/S +eave/SM +Eba/M +Ebba/M +ebb/DSG +EBCDIC +Ebeneezer/M +Ebeneser/M +Ebenezer/M +Eben/M +Eberhard/M +Eberto/M +Eb/MN +Ebola +Ebonee/M +Ebonics +Ebony/M +ebony/SM +Ebro/M +ebullience/SM +ebullient/Y +ebullition/SM +EC +eccentrically +eccentricity/SM +eccentric/MS +eccl +Eccles +Ecclesiastes/M +ecclesiastical/Y +ecclesiastic/MS +ECG +echelon/SGDM +echinoderm/SM +echo/DMG +echoed/A +echoes/A +echoic +echolocation/SM +clair/MS +clat/MS +eclectically +eclecticism/MS +eclectic/S +eclipse/MGSD +ecliptic/MS +eclogue/MS +ecocide/SM +ecol +Ecole/M +ecologic +ecological/Y +ecologist/MS +ecology/MS +Eco/M +econ +Econometrica/M +econometricians +econometric/S +econometrics/M +economical/YU +economic/S +economics/M +economist/MS +economization +economize/GZSRD +economizer/M +economizing/U +economy/MS +ecosystem/MS +ecru/SM +ecstasy/MS +Ecstasy/S +ecstatically +ecstatic/S +ectoplasm/M +Ecuadoran/S +Ecuadorean/S +Ecuadorian/S +Ecuador/M +ecumenical/Y +ecumenicism/SM +ecumenicist/MS +ecumenic/MS +ecumenics/M +ecumenism/SM +ecumenist/MS +eczema/MS +Eda/M +Edam/SM +Edan/M +ed/ASC +Edda/M +Eddie/M +Eddi/M +Edd/M +Eddy/M +eddy/SDMG +Edee/M +Edeline/M +edelweiss/MS +Ede/M +edema/SM +edematous +eden +Eden/M +Edgard/M +Edgardo/M +Edgar/M +edge/DRSMZGJ +edgeless +edger/M +Edgerton/M +Edgewater/M +edgewise +Edgewood/M +edgily +edginess/MS +edging/M +edgy/TRP +edibility/MS +edibleness/SM +edible/SP +edict/SM +Edie/M +edification/M +edifice/SM +edifier/M +edifying/U +edify/ZNXGRSD +Edik/M +Edi/MH +Edinburgh/M +Edin/M +Edison/M +editable +Edita/M +edited/IU +Editha/M +Edithe/M +Edith/M +edition/SM +editorialist/M +editorialize/DRSG +editorializer/M +editorial/YS +editor/MS +editorship/MS +edit/SADG +Ediva/M +Edlin/M +Edmond/M +Edmon/M +Edmonton/M +Edmund/M +Edna/M +Edouard/M +EDP +eds +Edsel/M +Edsger/M +EDT +Eduard/M +Eduardo/M +educability/SM +educable/S +educated/YP +educate/XASDGN +educationalists +educational/Y +education/AM +educationists +educative +educator/MS +educ/DBG +educe/S +eduction/M +Eduino/M +edutainment/S +Edvard/M +Edwardian +Edwardo/M +Edward/SM +Edwina/M +Edwin/M +Ed/XMN +Edy/M +Edythe/M +Edyth/M +EEC +EEG +eek/S +eelgrass/M +eel/MS +e'en +EEO +EEOC +e'er +eerie/RT +eerily +eeriness/MS +Eeyore/M +effaceable/I +effacement/MS +effacer/M +efface/SRDLG +effectiveness/ISM +effectives +effective/YIP +effector/MS +effect/SMDGV +effectual/IYP +effectualness/MI +effectuate/SDGN +effectuation/M +effeminacy/MS +effeminate/SY +effendi/MS +efferent/SY +effervesce/GSD +effervescence/SM +effervescent/Y +effeteness/SM +effete/YP +efficacious/IPY +efficaciousness/MI +efficacy/IMS +efficiency/MIS +efficient/ISY +Effie/M +effigy/SM +effloresce +efflorescence/SM +efflorescent +effluence/SM +effluent/MS +effluvia +effluvium/M +effluxion +efflux/M +effortlessness/SM +effortless/PY +effort/MS +effrontery/MS +effulgence/SM +effulgent +effuse/XSDVGN +effusion/M +effusiveness/MS +effusive/YP +EFL +e/FMDS +Efrain/M +Efrem/M +Efren/M +EFT +egad +egalitarian/I +egalitarianism/MS +egalitarians +EGA/M +Egan/M +Egbert/M +Egerton/M +eggbeater/SM +eggcup/MS +egger/M +egg/GMDRS +eggheaded/P +egghead/SDM +eggnog/SM +eggplant/MS +eggshell/SM +egis's +eglantine/MS +egocentrically +egocentricity/SM +egocentric/S +egoism/SM +egoistic +egoistical/Y +egoist/SM +egomaniac/MS +egomania/MS +Egon/M +Egor/M +ego/SM +egotism/SM +egotistic +egotistical/Y +egotist/MS +egregiousness/MS +egregious/PY +egress/SDMG +egret/SM +Egyptian/S +Egypt/M +Egyptology/M +eh +Ehrlich/M +Eichmann/M +eiderdown/SM +eider/SM +eidetic +Eiffel/M +eigenfunction/MS +eigenstate/S +eigenvalue/SM +eigenvector/MS +eighteen/MHS +eighteenths +eightfold +eighth/MS +eighths +eightieths +eightpence +eight/SM +eighty/SHM +Eileen/M +Eilis/M +Eimile/M +Einsteinian +einsteinium/MS +Einstein/SM +Eire/M +Eirena/M +Eisenhower/M +Eisenstein/M +Eisner/M +eisteddfod/M +either +ejaculate/SDXNG +ejaculation/M +ejaculatory +ejecta +ejection/SM +ejector/SM +eject/VGSD +Ekaterina/M +Ekberg/M +eked/A +eke/DSG +EKG +Ekstrom/M +Ektachrome/M +elaborateness/SM +elaborate/SDYPVNGX +elaboration/M +elaborators +Elaina/M +Elaine/M +Elana/M +eland/SM +Elane/M +lan/M +Elanor/M +elans +elapse/SDG +el/AS +elastically/I +elasticated +elasticity/SM +elasticize/GDS +elastic/S +elastodynamics +elastomer/M +elatedness/M +elated/PY +elater/M +elate/SRDXGN +elation/M +Elayne/M +Elba/MS +Elbe/M +Elberta/M +Elbertina/M +Elbertine/M +Elbert/M +elbow/GDMS +elbowroom/SM +Elbrus/M +Elden/M +elderberry/MS +elderflower +elderliness/M +elderly/PS +elder/SY +eldest +Eldin/M +Eldon/M +Eldorado's +Eldredge/M +Eldridge/M +Eleanora/M +Eleanore/M +Eleanor/M +Eleazar/M +electable/U +elect/ASGD +elected/U +electioneer/GSD +election/SAM +electiveness/M +elective/SPY +electoral/Y +electorate/SM +elector/SM +Electra/M +electress/M +electricalness/M +electrical/PY +electrician/SM +electricity/SM +electric/S +electrification/M +electrifier/M +electrify/ZXGNDRS +electrocardiogram/MS +electrocardiograph/M +electrocardiographs +electrocardiography/MS +electrochemical/Y +electrocute/GNXSD +electrocution/M +electrode/SM +electrodynamics/M +electrodynamic/YS +electroencephalogram/SM +electroencephalographic +electroencephalograph/M +electroencephalographs +electroencephalography/MS +electrologist/MS +electroluminescent +electrolysis/M +electrolyte/SM +electrolytic +electrolytically +electrolyze/SDG +electro/M +electromagnetic +electromagnetically +electromagnetism/SM +electromagnet/SM +electromechanical +electromechanics +electromotive +electromyograph +electromyographic +electromyographically +electromyography/M +electronegative +electronically +electronic/S +electronics/M +electron/MS +electrophoresis/M +electrophorus/M +electroplate/DSG +electroscope/MS +electroscopic +electroshock/GDMS +electrostatic/S +electrostatics/M +electrotherapist/M +electrotype/GSDZM +electroweak +eleemosynary +Eleen/M +elegance/ISM +elegant/YI +elegiacal +elegiac/S +elegy/SM +elem +elemental/YS +elementarily +elementariness/M +elementary/P +element/MS +Elena/M +Elene/M +Eleni/M +Elenore/M +Eleonora/M +Eleonore/M +elephantiases +elephantiasis/M +elephantine +elephant/SM +elevated/S +elevate/XDSNG +elevation/M +elevator/SM +eleven/HM +elevens/S +elevenths +elev/NX +Elfie/M +elfin/S +elfish +elf/M +Elfreda/M +Elfrida/M +Elfrieda/M +Elga/M +Elgar/M +Elianora/M +Elianore/M +Elia/SM +Elicia/M +elicitation/MS +elicit/GSD +elide/GSD +Elie/M +eligibility/ISM +eligible/SI +Elihu/M +Elijah/M +Eli/M +eliminate/XSDYVGN +elimination/M +eliminator/SM +Elinore/M +Elinor/M +Eliot/M +Elisabeth/M +Elisabet/M +Elisabetta/M +Elisa/M +Elise/M +Eliseo/M +Elisha/M +elision/SM +Elissa/M +Elita/M +elite/MPS +elitism/SM +elitist/SM +elixir/MS +Elizabethan/S +Elizabeth/M +Elizabet/M +Eliza/M +Elka/M +Elke/M +Elkhart/M +elk/MS +Elladine/M +Ella/M +Ellary/M +Elle/M +Ellene/M +Ellen/M +Ellerey/M +Ellery/M +Ellesmere/M +Ellette/M +Ellie/M +Ellington/M +Elliot/M +Elliott/M +ellipse/MS +ellipsis/M +ellipsoidal +ellipsoid/MS +ellipsometer/MS +ellipsometry +elliptic +elliptical/YS +ellipticity/M +Elli/SM +Ellison/M +Ellissa/M +ell/MS +Ellswerth/M +Ellsworth/M +Ellwood/M +Elly/M +Ellyn/M +Ellynn/M +Elma/M +Elmer/M +Elmhurst/M +Elmira/M +elm/MRS +Elmo/M +Elmore/M +Elmsford/M +El/MY +Elna/MH +Elnar/M +Elnath/M +Elnora/M +Elnore/M +elocutionary +elocutionist/MS +elocution/SM +elodea/S +Elohim/M +Eloisa/M +Eloise/M +elongate/NGXSD +elongation/M +Elonore/M +elopement/MS +eloper/M +elope/SRDLG +eloquence/SM +eloquent/IY +Elora/M +Eloy/M +Elroy/M +els +Elsa/M +Elsbeth/M +else/M +Else/M +Elset/M +elsewhere +Elsey/M +Elsie/M +Elsi/M +Elsinore/M +Elspeth/M +Elston/M +Elsworth/M +Elsy/M +Eltanin/M +Elton/M +eluate/SM +elucidate/SDVNGX +elucidation/M +elude/GSD +elusiveness/SM +elusive/YP +elute/DGN +elution/M +Elva/M +elven +Elvera/M +elver/SM +elves/M +Elvia/M +Elvina/M +Elvin/M +Elvira/M +elvish +Elvis/M +Elvyn/M +Elwin/M +Elwira/M +Elwood/M +Elwyn/M +Ely/M +Elyn/M +Elyse/M +Elysees +Elyse/M +Elysha/M +Elysia/M +elysian +Elysian +Elysium/SM +Elyssa/M +EM +emaciate/NGXDS +emaciation/M +emacs/M +Emacs/M +email/SMDG +Emalee/M +Emalia/M +Ema/M +emanate/XSDVNG +emanation/M +emancipate/DSXGN +emancipation/M +emancipator/MS +Emanuele/M +Emanuel/M +emasculate/GNDSX +emasculation/M +embalmer/M +embalm/ZGRDS +embank/GLDS +embankment/MS +embarcadero +embargoes +embargo/GMD +embark/ADESG +embarkation/EMS +embarrassedly +embarrassed/U +embarrassing/Y +embarrassment/MS +embarrass/SDLG +embassy/MS +embattle/DSG +embeddable +embedded +embedder +embedding/MS +embed/S +embellished/U +embellisher/M +embellish/LGRSD +embellishment/MS +ember/MS +embezzle/LZGDRS +embezzlement/MS +embezzler/M +embitter/LGDS +embitterment/SM +emblazon/DLGS +emblazonment/SM +emblematic +emblem/GSMD +embodier/M +embodiment/ESM +embody/ESDGA +embolden/DSG +embolism/SM +embosom +embosser/M +emboss/ZGRSD +embouchure/SM +embower/GSD +embraceable +embracer/M +embrace/RSDVG +embracing/Y +embrasure/MS +embrittle +embrocation/SM +embroiderer/M +embroider/SGZDR +embroidery/MS +embroilment/MS +embroil/SLDG +embryologist/SM +embryology/MS +embryonic +embryo/SM +emceeing +emcee/SDM +Emelda/M +Emelen/M +Emelia/M +Emelina/M +Emeline/M +Emelita/M +Emelyne/M +emendation/MS +emend/SRDGB +emerald/SM +Emera/M +emerge/ADSG +emergence/MAS +emergency/SM +emergent/S +emerita +emeritae +emeriti +emeritus +Emerson/M +Emery/M +emery/MGSD +emetic/S +emf/S +emigrant/MS +emigrate/SDXNG +emigration/M +migr/S +Emilee/M +Emile/M +Emilia/M +Emilie/M +Emili/M +Emiline/M +Emilio/M +Emil/M +Emily/M +eminence/MS +Eminence/MS +eminent/Y +emirate/SM +emir/SM +emissary/SM +emission/AMS +emissivity/MS +emit/S +emittance/M +emitted +emitter/SM +emitting +Emlen/M +Emlyn/M +Emlynne/M +Emlynn/M +em/M +Em/M +Emmalee/M +Emmaline/M +Emmalyn/M +Emmalynne/M +Emmalynn/M +Emma/M +Emmanuel/M +Emmeline/M +Emmerich/M +Emmery/M +Emmet/M +Emmett/M +Emmey/M +Emmie/M +Emmi/M +Emmit/M +Emmott/M +Emmye/M +Emmy/SM +Emogene/M +emollient/S +emolument/SM +Emory/M +emote/SDVGNX +emotionalism/MS +emotionality/M +emotionalize/GDS +emotional/UY +emotionless +emotion/M +emotive/Y +empaneled +empaneling +empath +empathetic +empathetical/Y +empathic +empathize/SDG +empathy/MS +emperor/MS +emphases +emphasis/M +emphasize/ZGCRSDA +emphatically/U +emphatic/U +emphysema/SM +emphysematous +empire/MS +empirical/Y +empiricism/SM +empiricist/SM +empiric/SM +emplace/L +emplacement/MS +employability/UM +employable/US +employed/U +employee/SM +employer/SM +employ/LAGDS +employment/UMAS +emporium/MS +empower/GLSD +empowerment/MS +empress/MS +emptier/M +emptily +emptiness/SM +empty/GRSDPT +empyrean/SM +ems/C +EMT +emulate/SDVGNX +emulation/M +emulative/Y +emulator/MS +emulsification/M +emulsifier/M +emulsify/NZSRDXG +emulsion/SM +emu/SM +Emylee/M +Emyle/M +enabler/M +enable/SRDZG +enactment/ASM +enact/SGALD +enameler/M +enamelware/SM +enamel/ZGJMDRS +enamor/DSG +en/BM +enc +encamp/LSDG +encampment/MS +encapsulate/SDGNX +encapsulation/M +encase/GSDL +encasement/SM +encephalitic +encephalitides +encephalitis/M +encephalographic +encephalopathy/M +enchain/SGD +enchanter/MS +enchant/ESLDG +enchanting/Y +enchantment/MSE +enchantress/MS +enchilada/SM +encipherer/M +encipher/SRDG +encircle/GLDS +encirclement/SM +encl +enclave/MGDS +enclosed/U +enclose/GDS +enclosure/SM +encoder/M +encode/ZJGSRD +encomium/SM +encompass/GDS +encore/GSD +encounter/GSD +encouragement/SM +encourager/M +encourage/SRDGL +encouraging/Y +encroacher/M +encroach/LGRSD +encroachment/MS +encrustation/MS +encrust/DSG +encrypt/DGS +encrypted/U +encryption/SM +encumbered/U +encumber/SEDG +encumbrancer/M +encumbrance/SRM +ency +encyclical/SM +encyclopaedia's +encyclopedia/SM +encyclopedic +encyst/GSLD +encystment/MS +endanger/DGSL +endangerment/SM +endear/GSLD +endearing/Y +endearment/MS +endeavored/U +endeavorer/M +endeavor/GZSMRD +endemically +endemicity +endemic/S +ender/M +endgame/M +Endicott/M +ending/M +endive/SM +endlessness/MS +endless/PY +endmost +endnote/MS +endocrine/S +endocrinologist/SM +endocrinology/SM +endogamous +endogamy/M +endogenous/Y +endomorphism/SM +endorse/DRSZGL +endorsement/MS +endorser/M +endoscope/MS +endoscopic +endoscopy/SM +endosperm/M +endothelial +endothermic +endow/GSDL +endowment/SM +endpoint/MS +endue/SDG +endungeoned +endurable/U +endurably/U +endurance/SM +endure/BSDG +enduringness/M +enduring/YP +endways +Endymion/M +end/ZGVMDRSJ +ENE +enema/SM +enemy/SM +energetically +energetic/S +energetics/M +energized/U +energizer/M +energize/ZGDRS +energy/MS +enervate/XNGVDS +enervation/M +enfeeble/GLDS +enfeeblement/SM +enfilade/MGDS +enfold/SGD +enforceability/M +enforceable/U +enforced/Y +enforce/LDRSZG +enforcement/SM +enforcer/M +enforcible/U +enfranchise/ELDRSG +enfranchisement/EMS +enfranchiser/M +engage/ADSGE +engagement/SEM +engaging/Y +Engelbert/M +Engel/MS +engender/DGS +engineer/GSMDJ +engineering/MY +engine/MGSD +England/M +england/ZR +Englebert/M +Englewood/M +English/GDRSM +Englishman/M +Englishmen +Englishwoman/M +Englishwomen +Eng/M +engorge/LGDS +engorgement/MS +Engracia/M +engram/MS +engraver/M +engrave/ZGDRSJ +engraving/M +engrossed/Y +engrosser/M +engross/GLDRS +engrossing/Y +engrossment/SM +engulf/GDSL +engulfment/SM +enhanceable +enhance/LZGDRS +enhancement/MS +enhancer/M +enharmonic +Enid/M +Enif/M +enigma/MS +enigmatic +enigmatically +Eniwetok/M +enjambement's +enjambment/MS +enjoinder +enjoin/GSD +enjoyability +enjoyableness/M +enjoyable/P +enjoyably +enjoy/GBDSL +enjoyment/SM +Enkidu/M +enlargeable +enlarge/LDRSZG +enlargement/MS +enlarger/M +enlightened/U +enlighten/GDSL +enlightening/U +enlightenment/SM +enlistee/MS +enlister/M +enlistment/SAM +enlist/SAGDL +enliven/LDGS +enlivenment/SM +enmesh/DSLG +enmeshment/SM +enmity/MS +Ennis/M +ennoble/LDRSG +ennoblement/SM +ennobler/M +ennui/SM +Enoch/M +enormity/SM +enormousness/MS +enormous/YP +Enos +enough +enoughs +enplane/DSG +enqueue/DS +enquirer/S +enquiringly +enrage/SDG +enrapture/GSD +Enrica/M +enricher/M +Enrichetta/M +enrich/LDSRG +enrichment/SM +Enrico/M +Enrika/M +Enrique/M +Enriqueta/M +enrobed +enrollee/SM +enroll/LGSD +enrollment/SM +ens +ensconce/DSG +ensemble/MS +enshrine/DSLG +enshrinement/SM +enshroud/DGS +ensign/SM +ensilage/DSMG +enslavement/MS +enslaver/M +enslave/ZGLDSR +ensnare/GLDS +ensnarement/SM +Ensolite/M +ensue/SDG +ensurer/M +ensure/SRDZG +entailer/M +entailment/MS +entail/SDRLG +entangle/EGDRSL +entanglement/ESM +entangler/EM +entente/MS +enter/ASDG +entered/U +enterer/M +enteritides +enteritis/SM +enterprise/GMSR +Enterprise/M +enterpriser/M +enterprising/Y +entertainer/M +entertaining/Y +entertainment/SM +entertain/SGZRDL +enthalpy/SM +enthrall/GDSL +enthrallment/SM +enthrone/GDSL +enthronement/MS +enthuse/DSG +enthusiasm/SM +enthusiastically/U +enthusiastic/U +enthusiast/MS +enticement/SM +entice/SRDJLZG +enticing/Y +entire/SY +entirety/SM +entitle/GLDS +entitlement/MS +entity/SM +entomb/GDSL +entombment/MS +entomological +entomologist/S +entomology/MS +entourage/SM +entr'acte/S +entrails +entrainer/M +entrain/GSLDR +entrancement/MS +entrance/MGDSL +entranceway/M +entrancing/Y +entrant/MS +entrapment/SM +entrapped +entrapping +entrap/SL +entreating/Y +entreat/SGD +entreaty/SM +entre/S +entrench/LSDG +entrenchment/MS +entrepreneurial +entrepreneur/MS +entrepreneurship/M +entropic +entropy/MS +entrust/DSG +entry/ASM +entryway/SM +entwine/DSG +enumerable +enumerate/AN +enumerated/U +enumerates +enumerating +enumeration's/A +enumeration/SM +enumerative +enumerator/SM +enunciable +enunciated/U +enunciate/XGNSD +enunciation/M +enureses +enuresis/M +envelope/MS +enveloper/M +envelopment/MS +envelop/ZGLSDR +envenom/SDG +enviableness/M +enviable/U +enviably +envied/U +envier/M +enviousness/SM +envious/PY +environ/LGSD +environmentalism/SM +environmentalist/SM +environmental/Y +environment/MS +envisage/DSG +envision/GSD +envoy/SM +envying/Y +envy/SRDMG +enzymatic +enzymatically +enzyme/SM +enzymology/M +Eocene +EOE +eohippus/M +Eolanda/M +Eolande/M +eolian +eon/SM +EPA +epaulet/SM +pe/S +ephedrine/MS +ephemeral/SY +ephemera/MS +ephemerids +ephemeris/M +Ephesian/S +Ephesians/M +Ephesus/M +Ephraim/M +Ephrayim/M +Ephrem/M +epically +epicenter/SM +epic/SM +Epictetus/M +Epicurean +epicurean/S +epicure/SM +Epicurus/M +epicycle/MS +epicyclic +epicyclical/Y +epicycloid/M +epidemically +epidemic/MS +epidemiological/Y +epidemiologist/MS +epidemiology/MS +epidermal +epidermic +epidermis/MS +epidural +epigenetic +epiglottis/SM +epigrammatic +epigram/MS +epigrapher/M +epigraph/RM +epigraphs +epigraphy/MS +epilepsy/SM +epileptic/S +epilogue/SDMG +Epimethius/M +epinephrine/SM +epiphany/SM +Epiphany/SM +epiphenomena +episcopacy/MS +episcopalian +Episcopalian/S +Episcopal/S +episcopal/Y +episcopate/MS +episode/SM +episodic +episodically +epistemic +epistemological/Y +epistemology/M +epistle/MRS +Epistle/SM +epistolary/S +epistolatory +epitaph/GMD +epitaphs +epitaxial/Y +epitaxy/M +epithelial +epithelium/MS +epithet/MS +epitome/MS +epitomized/U +epitomizer/M +epitomize/SRDZG +epochal/Y +epoch/M +epochs +eponymous +epoxy/GSD +epsilon/SM +Epsom/M +Epstein/M +equability/MS +equableness/M +equable/P +equably +equaling +equality/ISM +equalization/MS +equalize/DRSGJZ +equalized/U +equalizer/M +equalizes/U +equal/USDY +equanimity/MS +equate/NGXBSD +equation/M +equatorial/S +equator/SM +equerry/MS +equestrianism/SM +equestrian/S +equestrienne/SM +equiangular +equidistant/Y +equilateral/S +equilibrate/GNSD +equilibration/M +equilibrium/MSE +equine/S +equinoctial/S +equinox/MS +equipage/SM +equipartition/M +equip/AS +equipment/SM +equipoise/GMSD +equipotent +equipped/AU +equipping/A +equiproportional +equiproportionality +equiproportionate +equitable/I +equitableness/M +equitably/I +equitation/SM +equity/IMS +equiv +equivalence/DSMG +equivalent/SY +equivocalness/MS +equivocal/UY +equivocate/NGSDX +equivocation/M +equivocator/SM +Equuleus/M +ER +ERA +eradicable/I +eradicate/SDXVGN +eradication/M +eradicator/SM +era/MS +Eran/M +erase/N +eraser/M +erasion/M +Erasmus/M +eras/SRDBGZ +Erastus/M +erasure/MS +Erato/M +Eratosthenes/M +erbium/SM +Erda/M +ere +Erebus/M +erect/GPSRDY +erectile +erection/SM +erectness/MS +erector/SM +Erek/M +erelong +eremite/MS +Erena/M +ergo +ergodic +ergodicity/M +ergonomically +ergonomics/M +ergonomic/U +ergophobia +ergosterol/SM +ergot/SM +erg/SM +Erhard/M +Erhart/M +Erica/M +Ericha/M +Erich/M +Ericka/M +Erick/M +Erickson/M +Eric/M +Ericson's +Ericsson's +Eridanus/M +Erie/SM +Erika/M +Erik/M +Erikson/M +Erina/M +Erin/M +Erinna/M +Erinn/M +eris +Eris +Eritrea/M +Erlang/M +Erlenmeyer/M +Erl/M +Er/M +Erma/M +Ermanno/M +Ermengarde/M +Ermentrude/M +Ermina/M +ermine/MSD +Erminia/M +Erminie/M +Ermin/M +Ernaline/M +Erna/M +Ernesta/M +Ernestine/M +Ernest/M +Ernesto/M +Ernestus/M +Ernie/M +Ernst/M +Erny/M +erode/SDG +erodible +erogenous +erosible +erosional +erosion/SM +erosiveness/M +erosive/P +Eros/SM +erotically +erotica/M +eroticism/MS +erotic/S +errancy/MS +errand/MS +errantry/M +errant/YS +errata/SM +erratically +erratic/S +erratum/MS +err/DGS +Errick/M +erring/UY +Erroll/M +Errol/M +erroneousness/M +erroneous/YP +error/SM +ersatz/S +Erse/M +Erskine/M +erst +erstwhile +Ertha/M +eructation/MS +eruct/DGS +erudite/NYX +erudition/M +erupt/DSVG +eruption/SM +eruptive/SY +Ervin/M +ErvIn/M +Erv/M +Erwin/M +Eryn/M +erysipelas/SM +erythrocyte/SM +es +e's +Es +E's +Esau/M +escadrille/M +escalate/CDSXGN +escalation/MC +escalator/SM +escallop/SGDM +escapable/I +escapade/SM +escapee/MS +escape/LGSRDB +escapement/MS +escaper/M +escapism/SM +escapist/S +escapology +escarole/MS +escarpment/MS +eschatology/M +Escherichia/M +Escher/M +eschew/SGD +Escondido/M +escort/SGMD +escritoire/SM +escrow/DMGS +escudo/MS +escutcheon/SM +Esdras/M +ESE +Eskimo/SM +ESL +Esma/M +Esmaria/M +Esmark/M +Esme/M +Esmeralda/M +esophageal +esophagi +esophagus/M +esoteric +esoterica +esoterically +esp +ESP +espadrille/MS +Espagnol/M +espalier/SMDG +especial/Y +Esperanto/M +Esperanza/M +Espinoza/M +espionage/SM +esplanade/SM +Esp/M +Esposito/M +espousal/MS +espouser/M +espouse/SRDG +espresso/SM +esprit/SM +espy/GSD +Esq/M +esquire/GMSD +Esquire/S +Esra/M +Essa/M +essayer/M +essayist/SM +essay/SZMGRD +essence/MS +Essene/SM +Essen/M +essentialist/M +essentially +essentialness/M +essential/USI +Essequibo/M +Essex/M +Essie/M +Essy/M +EST +established/U +establisher/M +establish/LAEGSD +establishment/EMAS +Establishment/MS +Esta/M +estate/GSDM +Esteban/M +esteem/EGDS +Estela/M +Estele/M +Estella/M +Estelle/M +Estell/M +Estel/M +Esterhzy/M +ester/M +Ester/M +Estes +Estevan/M +Esther/M +esthete's +esthetically +esthetic's +esthetics's +estimable/I +estimableness/M +estimate/XDSNGV +estimating/A +estimation/M +estimator/SM +Estonia/M +Estonian/S +estoppal +Estrada/M +estrange/DRSLG +estrangement/SM +estranger/M +Estrella/M +Estrellita/M +estrogen/SM +estrous +estrus/SM +est/RZ +estuarine +estuary/SM +et +ET +ETA +Etan/M +eta/SM +etc +etcetera/SM +etcher/M +etch/GZJSRD +etching/M +ETD +eternalness/SM +eternal/PSY +eternity/SM +ethane/SM +Ethan/M +ethanol/MS +Ethelbert/M +Ethelda/M +Ethelind/M +Etheline/M +Ethelin/M +Ethel/M +Ethelred/M +Ethelyn/M +Ethe/M +etherealness/M +ethereal/PY +etherized +Ethernet/MS +ether/SM +ethically/U +ethicalness/M +ethical/PYS +ethicist/S +ethic/MS +Ethiopia/M +Ethiopian/S +ethnically +ethnicity/MS +ethnic/S +ethnocentric +ethnocentrism/MS +ethnographers +ethnographic +ethnography/M +ethnological +ethnologist/SM +ethnology/SM +ethnomethodology +ethological +ethologist/MS +ethology/SM +ethos/SM +ethylene/MS +Ethyl/M +ethyl/SM +Etienne/M +etiologic +etiological +etiology/SM +etiquette/SM +Etna/M +Etruria/M +Etruscan/MS +Etta/M +Ettie/M +Etti/M +Ettore/M +Etty/M +tude/MS +etymological/Y +etymologist/SM +etymology/MS +EU +eucalypti +eucalyptus/SM +Eucharistic +Eucharist/SM +euchre/MGSD +euclidean +Euclid/M +Eudora/M +Euell/M +Eugene/M +Eugenia/M +eugenically +eugenicist/SM +eugenic/S +eugenics/M +Eugenie/M +Eugenio/M +Eugenius/M +Eugen/M +Eugine/M +Eulalie/M +Eula/M +Eulerian/M +Euler/M +eulogistic +eulogist/MS +eulogized/U +eulogize/GRSDZ +eulogizer/M +eulogy/MS +Eu/M +Eumenides +Eunice/M +eunuch/M +eunuchs +Euphemia/M +euphemism/MS +euphemistic +euphemistically +euphemist/M +euphonious/Y +euphonium/M +euphony/SM +euphoria/SM +euphoric +euphorically +Euphrates/M +Eurasia/M +Eurasian/S +eureka/S +Euripides/M +Eur/M +Eurodollar/SM +Europa/M +Europeanization/SM +Europeanized +European/MS +Europe/M +europium/MS +Eurydice/M +Eustace/M +Eustachian/M +Eustacia/M +eutectic +Euterpe/M +euthanasia/SM +euthenics/M +evacuate/DSXNGV +evacuation/M +evacuee/MS +evader/M +evade/SRDBGZ +Evaleen/M +evaluable +evaluate/ADSGNX +evaluated/U +evaluational +evaluation/MA +evaluative +evaluator/MS +Eva/M +evanescence/MS +evanescent +Evangelia/M +evangelic +evangelicalism/SM +Evangelical/S +evangelical/YS +Evangelina/M +Evangeline/M +Evangelin/M +evangelism/SM +evangelistic +evangelist/MS +Evangelist/MS +evangelize/GDS +Evania/M +Evan/MS +Evanne/M +Evanston/M +Evansville/M +evaporate/VNGSDX +evaporation/M +evaporative/Y +evaporator/MS +evasion/SM +evasiveness/SM +evasive/PY +Eveleen/M +Evelina/M +Eveline/M +Evelin/M +Evelyn/M +Eve/M +evened +evener/M +evenhanded/YP +evening/SM +Evenki/M +Even/M +evenness/MSU +even/PUYRT +evens +evensong/MS +eventfulness/SM +eventful/YU +eventide/SM +event/SGM +eventuality/MS +eventual/Y +eventuate/GSD +Everard/M +Eveready/M +Evered/M +Everest/M +Everette/M +Everett/M +everglade/MS +Everglades +evergreen/S +Everhart/M +everlastingness/M +everlasting/PYS +everliving +evermore +EverReady/M +eve/RSM +ever/T +every +everybody/M +everydayness/M +everyday/P +everyman +everyone/MS +everyplace +everything +everywhere +eve's/A +eves/A +Evey/M +evict/DGS +eviction/SM +evidence/MGSD +evidential/Y +evident/YS +Evie/M +evildoer/SM +evildoing/MS +evilness/MS +evil/YRPTS +evince/SDG +Evin/M +eviscerate/GNXDS +evisceration/M +Evita/M +Ev/MN +evocable +evocate/NVX +evocation/M +evocativeness/M +evocative/YP +evoke/SDG +evolute/NMXS +evolutionarily +evolutionary +evolutionist/MS +evolution/M +evolve/SDG +Evonne/M +Evvie/M +Evvy/M +Evy/M +Evyn/M +Ewan/M +Eward/M +Ewart/M +Ewell/M +ewe/MZRS +Ewen/M +ewer/M +Ewing/M +exacerbate/NGXDS +exacerbation/M +exacter/M +exactingness/M +exacting/YP +exaction/SM +exactitude/ISM +exactly/I +exactness/MSI +exact/TGSPRDY +exaggerate/DSXNGV +exaggerated/YP +exaggeration/M +exaggerative/Y +exaggerator/MS +exaltation/SM +exalted/Y +exalter/M +exalt/ZRDGS +examen/M +examination/AS +examination's +examine/BGZDRS +examined/AU +examinees +examiner/M +examines/A +examining/A +exam/MNS +example/DSGM +exampled/U +exasperate/DSXGN +exasperated/Y +exasperating/Y +exasperation/M +Excalibur/M +excavate/NGDSX +excavation/M +excavator/SM +Excedrin/M +exceeder/M +exceeding/Y +exceed/SGDR +excelled +excellence/SM +excellency/MS +Excellency/MS +excellent/Y +excelling +excel/S +excelsior/S +except/DSGV +exceptionable/U +exceptionalness/M +exceptional/YU +exception/BMS +excerpter/M +excerpt/GMDRS +excess/GVDSM +excessiveness/M +excessive/PY +exchangeable +exchange/GDRSZ +exchanger/M +exchequer/SM +Exchequer/SM +excise/XMSDNGB +excision/M +excitability/MS +excitableness/M +excitable/P +excitably +excitation/SM +excitatory +excited/Y +excitement/MS +exciter/M +excite/RSDLBZG +excitingly +exciting/U +exciton/M +exclaimer/M +exclaim/SZDRG +exclamation/MS +exclamatory +exclude/DRSG +excluder/M +exclusionary +exclusioner/M +exclusion/SZMR +exclusiveness/SM +exclusive/SPY +exclusivity/MS +excommunicate/XVNGSD +excommunication/M +excoriate/GNXSD +excoriation/M +excremental +excrement/SM +excrescence/MS +excrescent +excreta +excrete/NGDRSX +excreter/M +excretion/M +excretory/S +excruciate/NGDS +excruciating/Y +excruciation/M +exculpate/XSDGN +exculpation/M +exculpatory +excursionist/SM +excursion/MS +excursiveness/SM +excursive/PY +excursus/MS +excusable/IP +excusableness/IM +excusably/I +excuse/BGRSD +excused/U +excuser/M +exec/MS +execrableness/M +execrable/P +execrably +execrate/DSXNGV +execration/M +executable/MS +execute/NGVZBXDRS +executer/M +executional +executioner/M +execution/ZMR +executive/SM +executor/SM +executrices +executrix/M +exegeses +exegesis/M +exegete/M +exegetical +exegetic/S +exemplariness/M +exemplar/MS +exemplary/P +exemplification/M +exemplifier/M +exemplify/ZXNSRDG +exemption/MS +exempt/SDG +exerciser/M +exercise/ZDRSGB +exertion/MS +exert/SGD +Exeter/M +exeunt +exhalation/SM +exhale/GSD +exhausted/Y +exhauster/M +exhaustible/I +exhausting/Y +exhaustion/SM +exhaustiveness/MS +exhaustive/YP +exhaust/VGRDS +exhibitioner/M +exhibitionism/MS +exhibitionist/MS +exhibition/ZMRS +exhibitor/SM +exhibit/VGSD +exhilarate/XSDVNG +exhilarating/Y +exhilaration/M +exhortation/SM +exhort/DRSG +exhorter/M +exhumation/SM +exhume/GRSD +exhumer/M +exigence/S +exigency/SM +exigent/SY +exiguity/SM +exiguous +exile/SDGM +existence/MS +existent/I +existentialism/MS +existentialistic +existentialist/MS +existential/Y +existents +exist/SDG +exit/MDSG +exobiology/MS +exocrine +Exodus/M +exodus/SM +exogamous +exogamy/M +exogenous/Y +exonerate/SDVGNX +exoneration/M +exorbitance/MS +exorbitant/Y +exorcise/SDG +exorcism/SM +exorcist/SM +exorcizer/M +exoskeleton/MS +exosphere/SM +exothermic +exothermically +exotica +exotically +exoticism/SM +exoticness/M +exotic/PS +exp +expandability/M +expand/DRSGZB +expanded/U +expander/M +expanse/DSXGNVM +expansible +expansionary +expansionism/MS +expansionist/MS +expansion/M +expansiveness/S +expansive/YP +expatiate/XSDNG +expatiation/M +expatriate/SDNGX +expatriation/M +expectancy/MS +expectant/YS +expectational +expectation/MS +expected/UPY +expecting/Y +expectorant/S +expectorate/NGXDS +expectoration/M +expect/SBGD +expedience/IS +expediency/IMS +expedients +expedient/YI +expediter/M +expedite/ZDRSNGX +expeditionary +expedition/M +expeditiousness/MS +expeditious/YP +expeditor's +expellable +expelled +expelling +expel/S +expendable/S +expended/U +expender/M +expenditure/SM +expend/SDRGB +expense/DSGVM +expensive/IYP +expensiveness/SMI +experienced/U +experience/ISDM +experiencing +experiential/Y +experimentalism/M +experimentalist/SM +experimental/Y +experimentation/SM +experimenter/M +experiment/GSMDRZ +experted +experting +expertise/SM +expertize/GD +expertnesses +expertness/IM +expert/PISY +expert's +expiable/I +expiate/XGNDS +expiation/M +expiatory +expiration/MS +expired/U +expire/SDG +expiry/MS +explainable/UI +explain/ADSG +explained/U +explainer/SM +explanation/MS +explanatory +expletive/SM +explicable/I +explicate/VGNSDX +explication/M +explicative/Y +explicitness/SM +explicit/PSY +explode/DSRGZ +exploded/U +exploder/M +exploitation/MS +exploitative +exploited/U +exploiter/M +exploit/ZGVSMDRB +exploration/MS +exploratory +explore/DSRBGZ +explored/U +explorer/M +explosion/MS +explosiveness/SM +explosive/YPS +expo/MS +exponential/SY +exponentiate/XSDNG +exponentiation/M +exponent/MS +exportability +exportable +export/AGSD +exportation/SM +exporter/MS +export's +expose +exposed/U +exposer/M +exposit/D +exposition/SM +expositor/MS +expository +expos/RSDZG +expostulate/DSXNG +expostulation/M +exposure/SM +expounder/M +expound/ZGSDR +expressed/U +expresser/M +express/GVDRSY +expressibility/I +expressible/I +expressibly/I +expressionism/SM +expressionistic +expressionist/S +expressionless/YP +expression/MS +expressive/IYP +expressiveness/MS +expressiveness's/I +expressway/SM +expropriate/XDSGN +expropriation/M +expropriator/SM +expulsion/MS +expunge/GDSR +expunger/M +expurgated/U +expurgate/SDGNX +expurgation/M +exquisiteness/SM +exquisite/YPS +ex/S +ext +extant +extemporaneousness/MS +extemporaneous/YP +extempore/S +extemporization/SM +extemporizer/M +extemporize/ZGSRD +extendability/M +extendedly +extendedness/M +extended/U +extender/M +extendibility/M +extendibles +extend/SGZDR +extensibility/M +extensible/I +extensional/Y +extension/SM +extensiveness/SM +extensive/PY +extensor/MS +extent/SM +extenuate/XSDGN +extenuation/M +exterior/MYS +exterminate/XNGDS +extermination/M +exterminator/SM +externalities +externalization/SM +externalize/GDS +external/YS +extern/M +extinct/DGVS +extinction/MS +extinguishable/I +extinguish/BZGDRS +extinguisher/M +extirpate/XSDVNG +extirpation/M +extolled +extoller/M +extolling +extol/S +extort/DRSGV +extorter/M +extortionate/Y +extortioner/M +extortionist/SM +extortion/ZSRM +extracellular/Y +extract/GVSBD +extraction/SM +extractive/Y +extractor/SM +extracurricular/S +extradite/XNGSDB +extradition/M +extragalactic +extralegal/Y +extramarital +extramural +extraneousness/M +extraneous/YP +extraordinarily +extraordinariness/M +extraordinary/PS +extrapolate/XVGNSD +extrapolation/M +extra/S +extrasensory +extraterrestrial/S +extraterritorial +extraterritoriality/MS +extravagance/MS +extravagant/Y +extravaganza/SM +extravehicular +extravert's +extrema +extremal +extreme/DSRYTP +extremeness/MS +extremism/SM +extremist/MS +extremity/SM +extricable/I +extricate/XSDNG +extrication/M +extrinsic +extrinsically +extroversion/SM +extrovert/GMDS +extrude/GDSR +extruder/M +extrusion/MS +extrusive +exuberance/MS +exuberant/Y +exudate/XNM +exudation/M +exude/GSD +exultant/Y +exultation/SM +exult/DGS +exulting/Y +exurban +exurbanite/SM +exurbia/MS +exurb/MS +Exxon/M +Eyck/M +Eyde/M +Eydie/M +eyeball/GSMD +eyebrow/MS +eyed/P +eyedropper/MS +eyeful/MS +eye/GDRSMZ +eyeglass/MS +eyelash/MS +eyeless +eyelet/GSMD +eyelid/SM +eyeliner/MS +eyeopener/MS +eyeopening +eyepiece/SM +eyer/M +eyeshadow +eyesight/MS +eyesore/SM +eyestrain/MS +eyeteeth +eyetooth/M +eyewash/MS +eyewitness/SM +Eyre/M +eyrie's +Eysenck/M +Ezechiel/M +Ezekiel/M +Ezequiel/M +Eziechiele/M +Ezmeralda/M +Ezra/M +Ezri/M +F +FAA +Fabe/MR +Faberg/M +Faber/M +Fabiano/M +Fabian/S +Fabien/M +Fabio/M +fable/GMSRD +fabler/M +fabricate/SDXNG +fabrication/M +fabricator/MS +fabric/MS +fabulists +fabulousness/M +fabulous/YP +facade/GMSD +face/AGCSD +facecloth +facecloths +faceless/P +faceplate/M +facer/CM +face's +facetiousness/MS +facetious/YP +facet/SGMD +facial/YS +facileness/M +facile/YP +facilitate/VNGXSD +facilitation/M +facilitator/SM +facilitatory +facility/MS +facing/MS +facsimileing +facsimile/MSD +factional +factionalism/SM +faction/SM +factiousness/M +factious/PY +factitious +fact/MS +facto +factoid/S +factorial/MS +factoring/A +factoring's +factorisable +factorization/SM +factorize/GSD +factor/SDMJG +factory/MS +factotum/MS +factuality/M +factualness/M +factual/PY +faculty/MS +faddish +faddist/SM +fadedly +faded/U +fadeout +fader/M +fade/S +fading's +fading/U +fad/ZGSMDR +Fae/M +faerie/MS +Faeroe/M +faery's +Fafnir/M +fagged +fagging +faggoting's +Fagin/M +fag/MS +fagoting/M +fagot/MDSJG +Fahd/M +Fahrenheit/S +faence/S +failing's +failing/UY +fail/JSGD +faille/MS +failsafe +failure/SM +Faina/M +fain/GTSRD +fainter/M +fainthearted +faintness/MS +faint/YRDSGPT +Fairbanks +Fairchild/M +faired +Fairfax/M +Fairfield/M +fairgoer/S +fairground/MS +fairing/MS +fairish +Fairleigh/M +fairless +Fairlie/M +Fair/M +Fairmont/M +fairness's +fairness/US +Fairport/M +fairs +fair/TURYP +Fairview/M +fairway/MS +fairyland/MS +fairy/MS +fairytale +Faisalabad +Faisal/M +faithed +faithfulness/MSU +faithfuls +faithful/UYP +faithing +faithlessness/SM +faithless/YP +Faith/M +faiths +faith's +faith/U +fajitas +faker/M +fake/ZGDRS +fakir/SM +falafel +falconer/M +falconry/MS +falcon/ZSRM +Falito/M +Falkland/MS +Falk/M +Falkner/M +fallaciousness/M +fallacious/PY +fallacy/MS +faller/M +fallibility/MSI +fallible/I +fallibleness/MS +fallibly/I +falloff/S +Fallon/M +fallopian +Fallopian/M +fallout/MS +fallowness/M +fallow/PSGD +fall/SGZMRN +falsehood/SM +falseness/SM +false/PTYR +falsetto/SM +falsie/MS +falsifiability/M +falsifiable/U +falsification/M +falsifier/M +falsify/ZRSDNXG +falsity/MS +Falstaff/M +falterer/M +faltering/UY +falter/RDSGJ +Falwell/M +fa/M +famed/C +fame/DSMG +fames/C +familial +familiarity/MUS +familiarization/MS +familiarized/U +familiarizer/M +familiarize/ZGRSD +familiarizing/Y +familiarly/U +familiarness/M +familiar/YPS +family/MS +famine/SM +faming/C +famish/GSD +famously/I +famousness/M +famous/PY +fanaticalness/M +fanatical/YP +fanaticism/MS +fanatic/SM +Fanchette/M +Fanchon/M +fancied +Fancie/M +fancier/SM +fanciest +fancifulness/MS +fanciful/YP +fancily +fanciness/SM +fancying +fancy/IS +Fancy/M +fancywork/SM +fandango/SM +Fanechka/M +fanfare/SM +fanfold/M +fang/DMS +fangled +Fania/M +fanlight/SM +Fan/M +fanned +Fannie/M +Fanni/M +fanning +fanny/SM +Fanny/SM +fanout +fan/SM +fantail/SM +fantasia/SM +fantasist/M +fantasize/SRDG +fantastical/Y +fantastic/S +fantasy/GMSD +Fanya/M +fanzine/S +FAQ/SM +Faraday/M +farad/SM +Farah/M +Fara/M +Farand/M +faraway +Farber/M +farce/SDGM +farcical/Y +fare/MS +farer/M +farewell/DGMS +farfetchedness/M +far/GDR +Fargo/M +Farica/M +farinaceous +farina/MS +Farkas/M +Farlay/M +Farlee/M +Farleigh/M +Farley/M +Farlie/M +Farly/M +farmer/M +Farmer/M +farmhand/S +farmhouse/SM +farming/M +Farmington/M +farmland/SM +farm/MRDGZSJ +farmstead/SM +farmworker/S +Far/MY +farmyard/MS +faro/MS +farragoes +farrago/M +Farragut/M +Farrah/M +Farrakhan/M +Farra/M +Farrand/M +Farrell/M +Farrel/M +farrier/SM +Farris/M +Farr/M +farrow/DMGS +farseeing +farsightedness/SM +farsighted/YP +farther +farthermost +farthest +farthing/SM +fart/MDGS! +fas +fascia/SM +fascicle/DSM +fasciculate/DNX +fasciculation/M +fascinate/SDNGX +fascinating/Y +fascination/M +fascism/MS +Fascism's +fascistic +Fascist's +fascist/SM +fashionableness/M +fashionable/PS +fashionably/U +fashion/ADSG +fashioner/SM +fashion's +Fassbinder/M +fastback/MS +fastball/S +fasten/AGUDS +fastener/MS +fastening/SM +fast/GTXSPRND +fastidiousness/MS +fastidious/PY +fastness/MS +fatalism/MS +fatalistic +fatalistically +fatalist/MS +fatality/MS +fatal/SY +fatback/SM +fatefulness/MS +fateful/YP +fate/MS +Fates +fatheaded/P +fathead/SMD +father/DYMGS +fathered/U +fatherhood/MS +fatherland/SM +fatherless +fatherliness/M +fatherly/P +Father/SM +fathomable/U +fathomless +fathom/MDSBG +fatigued/U +fatigue/MGSD +fatiguing/Y +Fatima/M +fatness/SM +fat/PSGMDY +fatso/M +fatted +fattener/M +fatten/JZGSRD +fatter +fattest/M +fattiness/SM +fatting +fatty/RSPT +fatuity/MS +fatuousness/SM +fatuous/YP +fatwa/SM +faucet/SM +Faulknerian +Faulkner/M +fault/CGSMD +faultfinder/MS +faultfinding/MS +faultily +faultiness/MS +faultlessness/SM +faultless/PY +faulty/RTP +fauna/MS +Faunie/M +Faun/M +faun/MS +Fauntleroy/M +Faustian +Faustina/M +Faustine/M +Faustino/M +Faust/M +Faustus/M +fauvism/S +favorableness/MU +favorable/UMPS +favorably/U +favoredness/M +favored's/U +favored/YPSM +favorer/EM +favor/ESMRDGZ +favoring/MYS +favorings/U +favorite/SMU +favoritism/MS +favors/A +Fawkes/M +Fawne/M +fawner/M +fawn/GZRDMS +Fawnia/M +fawning/Y +Fawn/M +fax/GMDS +Fax/M +Faydra/M +Faye/M +Fayette/M +Fayetteville/M +Fayina/M +Fay/M +fay/MDRGS +Fayre/M +Faythe/M +Fayth/M +faze/DSG +FBI +FCC +FD +FDA +FDIC +FDR/M +fealty/MS +fearfuller +fearfullest +fearfulness/MS +fearful/YP +fearlessness/MS +fearless/PY +fear/RDMSG +fearsomeness/M +fearsome/PY +feasibility/SM +feasibleness/M +feasible/UI +feasibly/U +feaster/M +feast/GSMRD +feater/C +featherbed +featherbedding/SM +featherbrain/MD +feathered/U +feathering/M +featherless +featherlight +Featherman/M +feathertop +featherweight/SM +feathery/TR +feather/ZMDRGS +feat/MYRGTS +feats/C +featureless +feature/MGSD +Feb/M +febrile +February/MS +fecal +feces +fecklessness/M +feckless/PY +fecundability +fecundate/XSDGN +fecundation/M +fecund/I +fecundity/SM +federalism/SM +Federalist +federalist/MS +federalization/MS +federalize/GSD +Federal/S +federal/YS +federated/U +federate/FSDXVNG +federation/FM +federative/Y +Federica/M +Federico/M +FedEx/M +Fedora/M +fedora/SM +feds +Fed/SM +fed/U +feebleness/SM +feeble/TPR +feebly +feedback/SM +feedbag/MS +feeder/M +feed/GRZJS +feeding/M +feedlot/SM +feedstock +feedstuffs +feeing +feeler/M +feel/GZJRS +feelingly/U +feeling/MYP +feelingness/M +Fee/M +fee/MDS +feet/M +feigned/U +feigner/M +feign/RDGS +feint/MDSG +feisty/RT +Felder/M +Feldman/M +feldspar/MS +Felecia/M +Felicdad/M +Felice/M +Felicia/M +Felicio/M +felicitate/XGNSD +felicitation/M +felicitous/IY +felicitousness/M +felicity/IMS +Felicity/M +Felicle/M +Felic/M +Felike/M +Feliks/M +feline/SY +Felipa/M +Felipe/M +Felisha/M +Felita/M +Felix/M +Feliza/M +Felizio/M +fella/S +fellatio/SM +felled/A +feller/M +felling/A +Fellini/M +fellness/M +fellowman +fellowmen +fellow/SGDYM +fellowshipped +fellowshipping +fellowship/SM +fell/PSGZTRD +feloniousness/M +felonious/PY +felon/MS +felony/MS +felt/GSD +felting/M +Fe/M +female/MPS +femaleness/SM +feminineness/M +feminine/PYS +femininity/MS +feminism/MS +feminist/MS +femme/MS +femoral +fem/S +femur/MS +fenced/U +fencepost/M +fencer/M +fence/SRDJGMZ +fencing/M +fender/CM +fend/RDSCZG +Fenelia/M +fenestration/CSM +Fenian/M +fenland/M +fen/MS +fennel/SM +Fenwick/M +Feodora/M +Feodor/M +feral +Ferber/M +Ferdie/M +Ferdinanda/M +Ferdinande/M +Ferdinand/M +Ferdinando/M +Ferd/M +Ferdy/M +fer/FLC +Fergus/M +Ferguson/M +Ferlinghetti/M +Fermat/M +fermentation/MS +fermented +fermenter +ferment/FSCM +fermenting +Fermi/M +fermion/MS +fermium/MS +Fernanda/M +Fernande/M +Fernandez/M +Fernandina/M +Fernando/M +Ferne/M +fernery/M +Fern/M +fern/MS +ferny/TR +ferociousness/MS +ferocious/YP +ferocity/MS +Ferrari/M +Ferraro/M +Ferreira/M +Ferrell/M +Ferrel/M +Ferrer/M +ferreter/M +ferret/SMRDG +ferric +ferris +Ferris +ferrite/M +ferro +ferroelectric +ferromagnetic +ferromagnet/M +ferrous +ferrule/MGSD +ferryboat/MS +ferryman/M +ferrymen +ferry/SDMG +fertileness/M +fertile/YP +fertility/IMS +fertilization/ASM +fertilized/U +fertilizer/M +fertilizes/A +fertilize/SRDZG +ferule/SDGM +fervency/MS +fervent/Y +fervidness/M +fervid/YP +fervor/MS +fess/KGFSD +Fess/M +fess's +festal/S +fester/GD +festival/SM +festiveness/SM +festive/PY +festivity/SM +festoon/SMDG +fest/RVZ +fetal +feta/MS +fetcher/M +fetching/Y +fetch/RSDGZ +feted +fte/MS +fetich's +fetidness/SM +fetid/YP +feting +fetishism/SM +fetishistic +fetishist/SM +fetish/MS +fetlock/MS +fetter's +fetter/UGSD +fettle/GSD +fettling/M +fettuccine/S +fetus/SM +feudalism/MS +feudalistic +feudal/Y +feudatory/M +feud/MDSG +feverishness/SM +feverish/PY +fever/SDMG +fewness/MS +few/PTRS +Fey/M +Feynman/M +fey/RT +fez/M +Fez/M +fezzes +ff +FHA +fiance/S +fianc/MS +Fianna/M +Fiann/M +fiascoes +fiasco/M +Fiat/M +fiat/MS +fibbed +fibber/MS +fibbing +fiberboard/MS +fiber/DM +fiberfill/S +Fiberglas/M +fiberglass/DSMG +Fibonacci/M +fibrillate/XGNDS +fibrillation/M +fibril/MS +fibrin/MS +fibroblast/MS +fibroid/S +fibroses +fibrosis/M +fibrousness/M +fibrous/YP +fib/SZMR +fibulae +fibula/M +fibular +FICA +fices +fiche/SM +Fichte/M +fichu/SM +fickleness/MS +fickle/RTP +ficos +fictionalization/MS +fictionalize/DSG +fictional/Y +fiction/SM +fictitiousness/M +fictitious/PY +fictive/Y +ficus +fiddle/GMZJRSD +fiddler/M +fiddlestick/SM +fiddly +fide/F +Fidela/M +Fidelia/M +Fidelio/M +fidelity/IMS +Fidelity/M +Fidel/M +fidget/DSG +fidgety +Fidole/M +Fido/M +fiducial/Y +fiduciary/MS +fiefdom/S +fief/MS +fielded +fielder/IM +fielding +Fielding/M +Field/MGS +fieldstone/M +fieldworker/M +fieldwork/ZMRS +field/ZISMR +fiendishness/M +fiendish/YP +fiend/MS +fierceness/SM +fierce/RPTY +fierily +fieriness/MS +fiery/PTR +fie/S +fies/C +fiesta/MS +fife/DRSMZG +fifer/M +Fifi/M +Fifine/M +FIFO +fifteen/HRMS +fifteenths +fifths +fifth/Y +fiftieths +fifty/HSM +Figaro/M +figged +figging +fightback +fighter/MIS +fighting/IS +fight/ZSJRG +figment/MS +fig/MLS +Figueroa/M +figural +figuration/FSM +figurativeness/M +figurative/YP +figure/GFESD +figurehead/SM +figurer/SM +figure's +figurine/SM +figuring/S +Fijian/SM +Fiji/M +filamentary +filament/MS +filamentous +Filberte/M +Filbert/M +filbert/MS +Filberto/M +filch/SDG +filed/AC +file/KDRSGMZ +filename/SM +filer/KMCS +files/AC +filet's +filial/UY +Filia/M +filibusterer/M +filibuster/MDRSZG +Filide/M +filigreeing +filigree/MSD +filing/AC +filings +Filipino/SM +Filip/M +Filippa/M +Filippo/M +fill/BAJGSD +filled/U +filler/MS +filleting/M +fillet/MDSG +filling/M +fillip/MDGS +Fillmore/M +filly/SM +filmdom/M +Filmer/M +filminess/SM +filming/M +filmmaker/S +Filmore/M +film/SGMD +filmstrip/SM +filmy/RTP +Filofax/S +filtered/U +filterer/M +filter/RDMSZGB +filthily +filthiness/SM +filth/M +filths +filthy/TRSDGP +filtrated/I +filtrate/SDXMNG +filtrates/I +filtrating/I +filtration/IMS +finagler/M +finagle/RSDZG +finale/MS +finalist/MS +finality/MS +finalization/SM +finalize/GSD +final/SY +Fina/M +financed/A +finance/MGSDJ +finances/A +financial/Y +financier/DMGS +financing/A +Finch/M +finch/MS +findable/U +find/BRJSGZ +finder/M +finding/M +Findlay/M +Findley/M +fine/FGSCRDA +finely +fineness/MS +finery/MAS +fine's +finespun +finesse/SDMG +fingerboard/SM +fingerer/M +fingering/M +fingerless +fingerling/M +fingernail/MS +fingerprint/SGDM +finger/SGRDMJ +fingertip/MS +finial/SM +finical +finickiness/S +finicky/RPT +fining/M +finished/UA +finisher/M +finishes/A +finish/JZGRSD +finis/SM +finite/ISPY +finitely/C +finiteness/MIC +fink/GDMS +Finland/M +Finlay/M +Finley/M +Fin/M +Finnbogadottir/M +finned +Finnegan/M +finner +finning +Finnish +Finn/MS +finny/RT +fin/TGMDRS +Fiona/M +Fionna/M +Fionnula/M +fiord's +Fiorello/M +Fiorenze/M +Fiori/M +f/IRAC +firearm/SM +fireball/SM +fireboat/M +firebomb/MDSG +firebox/MS +firebrand/MS +firebreak/SM +firebrick/SM +firebug/SM +firecracker/SM +firedamp/SM +fired/U +firefight/JRGZS +firefly/MS +Firefox/M +fireguard/M +firehouse/MS +firelight/GZSM +fireman/M +firemen +fire/MS +fireplace/MS +fireplug/MS +firepower/SM +fireproof/SGD +firer/M +firesafe +fireside/SM +Firestone/M +firestorm/SM +firetrap/SM +firetruck/S +firewall/S +firewater/SM +firewood/MS +firework/MS +firing/M +firkin/M +firmament/MS +firmer +firmest +firm/ISFDG +firmly/I +firmness/MS +firm's +firmware/MS +firring +firstborn/S +firsthand +first/SY +firth/M +firths +fir/ZGJMDRHS +fiscal/YS +Fischbein/M +Fischer/M +fishbowl/MS +fishcake/S +fisher/M +Fisher/M +fisherman/M +fishermen/M +fishery/MS +fishhook/MS +fishily +fishiness/MS +fishing/M +fish/JGZMSRD +Fishkill/M +fishmeal +fishmonger/MS +fishnet/SM +fishpond/SM +fishtail/DMGS +fishtanks +fishwife/M +fishwives +fishy/TPR +Fiske/M +Fisk/M +fissile +fissionable/S +fission/BSDMG +fissure/MGSD +fistfight/SM +fistful/MS +fisticuff/SM +fist/MDGS +fistula/SM +fistulous +Fitchburg/M +Fitch/M +fitfulness/SM +fitful/PY +fitments +fitness/USM +fits/AK +fit's/K +fitted/UA +fitter/SM +fittest +fitting/AU +fittingly +fittingness/M +fittings +fit/UYPS +Fitzgerald/M +Fitz/M +Fitzpatrick/M +Fitzroy/M +fivefold +five/MRS +fiver/M +fixable +fixate/VNGXSD +fixatifs +fixation/M +fixative/S +fixedness/M +fixed/YP +fixer/SM +fixes/I +fixing/SM +fixity/MS +fixture/SM +fix/USDG +Fizeau/M +fizzer/M +fizzle/GSD +fizz/SRDG +fizzy/RT +fjord/SM +FL +flabbergast/GSD +flabbergasting/Y +flabbily +flabbiness/SM +flabby/TPR +flab/MS +flaccidity/MS +flaccid/Y +flack/SGDM +flagella/M +flagellate/DSNGX +flagellation/M +flagellum/M +flagged +flaggingly/U +flagging/SMY +flagman/M +flagmen +flag/MS +flagon/SM +flagpole/SM +flagrance/MS +flagrancy/SM +flagrant/Y +flagship/MS +flagstaff/MS +flagstone/SM +flail/SGMD +flair/SM +flaker/M +flake/SM +flakiness/MS +flak/RDMGS +flaky/PRT +Fla/M +flamb/D +flambeing +flambes +flamboyance/MS +flamboyancy/MS +flamboyant/YS +flamenco/SM +flamen/M +flameproof/DGS +flamer/IM +flame's +flame/SIGDR +flamethrower/SM +flamingo/SM +flaming/Y +flammability/ISM +flammable/SI +flam/MRNDJGZ +Flanagan/M +Flanders/M +flange/GMSD +flanker/M +flank/SGZRDM +flan/MS +flannel/DMGS +flannelet/MS +flannelette's +flapjack/SM +flap/MS +flapped +flapper/SM +flapping +flaps/M +flare/SDG +flareup/S +flaring/Y +flashback/SM +flashbulb/SM +flashcard/S +flashcube/MS +flasher/M +flashgun/S +flashily +flashiness/SM +flashing/M +flash/JMRSDGZ +flashlight/MS +flashy/TPR +flask/SM +flatbed/S +flatboat/MS +flatcar/MS +flatfeet +flatfish/SM +flatfoot/SGDM +flathead/M +flatiron/SM +flatland/RS +flatmate/M +flat/MYPS +flatness/MS +flatted +flattener/M +flatten/SDRG +flatter/DRSZG +flatterer/M +flattering/YU +flattery/SM +flattest/M +flatting +flattish +Flatt/M +flattop/MS +flatulence/SM +flatulent/Y +flatus/SM +flatware/MS +flatworm/SM +Flaubert/M +flaunting/Y +flaunt/SDG +flautist/SM +flavored/U +flavorer/M +flavorful +flavoring/M +flavorless +flavor/SJDRMZG +flavorsome +flaw/GDMS +flawlessness/MS +flawless/PY +flax/MSN +flaxseed/M +flayer/M +flay/RDGZS +fleabag/MS +fleabites +flea/SM +fleawort/M +fleck/GRDMS +Fledermaus/M +fledged/U +fledge/GSD +fledgling/SM +fleecer/M +fleece/RSDGMZ +fleeciness/SM +fleecy/RTP +fleeing +flee/RS +fleetingly/M +fleetingness/SM +fleeting/YP +fleet/MYRDGTPS +fleetness/MS +Fleischer/M +Fleischman/M +Fleisher/M +Fleming/M +Flemished/M +Flemish/GDSM +Flemishing/M +Flem/JGM +Flemming/M +flesher/M +fleshiness/M +flesh/JMYRSDG +fleshless +fleshly/TR +fleshpot/SM +fleshy/TPR +fletch/DRSGJ +fletcher/M +Fletcher/M +fletching/M +Fletch/MR +Fleurette/M +Fleur/M +flew/S +flews/M +flexed/I +flexibility/MSI +flexible/I +flexibly/I +flexitime's +flex/MSDAG +flextime/S +flexural +flexure/M +fl/GJD +flibbertigibbet/MS +flicker/GD +flickering/Y +flickery +flick/GZSRD +flier/M +flight/GMDS +flightiness/SM +flightless +flightpath +flighty/RTP +flimflammed +flimflamming +flimflam/MS +flimsily +flimsiness/MS +flimsy/PTRS +flincher/M +flinch/GDRS +flinching/U +flinger/M +fling/RMG +Flin/M +Flinn/M +flintiness/M +flintless +flintlock/MS +Flint/M +flint/MDSG +Flintstones +flinty/TRP +flipflop +flippable +flippancy/MS +flippant/Y +flipped +flipper/SM +flippest +flipping +flip/S +flirtation/SM +flirtatiousness/MS +flirtatious/PY +flirt/GRDS +flit/S +flitted +flitting +floater/M +float/SRDGJZ +floaty +flocculate/GNDS +flocculation/M +flock/SJDMG +floe/MS +flogged +flogger/SM +flogging/SM +flog/S +Flo/M +floodgate/MS +floodlight/DGMS +floodlit +floodplain/S +flood/SMRDG +floodwater/SM +floorboard/MS +floorer/M +flooring/M +floor/SJRDMG +floorspace +floorwalker/SM +floozy/SM +flophouse/SM +flop/MS +flopped +flopper/M +floppily +floppiness/SM +flopping +floppy/TMRSP +floral/SY +Flora/M +Florance/M +flora/SM +Florella/M +Florence/M +Florencia/M +Florentia/M +Florentine/S +Florenza/M +florescence/MIS +florescent/I +Flore/SM +floret/MS +Florette/M +Floria/M +Florian/M +Florida/M +Floridan/S +Floridian/S +floridness/SM +florid/YP +Florie/M +Florina/M +Florinda/M +Florine/M +florin/MS +Flori/SM +florist/MS +Flor/M +Florrie/M +Florri/M +Florry/M +Flory/M +floss/GSDM +Flossie/M +Flossi/M +Flossy/M +flossy/RST +flotation/SM +flotilla/SM +flotsam/SM +flounce/GDS +flouncing/M +flouncy/RT +flounder/SDG +flourisher/M +flourish/GSRD +flourishing/Y +flour/SGDM +floury/TR +flouter/M +flout/GZSRD +flowchart/SG +flowed +flowerbed/SM +flower/CSGD +flowerer/M +floweriness/SM +flowerless +flowerpot/MS +flower's +Flowers +flowery/TRP +flowing/Y +flow/ISG +flown +flowstone +Floyd/M +Flss/M +flt +flubbed +flubbing +flub/S +fluctuate/XSDNG +fluctuation/M +fluency/MS +fluently +fluent/SF +flue/SM +fluffiness/SM +fluff/SGDM +fluffy/PRT +fluidity/SM +fluidized +fluid/MYSP +fluidness/M +fluke/SDGM +fluky/RT +flume/SDGM +flummox/DSG +flu/MS +flung +flunkey's +flunk/SRDG +flunky/MS +fluoresce/GSRD +fluorescence/MS +fluorescent/S +fluoridate/XDSGN +fluoridation/M +fluoride/SM +fluorimetric +fluorinated +fluorine/SM +fluorite/MS +fluorocarbon/MS +fluoroscope/MGDS +fluoroscopic +flurry/GMDS +flushness/M +flush/TRSDPBG +fluster/DSG +fluter/M +flute/SRDGMJ +fluting/M +flutist/MS +flutter/DRSG +flutterer/M +fluttery +fluxed/A +fluxes/A +flux/IMS +fluxing +flyaway +flyblown +flyby/M +flybys +flycatcher/MS +flyer's +fly/JGBDRSTZ +flyleaf/M +flyleaves +Flynn/M +flyover/MS +flypaper/MS +flysheet/S +flyspeck/MDGS +flyswatter/S +flyway/MS +flyweight/MS +flywheel/MS +FM +Fm/M +FNMA/M +foal/MDSG +foaminess/MS +foam/MRDSG +foamy/RPT +fobbed +fobbing +fob/SM +focal/F +focally +Foch/M +foci/M +focused/AU +focuser/M +focuses/A +focus/SRDMBG +fodder/GDMS +foe/SM +foetid +FOFL +fogbound +fogged/C +foggily +fogginess/MS +fogging/C +foggy/RPT +foghorn/SM +fogs/C +fog/SM +fogyish +fogy/SM +foible/MS +foil/GSD +foist/GDS +Fokker/M +foldable/U +foldaway/S +folded/AU +folder/M +foldout/MS +fold/RDJSGZ +folds/UA +Foley/M +foliage/MSD +foliate/CSDXGN +foliation/CM +folio/SDMG +folklike +folklore/MS +folkloric +folklorist/SM +folk/MS +folksiness/MS +folksinger/S +folksinging/S +folksong/S +folksy/TPR +folktale/S +folkway/S +foll +follicle/SM +follicular +follower/M +follow/JSZBGRD +followup's +folly/SM +Folsom +fol/Y +Fomalhaut/M +fomentation/SM +fomenter/M +foment/RDSG +Fonda/M +fondant/SM +fondle/GSRD +fondler/M +fondness/MS +fond/PMYRDGTS +fondue/MS +Fons +Fonsie/M +Fontainebleau/M +Fontaine/M +Fontana/M +fontanelle's +fontanel/MS +font/MS +Fonzie/M +Fonz/M +foodie/S +food/MS +foodstuff/MS +foolery/MS +foolhardily +foolhardiness/SM +foolhardy/PTR +foolishness/SM +foolish/PRYT +fool/MDGS +foolproof +foolscap/MS +footage/SM +football/SRDMGZ +footbridge/SM +Foote/M +footer/M +footfall/SM +foothill/SM +foothold/MS +footing/M +footless +footlights +footling +footlocker/SM +footloose +footman/M +footmarks +footmen +footnote/MSDG +footpad/SM +footpath/M +footpaths +footplate/M +footprint/MS +footrace/S +footrest/MS +footsie/SM +foot/SMRDGZJ +footsore +footstep/SM +footstool/SM +footwear/M +footwork/SM +fop/MS +fopped +foppery/MS +fopping +foppishness/SM +foppish/YP +forage/GSRDMZ +forager/M +forayer/M +foray/SGMRD +forbade +forbearance/SM +forbearer/M +forbear/MRSG +Forbes/M +forbidden +forbiddingness/M +forbidding/YPS +forbid/S +forbore +forborne +forced/Y +forcefield/MS +forcefulness/MS +forceful/PY +forceps/M +forcer/M +force/SRDGM +forcibleness/M +forcible/P +forcibly +fordable/U +Fordham/M +Ford/M +ford/SMDBG +forearm/GSDM +forebear/MS +forebode/GJDS +forebodingness/M +foreboding/PYM +forecaster/M +forecastle/MS +forecast/SZGR +foreclose/GSD +foreclosure/MS +forecourt/SM +foredoom/SDG +forefather/SM +forefeet +forefinger/MS +forefoot/M +forefront/SM +foregoer/M +foregoing/S +foregone +foregos +foreground/MGDS +forehand/S +forehead/MS +foreigner/M +foreignness/SM +foreign/PRYZS +foreknew +foreknow/GS +foreknowledge/MS +foreknown +foreleg/MS +forelimb/MS +forelock/MDSG +foreman/M +Foreman/M +foremast/SM +foremen +foremost +forename/DSM +forenoon/SM +forensically +forensic/S +forensics/M +foreordain/DSG +forepart/MS +forepaws +forepeople +foreperson/S +foreplay/MS +forequarter/SM +forerunner/MS +fore/S +foresail/SM +foresaw +foreseeable/U +foreseeing +foreseen/U +foreseer/M +foresee/ZSRB +foreshadow/SGD +foreshore/M +foreshorten/DSG +foresightedness/SM +foresighted/PY +foresight/SMD +foreskin/SM +forestaller/M +forestall/LGSRD +forestallment/M +forestation/MCS +forestations/A +forest/CSAGD +Forester/M +forester/SM +forestland/S +Forest/MR +forestry/MS +forest's +foretaste/MGSD +foreteller/M +foretell/RGS +forethought/MS +foretold +forevermore +forever/PS +forewarner/M +forewarn/GSJRD +forewent +forewoman/M +forewomen +foreword/SM +forfeiter/M +forfeiture/MS +forfeit/ZGDRMS +forfend/GSD +forgather/GSD +forgave +forged/A +forge/JVGMZSRD +forger/M +forgery/MS +forges/A +forgetfulness/SM +forgetful/PY +forget/SV +forgettable/U +forgettably/U +forgetting +forging/M +forgivable/U +forgivably/U +forgiven +forgiveness/SM +forgiver/M +forgive/SRPBZG +forgivingly +forgivingness/M +forgiving/UP +forgoer/M +forgoes +forgone +forgo/RSGZ +forgot +forgotten/U +for/HT +forkful/S +fork/GSRDM +forklift/DMSG +forlornness/M +forlorn/PTRY +formability/AM +formaldehyde/SM +formalin/M +formalism/SM +formalistic +formalist/SM +formality/SMI +formal/IY +formalization/SM +formalized/U +formalizer/M +formalizes/I +formalize/ZGSRD +formalness/M +formals +formant/MIS +format/AVS +formate/MXGNSD +formation/AFSCIM +formatively/I +formativeness/IM +formative/SYP +format's +formatted/UA +formatter/A +formatters +formatter's +formatting/A +form/CGSAFDI +formed/U +former/FSAI +formerly +formfitting +formic +Formica/MS +formidableness/M +formidable/P +formidably +formlessness/MS +formless/PY +Formosa/M +Formosan +form's +formulaic +formula/SM +formulate/AGNSDX +formulated/U +formulation/AM +formulator/SM +fornicate/GNXSD +fornication/M +fornicator/SM +Forrester/M +Forrest/RM +forsaken +forsake/SG +forsook +forsooth +Forster/M +forswear/SG +forswore +forsworn +forsythia/MS +Fortaleza/M +forte/MS +forthcome/JG +forthcoming/U +FORTH/M +forthrightness/SM +forthright/PYS +forthwith +fortieths +fortification/MS +fortified/U +fortifier/SM +fortify/ADSG +fortiori +fortissimo/S +fortitude/SM +fortnightly/S +fortnight/MYS +FORTRAN +Fortran/M +fortress/GMSD +fort/SM +fortuitousness/SM +fortuitous/YP +fortuity/MS +fortunateness/M +fortunate/YUS +fortune/MGSD +fortuneteller/SM +fortunetelling/SM +forty/SRMH +forum/MS +forwarder/M +forwarding/M +forwardness/MS +forward/PTZSGDRY +forwent +fossiliferous +fossilization/MS +fossilized/U +fossilize/GSD +fossil/MS +Foss/M +fosterer/M +Foster/M +foster/SRDG +Foucault/M +fought +foulard/SM +foulmouth/D +foulness/MS +fouls/M +foul/SYRDGTP +foundational +foundation/SM +founded/UF +founder/MDG +founder's/F +founding/F +foundling/MS +found/RDGZS +foundry/MS +founds/KF +fountainhead/SM +fountain/SMDG +fount/MS +fourfold +Fourier/M +fourpence/M +fourpenny +fourposter/SM +fourscore/S +four/SHM +foursome/SM +foursquare +fourteener/M +fourteen/SMRH +fourteenths +Fourth +fourths +Fourths +fourth/Y +fovea/M +fowler/M +Fowler/M +fowling/M +fowl/SGMRD +foxfire/SM +foxglove/SM +Foxhall/M +foxhole/SM +foxhound/SM +foxily +foxiness/MS +foxing/M +fox/MDSG +Fox/MS +foxtail/M +foxtrot/MS +foxtrotted +foxtrotting +foxy/TRP +foyer/SM +FPO +fps +fr +fracas/SM +fractal/SM +fractional/Y +fractionate/DNG +fractionation/M +fractioned +fractioning +fraction/ISMA +fractiousness/SM +fractious/PY +fracture/MGDS +fragile/Y +fragility/MS +fragmentarily +fragmentariness/M +fragmentary/P +fragmentation/MS +fragment/SDMG +Fragonard/M +fragrance/SM +fragrant/Y +frailness/MS +frail/STPYR +frailty/MS +framed/U +framer/M +frame/SRDJGMZ +framework/SM +framing/M +Francaise/M +France/MS +Francene/M +Francesca/M +Francesco/M +franchisee/S +franchise/ESDG +franchiser/SM +franchise's +Franchot/M +Francie/M +Francine/M +Francis +Francisca/M +Franciscan/MS +Francisco/M +Franciska/M +Franciskus/M +francium/MS +Francklin/M +Francklyn/M +Franck/M +Francoise/M +Francois/M +Franco/M +francophone/M +franc/SM +Francyne/M +frangibility/SM +frangible +Frankel/M +Frankenstein/MS +franker/M +Frankford/M +Frankfort/M +Frankfurter/M +frankfurter/MS +Frankfurt/RM +Frankie/M +frankincense/MS +Frankish/M +franklin/M +Franklin/M +Franklyn/M +frankness/MS +frank/SGTYRDP +Frank/SM +Franky/M +Fran/MS +Frannie/M +Franni/M +Franny/M +Fransisco/M +frantically +franticness/M +frantic/PY +Frants/M +Franzen/M +Franz/NM +frapp +frappeed +frappeing +frappes +Frasco/M +Fraser/M +Frasier/M +Frasquito/M +fraternal/Y +fraternity/MSF +fraternization/SM +fraternize/GZRSD +fraternizer/M +fraternizing/U +frat/MS +fratricidal +fratricide/MS +fraud/CS +fraud's +fraudsters +fraudulence/S +fraudulent/YP +fraught/SGD +Fraulein/S +Frau/MN +fray/CSDG +Frayda/M +Frayne/M +fray's +Fraze/MR +Frazer/M +Frazier/M +frazzle/GDS +freakishness/SM +freakish/YP +freak/SGDM +freaky/RT +freckle/GMDS +freckly/RT +Freda/M +Freddie/M +Freddi/M +Freddy/M +Fredek/M +Fredelia/M +Frederica/M +Frederich/M +Fredericka/M +Frederick/MS +Frederic/M +Frederico/M +Fredericton/M +Frederigo/M +Frederik/M +Frederique/M +Fredholm/M +Fredia/M +Fredi/M +Fred/M +Fredra/M +Fredrick/M +Fredrickson/M +Fredric/M +Fredrika/M +freebase/GDS +freebie/MS +freebooter/M +freeboot/ZR +freeborn +freedman/M +Freedman/M +freedmen +freedom/MS +freehand/D +freehanded/Y +freeholder/M +freehold/ZSRM +freeing/S +freelance/SRDGZM +Freeland/M +freeloader/M +freeload/SRDGZ +Free/M +freeman/M +Freeman/M +freemasonry/M +Freemasonry/MS +Freemason/SM +freemen +Freemon/M +freeness/M +Freeport/M +freestanding +freestone/SM +freestyle/SM +freethinker/MS +freethinking/S +Freetown/M +freeway/MS +freewheeler/M +freewheeling/P +freewheel/SRDMGZ +freewill +free/YTDRSP +freezable +freezer/SM +freeze/UGSA +freezing/S +Freida/M +freighter/M +freight/ZGMDRS +Fremont/M +Frenchman/M +French/MDSG +Frenchmen +Frenchwoman/M +Frenchwomen +frenetically +frenetic/S +frenzied/Y +frenzy/MDSG +freon/S +Freon/SM +freq +frequency/ISM +frequented/U +frequenter/MS +frequentest +frequenting +frequent/IY +frequentness/M +frequents +fresco/DMG +frescoes +fresh/AZSRNDG +freshener/M +freshen/SZGDR +fresher/MA +freshest +freshet/SM +freshly +freshman/M +freshmen +freshness/MS +freshwater/SM +Fresnel/M +Fresno/M +fretboard +fretfulness/MS +fretful/PY +fret/S +fretsaw/S +fretted +fretting +fretwork/MS +Freudian/S +Freud/M +Freya/M +Frey/M +friableness/M +friable/P +friary/MS +friar/YMS +fricasseeing +fricassee/MSD +frication/M +fricative/MS +Frick/M +frictional/Y +frictionless/Y +friction/MS +Friday/SM +fridge/SM +fried/A +Frieda/M +Friedan/M +friedcake/SM +Friederike/M +Friedman/M +Friedrich/M +Friedrick/M +friendlessness/M +friendless/P +friendlies +friendlily +friendliness/USM +friendly/PUTR +friend/SGMYD +friendship/MS +frier's +fries/M +frieze/SDGM +frigate/SM +Frigga/M +frigged +frigging/S +frighten/DG +frightening/Y +frightfulness/MS +frightful/PY +fright/GXMDNS +Frigidaire/M +frigidity/MS +frigidness/SM +frigid/YP +frig/S +frill/MDGS +frilly/RST +Fri/M +fringe/IGSD +fringe's +frippery/SM +Frisbee/MS +Frisco/M +Frisian/SM +frisker/M +friskily +friskiness/SM +frisk/RDGS +frisky/RTP +frisson/M +Frito/M +fritterer/M +fritter/RDSG +Fritz/M +fritz/SM +frivolity/MS +frivolousness/SM +frivolous/PY +frizz/GYSD +frizzle/DSG +frizzly/RT +frizzy/RT +Fr/MD +Frobisher/M +frocking/M +frock's +frock/SUDGC +frogged +frogging +frogman/M +frogmarched +frogmen +frog/MS +fro/HS +Froissart/M +frolicked +frolicker/SM +frolicking +frolic/SM +frolicsome +from +Fromm/M +frond/SM +frontage/MS +frontal/SY +Frontenac/M +front/GSFRD +frontier/SM +frontiersman/M +frontiersmen +frontispiece/SM +frontrunner's +front's +frontward/S +frosh/M +Frostbelt/M +frostbite/MS +frostbit/G +frostbiting/M +frostbitten +frost/CDSG +frosteds +frosted/U +frostily +frostiness/SM +frosting/MS +Frost/M +frost's +frosty/PTR +froth/GMD +frothiness/SM +froths +frothy/TRP +froufrou/MS +frowardness/MS +froward/P +frowner/M +frowning/Y +frown/RDSG +frowzily +frowziness/SM +frowzy/RPT +frozenness/M +frozen/YP +froze/UA +fructify/GSD +fructose/MS +Fruehauf/M +frugality/SM +frugal/Y +fruitcake/SM +fruiterer/M +fruiter/RM +fruitfuller +fruitfullest +fruitfulness/MS +fruitful/UYP +fruit/GMRDS +fruitiness/MS +fruition/SM +fruitlessness/MS +fruitless/YP +fruity/RPT +frumpish +frump/MS +frumpy/TR +Frunze/M +frustrater/M +frustrate/RSDXNG +frustrating/Y +frustration/M +frustum/SM +Frye/M +fryer/MS +Fry/M +fry/NGDS +F's +f's/KA +FSLIC +ft/C +FTC +FTP +fuchsia/MS +Fuchs/M +fucker/M! +fuck/GZJRDMS! +FUD +fuddle/GSD +fudge/GMSD +fuel/ASDG +fueler/SM +fuel's +Fuentes/M +fugal +Fugger/M +fugitiveness/M +fugitive/SYMP +fugue/GMSD +fuhrer/S +Fuji/M +Fujitsu/M +Fujiyama +Fukuoka/M +Fulani/M +Fulbright/M +fulcrum/SM +fulfilled/U +fulfiller/M +fulfill/GLSRD +fulfillment/MS +fullback/SMG +fuller/DMG +Fuller/M +Fullerton/M +fullish +fullness/MS +full/RDPSGZT +fullstops +fullword/SM +fully +fulminate/XSDGN +fulmination/M +fulness's +fulsomeness/SM +fulsome/PY +Fulton/M +Fulvia/M +fumble/GZRSD +fumbler/M +fumbling/Y +fume/DSG +fumigant/MS +fumigate/NGSDX +fumigation/M +fumigator/SM +fuming/Y +fumy/TR +Funafuti +functionalism/M +functionalist/SM +functionality/S +functional/YS +functionary/MS +function/GSMD +functor/SM +fundamentalism/SM +fundamentalist/SM +fundamental/SY +fund/ASMRDZG +funded/U +fundholders +fundholding +funding/S +Fundy/M +funeral/MS +funerary +funereal/Y +funfair/M +fungal/S +fungible/M +fungicidal +fungicide/SM +fungi/M +fungoid/S +fungous +fungus/M +funicular/SM +funk/GSDM +funkiness/S +funky/RTP +fun/MS +funned +funnel/SGMD +funner +funnest +funnily/U +funniness/SM +funning +funny/RSPT +furbelow/MDSG +furbisher/M +furbish/GDRSA +furiousness/M +furious/RYP +furlong/MS +furlough/DGM +furloughs +furl/UDGS +furn +furnace/GMSD +furnished/U +furnisher/MS +furnish/GASD +furnishing/SM +furniture/SM +furore/MS +furor/MS +fur/PMS +furred +furrier/M +furriness/SM +furring/SM +furrow/DMGS +furry/RTZP +furtherance/MS +furtherer/M +furthermore +furthermost +further/TGDRS +furthest +furtiveness/SM +furtive/PY +fury/SM +furze/SM +fusebox/S +fusee/SM +fuse/FSDAGCI +fuselage/SM +fuse's/A +Fushun/M +fusibility/SM +fusible/I +fusiform +fusilier/MS +fusillade/SDMG +fusion/KMFSI +fussbudget/MS +fusser/M +fussily +fussiness/MS +fusspot/SM +fuss/SRDMG +fussy/PTR +fustian/MS +fustiness/MS +fusty/RPT +fut +futileness/M +futile/PY +futility/MS +futon/S +future/SM +futurism/SM +futuristic/S +futurist/S +futurity/MS +futurologist/S +futurology/MS +futz/GSD +fuze's +Fuzhou/M +Fuzzbuster/M +fuzzily +fuzziness/SM +fuzz/SDMG +fuzzy/PRT +fwd +FWD +fwy +FY +FYI +GA +gabardine/SM +gabbed +Gabbey/M +Gabbie/M +Gabbi/M +gabbiness/S +gabbing +gabble/SDG +Gabby/M +gabby/TRP +Gabe/M +gaberdine's +Gabey/M +gabfest/MS +Gabie/M +Gabi/M +gable/GMSRD +Gable/M +Gabonese +Gabon/M +Gaborone/M +Gabriela/M +Gabriele/M +Gabriella/M +Gabrielle/M +Gabriellia/M +Gabriell/M +Gabriello/M +Gabriel/M +Gabrila/M +gab/S +Gaby/M +Gacrux/M +gadabout/MS +gadded +gadder/MS +gadding +gadfly/MS +gadgetry/MS +gadget/SM +gadolinium/MS +gad/S +Gadsden/M +Gaea/M +Gaelan/M +Gaelic/M +Gael/SM +Gae/M +gaffe/MS +gaffer/M +gaff/SGZRDM +gaga +Gagarin/M +gag/DRSG +Gage/M +gager/M +gage/SM +gagged +gagging +gaggle/SDG +gagwriter/S +gaiety/MS +Gaile/M +Gail/M +gaily +gain/ADGS +gainer/SM +Gaines/M +Gainesville/M +gainfulness/M +gainful/YP +gaining/S +gainly/U +gainsaid +gainsayer/M +gainsay/RSZG +Gainsborough/M +gaiter/M +gait/GSZMRD +Gaithersburg/M +galactic +Galahad/MS +Galapagos/M +gal/AS +gala/SM +Galatea/M +Galatia/M +Galatians/M +Galaxy/M +galaxy/MS +Galbraith/M +Galbreath/M +gale/AS +Gale/M +galen +galena/MS +galenite/M +Galen/M +gale's +Galibi/M +Galilean/MS +Galilee/M +Galileo/M +Galina/M +Gallagher/M +gallanted +gallanting +gallantry/MS +gallants +gallant/UY +Gallard/M +gallbladder/MS +Gallegos/M +galleon/SM +galleria/S +gallery/MSDG +galley/MS +Gallic +Gallicism/SM +gallimaufry/MS +galling/Y +gallium/SM +gallivant/GDS +Gall/M +gallonage/M +gallon/SM +galloper/M +gallop/GSRDZ +Galloway/M +gallows/M +gall/SGMD +gallstone/MS +Gallup/M +Gal/MN +Galois/M +galoot/MS +galore/S +galosh/GMSD +gal's +Galsworthy/M +galumph/GD +galumphs +galvanic +Galvani/M +galvanism/MS +galvanization/SM +galvanize/SDG +Galvan/M +galvanometer/SM +galvanometric +Galven/M +Galveston/M +Galvin/M +Ga/M +Gamaliel/M +Gama/M +Gambia/M +Gambian/S +gambit/MS +gamble/GZRSD +Gamble/M +gambler/M +gambol/SGD +gamecock/SM +gamekeeper/MS +gameness/MS +game/PJDRSMYTZG +gamesmanship/SM +gamesmen +gamester/M +gamest/RZ +gamete/MS +gametic +gamine/SM +gaminess/MS +gaming/M +gamin/MS +gamma/MS +gammon/DMSG +Gamow/M +gamut/MS +gamy/TRP +gander/DMGS +Gandhian +Gandhi/M +gangbusters +ganger/M +Ganges/M +gang/GRDMS +gangland/SM +ganglia/M +gangling +ganglionic +ganglion/M +gangplank/SM +gangrene/SDMG +gangrenous +gangster/SM +Gangtok/M +gangway/MS +Gan/M +gannet/SM +Gannie/M +Gannon/M +Ganny/M +gantlet/GMDS +Gantry/M +gantry/MS +Ganymede/M +GAO +gaoler/M +gaol/MRDGZS +gaper/M +gape/S +gaping/Y +gapped +gapping +gap/SJMDRG +garage/GMSD +Garald/M +garbageman/M +garbage/SDMG +garbanzo/MS +garb/DMGS +garbler/M +garble/RSDG +Garbo/M +Garcia/M +garon/SM +gardener/M +Gardener/M +gardenia/SM +gardening/M +garden/ZGRDMS +Gardie/M +Gardiner/M +Gard/M +Gardner/M +Gardy/M +Garek/M +Gare/MH +Gareth/M +Garey/M +Garfield/M +garfish/MS +Garfunkel/M +Gargantua/M +gargantuan +gargle/SDG +gargoyle/DSM +Garibaldi/M +Garik/M +garishness/MS +garish/YP +Garland/M +garland/SMDG +garlicked +garlicking +garlicky +garlic/SM +garment/MDGS +Gar/MH +Garner/M +garner/SGD +Garnet/M +garnet/SM +Garnette/M +Garnett/M +garnish/DSLG +garnisheeing +garnishee/SDM +garnishment/MS +Garold/M +garote's +garotte's +Garrard/M +garred +Garrek/M +Garreth/M +Garret/M +garret/SM +Garrett/M +Garrick/M +Garrik/M +garring +Garrison/M +garrison/SGMD +garroter/M +garrote/SRDMZG +Garrot/M +garrotte's +Garrott/M +garrulity/SM +garrulousness/MS +garrulous/PY +Garry/M +gar/SLM +garter/SGDM +Garth/M +Garvey/M +Garvin/M +Garv/M +Garvy/M +Garwin/M +Garwood/M +Gary/M +Garza/M +gasbag/MS +Gascony/M +gaseousness/M +gaseous/YP +gases/C +gas/FC +gash/GTMSRD +gasification/M +gasifier/M +gasify/SRDGXZN +gasket/SM +gaslight/DMS +gasohol/S +gasoline/MS +gasometer/M +Gaspard/M +Gaspar/M +Gasparo/M +gasper/M +Gasper/M +gasp/GZSRD +gasping/Y +gas's +gassed/C +Gasser/M +gasser/MS +Gasset/M +gassiness/M +gassing/SM +gassy/PTR +Gaston/M +gastric +gastritides +gastritis/MS +gastroenteritides +gastroenteritis/M +gastrointestinal +gastronome/SM +gastronomic +gastronomical/Y +gastronomy/MS +gastropod/SM +gasworks/M +gateau/MS +gateaux +gatecrash/GZSRD +gatehouse/MS +gatekeeper/SM +gate/MGDS +gatepost/SM +Gates +gateway/MS +gathered/IA +gatherer/M +gathering/M +gather/JRDZGS +gathers/A +Gatlinburg/M +Gatling/M +Gatorade/M +gator/MS +Gatsby/M +Gatun/M +gaucheness/SM +gaucherie/SM +gauche/TYPR +gaucho/SM +gaudily +gaudiness/MS +gaudy/PRST +gauge/SM +gaugeable +gauger/M +Gauguin/M +Gaulish/M +Gaulle/M +Gaul/MS +Gaultiero/M +gauntlet/GSDM +Gauntley/M +gauntness/MS +gaunt/PYRDSGT +gauss/C +gausses +Gaussian +Gauss/M +gauss's +Gautama/M +Gauthier/M +Gautier/M +gauze/SDGM +gauziness/MS +gauzy/TRP +Gavan/M +gave +gavel/GMDS +Gaven/M +Gavin/M +Gav/MN +gavotte/MSDG +Gavra/M +Gavrielle/M +Gawain/M +Gawen/M +gawkily +gawkiness/MS +gawk/SGRDM +gawky/RSPT +Gayel/M +Gayelord/M +Gaye/M +gayety's +Gayla/M +Gayleen/M +Gaylene/M +Gayler/M +Gayle/RM +Gaylord/M +Gaylor/M +Gay/M +gayness/SM +Gaynor/M +gay/RTPS +Gaza/M +gazebo/SM +gaze/DRSZG +gazelle/MS +gazer/M +gazetteer/SGDM +gazette/MGSD +Gaziantep/M +gazillion/S +gazpacho/MS +GB +G/B +Gdansk/M +Gd/M +GDP +Gearalt/M +Gearard/M +gearbox/SM +gear/DMJSG +gearing/M +gearshift/MS +gearstick +gearwheel/SM +Geary/M +gecko/MS +GED +geegaw's +geeing +geek/SM +geeky/RT +geese/M +geest/M +gee/TDS +geezer/MS +Gehenna/M +Gehrig/M +Geiger/M +Geigy/M +geisha/M +gelatinousness/M +gelatinous/PY +gelatin/SM +gelcap +gelding/M +geld/JSGD +gelid +gelignite/MS +gelled +gelling +gel/MBS +Gelya/M +Ge/M +GE/M +Gemini/SM +gemlike +Gemma/M +gemmed +gemming +gem/MS +gemological +gemologist/MS +gemology/MS +gemstone/SM +gen +Gena/M +Genaro/M +gendarme/MS +gender/DMGS +genderless +genealogical/Y +genealogist/SM +genealogy/MS +Gene/M +gene/MS +generalissimo/SM +generalist/MS +generality/MS +generalizable/SM +generalization/MS +generalized/U +generalize/GZBSRD +generalizer/M +general/MSPY +generalness/M +generalship/SM +genera/M +generate/CXAVNGSD +generational +generation/MCA +generative/AY +generators/A +generator/SM +generically +generic/PS +generosity/MS +generously/U +generousness/SM +generous/PY +Genesco/M +genesis/M +Genesis/M +genes/S +genetically +geneticist/MS +genetic/S +genetics/M +Genet/M +Geneva/M +Genevieve/M +Genevra/M +Genghis/M +geniality/FMS +genially/F +genialness/M +genial/PY +Genia/M +genies/K +genie/SM +genii/M +genitalia +genitals +genital/YF +genitive/SM +genitourinary +genius/SM +Gen/M +Genna/M +Gennie/M +Gennifer/M +Genni/M +Genny/M +Genoa/SM +genocidal +genocide/SM +Geno/M +genome/SM +genotype/MS +Genovera/M +genre/MS +gent/AMS +genteelness/MS +genteel/PRYT +gentian/SM +gentile/S +Gentile's +gentility/MS +gentlefolk/S +gentlemanliness/M +gentlemanly/U +gentleman/YM +gentlemen +gentleness/SM +gentle/PRSDGT +gentlewoman/M +gentlewomen/M +gently +gentrification/M +gentrify/NSDGX +Gentry/M +gentry/MS +genuflect/GDS +genuflection/MS +genuineness/SM +genuine/PY +genus +Genvieve/M +geocentric +geocentrically +geocentricism +geochemical/Y +geochemistry/MS +geochronology/M +geodesic/S +geode/SM +geodesy/MS +geodetic/S +Geoff/M +Geoffrey/M +Geoffry/M +geog +geographer/MS +geographic +geographical/Y +geography/MS +geologic +geological/Y +geologist/MS +geology/MS +geom +Geo/M +geomagnetic +geomagnetically +geomagnetism/SM +geometer/MS +geometrical/Y +geometrician/M +geometric/S +geometry/MS +geomorphological +geomorphology/M +geophysical/Y +geophysicist/MS +geophysics/M +geopolitical/Y +geopolitic/S +geopolitics/M +Georas/M +Geordie/M +Georgeanna/M +Georgeanne/M +Georgena/M +George/SM +Georgeta/M +Georgetown/M +Georgetta/M +Georgette/M +Georgia/M +Georgiana/M +Georgianna/M +Georgianne/M +Georgian/S +Georgie/M +Georgi/M +Georgina/M +Georgine/M +Georg/M +Georgy/M +geostationary +geosynchronous +geosyncline/SM +geothermal +geothermic +Geralda/M +Geraldine/M +Gerald/M +geranium/SM +Gerard/M +Gerardo/M +Gerber/M +gerbil/MS +Gerda/M +Gerek/M +Gerhardine/M +Gerhard/M +Gerhardt/M +Gerianna/M +Gerianne/M +geriatric/S +geriatrics/M +Gerick/M +Gerik/M +Geri/M +Geritol/M +Gerladina/M +Ger/M +Germaine/M +Germain/M +Germana/M +germane +Germania/M +Germanic/M +germanium/SM +germanized +German/SM +Germantown/M +Germany/M +Germayne/M +germen/M +germicidal +germicide/MS +germinal/Y +germinated/U +germinate/XVGNSD +germination/M +germinative/Y +germ/MNS +Gerome/M +Geronimo/M +gerontocracy/M +gerontological +gerontologist/SM +gerontology/SM +Gerrard/M +Gerrie/M +Gerrilee/M +Gerri/M +Gerry/M +gerrymander/SGD +Gershwin/MS +Gerta/M +Gertie/M +Gerti/M +Gert/M +Gertruda/M +Gertrude/M +Gertrudis/M +Gertrud/M +Gerty/M +gerundive/M +gerund/SVM +Gery/M +gestalt/M +gestapo/S +Gestapo/SM +gestate/SDGNX +gestational +gestation/M +gesticulate/XSDVGN +gesticulation/M +gesticulative/Y +gestural +gesture/SDMG +gesundheit +getaway/SM +Gethsemane/M +get/S +getter/SDM +getting +Getty/M +Gettysburg/M +getup/MS +gewgaw/MS +Gewrztraminer +geyser/GDMS +Ghanaian/MS +Ghana/M +Ghanian's +ghastliness/MS +ghastly/TPR +ghat/MS +Ghats/M +Ghent/M +Gherardo/M +gherkin/SM +ghetto/DGMS +ghettoize/SDG +Ghibelline/M +ghostlike +ghostliness/MS +ghostly/TRP +ghost/SMYDG +ghostwrite/RSGZ +ghostwritten +ghostwrote +ghoulishness/SM +ghoulish/PY +ghoul/SM +GHQ +GHz +GI +Giacinta/M +Giacobo/M +Giacometti/M +Giacomo/M +Giacopo/M +Giana/M +Gianina/M +Gian/M +Gianna/M +Gianni/M +Giannini/M +giantess/MS +giantkiller +giant/SM +Giauque/M +Giavani/M +gibber/DGS +gibberish/MS +gibbet/MDSG +Gibbie/M +Gibb/MS +Gibbon/M +gibbon/MS +gibbousness/M +gibbous/YP +Gibby/M +gibe/GDRS +giber/M +giblet/MS +Gib/M +Gibraltar/MS +Gibson/M +giddap +giddily +giddiness/SM +Giddings/M +giddy/GPRSDT +Gide/M +Gideon/MS +Gielgud/M +Gienah/M +Giffard/M +Giffer/M +Giffie/M +Gifford/M +Giff/RM +Giffy/M +giftedness/M +gifted/PY +gift/SGMD +gigabyte/S +gigacycle/MS +gigahertz/M +gigantically +giganticness/M +gigantic/P +gigavolt +gigawatt/M +gigged +gigging +giggler/M +giggle/RSDGZ +giggling/Y +giggly/TR +Gigi/M +gig/MS +GIGO +gigolo/MS +gila +Gila/M +Gilberta/M +Gilberte/M +Gilbertina/M +Gilbertine/M +gilbert/M +Gilbert/M +Gilberto/M +Gilbertson/M +Gilburt/M +Gilchrist/M +Gilda/M +gilder/M +gilding/M +gild/JSGZRD +Gilead/M +Gilemette/M +Giles +Gilgamesh/M +Gilkson/M +Gillan/M +Gilles +Gillespie/M +Gillette/M +Gilliam/M +Gillian/M +Gillie/M +Gilligan/M +Gilli/M +Gill/M +gill/SGMRD +Gilly/M +Gilmore/M +Gil/MY +gilt/S +gimbaled +gimbals +Gimbel/M +gimcrackery/SM +gimcrack/S +gimlet/MDSG +gimme/S +gimmick/GDMS +gimmickry/MS +gimmicky +gimp/GSMD +gimpy/RT +Gina/M +Ginelle/M +Ginevra/M +gingerbread/SM +gingerliness/M +gingerly/P +Ginger/M +ginger/SGDYM +gingersnap/SM +gingery +gingham/SM +gingivitis/SM +Gingrich/M +ginkgoes +ginkgo/M +ginmill +gin/MS +ginned +Ginnie/M +Ginnifer/M +Ginni/M +ginning +Ginny/M +Gino/M +Ginsberg/M +Ginsburg/M +ginseng/SM +Gioconda/M +Giordano/M +Giorgia/M +Giorgi/M +Giorgio/M +Giorgione/M +Giotto/M +Giovanna/M +Giovanni/M +Gipsy's +giraffe/MS +Giralda/M +Giraldo/M +Giraud/M +Giraudoux/M +girded/U +girder/M +girdle/GMRSD +girdler/M +gird/RDSGZ +girlfriend/MS +girlhood/SM +girlie/M +girlishness/SM +girlish/YP +girl/MS +giro/M +girt/GDS +girth/MDG +girths +Gisela/M +Giselbert/M +Gisele/M +Gisella/M +Giselle/M +Gish/M +gist/MS +git/M +Giuditta/M +Giulia/M +Giuliano/M +Giulietta/M +Giulio/M +Giuseppe/M +Giustina/M +Giustino/M +Giusto/M +giveaway/SM +giveback/S +give/HZGRS +given/SP +giver/M +giving/Y +Giza/M +Gizela/M +gizmo's +gizzard/SM +Gk/M +glac/DGS +glacial/Y +glaciate/XNGDS +glaciation/M +glacier/SM +glaciological +glaciologist/M +glaciology/M +gladded +gladden/GDS +gladder +gladdest +gladding +gladdy +glade/SM +gladiatorial +gladiator/SM +Gladi/M +gladiola/MS +gladioli +gladiolus/M +gladly/RT +Glad/M +gladness/MS +gladsome/RT +Gladstone/MS +Gladys +glad/YSP +glamor/DMGS +glamorization/MS +glamorizer/M +glamorize/SRDZG +glamorousness/M +glamorous/PY +glance/GJSD +glancing/Y +glanders/M +glandes +glandular/Y +gland/ZSM +glans/M +glare/SDG +glaringness/M +glaring/YP +Glaser/M +Glasgow/M +glasnost/S +glassblower/S +glassblowing/MS +glassful/MS +glass/GSDM +glasshouse/SM +glassily +glassiness/SM +glassless +Glass/M +glassware/SM +glasswort/M +glassy/PRST +Glastonbury/M +Glaswegian/S +glaucoma/SM +glaucous +glazed/U +glazer/M +glaze/SRDGZJ +glazier/SM +glazing/M +gleam/MDGS +gleaner/M +gleaning/M +glean/RDGZJS +Gleason/M +Gleda/M +gleed/M +glee/DSM +gleefulness/MS +gleeful/YP +gleeing +Glendale/M +Glenda/M +Glenden/M +Glendon/M +Glenine/M +Glen/M +Glenna/M +Glennie/M +Glennis/M +Glenn/M +glen/SM +glibber +glibbest +glibness/MS +glib/YP +glide/JGZSRD +glider/M +glim/M +glimmer/DSJG +glimmering/M +glimpse/DRSZMG +glimpser/M +glint/DSG +glissandi +glissando/M +glisten/DSG +glister/DGS +glitch/MS +glitter/GDSJ +glittering/Y +glittery +glitz/GSD +glitzy/TR +gloaming/MS +gloater/M +gloating/Y +gloat/SRDG +globalism/S +globalist/S +global/SY +globe/SM +globetrotter/MS +glob/GDMS +globularity/M +globularness/M +globular/PY +globule/MS +globulin/MS +glockenspiel/SM +glommed +gloom/GSMD +gloomily +gloominess/MS +gloomy/RTP +glop/MS +glopped +glopping +gloppy/TR +Gloria/M +Gloriana/M +Gloriane/M +glorification/M +glorifier/M +glorify/XZRSDNG +Glori/M +glorious/IYP +gloriousness/IM +Glory/M +glory/SDMG +glossary/MS +gloss/GSDM +glossily +glossiness/SM +glossolalia/SM +glossy/RSPT +glottal +glottalization/M +glottis/MS +Gloucester/M +gloveless +glover/M +Glover/M +glove/SRDGMZ +glower/GD +glow/GZRDMS +glowing/Y +glowworm/SM +glucose/SM +glue/DRSMZG +glued/U +gluer/M +gluey +gluier +gluiest +glummer +glummest +glumness/MS +glum/SYP +gluon/M +glutamate/M +gluten/M +glutenous +glutinousness/M +glutinous/PY +glut/SMNX +glutted +glutting +glutton/MS +gluttonous/Y +gluttony/SM +glyceride/M +glycerinate/MD +glycerine's +glycerin/SM +glycerolized/C +glycerol/SM +glycine/M +glycogen/SM +glycol/MS +Glynda/M +Glynis/M +Glyn/M +Glynnis/M +Glynn/M +glyph/M +glyphs +gm +GM +GMT +gnarl/SMDG +gnash/SDG +gnat/MS +gnawer/M +gnaw/GRDSJ +gnawing/M +gneiss/SM +Gnni/M +gnomelike +GNOME/M +gnome/SM +gnomic +gnomish +gnomonic +gnosticism +Gnosticism/M +gnostic/K +Gnostic/M +GNP +gnu/MS +goad/MDSG +goalie/SM +goalkeeper/MS +goalkeeping/M +goalless +goal/MDSG +goalmouth/M +goalpost/S +goalscorer +goalscoring +goaltender/SM +Goa/M +goatee/SM +goatherd/MS +goat/MS +goatskin/SM +gobbed +gobbet/MS +gobbing +gobbledegook's +gobbledygook/S +gobbler/M +gobble/SRDGZ +Gobi/M +goblet/MS +goblin/SM +gob/SM +Godard/M +Godart/M +godchild/M +godchildren +goddammit +goddamn/GS +Goddard/M +Goddart/M +goddaughter/SM +godded +goddess/MS +godding +Gdel/M +godfather/GSDM +godforsaken +Godfree/M +Godfrey/M +Godfry/M +godhead/S +godhood/SM +Godiva/M +godlessness/MS +godless/P +godlikeness/M +godlike/P +godliness/UMS +godly/UTPR +God/M +godmother/MS +Godot/M +godparent/SM +godsend/MS +god/SMY +godson/MS +Godspeed/S +Godthaab/M +Godunov/M +Godwin/M +Godzilla/M +Goebbels/M +Goering/M +goer/MG +goes +Goethals/M +Goethe/M +gofer/SM +Goff/M +goggler/M +goggle/SRDGZ +Gogh/M +Gog/M +Gogol/M +Goiania/M +going/M +goiter/SM +Golan/M +Golconda/M +Golda/M +Goldarina/M +Goldberg/M +goldbricker/M +goldbrick/GZRDMS +Golden/M +goldenness/M +goldenrod/SM +goldenseal/M +golden/TRYP +goldfinch/MS +goldfish/SM +Goldia/M +Goldie/M +Goldilocks/M +Goldi/M +Goldina/M +Golding/M +Goldman/M +goldmine/S +gold/MRNGTS +goldsmith/M +Goldsmith/M +goldsmiths +Goldstein/M +Goldwater/M +Goldwyn/M +Goldy/M +Goleta/M +golfer/M +golf/RDMGZS +Golgotha/M +Goliath/M +Goliaths +golly/S +Gomez/M +Gomorrah/M +Gompers/M +go/MRHZGJ +gonadal +gonad/SM +gondola/SM +gondolier/MS +Gondwanaland/M +goner/M +gone/RZN +gong/SGDM +gonion/M +gonna +gonorrheal +gonorrhea/MS +Gonzales/M +Gonzalez/M +Gonzalo/M +Goober/M +goober/MS +goodbye/MS +goodhearted +goodie's +goodish +goodly/TR +Good/M +Goodman/M +goodness/MS +goodnight +Goodrich/M +good/SYP +goodwill/MS +Goodwin/M +Goodyear/M +goody/SM +gooey +goofiness/MS +goof/SDMG +goofy/RPT +Google/M +gooier +gooiest +gook/SM +goo/MS +goon/SM +goop/SM +gooseberry/MS +goosebumps +goose/M +goos/SDG +GOP +Gopher +gopher/SM +Goran/M +Goraud/M +Gorbachev +Gordan/M +Gorden/M +Gordian/M +Gordie/M +Gordimer/M +Gordon/M +Gordy/M +gore/DSMG +Gore/M +Goren/M +Gorey/M +Gorgas +gorged/E +gorge/GMSRD +gorgeousness/SM +gorgeous/YP +gorger/EM +gorges/E +gorging/E +Gorgon/M +gorgon/S +Gorgonzola/M +Gorham/M +gorilla/MS +gorily +goriness/MS +goring/M +Gorky/M +gormandizer/M +gormandize/SRDGZ +gormless +gorp/S +gorse/SM +gory/PRT +gos +goshawk/MS +gosh/S +gosling/M +gospeler/M +gospel/MRSZ +Gospel/SM +gossamer/SM +gossipy +gossip/ZGMRDS +gotcha/SM +Gteborg/M +Gotham/M +Gothart/M +Gothicism/M +Gothic/S +Goth/M +Goths +got/IU +goto +GOTO/MS +gotta +gotten/U +Gottfried/M +Goucher/M +Gouda/SM +gouge/GZSRD +gouger/M +goulash/SM +Gould/M +Gounod/M +gourde/SM +gourd/MS +gourmand/MS +gourmet/MS +gout/SM +gouty/RT +governable/U +governance/SM +governed/U +governess/SM +govern/LBGSD +governmental/Y +government/MS +Governor +governor/MS +governorship/SM +gov/S +govt +gown/GSDM +Goya/M +GP +GPA +GPO +GPSS +gr +grabbed +grabber/SM +grabbing/S +grab/S +Gracchus/M +grace/ESDMG +graceful/EYPU +gracefuller +gracefullest +gracefulness/ESM +Graceland/M +gracelessness/MS +graceless/PY +Grace/M +Gracia/M +Graciela/M +Gracie/M +graciousness/SM +gracious/UY +grackle/SM +gradate/DSNGX +gradation/MCS +grade/ACSDG +graded/U +Gradeigh/M +gradely +grader/MC +grade's +Gradey/M +gradient/RMS +grad/MRDGZJS +gradualism/MS +gradualist/MS +gradualness/MS +gradual/SYP +graduand/SM +graduate/MNGDSX +graduation/M +Grady/M +Graehme/M +Graeme/M +Graffias/M +graffiti +graffito/M +Graff/M +grafter/M +grafting/M +graft/MRDSGZ +Grafton/M +Grahame/M +Graham/M +graham/SM +Graig/M +grail/S +Grail/SM +grainer/M +grain/IGSD +graininess/MS +graining/M +grain's +grainy/RTP +gram/KSM +Gram/M +grammarian/SM +grammar/MS +grammaticality/M +grammaticalness/M +grammatical/UY +grammatic/K +gramme/SM +Grammy/S +gramophone/SM +Grampians +grampus/SM +Granada/M +granary/MS +grandam/SM +grandaunt/MS +grandchild/M +grandchildren +granddaddy/MS +granddad/SM +granddaughter/MS +grandee/SM +grandeur/MS +grandfather/MYDSG +grandiloquence/SM +grandiloquent/Y +grandiose/YP +grandiosity/MS +grandkid/SM +grandma/MS +grandmaster/MS +grandmother/MYS +grandnephew/MS +grandness/MS +grandniece/SM +grandpa/MS +grandparent/MS +grandson/MS +grandstander/M +grandstand/SRDMG +grand/TPSYR +granduncle/MS +Grange/MR +grange/MSR +Granger/M +granite/MS +granitic +Gran/M +Grannie/M +Granny/M +granny/MS +granola/S +grantee/MS +granter/M +Grantham/M +Granthem/M +Grantley/M +Grant/M +grantor's +grant/SGZMRD +grantsmanship/S +granularity/SM +granular/Y +granulate/SDXVGN +granulation/M +granule/SM +granulocytic +Granville/M +grapefruit/SM +grape/SDGM +grapeshot/M +grapevine/MS +grapheme/M +graph/GMD +graphical/Y +graphicness/M +graphic/PS +graphics/M +graphite/SM +graphologist/SM +graphology/MS +graphs +grapnel/SM +grapple/DRSG +grappler/M +grappling/M +grasper/M +graspingness/M +grasping/PY +grasp/SRDBG +grass/GZSDM +grasshopper/SM +grassland/MS +Grass/M +grassroots +grassy/RT +Grata/M +gratefuller +gratefullest +gratefulness/USM +grateful/YPU +grater/M +grates/I +grate/SRDJGZ +Gratia/M +Gratiana/M +graticule/M +gratification/M +gratified/U +gratifying/Y +gratify/NDSXG +grating/YM +gratis +gratitude/IMS +gratuitousness/MS +gratuitous/PY +gratuity/SM +gravamen/SM +gravedigger/SM +gravel/SGMYD +graven +graveness/MS +graver/M +graveside/S +Graves/M +grave/SRDPGMZTY +gravestone/SM +graveyard/MS +gravidness/M +gravid/PY +gravimeter/SM +gravimetric +gravitas +gravitate/XVGNSD +gravitational/Y +gravitation/M +graviton/SM +gravity/MS +gravy/SM +graybeard/MS +Grayce/M +grayish +Gray/M +grayness/S +gray/PYRDGTS +Grayson/M +graze/GZSRD +grazer/M +Grazia/M +grazing/M +grease/GMZSRD +greasepaint/MS +greaseproof +greaser/M +greasily +greasiness/SM +greasy/PRT +greatcoat/DMS +greaten/DG +greathearted +greatness/MS +great/SPTYRN +grebe/MS +Grecian/S +Greece/M +greed/C +greedily +greediness/SM +greeds +greed's +greedy/RTP +Greek/SM +Greeley/M +greenback/MS +greenbelt/S +Greenberg/M +Greenblatt/M +Greenbriar/M +Greene/M +greenery/MS +Greenfeld/M +greenfield +Greenfield/M +greenfly/M +greengage/SM +greengrocer/SM +greengrocery/M +greenhorn/SM +greenhouse/SM +greening/M +greenish/P +Greenland/M +Green/M +greenmail/GDS +greenness/MS +Greenpeace/M +greenroom/SM +Greensboro/M +Greensleeves/M +Greensville/M +greensward/SM +green/SYRDMPGT +Greentree/M +Greenville/M +Greenwich/M +greenwood/MS +Greer/M +greeter/M +greeting/M +greets/A +greet/SRDJGZ +gregariousness/MS +gregarious/PY +Gregg/M +Greggory/M +Greg/M +Gregoire/M +Gregoor/M +Gregorian +Gregorio/M +Gregorius/M +Gregor/M +Gregory/M +gremlin/SM +Grenada/M +grenade/MS +Grenadian/S +grenadier/SM +Grenadines +grenadine/SM +Grendel/M +Grenier/M +Grenoble/M +Grenville/M +Gresham/M +Gretal/M +Greta/M +Gretchen/M +Gretel/M +Grete/M +Grethel/M +Gretna/M +Gretta/M +Gretzky/M +grew/A +greybeard/M +greyhound/MS +Grey/M +greyness/M +gridded +griddlecake/SM +griddle/DSGM +gridiron/GSMD +gridlock/DSG +grids/A +grid/SGM +grief/MS +Grieg/M +Grier/M +grievance/SM +griever/M +grieve/SRDGZ +grieving/Y +grievousness/SM +grievous/PY +Griffie/M +Griffin/M +griffin/SM +Griffith/M +Griff/M +griffon's +Griffy/M +griller/M +grille/SM +grill/RDGS +grillwork/M +grimace/DRSGM +grimacer/M +Grimaldi/M +grime/MS +Grimes +griminess/MS +grimmer +grimmest +Grimm/M +grimness/MS +grim/PGYD +grimy/TPR +Grinch/M +grind/ASG +grinder/MS +grinding/SY +grindstone/SM +gringo/SM +grinned +grinner/M +grinning/Y +grin/S +griper/M +gripe/S +grippe/GMZSRD +gripper/M +gripping/Y +grip/SGZMRD +Griselda/M +grisliness/SM +grisly/RPT +Gris/M +Grissel/M +gristle/SM +gristliness/M +gristly/TRP +gristmill/MS +grist/MYS +Griswold/M +grit/MS +gritted +gritter/MS +grittiness/SM +gritting +gritty/PRT +Griz/M +grizzle/DSG +grizzling/M +grizzly/TRS +Gr/M +groaner/M +groan/GZSRDM +groat/SM +grocer/MS +grocery/MS +groggily +grogginess/SM +groggy/RPT +grog/MS +groin/MGSD +grokked +grokking +grok/S +grommet/GMDS +Gromyko/M +groofs +groomer/M +groom/GZSMRD +groomsman/M +groomsmen +Groot/M +groover/M +groove/SRDGM +groovy/TR +groper/M +grope/SRDJGZ +Gropius/M +grosbeak/SM +grosgrain/MS +Gross +Grosset/M +gross/GTYSRDP +Grossman/M +grossness/MS +Grosvenor/M +Grosz/M +grotesqueness/MS +grotesque/PSY +Grotius/M +Groton/M +grottoes +grotto/M +grouch/GDS +grouchily +grouchiness/MS +grouchy/RPT +groundbreaking/S +grounded/U +grounder/M +groundhog/SM +ground/JGZMDRS +groundlessness/M +groundless/YP +groundnut/MS +groundsheet/M +groundskeepers +groundsman/M +groundswell/S +groundwater/S +groundwork/SM +grouped/A +grouper/M +groupie/MS +grouping/M +groups/A +group/ZJSMRDG +grouse/GMZSRD +grouser/M +grouter/M +grout/GSMRD +groveler/M +grovelike +groveling/Y +grovel/SDRGZ +Grover/M +Grove/RM +grove/SRMZ +grower/M +grow/GZYRHS +growing/I +growingly +growler/M +growling/Y +growl/RDGZS +growly/RP +grown/IA +grownup/MS +grows/A +growth/IMA +growths/IA +grubbed +grubber/SM +grubbily +grubbiness/SM +grubbing +grubby/RTP +grub/MS +grubstake/MSDG +grudge/GMSRDJ +grudger/M +grudging/Y +grueling/Y +gruel/MDGJS +gruesomeness/SM +gruesome/RYTP +gruffness/MS +gruff/PSGTYRD +grumble/GZJDSR +grumbler/M +grumbling/Y +Grumman/M +grumpily +grumpiness/MS +grump/MDGS +grumpy/TPR +Grundy/M +Grnewald/M +grunge/S +grungy/RT +grunion/SM +grunter/M +grunt/SGRD +Grusky/M +Grus/M +Gruyre +Gruyeres +gryphon's +g's +G's +gs/A +GSA +gt +GU +guacamole/MS +Guadalajara/M +Guadalcanal/M +Guadalquivir/M +Guadalupe/M +Guadeloupe/M +Guallatiri/M +Gualterio/M +Guamanian/SM +Guam/M +Guangzhou +guanine/MS +guano/MS +Guantanamo/M +Guarani/M +guarani/SM +guaranteeing +guarantee/RSDZM +guarantor/SM +guaranty/MSDG +guardedness/UM +guarded/UYP +guarder/M +guardhouse/SM +Guardia/M +guardianship/MS +guardian/SM +guardrail/SM +guard/RDSGZ +guardroom/SM +guardsman/M +guardsmen +Guarnieri/M +Guatemala/M +Guatemalan/S +guava/SM +Guayaquil/M +gubernatorial +Gucci/M +gudgeon/M +Guelph/M +Guendolen/M +Guenevere/M +Guenna/M +Guenther/M +guernsey/S +Guernsey/SM +Guerra/M +Guerrero/M +guerrilla/MS +guessable/U +guess/BGZRSD +guessed/U +guesser/M +guesstimate/DSMG +guesswork/MS +guest/SGMD +Guevara/M +guffaw/GSDM +guff/SM +Guggenheim/M +Guglielma/M +Guglielmo/M +Guhleman/M +GUI +Guiana/M +guidance/MS +guidebook/SM +guided/U +guide/GZSRD +guideline/SM +guidepost/MS +guider/M +Guido/M +Guilbert/M +guilder/M +guildhall/SM +guild/SZMR +guileful +guilelessness/MS +guileless/YP +guile/SDGM +Guillaume/M +Guillema/M +Guillemette/M +guillemot/MS +Guillermo/M +guillotine/SDGM +guiltily +guiltiness/MS +guiltlessness/M +guiltless/YP +guilt/SM +guilty/PTR +Gui/M +Guinea/M +Guinean/S +guinea/SM +Guinevere/M +Guinna/M +Guinness/M +guise's +guise/SDEG +guitarist/SM +guitar/SM +Guiyang +Guizot/M +Gujarati/M +Gujarat/M +Gujranwala/M +gulag/S +gulch/MS +gulden/MS +gulf/DMGS +Gullah/M +gullet/MS +gulley's +gullibility/MS +gullible +Gulliver/M +gull/MDSG +gully/SDMG +gulp/RDGZS +gumboil/MS +gumbo/MS +gumboots +gumdrop/SM +gummed +gumminess/M +gumming/C +gum/MS +gummy/RTP +gumption/SM +gumshoeing +gumshoe/SDM +gumtree/MS +Gunar/M +gunboat/MS +Gunderson/M +gunfighter/M +gunfight/SRMGZ +gunfire/SM +gunflint/M +gunfought +Gunilla/M +gunk/SM +gunky/RT +Gun/M +gunman/M +gunmen +gunmetal/MS +gun/MS +Gunnar/M +gunned +gunnel's +Gunner/M +gunner/SM +gunnery/MS +gunning/M +gunnysack/SM +gunny/SM +gunpoint/MS +gunpowder/SM +gunrunner/MS +gunrunning/MS +gunship/S +gunshot/SM +gunslinger/M +gunsling/GZR +gunsmith/M +gunsmiths +Guntar/M +Gunter/M +Gunther/M +gunwale/MS +Guofeng/M +guppy/SM +Gupta/M +gurgle/SDG +Gurkha/M +gurney/S +guru/MS +Gusella/M +gusher/M +gush/SRDGZ +gushy/TR +Gus/M +Guss +gusset/MDSG +Gussie/M +Gussi/M +gussy/GSD +Gussy/M +Gustaf/M +Gustafson/M +Gusta/M +gustatory +Gustave/M +Gustav/M +Gustavo/M +Gustavus/M +gusted/E +Gustie/M +gustily +Gusti/M +gustiness/M +gusting/E +gust/MDGS +gustoes +gusto/M +gusts/E +Gusty/M +gusty/RPT +Gutenberg/M +Guthrey/M +Guthrie/M +Guthry/M +Gutierrez/M +gutlessness/S +gutless/P +gutser/M +gutsiness/M +gut/SM +guts/R +gutsy/PTR +gutted +gutter/GSDM +guttering/M +guttersnipe/M +gutting +gutturalness/M +guttural/SPY +gutty/RSMT +Guyana/M +Guyanese +Guy/M +guy/MDRZGS +Guzman/M +guzzle/GZRSD +guzzler/M +g/VBX +Gwalior/M +Gwendolen/M +Gwendoline/M +Gwendolin/M +Gwendolyn/M +Gweneth/M +Gwenette/M +Gwen/M +Gwenneth/M +Gwennie/M +Gwenni/M +Gwenny/M +Gwenora/M +Gwenore/M +Gwyneth/M +Gwyn/M +Gwynne/M +gymkhana/SM +gym/MS +gymnasia's +gymnasium/SM +gymnastically +gymnastic/S +gymnastics/M +gymnast/SM +gymnosperm/SM +gynecologic +gynecological/MS +gynecologist/SM +gynecology/MS +gypped +gypper/S +gypping +gyp/S +gypsite +gypster/S +gypsum/MS +gypsy/SDMG +Gypsy/SM +gyrate/XNGSD +gyration/M +gyrator/MS +gyrfalcon/SM +gyrocompass/M +gyro/MS +gyroscope/SM +gyroscopic +gyve/GDS +H +Haag/M +Haas/M +Habakkuk/M +habeas +haberdasher/SM +haberdashery/SM +Haber/M +Haberman/M +Habib/M +habiliment/SM +habitability/MS +habitableness/M +habitable/P +habitant/ISM +habitation/MI +habitations +habitat/MS +habit/IBDGS +habit's +habitualness/SM +habitual/SYP +habituate/SDNGX +habituation/M +habitu/MS +hacienda/MS +hacker/M +Hackett/M +hack/GZSDRBJ +hackler/M +hackle/RSDMG +hackney/SMDG +hacksaw/SDMG +hackwork/S +Hadamard/M +Hadar/M +Haddad/M +haddock/MS +hades +Hades +had/GD +hadji's +hadj's +Hadlee/M +Hadleigh/M +Hadley/M +Had/M +hadn't +Hadria/M +Hadrian/M +hadron/MS +hadst +haemoglobin's +haemophilia's +haemorrhage's +Hafiz/M +hafnium/MS +haft/GSMD +Hagan/M +Hagar/M +Hagen/M +Hager/M +Haggai/M +haggardness/MS +haggard/SYP +hagged +hagging +haggish +haggis/SM +haggler/M +haggle/RSDZG +Hagiographa/M +hagiographer/SM +hagiography/MS +hag/SMN +Hagstrom/M +Hague/M +ha/H +hahnium/S +Hahn/M +Haifa/M +haiku/M +Hailee/M +hailer/M +Hailey/M +hail/SGMDR +hailstone/SM +hailstorm/SM +Haily/M +Haiphong/M +hairball/SM +hairbreadth/M +hairbreadths +hairbrush/SM +haircare +haircloth/M +haircloths +haircut/MS +haircutting +hairdo/SM +hairdresser/SM +hairdressing/SM +hairdryer/S +hairiness/MS +hairlessness/M +hairless/P +hairlike +hairline/SM +hairnet/MS +hairpiece/MS +hairpin/MS +hairsbreadth +hairsbreadths +hair/SDM +hairsplitter/SM +hairsplitting/MS +hairspray +hairspring/SM +hairstyle/SMG +hairstylist/S +hairy/PTR +Haitian/S +Haiti/M +hajjes +hajji/MS +hajj/M +Hakeem/M +hake/MS +Hakim/M +Hakka/M +Hakluyt/M +halalled +halalling +halal/S +halberd/SM +halcyon/S +Haldane/M +Haleakala/M +Haleigh/M +hale/ISRDG +Hale/M +haler/IM +halest +Halette/M +Haley/M +halfback/SM +halfbreed +halfheartedness/MS +halfhearted/PY +halfpence/S +halfpenny/MS +halfpennyworth +half/PM +halftime/S +halftone/MS +halfway +halfword/MS +halibut/SM +halide/SM +Halie/M +Halifax/M +Hali/M +Halimeda/M +halite/MS +halitoses +halitosis/M +hallelujah +hallelujahs +Halley/M +halliard's +Hallie/M +Halli/M +Hallinan/M +Hall/M +Hallmark/M +hallmark/SGMD +hallo/GDS +halloo's +Halloween/MS +hallowing +hallows +hallow/UD +hall/SMR +Hallsy/M +hallucinate/VNGSDX +hallucination/M +hallucinatory +hallucinogenic/S +hallucinogen/SM +hallway/SM +Hally/M +halocarbon +halogenated +halogen/SM +halon +halo/SDMG +Halpern/M +Halsey/M +Hal/SMY +Halsy/M +halter/GDM +halt/GZJSMDR +halting/Y +halve/GZDS +halves/M +halyard/MS +Ha/M +Hamal/M +Haman/M +hamburger/M +Hamburg/MS +hamburg/SZRM +Hamel/M +Hamey/M +Hamhung/M +Hamid/M +Hamilcar/M +Hamil/M +Hamiltonian/MS +Hamilton/M +Hamish/M +Hamitic/M +Hamlen/M +Hamlet/M +hamlet/MS +Hamlin/M +Ham/M +Hammad/M +Hammarskjold/M +hammed +hammerer/M +hammerhead/SM +hammering/M +hammerless +hammerlock/MS +Hammerstein/M +hammertoe/SM +hammer/ZGSRDM +Hammett/M +hamming +hammock/MS +Hammond/M +Hammurabi/M +hammy/RT +Hamnet/M +hampered/U +hamper/GSD +Hampshire/M +Hampton/M +ham/SM +hamster/MS +hamstring/MGS +hamstrung +Hamsun/M +Hana/M +Hanan/M +Hancock/M +handbagged +handbagging +handbag/MS +handball/SM +handbarrow/MS +handbasin +handbill/MS +handbook/SM +handbrake/M +handcar/SM +handcart/MS +handclasp/MS +handcraft/GMDS +handcuff/GSD +handcuffs/M +handedness/M +handed/PY +Handel/M +hander/S +handful/SM +handgun/SM +handhold/M +handicapped +handicapper/SM +handicapping +handicap/SM +handicraftsman/M +handicraftsmen +handicraft/SMR +handily/U +handiness/SM +handiwork/MS +handkerchief/MS +handleable +handlebar/SM +handle/MZGRSD +handler/M +handless +handling/M +handmade +handmaiden/M +handmaid/NMSX +handout/SM +handover +handpick/GDS +handrail/SM +hand's +handsaw/SM +handset/SM +handshake/GMSR +handshaker/M +handshaking/M +handsomely/U +handsomeness/MS +handsome/RPTY +handspike/SM +handspring/SM +handstand/MS +hand/UDSG +handwork/SM +handwoven +handwrite/GSJ +handwriting/M +handwritten +Handy/M +handyman/M +handymen +handy/URT +Haney/M +hangar/SGDM +hangdog/S +hanged/A +hanger/M +hang/GDRZBSJ +hanging/M +hangman/M +hangmen +hangnail/MS +hangout/MS +hangover/SM +hangs/A +Hangul/M +hangup/S +Hangzhou +Hankel/M +hankerer/M +hanker/GRDJ +hankering/M +hank/GZDRMS +hankie/SM +Hank/M +hanky's +Hannah/M +Hanna/M +Hannibal/M +Hannie/M +Hanni/MS +Hanny/M +Hanoi/M +Hanoverian +Hanover/M +Hansel/M +Hansen/M +Hansiain/M +Han/SM +Hans/N +hansom/MS +Hanson/M +Hanuka/S +Hanukkah/M +Hanukkahs +Hapgood/M +haphazardness/SM +haphazard/SPY +haplessness/MS +hapless/YP +haploid/S +happed +happening/M +happen/JDGS +happenstance/SM +happily/U +happiness/UMS +happing +Happy/M +happy/UTPR +Hapsburg/M +hap/SMY +Harald/M +harangue/GDRS +haranguer/M +Harare +harasser/M +harass/LSRDZG +harassment/SM +Harbert/M +harbinger/DMSG +Harbin/M +harborer/M +harbor/ZGRDMS +Harcourt/M +hardback/SM +hardball/SM +hardboard/SM +hardboiled +hardbound +hardcore/MS +hardcover/SM +hardened/U +hardener/M +hardening/M +harden/ZGRD +hardhat/S +hardheadedness/SM +hardheaded/YP +hardheartedness/SM +hardhearted/YP +hardihood/MS +hardily +hardiness/SM +Harding/M +Hardin/M +hardliner/S +hardness/MS +hardscrabble +hardshell +hardship/MS +hardstand/S +hardtack/MS +hardtop/MS +hardware/SM +hardwire/DSG +hardwood/MS +hardworking +Hardy/M +hard/YNRPJGXTS +hardy/PTRS +harebell/MS +harebrained +harelip/MS +harelipped +hare/MGDS +harem/SM +Hargreaves/M +hark/GDS +Harland/M +Harlan/M +Harlem/M +Harlene/M +Harlen/M +Harlequin +harlequin/MS +Harley/M +Harlie/M +Harli/M +Harlin/M +harlotry/MS +harlot/SM +Harlow/M +Harman/M +harmed/U +harmer/M +harmfulness/MS +harmful/PY +harmlessness/SM +harmless/YP +harm/MDRGS +Harmonia/M +harmonically +harmonica/MS +harmonic/S +harmonics/M +Harmonie/M +harmonious/IPY +harmoniousness/MS +harmoniousness's/I +harmonium/MS +harmonization/A +harmonizations +harmonization's +harmonized/U +harmonizer/M +harmonizes/UA +harmonize/ZGSRD +Harmon/M +harmony/EMS +Harmony/M +harness/DRSMG +harnessed/U +harnesser/M +harnesses/U +Harold/M +Haroun/M +harper/M +Harper/M +harping/M +harpist/SM +harp/MDRJGZS +Harp/MR +harpooner/M +harpoon/SZGDRM +harpsichordist/MS +harpsichord/SM +harpy/SM +Harpy/SM +Harrell/M +harridan/SM +Harrie/M +harrier/M +Harriet/M +Harrietta/M +Harriette/M +Harriett/M +Harrington/M +Harriot/M +Harriott/M +Harrisburg/M +Harri/SM +Harrisonburg/M +Harrison/M +harrower/M +harrow/RDMGS +harrumph/SDG +Harry/M +harry/RSDGZ +harshen/GD +harshness/SM +harsh/TRNYP +Harte/M +Hartford/M +Hartley/M +Hartline/M +Hart/M +Hartman/M +hart/MS +Hartwell/M +Harvard/M +harvested/U +harvester/M +harvestman/M +harvest/MDRZGS +Harvey/MS +Harv/M +Harwell/M +Harwilll/M +has +Hasbro/M +hash/AGSD +Hasheem/M +hasher/M +Hashim/M +hashing/M +hashish/MS +hash's +Hasidim +Haskell/M +Haskel/M +Haskins/M +Haslett/M +hasn't +hasp/GMDS +hassle/MGRSD +hassock/MS +haste/MS +hastener/M +hasten/GRD +hast/GXJDN +Hastie/M +hastily +hastiness/MS +Hastings/M +Hasty/M +hasty/RPT +hatchback/SM +hatcheck/S +hatched/U +hatcher/M +hatchery/MS +hatchet/MDSG +hatching/M +hatch/RSDJG +Hatchure/M +hatchway/MS +hatefulness/MS +hateful/YP +hater/M +hate/S +Hatfield/M +Hathaway/M +hatless +hat/MDRSZG +hatred/SM +hatstands +hatted +Hatteras/M +hatter/SM +Hattie/M +Hatti/M +hatting +Hatty/M +hauberk/SM +Haugen/M +haughtily +haughtiness/SM +haughty/TPR +haulage/MS +hauler/M +haul/SDRGZ +haunch/GMSD +haunter/M +haunting/Y +haunt/JRDSZG +Hauptmann/M +Hausa/M +Hausdorff/M +Hauser/M +hauteur/MS +Havana/SM +Havarti +Havel/M +haven/DMGS +Haven/M +haven't +haver/G +haversack/SM +have/ZGSR +havocked +havocking +havoc/SM +Haw +Hawaiian/S +Hawaii/M +hawker/M +hawk/GZSDRM +Hawking +hawking/M +Hawkins/M +hawkishness/S +hawkish/P +Hawley/M +haw/MDSG +hawser/M +haws/RZ +Hawthorne/M +hawthorn/MS +haycock/SM +Hayden/M +Haydn/M +Haydon/M +Hayes +hayfield/MS +hay/GSMDR +Hayley/M +hayloft/MS +haymow/MS +Haynes +hayrick/MS +hayride/MS +hayseed/MS +Hay/SM +haystack/SM +haywain +Hayward/M +haywire/MS +Haywood/M +Hayyim/M +hazard/MDGS +hazardousness/M +hazardous/PY +haze/DSRJMZG +Hazel/M +hazel/MS +hazelnut/SM +Haze/M +hazer/M +hazily +haziness/MS +hazing/M +Hazlett/M +Hazlitt/M +hazy/PTR +HBO/M +hdqrs +HDTV +headache/MS +headband/SM +headboard/MS +headcount +headdress/MS +header/M +headfirst +headgear/SM +headhunter/M +headhunting/M +headhunt/ZGSRDMJ +headily +headiness/S +heading/M +headlamp/S +headland/MS +headlessness/M +headless/P +headlight/MS +headline/DRSZMG +headliner/M +headlock/MS +headlong +Head/M +headman/M +headmaster/MS +headmastership/M +headmen +headmistress/MS +headphone/SM +headpiece/SM +headpin/MS +headquarter/GDS +headrest/MS +headroom/SM +headscarf/M +headset/SM +headship/SM +headshrinker/MS +head/SJGZMDR +headsman/M +headsmen +headstall/SM +headstand/MS +headstock/M +headstone/MS +headstrong +headwaiter/SM +headwall/S +headwater/S +headway/MS +headwind/SM +headword/MS +heady/PTR +heal/DRHSGZ +healed/U +healer/M +Heall/M +healthfully +healthfulness/SM +healthful/U +healthily/U +healthiness/MSU +health/M +healths +healthy/URPT +heap/SMDG +heard/UA +hearer/M +hearing/AM +hearken/SGD +hearsay/SM +hearse/M +hears/SDAG +Hearst/M +heartache/SM +heartbeat/MS +heartbreak/GMS +heartbreaking/Y +heartbroke +heartbroken +heartburning/M +heartburn/SGM +hearted/Y +hearten/EGDS +heartening/EY +heartfelt +hearth/M +hearthrug +hearths +hearthstone/MS +heartily +heartiness/SM +heartland/SM +heartlessness/SM +heartless/YP +heartrending/Y +heartsickness/MS +heartsick/P +heart/SMDNXG +heartstrings +heartthrob/MS +heartwarming +Heartwood/M +heartwood/SM +hearty/TRSP +hear/ZTSRHJG +heatedly +heated/UA +heater/M +heathendom/SM +heathenish/Y +heathenism/MS +heathen/M +heather/M +Heather/M +heathery +Heathkit/M +heathland +Heathman/M +Heath/MR +heath/MRNZX +heaths +heatproof +heats/A +heat/SMDRGZBJ +heatstroke/MS +heatwave +heave/DSRGZ +heavenliness/M +heavenly/PTR +heaven/SYM +heavenward/S +heaver/M +heaves/M +heavily +heaviness/MS +Heaviside/M +heavyhearted +heavyset +heavy/TPRS +heavyweight/SM +Hebe/M +hebephrenic +Hebert/M +Heb/M +Hebraic +Hebraism/MS +Hebrew/SM +Hebrides/M +Hecate/M +hecatomb/M +heckler/M +heckle/RSDZG +heck/S +hectare/MS +hectically +hectic/S +hectogram/MS +hectometer/SM +Hector/M +hector/SGD +Hecuba/M +he'd +Heda/M +Hedda/M +Heddie/M +Heddi/M +hedge/DSRGMZ +hedgehog/MS +hedgehopped +hedgehopping +hedgehop/S +hedger/M +hedgerow/SM +hedging/Y +Hedi/M +hedonism/SM +hedonistic +hedonist/MS +Hedvige/M +Hedvig/M +Hedwiga/M +Hedwig/M +Hedy/M +heeded/U +heedfulness/M +heedful/PY +heeding/U +heedlessness/SM +heedless/YP +heed/SMGD +heehaw/DGS +heeler/M +heeling/M +heelless +heel/SGZMDR +Heep/M +Hefner/M +heft/GSD +heftily +heftiness/SM +hefty/TRP +Hegelian +Hegel/M +hegemonic +hegemony/MS +Hegira/M +hegira/S +Heida/M +Heidegger/M +Heidelberg/M +Heidie/M +Heidi/M +heifer/MS +Heifetz/M +heighten/GD +height/SMNX +Heimlich/M +Heindrick/M +Heineken/M +Heine/M +Heinlein/M +heinousness/SM +heinous/PY +Heinrich/M +Heinrick/M +Heinrik/M +Heinze/M +Heinz/M +heiress/MS +heirloom/MS +heir/SDMG +Heisenberg/M +Heiser/M +heister/M +heist/GSMRD +Hejira's +Helaina/M +Helaine/M +held +Helena/M +Helene/M +Helenka/M +Helen/M +Helga/M +Helge/M +helical/Y +helices/M +helicon/M +Helicon/M +helicopter/GSMD +heliocentric +heliography/M +Heliopolis/M +Helios/M +heliosphere +heliotrope/SM +heliport/MS +helium/MS +helix/M +he'll +hellbender/M +hellbent +hellcat/SM +hellebore/SM +Hellene/SM +Hellenic +Hellenism/MS +Hellenistic +Hellenist/MS +Hellenization/M +Hellenize +heller/M +Heller/M +Hellespont/M +hellfire/M +hell/GSMDR +hellhole/SM +Helli/M +hellion/SM +hellishness/SM +hellish/PY +Hellman/M +hello/GMS +Hell's +helluva +helmed +helmet/GSMD +Helmholtz/M +helming +helms +helm's +helmsman/M +helmsmen +helm/U +Helmut/M +Hloise/M +helot/S +helper/M +helpfulness/MS +helpful/UY +help/GZSJDR +helping/M +helplessness/SM +helpless/YP +helpline/S +helpmate/SM +helpmeet's +Helsa/M +Helsinki/M +helve/GMDS +Helvetian/S +Helvetius/M +Helyn/M +He/M +hematite/MS +hematologic +hematological +hematologist/SM +hematology/MS +heme/MS +Hemingway/M +hemisphere/MSD +hemispheric +hemispherical +hemline/SM +hemlock/MS +hemmed +hemmer/SM +hemming +hem/MS +hemoglobin/MS +hemolytic +hemophiliac/SM +hemophilia/SM +hemorrhage/GMDS +hemorrhagic +hemorrhoid/MS +hemostat/SM +hemp/MNS +h/EMS +hemstitch/DSMG +henceforth +henceforward +hence/S +Hench/M +henchman/M +henchmen +Henderson/M +Hendrick/SM +Hendrickson/M +Hendrika/M +Hendrik/M +Hendrix/M +henge/M +Henka/M +Henley/M +hen/MS +henna/MDSG +Hennessey/M +henning +henpeck/GSD +Henrie/M +Henrieta/M +Henrietta/M +Henriette/M +Henrik/M +Henri/M +Henryetta/M +henry/M +Henry/M +Hensley/M +Henson/M +heparin/MS +hepatic/S +hepatitides +hepatitis/M +Hepburn/M +Hephaestus/M +Hephzibah/M +hepper +heppest +Hepplewhite +hep/S +heptagonal +heptagon/SM +heptane/M +heptathlon/S +her +Heracles/M +Heraclitus/M +heralded/U +heraldic +herald/MDSG +heraldry/MS +Hera/M +herbaceous +herbage/MS +herbalism +herbalist/MS +herbal/S +Herbart/M +Herbert/M +herbicidal +herbicide/MS +Herbie/M +herbivore/SM +herbivorous/Y +Herb/M +herb/MS +Herby/M +Herc/M +Herculaneum/M +herculean +Herculean +Hercule/MS +Herculie/M +herder/M +Herder/M +herd/MDRGZS +herdsman/M +herdsmen +hereabout/S +hereafter/S +hereby +hereditary +heredity/MS +Hereford/SM +herein +hereinafter +here/IS +hereof +hereon +here's +heres/M +heresy/SM +heretical +heretic/SM +hereto +heretofore +hereunder +hereunto +hereupon +herewith +Heriberto/M +heritable +heritage/MS +heritor/IM +Herkimer/M +Herman/M +Hermann/M +hermaphrodite/SM +hermaphroditic +Hermaphroditus/M +hermeneutic/S +hermeneutics/M +Hermes +hermetical/Y +hermetic/S +Hermia/M +Hermie/M +Hermina/M +Hermine/M +Herminia/M +Hermione/M +hermitage/SM +Hermite/M +hermitian +hermit/MS +Hermon/M +Hermosa/M +Hermosillo/M +Hermy/M +Hernandez/M +Hernando/M +hernial +hernia/MS +herniate/NGXDS +Herod/M +Herodotus/M +heroes +heroically +heroics +heroic/U +heroine/SM +heroin/MS +heroism/SM +Herold/M +hero/M +heron/SM +herpes/M +herpetologist/SM +herpetology/MS +Herrera/M +Herrick/M +herringbone/SDGM +Herring/M +herring/SM +Herrington/M +Herr/MG +Herschel/M +Hersch/M +herself +Hersey/M +Hershel/M +Hershey/M +Hersh/M +Herta/M +Hertha/M +hertz/M +Hertz/M +Hertzog/M +Hertzsprung/M +Herve/M +Hervey/M +Herzegovina/M +Herzl/M +hes +Hesiod/M +hesitance/S +hesitancy/SM +hesitantly +hesitant/U +hesitater/M +hesitate/XDRSNG +hesitating/UY +hesitation/M +Hesperus/M +Hesse/M +Hessian/MS +Hess/M +Hester/M +Hesther/M +Hestia/M +Heston/M +heterodox +heterodoxy/MS +heterodyne +heterogamous +heterogamy/M +heterogeneity/SM +heterogeneousness/M +heterogeneous/PY +heterosexuality/SM +heterosexual/YMS +heterostructure +heterozygous +Hettie/M +Hetti/M +Hetty/M +Heublein/M +heuristically +heuristic/SM +Heusen/M +Heuser/M +he/VMZ +hew/DRZGS +Hewe/M +hewer/M +Hewet/M +Hewett/M +Hewie/M +Hewitt/M +Hewlett/M +Hew/M +hexachloride/M +hexadecimal/YS +hexafluoride/M +hexagonal/Y +hexagon/SM +hexagram/SM +hexameter/SM +hex/DSRG +hexer/M +hey +heyday/MS +Heyerdahl/M +Heywood/M +Hezekiah/M +hf +HF +Hf/M +Hg/M +hgt +hgwy +HHS +HI +Hialeah/M +hiatus/SM +Hiawatha/M +hibachi/MS +hibernate/XGNSD +hibernation/M +hibernator/SM +Hibernia/M +Hibernian/S +hibiscus/MS +hiccup/MDGS +hickey/SM +Hickey/SM +Hickman/M +Hickok/M +hickory/MS +hick/SM +Hicks/M +hi/D +hidden/U +hideaway/SM +hidebound +hideousness/SM +hideous/YP +hideout/MS +hider/M +hide/S +hiding/M +hid/ZDRGJ +hieing +hierarchal +hierarchic +hierarchical/Y +hierarchy/SM +hieratic +hieroglyph +hieroglyphic/S +hieroglyphics/M +hieroglyphs +Hieronymus/M +hie/S +hifalutin +Higashiosaka +Higgins/M +highball/GSDM +highborn +highboy/MS +highbrow/SM +highchair/SM +highfalutin +Highfield/M +highhandedness/SM +highhanded/PY +highish +Highlander/SM +Highlands +highland/ZSRM +highlight/GZRDMS +Highness/M +highness/MS +highpoint +high/PYRT +highroad/MS +highs +hight +hightail/DGS +highwayman/M +highwaymen +highway/MS +hijacker/M +hijack/JZRDGS +hiker/M +hike/ZGDSR +Hilario/M +hilariousness/MS +hilarious/YP +hilarity/MS +Hilarius/M +Hilary/M +Hilbert/M +Hildagarde/M +Hildagard/M +Hilda/M +Hildebrand/M +Hildegaard/M +Hildegarde/M +Hilde/M +Hildy/M +Hillard/M +Hillary/M +hillbilly/MS +Hillcrest/M +Hillel/M +hiller/M +Hillery/M +hill/GSMDR +Hilliard/M +Hilliary/M +Hillie/M +Hillier/M +hilliness/SM +Hill/M +hillman +hillmen +hillock/SM +Hillsboro/M +Hillsdale/M +hillside/SM +hilltop/MS +hillwalking +Hillyer/M +Hilly/RM +hilly/TRP +hilt/MDGS +Hilton/M +Hi/M +Himalaya/MS +Himalayan/S +Himmler/M +him/S +himself +Hinayana/M +Hinda/M +Hindemith/M +Hindenburg/M +hindered/U +hinderer/M +hinder/GRD +Hindi/M +hindmost +hindquarter/SM +hindrance/SM +hind/RSZ +hindsight/SM +Hinduism/SM +Hindu/MS +Hindustani/MS +Hindustan/M +Hines/M +hinger +hinge's +hinge/UDSG +Hinkle/M +Hinsdale/M +hinterland/MS +hinter/M +hint/GZMDRS +Hinton/M +Hinze/M +hipbone/SM +hipness/S +Hipparchus/M +hipped +hipper +hippest +hippie/MTRS +hipping/M +Hippocrates/M +Hippocratic +hippodrome/MS +hippo/MS +hippopotamus/SM +hip/PSM +hippy's +hipster/MS +hiragana +Hiram/M +hire/AGSD +hireling/SM +hirer/SM +Hirey/M +hiring/S +Hirohito/M +Hiroshi/M +Hiroshima/M +Hirsch/M +hirsuteness/MS +hirsute/P +his +Hispanic/SM +Hispaniola/M +hiss/DSRMJG +hisser/M +hissing/M +Hiss/M +histamine/SM +histidine/SM +histochemic +histochemical +histochemistry/M +histogram/MS +histological +histologist/MS +histology/SM +historian/MS +historic +historicalness/M +historical/PY +historicism/M +historicist/M +historicity/MS +historiographer/SM +historiography/MS +history/MS +histrionically +histrionic/S +histrionics/M +hist/SDG +Hitachi/M +Hitchcock/M +hitcher/MS +hitchhike/RSDGZ +hitch/UGSD +hither +hitherto +Hitler/SM +hitless +hit/MS +hittable +hitter/SM +hitting +Hittite/SM +HIV +hive/MGDS +h'm +HM +HMO +Hmong +HMS +hoarder/M +hoarding/M +hoard/RDJZSGM +hoarfrost/SM +hoariness/MS +hoar/M +hoarseness/SM +hoarse/RTYP +hoary/TPR +hoaxer/M +hoax/GZMDSR +Hobard/M +Hobart/M +hobbed +Hobbes/M +hobbing +hobbit +hobbler/M +hobble/ZSRDG +Hobbs/M +hobbyhorse/SM +hobbyist/SM +hobby/SM +Hobday/M +Hobey/M +hobgoblin/MS +Hobie/M +hobnail/GDMS +hobnobbed +hobnobbing +hobnob/S +Hoboken/M +hobo/SDMG +hob/SM +hoc +hocker/M +hockey/SM +hock/GDRMS +Hockney/M +hockshop/SM +hodge/MS +Hodge/MS +hodgepodge/SM +Hodgkin/M +ho/DRYZ +hod/SM +Hoebart/M +hoecake/SM +hoedown/MS +hoeing +hoer/M +hoe/SM +Hoffa/M +Hoff/M +Hoffman/M +Hofstadter/M +Hogan/M +hogan/SM +Hogarth/M +hogback/MS +hogged +hogger +hogging +hoggish/Y +hogshead/SM +hog/SM +hogtie/SD +hogtying +hogwash/SM +Hohenlohe/M +Hohenstaufen/M +Hohenzollern/M +Hohhot/M +hoister/M +hoist/GRDS +hoke/DSG +hokey/PRT +hokier +hokiest +Hokkaido/M +hokum/MS +Hokusai/M +Holbein/M +Holbrook/M +Holcomb/M +holdall/MS +Holden/M +holder/M +Holder/M +holding/IS +holding's +hold/NRBSJGZ +holdout/SM +holdover/SM +holdup/MS +hole/MGDS +holey +holiday/GRDMS +Holiday/M +holidaymaker/S +holier/U +Holiness/MS +holiness/MSU +holistic +holistically +hollandaise +Hollandaise/M +Hollander/M +Holland/RMSZ +holler/GDS +Hollerith/M +Holley/M +Hollie/M +Holli/SM +Hollister/M +Holloway/M +hollowness/MS +hollow/RDYTGSP +hollowware/M +Hollyanne/M +hollyhock/MS +Holly/M +holly/SM +Hollywood/M +Holman/M +Holmes +holmium/MS +Holm/M +Holocaust +holocaust/MS +Holocene +hologram/SM +holograph/GMD +holographic +holographs +holography/MS +Holstein/MS +holster/MDSG +Holst/M +Holt/M +Holyoke/M +holy/SRTP +holystone/MS +Holzman/M +Ho/M +homage/MGSRD +homager/M +hombre/SM +homburg/SM +homebody/MS +homebound +homeboy/S +homebuilder/S +homebuilding +homebuilt +homecoming/MS +home/DSRMYZG +homegrown +homeland/SM +homelessness/SM +homeless/P +homelike +homeliness/SM +homely/RPT +homemade +homemake/JRZG +homemaker/M +homemaking/M +homeomorphic +homeomorphism/MS +homeomorph/M +homeopath +homeopathic +homeopaths +homeopathy/MS +homeostases +homeostasis/M +homeostatic +homeowner/S +homeownership +homepage +Homere/M +homer/GDM +Homeric +homerists +Homer/M +homeroom/MS +Homerus/M +homeschooling/S +homesickness/MS +homesick/P +homespun/S +homesteader/M +homestead/GZSRDM +homestretch/SM +hometown/SM +homeward +homeworker/M +homework/ZSMR +homeyness/MS +homey/PS +homicidal/Y +homicide/SM +homier +homiest +homiletic/S +homily/SM +hominess's +homing/M +hominid/MS +hominy/SM +Hom/MR +homogamy/M +homogenate/MS +homogeneity/ISM +homogeneous/PY +homogenization/MS +homogenize/DRSGZ +homogenizer/M +homograph/M +homographs +homological +homologous +homologue/M +homology/MS +homomorphic +homomorphism/SM +homonym/SM +homophobia/S +homophobic +homophone/MS +homopolymers +homosexuality/SM +homosexual/YMS +homo/SM +homotopy +homozygous/Y +honcho/DSG +Honda/M +Hondo/M +Honduran/S +Honduras/M +Honecker/M +hone/SM +honestly/E +honest/RYT +honesty/ESM +honeybee/SM +honeycomb/SDMG +honeydew/SM +honey/GSMD +honeylocust +Honey/M +honeymooner/M +honeymoon/RDMGZS +honeysuckle/MS +Honeywell/M +hong/M +Honiara/M +honker/M +honk/GZSDRM +honky/SM +Hon/M +hon/MDRSZTG +Honolulu/M +honorableness/SM +honorable/PSM +honorables/U +honorablies/U +honorably/UE +honorarily +honorarium/SM +honorary/S +honored/U +honoree/S +honor/ERDBZGS +honorer/EM +Honoria/M +honorific/S +Honor/M +honor's +honors/A +Honshu/M +hooch/MS +hoodedness/M +hooded/P +hoodlum/SM +Hood/M +hood/MDSG +hoodoo/DMGS +hoodwinker/M +hoodwink/SRDG +hooey/SM +hoof/DRMSG +hoofer/M +hoofmark/S +hookah/M +hookahs +hookedness/M +hooked/P +Hooke/MR +hooker/M +Hooker/M +hookey's +hook/GZDRMS +hooks/U +hookup/SM +hookworm/MS +hooky/SRMT +hooliganism/SM +hooligan/SM +hooper/M +Hooper/M +hoopla/SM +hoop/MDRSG +hooray/SMDG +hoosegow/MS +Hoosier/SM +hootch's +hootenanny/SM +hooter/M +hoot/MDRSGZ +Hoover/MS +hooves/M +hoped/U +hopefulness/MS +hopeful/SPY +hopelessness/SM +hopeless/YP +Hope/M +hoper/M +hope/SM +Hopewell/M +Hopi/SM +Hopkinsian/M +Hopkins/M +hopped +Hopper/M +hopper/MS +hopping/M +hoppled +hopples +hopscotch/MDSG +hop/SMDRG +Horace/M +Horacio/M +Horatia/M +Horatio/M +Horatius/M +horde/DSGM +horehound/MS +horizon/MS +horizontal/YS +Hormel/M +hormonal/Y +hormone/MS +Hormuz/M +hornbeam/M +hornblende/MS +Hornblower/M +hornedness/M +horned/P +Horne/M +hornet/MS +horn/GDRMS +horniness/M +hornless +hornlike +Horn/M +hornpipe/MS +horny/TRP +horologic +horological +horologist/MS +horology/MS +horoscope/MS +Horowitz/M +horrendous/Y +horribleness/SM +horrible/SP +horribly +horridness/M +horrid/PY +horrific +horrifically +horrify/DSG +horrifying/Y +horror/MS +hors/DSGX +horseback/MS +horsedom +horseflesh/M +horsefly/MS +horsehair/SM +horsehide/SM +horselaugh/M +horselaughs +horseless +horselike +horsely +horseman/M +horsemanship/MS +horsemen +horseplayer/M +horseplay/SMR +horsepower/SM +horseradish/SM +horse's +horseshoeing +horseshoe/MRSD +horseshoer/M +horsetail/SM +horse/UGDS +horsewhipped +horsewhipping +horsewhip/SM +horsewoman/M +horsewomen +horsey +horsier +horsiest +horsing/M +Horst/M +hortatory +Horten/M +Hortense/M +Hortensia/M +horticultural +horticulture/SM +horticulturist/SM +Hort/MN +Horton/M +Horus/M +hosanna/SDG +Hosea/M +hose/M +hosepipe +hos/GDS +hosier/MS +hosiery/SM +hosp +hospice/MS +hospitable/I +hospitably/I +hospitality/MS +hospitality's/I +hospitalization/MS +hospitalize/GSD +hospital/MS +hostage/MS +hosteler/M +hostelry/MS +hostel/SZGMRD +hostess/MDSG +hostile/YS +hostility/SM +hostler/MS +Host/MS +host/MYDGS +hotbed/MS +hotblooded +hotbox/MS +hotcake/S +hotchpotch/M +hotelier/MS +hotelman/M +hotel/MS +hotfoot/DGS +hothead/DMS +hotheadedness/SM +hotheaded/PY +hothouse/MGDS +hotness/MS +hotplate/SM +hotpot/M +hot/PSY +hotrod +hotshot/S +hotted +Hottentot/SM +hotter +hottest +hotting +Houdaille/M +Houdini/M +hough/M +hounder/M +hounding/M +hound/MRDSG +hourglass/MS +houri/MS +hourly/S +hour/YMS +house/ASDG +houseboat/SM +housebound +houseboy/SM +housebreaker/M +housebreaking/M +housebreak/JSRZG +housebroke +housebroken +housebuilding +housecleaning/M +houseclean/JDSG +housecoat/MS +housefly/MS +houseful/SM +householder/M +household/ZRMS +househusband/S +housekeeper/M +housekeeping/M +housekeep/JRGZ +houselights +House/M +housemaid/MS +houseman/M +housemen +housemother/MS +housemoving +houseparent/SM +houseplant/S +houser +house's +housetop/MS +housewares +housewarming/MS +housewifeliness/M +housewifely/P +housewife/YM +housewives +houseworker/M +housework/ZSMR +housing/MS +Housman/M +Houston/M +Houyhnhnm/M +HOV +hovel/GSMD +hovercraft/M +hoverer/M +hover/GRD +hove/ZR +Howard/M +howbeit +howdah/M +howdahs +howdy/GSD +Howell/MS +Howe/M +however +Howey/M +Howie/M +howitzer/MS +howler/M +howl/GZSMDR +Howrah/M +how/SM +howsoever +hoyden/DMGS +hoydenish +Hoyle/SM +hoy/M +Hoyt/M +hp +HP +HQ +hr +HR +HRH +Hrothgar/M +hrs +h's +H's +HS +HST +ht +HTML +Hts/M +HTTP +Huang/M +huarache/SM +hubba +Hubbard/M +Hubble/M +hubbub/SM +hubby/SM +hubcap/SM +Huber/M +Hube/RM +Hubert/M +Huberto/M +Hubey/M +Hubie/M +hub/MS +hubris/SM +huckleberry/SM +Huck/M +huckster/SGMD +HUD +Huddersfield/M +huddler/M +huddle/RSDMG +Hudson/M +hue/MDS +Huerta/M +Huey/M +huffily +huffiness/SM +Huff/M +Huffman/M +huff/SGDM +huffy/TRP +hugeness/MS +huge/YP +hugged +hugger +hugging/S +Huggins +Hughie/M +Hugh/MS +Hugibert/M +Hugo/M +hug/RTS +Huguenot/SM +Hugues/M +huh +huhs +Hui/M +Huitzilopitchli/M +hula/MDSG +Hulda/M +hulk/GDMS +hullabaloo/SM +huller/M +hulling/M +Hull/M +hull/MDRGZS +hullo/GSDM +humane/IY +humaneness/SM +humaner +humanest +human/IPY +humanism/SM +humanistic +humanist/SM +humanitarianism/SM +humanitarian/S +humanity/ISM +humanization/CSM +humanized/C +humanizer/M +humanize/RSDZG +humanizes/IAC +humanizing/C +humankind/M +humannesses +humanness/IM +humanoid/S +humans +Humbert/M +Humberto/M +humbleness/SM +humble/TZGPRSDJ +humbly +Humboldt/M +humbugged +humbugging +humbug/MS +humdinger/MS +humdrum/S +Hume/M +humeral/S +humeri +humerus/M +Humfrey/M +Humfrid/M +Humfried/M +humidification/MC +humidifier/CM +humidify/RSDCXGNZ +humidistat/M +humidity/MS +humidor/MS +humid/Y +humiliate/SDXNG +humiliating/Y +humiliation/M +humility/MS +hummed +Hummel/M +hummer/SM +humming +hummingbird/SM +hummock/MDSG +hummocky +hummus/S +humongous +humored/U +humorist/MS +humorlessness/MS +humorless/PY +humorousness/MS +humorous/YP +humor/RDMZGS +humpback/SMD +hump/GSMD +humph/DG +Humphrey/SM +humphs +Humpty/M +hum/S +humus/SM +Humvee +hunchback/DSM +hunch/GMSD +hundredfold/S +hundred/SHRM +hundredths +hundredweight/SM +Hunfredo/M +hung/A +Hungarian/MS +Hungary/M +hunger/SDMG +Hung/M +hungover +hungrily +hungriness/SM +hungry/RTP +hunker/DG +hunky/RST +hunk/ZRMS +Hun/MS +hunter/M +Hunter/M +hunt/GZJDRS +hunting/M +Huntington/M +Huntlee/M +Huntley/M +Hunt/MR +huntress/MS +huntsman/M +huntsmen +Huntsville/M +hurdle/JMZGRSD +hurdler/M +hurl/DRGZJS +Hurlee/M +Hurleigh/M +hurler/M +Hurley/M +hurling/M +Huron/SM +hurray/SDG +hurricane/MS +hurriedness/M +hurried/UY +hurry/RSDG +Hurst/M +hurter/M +hurtfulness/MS +hurtful/PY +hurting/Y +hurtle/SDG +hurts +hurt/U +Hurwitz/M +Hus +Husain's +husbander/M +husband/GSDRYM +husbandman/M +husbandmen +husbandry/SM +Husein/M +hush/DSG +husker/M +huskily +huskiness/MS +husking/M +husk/SGZDRM +husky/RSPT +hussar/MS +Hussein/M +Husserl/M +hussy/SM +hustings/M +hustler/M +hustle/RSDZG +Huston/M +Hutchins/M +Hutchinson/M +Hutchison/M +hutch/MSDG +hut/MS +hutted +hutting +Hutton/M +Hutu/M +Huxley/M +Huygens/M +huzzah/GD +huzzahs +hwy +Hyacintha/M +Hyacinthe/M +Hyacinthia/M +Hyacinthie/M +hyacinth/M +Hyacinth/M +hyacinths +Hyades +hyaena's +Hyannis/M +Hyatt/M +hybridism/SM +hybridization/S +hybridize/GSD +hybrid/MS +Hyde/M +Hyderabad/M +Hydra/M +hydra/MS +hydrangea/SM +hydrant/SM +hydrate/CSDNGX +hydrate's +hydration/MC +hydraulically +hydraulicked +hydraulicking +hydraulic/S +hydraulics/M +hydrazine/M +hydride/MS +hydrocarbon/SM +hydrocephali +hydrocephalus/MS +hydrochemistry +hydrochloric +hydrochloride/M +hydrodynamical +hydrodynamic/S +hydrodynamics/M +hydroelectric +hydroelectrically +hydroelectricity/SM +hydrofluoric +hydrofoil/MS +hydrogenate/CDSGN +hydrogenate's +hydrogenation/MC +hydrogenations +hydrogen/MS +hydrogenous +hydrological/Y +hydrologist/MS +hydrology/SM +hydrolysis/M +hydrolyzed/U +hydrolyze/GSD +hydromagnetic +hydromechanics/M +hydrometer/SM +hydrometry/MS +hydrophilic +hydrophobia/SM +hydrophobic +hydrophone/SM +hydroplane/DSGM +hydroponic/S +hydroponics/M +hydro/SM +hydrosphere/MS +hydrostatic/S +hydrostatics/M +hydrotherapy/SM +hydrothermal/Y +hydrous +hydroxide/MS +hydroxy +hydroxylate/N +hydroxyl/SM +hydroxyzine/M +hyena/MS +hygiene/MS +hygienically +hygienic/S +hygienics/M +hygienist/MS +hygrometer/SM +hygroscopic +hying +Hy/M +Hyman/M +hymeneal/S +Hymen/M +hymen/MS +Hymie/M +hymnal/SM +hymnbook/S +hymn/GSDM +Hynda/M +hype/MZGDSR +hyperactive/S +hyperactivity/SM +hyperbola/MS +hyperbole/MS +hyperbolic +hyperbolically +hyperboloidal +hyperboloid/SM +hypercellularity +hypercritical/Y +hypercube/MS +hyperemia/M +hyperemic +hyperfine +hypergamous/Y +hypergamy/M +hyperglycemia/MS +hyperinflation +Hyperion/M +hypermarket/SM +hypermedia/S +hyperplane/SM +hyperplasia/M +hypersensitiveness/MS +hypersensitive/P +hypersensitivity/MS +hypersonic +hyperspace/M +hypersphere/M +hypertension/MS +hypertensive/S +hypertext/SM +hyperthyroid +hyperthyroidism/MS +hypertrophy/MSDG +hypervelocity +hyperventilate/XSDGN +hyperventilation/M +hyphenated/U +hyphenate/NGXSD +hyphenation/M +hyphen/DMGS +hypnoses +hypnosis/M +hypnotherapy/SM +hypnotically +hypnotic/S +hypnotism/MS +hypnotist/SM +hypnotize/SDG +hypoactive +hypoallergenic +hypocellularity +hypochondriac/SM +hypochondria/MS +hypocrisy/SM +hypocrite/MS +hypocritical/Y +hypodermic/S +hypo/DMSG +hypoglycemia/SM +hypoglycemic/S +hypophyseal +hypophysectomized +hypotenuse/MS +hypothalami +hypothalamic +hypothalamically +hypothalamus/M +hypothermia/SM +hypotheses +hypothesis/M +hypothesizer/M +hypothesize/ZGRSD +hypothetic +hypothetical/Y +hypothyroid +hypothyroidism/SM +hypoxia/M +hyssop/MS +hysterectomy/MS +hysteresis/M +hysteria/SM +hysterical/YU +hysteric/SM +Hyundai/M +Hz +i +I +IA +Iaccoca/M +Iago/M +Iain/M +Ia/M +iambi +iambic/S +iamb/MS +iambus/SM +Ian/M +Ianthe/M +Ibadan/M +Ibbie/M +Ibby/M +Iberia/M +Iberian/MS +Ibero/M +ibex/MS +ibid +ibidem +ibis/SM +IBM/M +Ibo/M +Ibrahim/M +Ibsen/M +ibuprofen/S +Icarus/M +ICBM/S +ICC +iceberg/SM +iceboat/MS +icebound +icebox/MS +icebreaker/SM +icecap/SM +ice/GDSC +Icelander/M +Icelandic +Iceland/MRZ +Ice/M +iceman/M +icemen +icepack +icepick/S +ice's +Ichabod/M +ichneumon/M +ichthyologist/MS +ichthyology/MS +icicle/SM +icily +iciness/SM +icing/MS +icky/RT +iconic +icon/MS +iconoclasm/MS +iconoclastic +iconoclast/MS +iconography/MS +icosahedra +icosahedral +icosahedron/M +ictus/SM +ICU +icy/RPT +I'd +ID +Idahoan/S +Idahoes +Idaho/MS +Idalia/M +Idalina/M +Idaline/M +Ida/M +idealism/MS +idealistic +idealistically +idealist/MS +idealization/MS +idealized/U +idealize/GDRSZ +idealizer/M +ideal/MYS +idealogical +idea/SM +ideate/SN +ideation/M +Idelle/M +Idell/M +idem +idempotent/S +identicalness/M +identical/YP +identifiability +identifiable/U +identifiably +identification/M +identified/U +identifier/M +identify/XZNSRDG +identity/SM +ideogram/MS +ideographic +ideograph/M +ideographs +ideological/Y +ideologist/SM +ideologue/S +ideology/SM +ides +Idette/M +idiocy/MS +idiolect/M +idiomatically +idiomatic/P +idiom/MS +idiopathic +idiosyncrasy/SM +idiosyncratic +idiosyncratically +idiotic +idiotically +idiot/MS +idleness/MS +idle/PZTGDSR +idler/M +id/MY +idolater/MS +idolatress/S +idolatrous +idolatry/SM +idolization/SM +idolized/U +idolizer/M +idolize/ZGDRS +idol/MS +ids +IDs +idyllic +idyllically +idyll/MS +IE +IEEE +Ieyasu/M +if +iffiness/S +iffy/TPR +Ifni/M +ifs +Iggie/M +Iggy/M +igloo/MS +Ignace/M +Ignacio/M +Ignacius/M +Ignatius/M +Ignazio/M +Ignaz/M +igneous +ignitable +ignite/ASDG +igniter/M +ignition/MS +ignobleness/M +ignoble/P +ignobly +ignominious/Y +ignominy/MS +ignoramus/SM +ignorance/MS +ignorantness/M +ignorant/SPY +ignorer/M +ignore/SRDGB +Igor/M +iguana/MS +Iguassu/M +ii +iii +Ijsselmeer/M +Ike/M +Ikey/M +Ikhnaton/M +ikon's +IL +Ilaire/M +Ila/M +Ilario/M +ilea +Ileana/M +Ileane/M +ileitides +ileitis/M +Ilene/M +ileum/M +ilia +iliac +Iliad/MS +Ilise/M +ilium/M +Ilka/M +ilk/MS +I'll +Illa/M +illegality/MS +illegal/YS +illegibility/MS +illegible +illegibly +illegitimacy/SM +illegitimate/SDGY +illiberality/SM +illiberal/Y +illicitness/MS +illicit/YP +illimitableness/M +illimitable/P +Illinoisan/MS +Illinois/M +illiquid +illiteracy/MS +illiterateness/M +illiterate/PSY +Ill/M +illness/MS +illogicality/SM +illogicalness/M +illogical/PY +illogic/M +ill/PS +illume/DG +illuminate/XSDVNG +Illuminati +illuminatingly +illuminating/U +illumination/M +illumine/BGSD +illusionary +illusion/ES +illusionist/MS +illusion's +illusiveness/M +illusive/PY +illusoriness/M +illusory/P +illustrated/U +illustrate/VGNSDX +illustration/M +illustrative/Y +illustrator/SM +illustriousness/SM +illustrious/PY +illus/V +illy +Ilona/M +Ilsa/M +Ilse/M +Ilysa/M +Ilyse/M +Ilyssa/M +Ilyushin/M +I'm +image/DSGM +Imagen/M +imagery/MS +imaginableness +imaginable/U +imaginably/U +imaginariness/M +imaginary/PS +imagination/MS +imaginativeness/M +imaginative/UY +imagined/U +imaginer/M +imagine/RSDJBG +imagoes +imago/M +imam/MS +imbalance/SDM +imbecile/YMS +imbecilic +imbecility/MS +imbiber/M +imbibe/ZRSDG +imbrication/SM +Imbrium/M +imbroglio/MS +imbruing +imbue/GDS +Imelda/M +IMF +IMHO +imitable/I +imitate/SDVNGX +imitation/M +imitativeness/MS +imitative/YP +imitator/SM +immaculateness/SM +immaculate/YP +immanence/S +immanency/MS +immanent/Y +Immanuel/M +immateriality/MS +immaterialness/MS +immaterial/PY +immatureness/M +immature/SPY +immaturity/MS +immeasurableness/M +immeasurable/P +immeasurably +immediacy/MS +immediateness/SM +immediate/YP +immemorial/Y +immenseness/M +immense/PRTY +immensity/MS +immerse/RSDXNG +immersible +immersion/M +immigrant/SM +immigrate/NGSDX +immigration/M +imminence/SM +imminentness/M +imminent/YP +immobile +immobility/MS +immobilization/MS +immobilize/DSRG +immoderateness/M +immoderate/NYP +immoderation/M +immodest/Y +immodesty/SM +immolate/SDNGX +immolation/M +immorality/MS +immoral/Y +immortality/SM +immortalized/U +immortalize/GDS +immortal/SY +immovability/SM +immovableness/M +immovable/PS +immovably +immune/S +immunity/SM +immunization/MS +immunize/GSD +immunoassay/M +immunodeficiency/S +immunodeficient +immunologic +immunological/Y +immunologist/SM +immunology/MS +immure/GSD +immutability/MS +immutableness/M +immutable/P +immutably +IMNSHO +IMO +Imogene/M +Imogen/M +Imojean/M +impaction/SM +impactor/SM +impact/VGMRDS +impaired/U +impairer/M +impair/LGRDS +impairment/SM +impala/MS +impale/GLRSD +impalement/SM +impaler/M +impalpable +impalpably +impanel/DGS +impartation/M +impart/GDS +impartiality/SM +impartial/Y +impassableness/M +impassable/P +impassably +impasse/SXBMVN +impassibility/SM +impassible +impassibly +impassion/DG +impassioned/U +impassiveness/MS +impassive/YP +impassivity/MS +impasto/SM +impatience/SM +impatiens/M +impatient/Y +impeachable/U +impeach/DRSZGLB +impeacher/M +impeachment/MS +impeccability/SM +impeccable/S +impeccably +impecuniousness/MS +impecunious/PY +impedance/MS +impeded/U +impeder/M +impede/S +imped/GRD +impedimenta +impediment/SM +impelled +impeller/MS +impelling +impel/S +impend/DGS +impenetrability/MS +impenetrableness/M +impenetrable/P +impenetrably +impenitence/MS +impenitent/YS +imperativeness/M +imperative/PSY +imperceivable +imperceptibility/MS +imperceptible +imperceptibly +imperceptive +imperf +imperfectability +imperfection/MS +imperfectness/SM +imperfect/YSVP +imperialism/MS +imperialistic +imperialistically +imperialist/SM +imperial/YS +imperil/GSLD +imperilment/SM +imperiousness/MS +imperious/YP +imperishableness/M +imperishable/SP +imperishably +impermanence/MS +impermanent/Y +impermeability/SM +impermeableness/M +impermeable/P +impermeably +impermissible +impersonality/M +impersonalized +impersonal/Y +impersonate/XGNDS +impersonation/M +impersonator/SM +impertinence/SM +impertinent/YS +imperturbability/SM +imperturbable +imperturbably +imperviousness/M +impervious/PY +impetigo/MS +impetuosity/MS +impetuousness/MS +impetuous/YP +impetus/MS +impiety/MS +impinge/LS +impingement/MS +imping/GD +impiousness/SM +impious/PY +impishness/MS +impish/YP +implacability/SM +implacableness/M +implacable/P +implacably +implantation/SM +implant/BGSDR +implanter/M +implausibility/MS +implausible +implausibly +implementability +implementable/U +implementation/A +implementations +implementation's +implemented/AU +implementer/M +implementing/A +implementor/MS +implement/SMRDGZB +implicant/SM +implicate/VGSD +implication/M +implicative/PY +implicitness/SM +implicit/YP +implied/Y +implode/GSD +implore/GSD +imploring/Y +implosion/SM +implosive/S +imply/GNSDX +impoliteness/MS +impolite/YP +impoliticness/M +impolitic/PY +imponderableness/M +imponderable/PS +importance/SM +important/Y +importation/MS +importer/M +importing/A +import/SZGBRD +importunateness/M +importunate/PYGDS +importuner/M +importune/SRDZYG +importunity/SM +imposable +impose/ASDG +imposer/SM +imposingly +imposing/U +imposition/SM +impossibility/SM +impossibleness/M +impossible/PS +impossibly +imposter's +impostor/SM +impost/SGMD +imposture/SM +impotence/MS +impotency/S +impotent/SY +impound/GDS +impoundments +impoverisher/M +impoverish/LGDRS +impoverishment/SM +impracticableness/M +impracticable/P +impracticably +impracticality/SM +impracticalness/M +impractical/PY +imprecate/NGXSD +imprecation/M +impreciseness/MS +imprecise/PYXN +imprecision/M +impregnability/MS +impregnableness/M +impregnable/P +impregnably +impregnate/DSXNG +impregnation/M +impresario/SM +impress/DRSGVL +impressed/U +impresser/M +impressibility/MS +impressible +impressionability/SM +impressionableness/M +impressionable/P +impression/BMS +impressionism/SM +impressionistic +impressionist/MS +impressiveness/MS +impressive/YP +impressment/M +imprimatur/SM +imprinter/M +imprinting/M +imprint/SZDRGM +imprison/GLDS +imprisonment/MS +improbability/MS +improbableness/M +improbable/P +improbably +impromptu/S +improperness/M +improper/PY +impropitious +impropriety/SM +improved/U +improvement/MS +improver/M +improve/SRDGBL +improvidence/SM +improvident/Y +improvisational +improvisation/MS +improvisatory +improviser/M +improvise/RSDZG +imprudence/SM +imprudent/Y +imp/SGMDRY +impudence/MS +impudent/Y +impugner/M +impugn/SRDZGB +impulse/XMVGNSD +impulsion/M +impulsiveness/MS +impulsive/YP +impunity/SM +impureness/M +impure/RPTY +impurity/MS +imputation/SM +impute/SDBG +Imus/M +IN +inaction +inactive +inadequate/S +inadvertence/MS +inadvertent/Y +inalienability/MS +inalienably +inalterableness/M +inalterable/P +Ina/M +inamorata/MS +inane/SRPYT +inanimateness/S +inanimate/P +inanity/MS +inappeasable +inappropriate/P +inarticulate/P +in/AS +inasmuch +inaugural/S +inaugurate/XSDNG +inauguration/M +inauthenticity +inbound/G +inbred/S +inbreed/JG +incalculableness/M +incalculably +incandescence/SM +incandescent/YS +incant +incantation/SM +incantatory +incapable/S +incapacitate/GNSD +incapacitation/M +incarcerate/XGNDS +incarceration/M +incarnadine/GDS +incarnate/AGSDNX +incarnation/AM +Inca/SM +incendiary/S +incense/MGDS +incentive/ESM +incentively +incept/DGVS +inception/MS +inceptive/Y +inceptor/M +incessant/Y +incest/SM +incestuousness/MS +incestuous/PY +inch/GMDS +inchoate/DSG +Inchon/M +inchworm/MS +incidence/MS +incidental/YS +incident/SM +incinerate/XNGSD +incineration/M +incinerator/SM +incipience/SM +incipiency/M +incipient/Y +incise/SDVGNX +incision/M +incisiveness/MS +incisive/YP +incisor/MS +incitement/MS +inciter/M +incite/RZL +incl +inclination/ESM +incline/EGSD +incliner/M +inclining/M +include/GDS +inclusion/MS +inclusiveness/MS +inclusive/PY +Inc/M +incognito/S +incoherency/M +income/M +incommode/DG +incommunicado +incomparable +incompetent/MS +incomplete/P +inconceivability/MS +inconceivableness/M +inconceivable/P +incondensable +incongruousness/S +inconsiderableness/M +inconsiderable/P +inconsistence +inconsolableness/M +inconsolable/P +inconsolably +incontestability/SM +incontestably +incontrovertibly +inconvenience/DG +inconvertibility +inconvertible +incorporable +incorporated/UE +incorporate/GASDXN +incorrect/P +incorrigibility/MS +incorrigibleness/M +incorrigible/SP +incorrigibly +incorruptible/S +incorruptibly +increase/JB +increaser/M +increasing/Y +incredibleness/M +incredible/P +incremental/Y +incrementation +increment/DMGS +incriminate/XNGSD +incrimination/M +incriminatory +incrustation/SM +inc/T +incubate/XNGVDS +incubation/M +incubator/MS +incubus/MS +inculcate/SDGNX +inculcation/M +inculpate/SDG +incumbency/MS +incumbent/S +incunabula +incunabulum +incurable/S +incurious +incursion/SM +ind +indebtedness/SM +indebted/P +indefatigableness/M +indefatigable/P +indefatigably +indefeasible +indefeasibly +indefinableness/M +indefinable/PS +indefinite/S +indelible +indelibly +indemnification/M +indemnify/NXSDG +indemnity/SM +indentation/SM +indented/U +indenter/M +indention/SM +indent/R +indenture/DG +Independence/M +indescribableness/M +indescribable/PS +indescribably +indestructibleness/M +indestructible/P +indestructibly +indeterminably +indeterminacy/MS +indeterminism +indexation/S +indexer/M +index/MRDZGB +India/M +Indiana/M +Indianan/S +Indianapolis/M +Indianian/S +Indian/SM +indicant/MS +indicate/DSNGVX +indication/M +indicative/SY +indicator/MS +indices's +indicter/M +indictment/SM +indict/SGLBDR +indifference +indigence/MS +indigenousness/M +indigenous/YP +indigent/SY +indigestible/S +indignant/Y +indignation/MS +indigo/SM +Indira/M +indirect/PG +indiscreet/P +indiscriminateness/M +indiscriminate/PY +indispensability/MS +indispensableness/M +indispensable/SP +indispensably +indisputableness/M +indisputable/P +indissolubleness/M +indissoluble/P +indissolubly +indistinguishableness/M +indistinguishable/P +indite/SDG +indium/SM +individualism/MS +individualistic +individualistically +individualist/MS +individuality/MS +individualization/SM +individualize/DRSGZ +individualized/U +individualizer/M +individualizes/U +individualizing/Y +individual/YMS +individuate/DSXGN +individuation/M +indivisibleness/M +indivisible/SP +indivisibly +Ind/M +Indochina/M +Indochinese +indoctrinate/GNXSD +indoctrination/M +indoctrinator/SM +indolence/SM +indolent/Y +indomitableness/M +indomitable/P +indomitably +Indonesia/M +Indonesian/S +indoor +Indore/M +Indra/M +indubitableness/M +indubitable/P +indubitably +inducement/MS +inducer/M +induce/ZGLSRD +inducible +inductance/MS +inductee/SM +induct/GV +induction/SM +inductiveness/M +inductive/PY +inductor/MS +indulge/GDRS +indulgence/SDGM +indulgent/Y +indulger/M +Indus/M +industrialism/MS +industrialist/MS +industrialization/MS +industrialized/U +industrialize/SDG +industrial/SY +industriousness/SM +industrious/YP +industry/SM +Indy/SM +inebriate/NGSDX +inebriation/M +inedible +ineducable +ineffability/MS +ineffableness/M +ineffable/P +ineffably +inelastic +ineligibly +ineluctable +ineluctably +ineptitude/SM +ineptness/MS +inept/YP +inequivalent +inerrant +inertial/Y +inertia/SM +inertness/MS +inert/SPY +Ines +inescapably +Inesita/M +Inessa/M +inestimably +inevitability/MS +inevitableness/M +inevitable/P +inevitably +inexact/P +inexhaustibleness/M +inexhaustible/P +inexhaustibly +inexorability/M +inexorableness/M +inexorable/P +inexorably +inexpedience/M +inexplicableness/M +inexplicable/P +inexplicably +inexplicit +inexpressibility/M +inexpressibleness/M +inexpressible/PS +inextricably +Inez/M +infamous +infamy/SM +infancy/M +infanticide/MS +infantile +infant/MS +infantryman/M +infantrymen +infantry/SM +infarction/SM +infarct/SM +infatuate/XNGSD +infatuation/M +infauna +infected/U +infecter +infect/ESGDA +infection/EASM +infectiousness/MS +infectious/PY +infective +infer/B +inference/GMSR +inferential/Y +inferiority/MS +inferior/SMY +infernal/Y +inferno/MS +inferred +inferring +infertile +infestation/MS +infester/M +infest/GSDR +infidel/SM +infighting/M +infill/MG +infiltrate/V +infiltrator/MS +infinitesimal/SY +infinite/V +infinitival +infinitive/YMS +infinitude/MS +infinitum +infinity/SM +infirmary/SM +infirmity/SM +infix/M +inflammableness/M +inflammable/P +inflammation/MS +inflammatory +inflatable/MS +inflate/NGBDRSX +inflater/M +inflationary +inflation/ESM +inflect/GVDS +inflectional/Y +inflection/SM +inflexibleness/M +inflexible/P +inflexion/SM +inflict/DRSGV +inflicter/M +infliction/SM +inflow/M +influenced/U +influencer/M +influence/SRDGM +influent +influential/SY +influenza/MS +infomercial/S +Informatica/M +informatics +informational +information/ES +informativeness/S +informative/UY +informatory +informed/U +informer/M +info/SM +infotainment/S +infra +infrared/SM +infrasonic +infrastructural +infrastructure/MS +infrequence/S +infringe/LR +infringement/SM +infringer/M +infuriate/GNYSD +infuriating/Y +infuriation/M +infuser/M +infuse/RZ +infusibleness/M +infusible/P +inf/ZT +Ingaberg/M +Ingaborg/M +Inga/M +Ingamar/M +Ingar/M +Ingeberg/M +Ingeborg/M +Ingelbert/M +Ingemar/M +ingeniousness/MS +ingenious/YP +ingnue/S +ingenuity/SM +ingenuous/EY +ingenuousness/MS +Inger/M +Inge/RM +Ingersoll/M +ingest/DGVS +ingestible +ingestion/SM +Inglebert/M +inglenook/MS +Inglewood/M +Inglis/M +Ingmar/M +ingoing +ingot/SMDG +ingrained/Y +Ingra/M +Ingram/M +ingrate/M +ingratiate/DSGNX +ingratiating/Y +ingratiation/M +ingredient/SM +Ingres/M +ingression/M +ingress/MS +Ingrid/M +Ingrim/M +ingrown/P +inguinal +Ingunna/M +inhabitable/U +inhabitance +inhabited/U +inhabiter/M +inhabit/R +inhalant/S +inhalation/SM +inhalator/SM +inhale/Z +inhere/DG +inherent/Y +inheritableness/M +inheritable/P +inheritance/EMS +inherit/BDSG +inherited/E +inheriting/E +inheritor/S +inheritress/MS +inheritrix/MS +inherits/E +inhibit/DVGS +inhibited/U +inhibiter's +inhibition/MS +inhibitor/MS +inhibitory +inhomogeneous +inhospitableness/M +inhospitable/P +inhospitality +Inigo/M +inimical/Y +inimitableness/M +inimitable/P +inimitably +inion +iniquitousness/M +iniquitous/PY +iniquity/MS +initialer/M +initial/GSPRDY +initialization/A +initializations +initialization's +initialize/ASDG +initialized/U +initializer/S +initiates +initiate/UD +initiating +initiation/SM +initiative/SM +initiator/MS +initiatory +injectable/U +inject/GVSDB +injection/MS +injector/SM +injunctive +injured/U +injurer/M +injure/SRDZG +injuriousness/M +injurious/YP +inkblot/SM +inker/M +inkiness/MS +inkling/SM +inkstand/SM +inkwell/SM +inky/TP +ink/ZDRJ +inland +inlander/M +inlay/RG +inletting +inly/G +inmost +Inna/M +innards +innateness/SM +innate/YP +innermost/S +innersole/S +innerspring +innervate/GNSDX +innervation/M +inner/Y +inning/M +Innis/M +innkeeper/MS +innocence/SM +Innocent/M +innocent/SYRT +innocuousness/MS +innocuous/PY +innovate/SDVNGX +innovation/M +innovative/P +innovator/MS +innovatory +Innsbruck/M +innuendo/MDGS +innumerability/M +innumerableness/M +innumerable/P +innumerably +innumerate +inn/ZGDRSJ +inoculate/ASDG +inoculation/MS +inoculative +inoffensive/P +Inonu/M +inopportuneness/M +inopportune/P +inordinateness/M +inordinate/PY +inorganic +inpatient +In/PM +input/MRDG +inquirer/M +inquire/ZR +inquiring/Y +inquiry/MS +inquisitional +inquisition/MS +Inquisition/MS +inquisitiveness/MS +inquisitive/YP +inquisitorial/Y +inquisitor/MS +INRI +inrush/M +ins +INS +insalubrious +insanitary +insatiability/MS +insatiableness/M +insatiable/P +insatiably +inscribe/Z +inscription/SM +inscrutability/SM +inscrutableness/SM +inscrutable/P +inscrutably +inseam +insecticidal +insecticide/MS +insectivore/SM +insectivorous +insecureness/M +insecure/P +inseminate/NGXSD +insemination/M +insensateness/M +insensate/P +insensible/P +insentient +inseparable/S +insert/ADSG +inserter/M +insertion/AMS +insetting +inshore +insider/M +inside/Z +insidiousness/MS +insidious/YP +insightful/Y +insigne's +insignia/SM +insignificant +insinuate/VNGXSD +insinuating/Y +insinuation/M +insinuator/SM +insipidity/MS +insipid/Y +insistence/SM +insistent/Y +insisting/Y +insist/SGD +insociable +insofar +insole/M +insolence/SM +insolent/YS +insolubleness/M +insoluble/P +insolubly +insomniac/S +insomnia/MS +insomuch +insouciance/SM +insouciant/Y +inspect/AGSD +inspection/SM +inspective +inspectorate/MS +inspector/SM +inspirational/Y +inspiration/MS +inspired/U +inspire/R +inspirer/M +inspiring/U +inspirit/DG +Inst +installable +install/ADRSG +installation/SM +installer/MS +installment/MS +instance/GD +instantaneousness/M +instantaneous/PY +instantiated/U +instantiate/SDXNG +instantiation/M +instant/SRYMP +instate/AGSD +inst/B +instead +instigate/XSDVGN +instigation/M +instigator/SM +instillation/SM +instinctive/Y +instinctual +instinct/VMS +instituter/M +institutes/M +institute/ZXVGNSRD +institutionalism/M +institutionalist/M +institutionalization/SM +institutionalize/GDS +institutional/Y +institution/AM +institutor's +instr +instruct/DSVG +instructed/U +instructional +instruction/MS +instructiveness/M +instructive/PY +instructor/MS +instrumentalist/MS +instrumentality/SM +instrumental/SY +instrumentation/SM +instrument/GMDS +insubordinate +insubstantial +insufferable +insufferably +insularity/MS +insular/YS +insulate/DSXNG +insulated/U +insulation/M +insulator/MS +insulin/MS +insult/DRSG +insulter/M +insulting/Y +insuperable +insuperably +insupportableness/M +insupportable/P +insurance/MS +insurance's/A +insure/BZGS +insured/S +insurer/M +insurgence/SM +insurgency/MS +insurgent/MS +insurmountably +insurrectionist/SM +insurrection/SM +intactness/M +intact/P +intaglio/GMDS +intake/M +intangible/M +integer/MS +integrability/M +integrable +integral/SYM +integrand/MS +integrate/AGNXEDS +integration/EMA +integrative/E +integrator/MS +integrity/SM +integument/SM +intellective/Y +intellect/MVS +intellectualism/MS +intellectuality/M +intellectualize/GSD +intellectualness/M +intellectual/YPS +intelligence/MSR +intelligencer/M +intelligentsia/MS +intelligent/UY +intelligibilities +intelligibility/UM +intelligibleness/MU +intelligible/PU +intelligibly/U +Intel/M +Intelsat/M +intemperate/P +intendant/MS +intendedness/M +intended/SYP +intender/M +intensification/M +intensifier/M +intensify/GXNZRSD +intensional/Y +intensiveness/MS +intensive/PSY +intentionality/M +intentional/UY +intention/SDM +intentness/SM +intent/YP +interaction/MS +interactive/PY +interactivity +interact/VGDS +interaxial +interbank +interbred +interbreed/GS +intercalate/GNVDS +intercalation/M +intercase +intercaste +interceder/M +intercede/SRDG +intercensal +intercept/DGS +interception/MS +interceptor/MS +intercession/MS +intercessor/SM +intercessory +interchangeability/M +interchangeableness/M +interchangeable/P +interchangeably +interchange/DSRGJ +interchanger/M +intercity +interclass +intercohort +intercollegiate +intercommunicate/SDXNG +intercommunication/M +intercom/SM +interconnectedness/M +interconnected/P +interconnect/GDS +interconnection/SM +interconnectivity +intercontinental +interconversion/M +intercorrelated +intercourse/SM +Interdata/M +interdenominational +interdepartmental/Y +interdependence/MS +interdependency/SM +interdependent/Y +interdiction/MS +interdict/MDVGS +interdisciplinary +interested/UYE +interest/GEMDS +interestingly/U +interestingness/M +interesting/YP +inter/ESTL +interface/SRDGM +interfacing/M +interfaith +interference/MS +interferer/M +interfere/SRDG +interfering/Y +interferometer/SM +interferometric +interferometry/M +interferon/MS +interfile/GSD +intergalactic +intergenerational +intergeneration/M +interglacial +intergovernmental +intergroup +interim/S +interindex +interindustry +interior/SMY +interj +interject/GDS +interjectional +interjection/MS +interlace/GSD +interlard/SGD +interlayer/G +interleave/SDG +interleukin/S +interlibrary +interlinear/S +interline/JGSD +interlingual +interlingua/M +interlining/M +interlink/GDS +interlisp/M +interlobular +interlocker/M +interlock/RDSG +interlocutor/MS +interlocutory +interlope/GZSRD +interloper/M +interlude/MSDG +intermarriage/MS +intermarry/GDS +intermediary/MS +intermediateness/M +intermediate/YMNGSDP +intermediation/M +interment/SME +intermeshed +intermetrics +intermezzi +intermezzo/SM +interminably +intermingle/DSG +intermission/MS +intermittent/Y +intermix/GSRD +intermodule +intermolecular/Y +internalization/SM +internalize/GDS +internal/SY +Internationale/M +internationalism/SM +internationalist/SM +internationality/M +internationalization/MS +internationalize/DSG +international/YS +internecine +internee/SM +interne's +Internet/M +INTERNET/M +internetwork +internist/SM +intern/L +internment/SM +internship/MS +internuclear +interocular +interoffice +interoperability +interpenetrates +interpersonal/Y +interplanetary +interplay/GSMD +interpol +interpolate/XGNVBDS +interpolation/M +Interpol/M +interpose/GSRD +interposer/M +interposition/MS +interpretable/U +interpret/AGSD +interpretation/MSA +interpretative/Y +interpreted/U +interpreter/SM +interpretive/Y +interpretor/S +interprocess +interprocessor +interquartile +interracial +interred/E +interregional +interregnum/MS +interrelatedness/M +interrelated/PY +interrelate/GNDSX +interrelation/M +interrelationship/SM +interring/E +interrogate/DSXGNV +interrogation/M +interrogative/SY +interrogator/SM +interrogatory/S +interrupted/U +interrupter/M +interruptibility +interruptible +interruption/MS +interrupt/VGZRDS +interscholastic +intersect/GDS +intersection/MS +intersession/MS +interspecies +intersperse/GNDSX +interspersion/M +interstage +interstate/S +interstellar +interstice/SM +interstitial/SY +intersurvey +intertask +intertwine/GSD +interurban/S +interval/MS +intervene/GSRD +intervener/M +intervenor/M +interventionism/MS +interventionist/S +intervention/MS +interview/AMD +interviewed/U +interviewee/SM +interviewer/SM +interviewing +interviews +intervocalic +interweave/GS +interwove +interwoven +intestacy/SM +intestinal/Y +intestine/SM +inti +intifada +intimacy/SM +intimal +intimateness/M +intimater/M +intimate/XYNGPDRS +intimation/M +intimidate/SDXNG +intimidating/Y +intimidation/M +into +intolerableness/M +intolerable/P +intolerant/PS +intonate/NX +intonation/M +intoxicant/MS +intoxicate/DSGNX +intoxicated/Y +intoxication/M +intra +intracellular +intracity +intraclass +intracohort +intractability/M +intractableness/M +intractable/P +intradepartmental +intrafamily +intragenerational +intraindustry +intraline +intrametropolitan +intramural/Y +intramuscular/Y +intranasal +intransigence/MS +intransigent/YS +intransitive/S +intraoffice +intraprocess +intrapulmonary +intraregional +intrasectoral +intrastate +intratissue +intrauterine +intravenous/YS +intrepidity/SM +intrepidness/M +intrepid/YP +intricacy/SM +intricateness/M +intricate/PY +intrigue/DRSZG +intriguer/M +intriguing/Y +intrinsically +intrinsic/S +introduce/ADSG +introducer/M +introduction/ASM +introductory +introit/SM +introject/SD +intro/S +introspection/MS +introspectiveness/M +introspective/YP +introspect/SGVD +introversion/SM +introvert/SMDG +intruder/M +intrude/ZGDSR +intrusion/SM +intrusiveness/MS +intrusive/SYP +intubate/NGDS +intubation/M +intuit/GVDSB +intuitionist/M +intuitiveness/MS +intuitive/YP +int/ZR +Inuit/MS +inundate/SXNG +inundation/M +inure/GDS +invader/M +invade/ZSRDG +invalid/GSDM +invalidism/MS +invariable/P +invariant/M +invasion/SM +invasive/P +invectiveness/M +invective/PSMY +inveigh/DRG +inveigher/M +inveighs +inveigle/DRSZG +inveigler/M +invent/ADGS +invented/U +invention/ASM +inventiveness/MS +inventive/YP +inventor/MS +inventory/SDMG +Inverness/M +inverse/YV +inverter/M +invertible +invert/ZSGDR +invest/ADSLG +investigate/XDSNGV +investigation/MA +investigator/MS +investigatory +investiture/SM +investment/ESA +investment's/A +investor/SM +inveteracy/MS +inveterate/Y +inviability +invidiousness/MS +invidious/YP +invigilate/GD +invigilator/SM +invigorate/ANGSD +invigorating/Y +invigoration/AM +invigorations +invincibility/SM +invincibleness/M +invincible/P +invincibly +inviolability/MS +inviolably +inviolateness/M +inviolate/YP +inviscid +invisibleness/M +invisible/S +invitational/S +invitation/MS +invited/U +invitee/S +inviter/M +invite/SRDG +inviting/Y +invocable +invocate +invoked/A +invoke/GSRDBZ +invoker/M +invokes/A +involuntariness/S +involuntary/P +involute/XYN +involution/M +involutorial +involvedly +involved/U +involve/GDSRL +involvement/SM +involver/M +invulnerability/M +invulnerableness/M +inwardness/M +inward/PY +ioctl +iodate/MGND +iodation/M +iodide/MS +iodinate/DNG +iodine/MS +iodize/GSD +Iolande/M +Iolanthe/M +Io/M +Iona/M +Ionesco/M +Ionian/M +ionic/S +Ionic/S +ionization's +ionization/SU +ionized/UC +ionize/GNSRDJXZ +ionizer's +ionizer/US +ionizes/U +ionizing/U +ionosphere/SM +ionospheric +ion's/I +ion/SMU +Iorgo/MS +Iormina/M +Iosep/M +iota/SM +IOU +Iowan/S +Iowa/SM +IPA +ipecac/MS +Iphigenia/M +ipso +Ipswich/M +IQ +Iqbal/M +Iquitos/M +Ira/M +Iranian/MS +Iran/M +Iraqi/SM +Iraq/M +IRA/S +irascibility/SM +irascible +irascibly +irateness/S +irate/RPYT +ireful +Ireland/M +ire/MGDS +Irena/M +Irene/M +irenic/S +iridescence/SM +iridescent/Y +irides/M +iridium/MS +irids +Irina/M +Iris +iris/GDSM +Irishman/M +Irishmen +Irish/R +Irishwoman/M +Irishwomen +Irita/M +irk/GDS +irksomeness/SM +irksome/YP +Irkutsk/M +Ir/M +Irma/M +ironclad/S +iron/DRMPSGJ +ironer/M +ironic +ironicalness/M +ironical/YP +ironing/M +ironmonger/M +ironmongery/M +ironside/MS +ironstone/MS +ironware/SM +ironwood/SM +ironworker/M +ironwork/MRS +irony/SM +Iroquoian/MS +Iroquois/M +irradiate/XSDVNG +irradiation/M +irrationality/MS +irrationalness/M +irrational/YSP +Irrawaddy/M +irreclaimable +irreconcilability/MS +irreconcilableness/M +irreconcilable/PS +irreconcilably +irrecoverableness/M +irrecoverable/P +irrecoverably +irredeemable/S +irredeemably +irredentism/M +irredentist/M +irreducibility/M +irreducible +irreducibly +irreflexive +irrefutable +irrefutably +irregardless +irregularity/SM +irregular/YS +irrelevance/SM +irrelevancy/MS +irrelevant/Y +irreligious +irremediableness/M +irremediable/P +irremediably +irremovable +irreparableness/M +irreparable/P +irreparably +irreplaceable/P +irrepressible +irrepressibly +irreproachableness/M +irreproachable/P +irreproachably +irreproducibility +irreproducible +irresistibility/M +irresistibleness/M +irresistible/P +irresistibly +irresoluteness/SM +irresolute/PNXY +irresolution/M +irresolvable +irrespective/Y +irresponsibility/SM +irresponsibleness/M +irresponsible/PS +irresponsibly +irretrievable +irretrievably +irreverence/MS +irreverent/Y +irreversible +irreversibly +irrevocableness/M +irrevocable/P +irrevocably +irrigable +irrigate/DSXNG +irrigation/M +irritability/MS +irritableness/M +irritable/P +irritably +irritant/S +irritate/DSXNGV +irritated/Y +irritating/Y +irritation/M +irrupt/GVSD +irruption/SM +IRS +Irtish/M +Irvine/M +Irving/M +Irvin/M +Irv/MG +Irwin/M +Irwinn/M +is +i's +Isaac/SM +Isaak/M +Isabelita/M +Isabella/M +Isabelle/M +Isabel/M +Isacco/M +Isac/M +Isadora/M +Isadore/M +Isador/M +Isahella/M +Isaiah/M +Isak/M +Isa/M +ISBN +Iscariot/M +Iseabal/M +Isfahan/M +Isherwood/M +Ishim/M +Ishmael/M +Ishtar/M +Isiahi/M +Isiah/M +Isidora/M +Isidore/M +Isidor/M +Isidoro/M +Isidro/M +isinglass/MS +Isis/M +Islamabad/M +Islamic/S +Islam/SM +islander/M +island/GZMRDS +Islandia/M +isle/MS +islet/SM +isl/GD +Ismael/M +ism/MCS +isn't +ISO +isobaric +isobar/MS +Isobel/M +isochronal/Y +isochronous/Y +isocline/M +isocyanate/M +isodine +isolate/SDXNG +isolationism/SM +isolationistic +isolationist/SM +isolation/M +isolator/MS +Isolde/M +isomeric +isomerism/SM +isomer/SM +isometrically +isometric/S +isometrics/M +isomorphic +isomorphically +isomorphism/MS +isomorph/M +isoperimetrical +isopleth/M +isopleths +isosceles +isostatic +isothermal/Y +isotherm/MS +isotonic +isotope/SM +isotopic +isotropic +isotropically +isotropy/M +Ispahan's +ispell/M +Ispell/M +Israeli/MS +Israelite/SM +Israel/MS +Issac/M +Issiah/M +Issie/M +Issi/M +issuable +issuance/MS +issuant +issued/A +issue/GMZDSR +issuer/AMS +issues/A +issuing/A +Issy/M +Istanbul/M +isthmian/S +isthmus/SM +Istvan/M +Isuzu/M +It +IT +Itaipu/M +ital +Italianate/GSD +Italian/MS +italicization/MS +italicized/U +italicize/GSD +italic/S +Ital/M +Italy/M +Itasca/M +itch/GMDS +itchiness/MS +Itch/M +itchy/RTP +ITcorp/M +ITCorp/M +it'd +Itel/M +itemization/SM +itemized/U +itemize/GZDRS +itemizer/M +itemizes/A +item/MDSG +iterate/ASDXVGN +iteration/M +iterative/YA +iterator/MS +Ithaca/M +Ithacan +itinerant/SY +itinerary/MS +it'll +it/MUS +Ito/M +its +itself +ITT +IUD/S +IV +Iva/M +Ivanhoe/M +Ivan/M +Ivar/M +I've +Ive/MRS +Iver/M +Ivette/M +Ivett/M +Ivie/M +iv/M +Ivonne/M +Ivor/M +Ivory/M +ivory/SM +IVs +Ivy/M +ivy/MDS +ix +Izaak/M +Izabel/M +Izak/M +Izanagi/M +Izanami/M +Izhevsk/M +Izmir/M +Izvestia/M +Izzy/M +jabbed +jabberer/M +jabber/JRDSZG +jabbing +Jabez/M +Jablonsky/M +jabot/MS +jab/SM +jacaranda/MS +Jacenta/M +Jacinda/M +Jacinta/M +Jacintha/M +Jacinthe/M +jackal/SM +jackass/SM +jackboot/DMS +jackdaw/SM +Jackelyn/M +jacketed/U +jacket/GSMD +jack/GDRMS +jackhammer/MDGS +Jackie/M +Jacki/M +jackknife/MGSD +jackknives +Jacklin/M +Jacklyn/M +Jack/M +Jackman/M +jackpot/MS +Jackqueline/M +Jackquelin/M +jackrabbit/DGS +Jacksonian +Jackson/SM +Jacksonville/M +jackstraw/MS +Jacky/M +Jaclin/M +Jaclyn/M +Jacobean +Jacobian/M +Jacobi/M +Jacobin/M +Jacobite/M +Jacobo/M +Jacobsen/M +Jacob/SM +Jacobs/N +Jacobson/M +Jacobus +Jacoby/M +jacquard/MS +Jacquard/SM +Jacqueline/M +Jacquelin/M +Jacquelyn/M +Jacquelynn/M +Jacquenetta/M +Jacquenette/M +Jacques/M +Jacquetta/M +Jacquette/M +Jacquie/M +Jacqui/M +jacuzzi +Jacuzzi/S +Jacynth/M +Jada/M +jadedness/SM +jaded/PY +jadeite/SM +Jade/M +jade/MGDS +Jaeger/M +Jae/M +jaggedness/SM +jagged/RYTP +Jagger/M +jaggers +jagging +jag/S +jaguar/MS +jailbird/MS +jailbreak/SM +jailer/M +jail/GZSMDR +Jaime/M +Jaimie/M +Jaine/M +Jainism/M +Jain/M +Jaipur/M +Jakarta/M +Jake/MS +Jakie/M +Jakob/M +jalapeo/S +jalopy/SM +jalousie/MS +Jamaal/M +Jamaica/M +Jamaican/S +Jamal/M +Jamar/M +jambalaya/MS +jamb/DMGS +jamboree/MS +Jamel/M +Jame/MS +Jameson/M +Jamestown/M +Jamesy/M +Jamey/M +Jamie/M +Jamill/M +Jamil/M +Jami/M +Jamima/M +Jamison/M +Jammal/M +jammed/U +Jammie/M +jamming/U +jam/SM +Janacek/M +Jana/M +Janaya/M +Janaye/M +Jandy/M +Janean/M +Janeczka/M +Janeen/M +Janeiro/M +Janek/M +Janela/M +Janella/M +Janelle/M +Janell/M +Janel/M +Jane/M +Janene/M +Janenna/M +Janessa/M +Janesville/M +Janeta/M +Janet/M +Janetta/M +Janette/M +Janeva/M +Janey/M +jangler/M +jangle/RSDGZ +jangly +Jania/M +Janice/M +Janie/M +Janifer/M +Janina/M +Janine/M +Janis/M +janissary/MS +Janith/M +janitorial +janitor/SM +Janka/M +Jan/M +Janna/M +Jannelle/M +Jannel/M +Jannie/M +Janos/M +Janot/M +Jansenist/M +Jansen/M +January/MS +Janus/M +Jany/M +Japanese/SM +Japan/M +japanned +japanner +japanning +japan/SM +jape/DSMG +Japura/M +Jaquelin/M +Jaquelyn/M +Jaquenetta/M +Jaquenette/M +Jaquith/M +Jarad/M +jardinire/MS +Jard/M +Jareb/M +Jared/M +jarful/S +jargon/SGDM +Jarib/M +Jarid/M +Jarlsberg +jar/MS +Jarrad/M +jarred +Jarred/M +Jarret/M +Jarrett/M +Jarrid/M +jarring/SY +Jarrod/M +Jarvis/M +Jase/M +Jasen/M +Jasmina/M +Jasmine/M +jasmine/MS +Jasmin/M +Jason/M +Jasper/M +jasper/MS +Jastrow/M +Jasun/M +jato/SM +jaundice/DSMG +jaundiced/U +jauntily +jauntiness/MS +jaunt/MDGS +jaunty/SRTP +Javanese +Java/SM +JavaScript/M +javelin/SDMG +Javier/M +jawbone/SDMG +jawbreaker/SM +jawline +jaw/SMDG +Jaxartes/M +Jayapura/M +jaybird/SM +Jaycee/SM +Jaye/M +Jay/M +Jaymee/M +Jayme/M +Jaymie/M +Jaynell/M +Jayne/M +jay/SM +Jayson/M +jaywalker/M +jaywalk/JSRDZG +Jazmin/M +jazziness/M +jazzmen +jazz/MGDS +jazzy/PTR +JCS +jct +JD +Jdavie/M +jealousness/M +jealous/PY +jealousy/MS +Jeana/M +Jeanelle/M +Jeane/M +Jeanette/M +Jeanie/M +Jeanine/M +Jean/M +jean/MS +Jeanna/M +Jeanne/M +Jeannette/M +Jeannie/M +Jeannine/M +Jecho/M +Jedd/M +Jeddy/M +Jedediah/M +Jedidiah/M +Jedi/M +Jed/M +jeep/GZSMD +Jeep/S +jeerer/M +jeering/Y +jeer/SJDRMG +Jeeves/M +jeez +Jefferey/M +Jeffersonian/S +Jefferson/M +Jeffery/M +Jeffie/M +Jeff/M +Jeffrey/SM +Jeffry/M +Jeffy/M +jehad's +Jehanna/M +Jehoshaphat/M +Jehovah/M +Jehu/M +jejuna +jejuneness/M +jejune/PY +jejunum/M +Jekyll/M +Jelene/M +jell/GSD +Jello/M +jello's +jellybean/SM +jellyfish/MS +jellying/M +jellylike +jellyroll/S +jelly/SDMG +Jemie/M +Jemimah/M +Jemima/M +Jemmie/M +jemmy/M +Jemmy/M +Jena/M +Jenda/M +Jenelle/M +Jenica/M +Jeniece/M +Jenifer/M +Jeniffer/M +Jenilee/M +Jeni/M +Jenine/M +Jenkins/M +Jen/M +Jenna/M +Jennee/M +Jenner/M +jennet/SM +Jennette/M +Jennica/M +Jennie/M +Jennifer/M +Jennilee/M +Jenni/M +Jennine/M +Jennings/M +Jenn/RMJ +Jenny/M +jenny/SM +Jeno/M +Jensen/M +Jens/N +jeopard +jeopardize/GSD +jeopardy/MS +Jephthah/M +Jerad/M +Jerald/M +Jeralee/M +Jeramey/M +Jeramie/M +Jere/M +Jereme/M +jeremiad/SM +Jeremiah/M +Jeremiahs +Jeremias/M +Jeremie/M +Jeremy/M +Jericho/M +Jeri/M +jerker/M +jerk/GSDRJ +jerkily +jerkiness/SM +jerkin/SM +jerkwater/S +jerky/RSTP +Jermaine/M +Jermain/M +Jermayne/M +Jeroboam/M +Jerold/M +Jerome/M +Jeromy/M +Jerrie/M +Jerrilee/M +Jerrilyn/M +Jerri/M +Jerrine/M +Jerrod/M +Jerrold/M +Jerrome/M +jerrybuilt +Jerrylee/M +jerry/M +Jerry/M +jersey/MS +Jersey/MS +Jerusalem/M +Jervis/M +Jes +Jessalin/M +Jessalyn/M +Jessa/M +Jessamine/M +jessamine's +Jessamyn/M +Jessee/M +Jesselyn/M +Jesse/M +Jessey/M +Jessica/M +Jessie/M +Jessika/M +Jessi/M +jess/M +Jess/M +Jessy/M +jest/DRSGZM +jester/M +jesting/Y +Jesuit/SM +Jesus +Jeth/M +Jethro/M +jetliner/MS +jet/MS +jetport/SM +jetsam/MS +jetted/M +jetting/M +jettison/DSG +jetty/RSDGMT +jeweler/M +jewelery/S +jewel/GZMRDS +Jewelled/M +Jewelle/M +jewellery's +Jewell/MD +Jewel/M +jewelry/MS +Jewess/SM +Jewishness/MS +Jewish/P +Jew/MS +Jewry/MS +Jezebel/MS +j/F +JFK/M +jg/M +jibbed +jibbing +jibe/S +jib/MDSG +Jidda/M +jiff/S +jiffy/SM +jigged +jigger/SDMG +jigging/M +jiggle/SDG +jiggly/TR +jig/MS +jigsaw/GSDM +jihad/SM +Jilin +Jillana/M +Jillane/M +Jillayne/M +Jilleen/M +Jillene/M +Jillian/M +Jillie/M +Jilli/M +Jill/M +Jilly/M +jilt/DRGS +jilter/M +Jimenez/M +Jim/M +Jimmie/M +jimmy/GSDM +Jimmy/M +jimsonweed/S +Jinan +jingler/M +jingle/RSDG +jingly/TR +jingoism/SM +jingoistic +jingoist/SM +jingo/M +Jinnah/M +jinni's +jinn/MS +Jinny/M +jinrikisha/SM +jinx/GMDS +jitney/MS +jitterbugged +jitterbugger +jitterbugging +jitterbug/SM +jitter/S +jittery/TR +jiujitsu's +Jivaro/M +jive/MGDS +Joachim/M +Joana/M +Joane/M +Joanie/M +Joan/M +Joanna/M +Joanne/SM +Joann/M +Joaquin/M +jobbed +jobber/MS +jobbery/M +jobbing/M +Jobey/M +jobholder/SM +Jobie/M +Jobi/M +Jobina/M +joblessness/MS +jobless/P +Jobrel/M +job/SM +Job/SM +Jobye/M +Joby/M +Jobyna/M +Jocasta/M +Joceline/M +Jocelin/M +Jocelyne/M +Jocelyn/M +jockey/SGMD +jock/GDMS +Jock/M +Jocko/M +jockstrap/MS +jocoseness/MS +jocose/YP +jocosity/SM +jocularity/SM +jocular/Y +jocundity/SM +jocund/Y +Jodee/M +jodhpurs +Jodie/M +Jodi/M +Jody/M +Joeann/M +Joela/M +Joelie/M +Joella/M +Joelle/M +Joellen/M +Joell/MN +Joelly/M +Joellyn/M +Joel/MY +Joelynn/M +Joe/M +Joesph/M +Joete/M +joey/M +Joey/M +jogged +jogger/SM +jogging/S +joggler/M +joggle/SRDG +Jogjakarta/M +jog/S +Johan/M +Johannah/M +Johanna/M +Johannes +Johannesburg/M +Johann/M +Johansen/M +Johanson/M +Johna/MH +Johnathan/M +Johnath/M +Johnathon/M +Johnette/M +Johnie/M +Johnna/M +Johnnie/M +johnnycake/SM +Johnny/M +johnny/SM +Johnsen/M +john/SM +John/SM +Johns/N +Johnson/M +Johnston/M +Johnstown/M +Johny/M +Joice/M +join/ADGFS +joined/U +joiner/FSM +joinery/MS +jointed/EYP +jointedness/ME +joint/EGDYPS +jointer/M +jointly/F +joint's +jointures +joist/GMDS +Jojo/M +joke/MZDSRG +joker/M +jokey +jokier +jokiest +jokily +joking/Y +Jolee/M +Joleen/M +Jolene/M +Joletta/M +Jolie/M +Joliet's +Joli/M +Joline/M +Jolla/M +jollification/MS +jollily +jolliness/SM +jollity/MS +jolly/TSRDGP +Jolson/M +jolt/DRGZS +jolter/M +Joly/M +Jolyn/M +Jolynn/M +Jo/MY +Jonah/M +Jonahs +Jonas +Jonathan/M +Jonathon/M +Jonell/M +Jone/MS +Jones/S +Jonie/M +Joni/MS +Jon/M +jonquil/MS +Jonson/M +Joplin/M +Jordain/M +Jordana/M +Jordanian/S +Jordan/M +Jordanna/M +Jordon/M +Jorey/M +Jorgan/M +Jorge/M +Jorgensen/M +Jorgenson/M +Jorie/M +Jori/M +Jorrie/M +Jorry/M +Jory/M +Joscelin/M +Josee/M +Josefa/M +Josefina/M +Josef/M +Joseito/M +Jose/M +Josepha/M +Josephina/M +Josephine/M +Joseph/M +Josephs +Josephson/M +Josephus/M +Josey/M +josh/DSRGZ +josher/M +Joshia/M +Josh/M +Joshuah/M +Joshua/M +Josiah/M +Josias/M +Josie/M +Josi/M +Josselyn/M +joss/M +jostle/SDG +Josue/M +Josy/M +jot/S +jotted +jotter/SM +jotting/SM +Joule/M +joule/SM +jounce/SDG +jouncy/RT +Jourdain/M +Jourdan/M +journalese/MS +journal/GSDM +journalism/SM +journalistic +journalist/SM +journalize/DRSGZ +journalized/U +journalizer/M +journey/DRMZSGJ +journeyer/M +journeyman/M +journeymen +jouster/M +joust/ZSMRDG +Jovanovich/M +Jove/M +joviality/SM +jovial/Y +Jovian +jowl/SMD +jowly/TR +Joya/M +Joyan/M +Joyann/M +Joycean +Joycelin/M +Joyce/M +Joye/M +joyfuller +joyfullest +joyfulness/SM +joyful/PY +joylessness/MS +joyless/PY +Joy/M +joy/MDSG +Joyner/M +joyousness/MS +joyous/YP +joyridden +joyride/SRZMGJ +joyrode +joystick/S +Jozef/M +JP +Jpn +Jr/M +j's +J's +Jsandye/M +Juana/M +Juanita/M +Juan/M +Juarez +Jubal/M +jubilant/Y +jubilate/XNGDS +jubilation/M +jubilee/SM +Judah/M +Judaic +Judaical +Judaism/SM +Judas/S +juddered +juddering +Judd/M +Judea/M +Jude/M +judge/AGDS +judger/M +judge's +judgeship/SM +judgmental/Y +judgment/MS +judicable +judicatory/S +judicature/MS +judicial/Y +judiciary/S +judicious/IYP +judiciousness/SMI +Judie/M +Judi/MH +Juditha/M +Judith/M +Jud/M +judo/MS +Judon/M +Judson/M +Judye/M +Judy/M +jugate/F +jugful/SM +jugged +Juggernaut/M +juggernaut/SM +jugging +juggler/M +juggle/RSDGZ +jugglery/MS +jug/MS +jugular/S +juice/GMZDSR +juicer/M +juicily +juiciness/MS +juicy/TRP +Juieta/M +jujitsu/MS +jujube/SM +juju/M +jujutsu's +jukebox/SM +juke/GS +Julee/M +Jule/MS +julep/SM +Julia/M +Juliana/M +Juliane/M +Julian/M +Julianna/M +Julianne/M +Juliann/M +Julie/M +julienne/GSD +Julienne/M +Julieta/M +Juliet/M +Julietta/M +Juliette/M +Juli/M +Julina/M +Juline/M +Julio/M +Julissa/M +Julita/M +Julius/M +Jul/M +Julys +July/SM +jumble/GSD +jumbo/MS +jumper/M +jump/GZDRS +jumpily +jumpiness/MS +jumpsuit/S +jumpy/PTR +jun +junco/MS +junction/IMESF +juncture/SFM +Juneau/M +June/MS +Junette/M +Jungfrau/M +Jungian +jungle/SDM +Jung/M +Junia/M +Junie/M +Junina/M +juniority/M +junior/MS +Junior/S +juniper/SM +junkerdom +Junker/SM +junketeer/SGDM +junket/SMDG +junk/GZDRMS +junkie/RSMT +junkyard/MS +Jun/M +Juno/M +junta/MS +Jupiter/M +Jurassic +juridic +juridical/Y +juried +jurisdictional/Y +jurisdiction/SM +jurisprudence/SM +jurisprudent +jurisprudential/Y +juristic +jurist/MS +juror/MS +Jurua/M +jury/IMS +jurying +juryman/M +jurymen +jurywoman/M +jurywomen +justed +Justen/M +juster/M +justest +Justice/M +justice/MIS +justiciable +justifiability/M +justifiable/U +justifiably/U +justification/M +justified/UA +justifier/M +justify/GDRSXZN +Justina/M +Justine/M +justing +Justinian/M +Justin/M +Justinn/M +Justino/M +Justis/M +justness/MS +justness's/U +justs +just/UPY +Justus/M +jute/SM +Jutish +Jutland/M +jut/S +jutted +jutting +Juvenal/M +juvenile/SM +juxtapose/SDG +juxtaposition/SM +JV +J/X +Jyoti/M +Kaaba/M +kabob/SM +kaboom +Kabuki +kabuki/SM +Kabul/M +Kacey/M +Kacie/M +Kacy/M +Kaddish/M +kaddish/S +Kaela/M +kaffeeklatch +kaffeeklatsch/S +Kafkaesque +Kafka/M +kaftan's +Kagoshima/M +Kahaleel/M +Kahlil/M +Kahlua/M +Kahn/M +Kaia/M +Kaifeng/M +Kaila/M +Kaile/M +Kailey/M +Kai/M +Kaine/M +Kain/M +kaiser/MS +Kaiser/SM +Kaitlin/M +Kaitlyn/M +Kaitlynn/M +Kaja/M +Kajar/M +Kakalina/M +Kalahari/M +Kala/M +Kalamazoo/M +Kalashnikov/M +Kalb/M +Kaleb/M +Kaleena/M +kaleidescope +kaleidoscope/SM +kaleidoscopic +kaleidoscopically +Kale/M +kale/MS +Kalgoorlie/M +Kalie/M +Kalila/M +Kalil/M +Kali/M +Kalina/M +Kalinda/M +Kalindi/M +Kalle/M +Kalli/M +Kally/M +Kalmyk +Kalvin/M +Kama/M +Kamchatka/M +Kamehameha/M +Kameko/M +Kamikaze/MS +kamikaze/SM +Kamilah/M +Kamila/M +Kamillah/M +Kampala/M +Kampuchea/M +Kanchenjunga/M +Kandace/M +Kandahar/M +Kandinsky/M +Kandy/M +Kane/M +kangaroo/SGMD +Kania/M +Kankakee/M +Kan/MS +Kannada/M +Kano/M +Kanpur/M +Kansan/S +Kansas +Kantian +Kant/M +Kanya/M +Kaohsiung/M +kaolinite/M +kaolin/MS +Kaplan/M +kapok/SM +Kaposi/M +kappa/MS +kaput/M +Karachi/M +Karaganda/M +Karakorum/M +karakul/MS +Karalee/M +Karalynn/M +Kara/M +Karamazov/M +karaoke/S +karate/MS +karat/SM +Karee/M +Kareem/M +Karel/M +Kare/M +Karena/M +Karenina/M +Karen/M +Karia/M +Karie/M +Karil/M +Karilynn/M +Kari/M +Karim/M +Karina/M +Karine/M +Karin/M +Kariotta/M +Karisa/M +Karissa/M +Karita/M +Karla/M +Karlan/M +Karlee/M +Karleen/M +Karlene/M +Karlen/M +Karlie/M +Karlik/M +Karlis +Karl/MNX +Karloff/M +Karlotta/M +Karlotte/M +Karly/M +Karlyn/M +karma/SM +Karmen/M +karmic +Karna/M +Karney/M +Karola/M +Karole/M +Karolina/M +Karoline/M +Karol/M +Karoly/M +Karon/M +Karo/YM +Karp/M +Karrah/M +Karrie/M +Karroo/M +Karry/M +kart/MS +Karylin/M +Karyl/M +Kary/M +Karyn/M +Kasai/M +Kasey/M +Kashmir/SM +Kaspar/M +Kasparov/M +Kasper/M +Kass +Kassandra/M +Kassey/M +Kassia/M +Kassie/M +Kassi/M +katakana +Katalin/M +Kata/M +Katee/M +Katelyn/M +Kate/M +Katerina/M +Katerine/M +Katey/M +Katha/M +Katharina/M +Katharine/M +Katharyn/M +Kathe/M +Katherina/M +Katherine/M +Katheryn/M +Kathiawar/M +Kathie/M +Kathi/M +Kathleen/M +Kathlin/M +Kath/M +Kathmandu +Kathrine/M +Kathryne/M +Kathryn/M +Kathye/M +Kathy/M +Katie/M +Kati/M +Katina/M +Katine/M +Katinka/M +Katleen/M +Katlin/M +Kat/M +Katmai/M +Katmandu's +Katowice/M +Katrina/M +Katrine/M +Katrinka/M +Kattie/M +Katti/M +Katuscha/M +Katusha/M +Katya/M +katydid/SM +Katy/M +Katz/M +Kauai/M +Kauffman/M +Kaufman/M +Kaunas/M +Kaunda/M +Kawabata/M +Kawasaki/M +kayak/SGDM +Kaycee/M +Kaye/M +Kayla/M +Kaylee/M +Kayle/M +Kayley/M +Kaylil/M +Kaylyn/M +Kay/M +Kayne/M +kayo/DMSG +Kazakh/M +Kazakhstan +Kazan/M +Kazantzakis/M +kazoo/SM +Kb +KB +KC +kcal/M +kc/M +KDE/M +Keane/M +Kean/M +Kearney/M +Keary/M +Keaton/M +Keats/M +kebab/SM +Keck/M +Keefe/MR +Keefer/M +Keegan/M +Keelby/M +Keeley/M +keel/GSMDR +keelhaul/SGD +Keelia/M +Keely/M +Keenan/M +Keene/M +keener/M +keen/GTSPYDR +keening/M +Keen/M +keenness/MS +keeper/M +keep/GZJSR +keeping/M +keepsake/SM +Keewatin/M +kegged +kegging +keg/MS +Keillor/M +Keir/M +Keisha/M +Keith/M +Kelbee/M +Kelby/M +Kelcey/M +Kelcie/M +Kelci/M +Kelcy/M +Kele/M +Kelila/M +Kellby/M +Kellen/M +Keller/M +Kelley/M +Kellia/M +Kellie/M +Kelli/M +Kellina/M +Kellogg/M +Kellsie/M +Kellyann/M +Kelly/M +kelp/GZMDS +Kelsey/M +Kelsi/M +Kelsy/M +Kelt's +Kelvin/M +kelvin/MS +Kelwin/M +Kemerovo/M +Kempis/M +Kemp/M +Kendall/M +Kendal/M +Kendell/M +Kendra/M +Kendre/M +Kendrick/MS +Kenilworth/M +Ken/M +Kenmore/M +ken/MS +Kenna/M +Kennan/M +Kennecott/M +kenned +Kennedy/M +kennel/GSMD +Kenneth/M +Kennett/M +Kennie/M +kenning +Kennith/M +Kenn/M +Kenny/M +keno/M +Kenon/M +Kenosha/M +Kensington/M +Kent/M +Kenton/M +Kentuckian/S +Kentucky/M +Kenya/M +Kenyan/S +Kenyatta/M +Kenyon/M +Keogh/M +Keokuk/M +kepi/SM +Kepler/M +kept +keratin/MS +kerbside +Kerby/M +kerchief/MDSG +Kerensky/M +Kerianne/M +Keriann/M +Keri/M +Kerk/M +Ker/M +Kermie/M +Kermit/M +Kermy/M +kerned +kernel/GSMD +kerning +Kern/M +kerosene/MS +Kerouac/M +Kerrie/M +Kerrill/M +Kerri/M +Kerrin/M +Kerr/M +Kerry/M +Kerstin/M +Kerwin/M +Kerwinn/M +Kesley/M +Keslie/M +Kessiah/M +Kessia/M +Kessler/M +kestrel/SM +ketch/MS +ketchup/SM +ketone/M +ketosis/M +Kettering/M +Kettie/M +Ketti/M +kettledrum/SM +kettleful +kettle/SM +Ketty/M +Kevan/M +Keven/M +Kevina/M +Kevin/M +Kevlar +Kev/MN +Kevon/M +Kevorkian/M +Kevyn/M +Kewaskum/M +Kewaunee/M +Kewpie/M +keyboardist/S +keyboard/RDMZGS +keyclick/SM +keyhole/MS +Key/M +Keynesian/M +Keynes/M +keynoter/M +keynote/SRDZMG +keypad/MS +keypuncher/M +keypunch/ZGRSD +keyring +key/SGMD +keystone/SM +keystroke/SDMG +keyword/SM +k/FGEIS +kg +K/G +KGB +Khabarovsk/M +Khachaturian/M +khaki/SM +Khalid/M +Khalil/M +Khan/M +khan/MS +Kharkov/M +Khartoum/M +Khayyam/M +Khmer/M +Khoisan/M +Khomeini/M +Khorana/M +Khrushchev/SM +Khufu/M +Khulna/M +Khwarizmi/M +Khyber/M +kHz/M +KIA +Kiah/M +Kial/M +kibble/GMSD +kibbutzim +kibbutz/M +kibitzer/M +kibitz/GRSDZ +kibosh/GMSD +Kickapoo/M +kickback/SM +kickball/MS +kicker/M +kick/GZDRS +kickoff/SM +kickstand/MS +kicky/RT +kidded +kidder/SM +kiddie/SD +kidding/YM +kiddish +Kidd/M +kiddo/SM +kiddying +kiddy's +kidless +kid/MS +kidnaper's +kidnaping's +kidnap/MSJ +kidnapped +kidnapper/SM +kidnapping/S +kidney/MS +kidskin/SM +Kieffer/M +kielbasa/SM +kielbasi +Kiele/M +Kiel/M +Kienan/M +kier/I +Kierkegaard/M +Kiersten/M +Kieth/M +Kiev/M +Kigali/M +Kikelia/M +Kikuyu/M +Kilauea/M +Kile/M +Kiley/M +Kilian/M +Kilimanjaro/M +kill/BJGZSDR +killdeer/SM +Killebrew/M +killer/M +Killian/M +Killie/M +killing/Y +killjoy/S +Killy/M +kiln/GDSM +kilobaud/M +kilobit/S +kilobuck +kilobyte/S +kilocycle/MS +kilogauss/M +kilogram/MS +kilohertz/M +kilohm/M +kilojoule/MS +kiloliter/MS +kilometer/SM +kilo/SM +kiloton/SM +kilovolt/SM +kilowatt/SM +kiloword +kilter/M +kilt/MDRGZS +Ki/M +Kimball/M +Kimbell/M +Kimberlee/M +Kimberley/M +Kimberli/M +Kimberly/M +Kimberlyn/M +Kimble/M +Kimbra/M +Kim/M +Kimmie/M +Kimmi/M +Kimmy/M +kimono/MS +Kincaid/M +kinda +kindergarten/MS +kindergrtner/SM +kinder/U +kindheartedness/MS +kindhearted/YP +kindle/AGRSD +kindler/M +kindliness/SM +kindliness's/U +kindling/M +kindly/TUPR +kindness's +kindness/US +kind/PSYRT +kindred/S +kinematic/S +kinematics/M +kinesics/M +kine/SM +kinesthesis +kinesthetically +kinesthetic/S +kinetically +kinetic/S +kinetics/M +kinfolk/S +kingbird/M +kingdom/SM +kingfisher/MS +kinglet/M +kingliness/M +kingly/TPR +King/M +kingpin/MS +Kingsbury/M +king/SGYDM +kingship/SM +Kingsley/M +Kingsly/M +Kingston/M +Kingstown/M +Kingwood/M +kink/GSDM +kinkily +kinkiness/SM +kinky/PRT +Kin/M +kin/MS +Kinna/M +Kinney/M +Kinnickinnic/M +Kinnie/M +Kinny/M +Kinsey/M +kinsfolk/S +Kinshasa/M +Kinshasha/M +kinship/SM +Kinsley/M +kinsman/M +kinsmen/M +kinswoman/M +kinswomen +kiosk/SM +Kiowa/SM +Kipling/M +Kip/M +kip/MS +Kippar/M +kipped +kipper/DMSG +Kipper/M +Kippie/M +kipping +Kipp/MR +Kippy/M +Kira/M +Kirbee/M +Kirbie/M +Kirby/M +Kirchhoff/M +Kirchner/M +Kirchoff/M +Kirghistan/M +Kirghizia/M +Kirghiz/M +Kiribati +Kiri/M +Kirinyaga/M +kirk/GDMS +Kirkland/M +Kirk/M +Kirkpatrick/M +Kirkwood/M +Kirov/M +kirsch/S +Kirsteni/M +Kirsten/M +Kirsti/M +Kirstin/M +Kirstyn/M +Kisangani/M +Kishinev/M +kismet/SM +kiss/DSRBJGZ +Kissee/M +kisser/M +Kissiah/M +Kissie/M +Kissinger/M +Kitakyushu/M +kitbag's +kitchener/M +Kitchener/M +kitchenette/SM +kitchen/GDRMS +kitchenware/SM +kiter/M +kite/SM +kith/MDG +kiths +Kit/M +kit/MDRGS +kitsch/MS +kitschy +kitted +kittenishness/M +kittenish/YP +kitten/SGDM +Kittie/M +Kitti/M +kitting +kittiwakes +Kitty/M +kitty/SM +Kiwanis/M +kiwifruit/S +kiwi/SM +Kizzee/M +Kizzie/M +KKK +kl +Klan/M +Klansman/M +Klara/M +Klarika/M +Klarrisa/M +Klaus/M +klaxon/M +Klee/M +Kleenex/SM +Klein/M +Kleinrock/M +Klemens/M +Klement/M +Kleon/M +kleptomaniac/SM +kleptomania/MS +Kliment/M +Kline/M +Klingon/M +Klondike/SDMG +kludger/M +kludge/RSDGMZ +kludgey +klutziness/S +klutz/SM +klutzy/TRP +Klux/M +klystron/MS +km +kn +knacker/M +knack/SGZRDM +knackwurst/MS +Knapp/M +knapsack/MS +Knauer/M +knavery/MS +knave/SM +knavish/Y +kneader/M +knead/GZRDS +kneecap/MS +kneecapped +kneecapping +knee/DSM +kneeing +kneeler/M +kneel/GRS +kneepad/SM +knell/SMDG +knelt +Knesset/M +knew +Kngwarreye/M +Knickerbocker/MS +knickerbocker/S +knickknack/SM +knick/ZR +Knievel/M +knife/DSGM +knighthood/MS +knightliness/MS +knightly/P +Knight/M +knight/MDYSG +knish/MS +knit/AU +knits +knitted +knitter/MS +knitting/SM +knitwear/M +knives/M +knobbly +knobby/RT +Knobeloch/M +knob/MS +knockabout/M +knockdown/S +knocker/M +knock/GZSJRD +knockoff/S +knockout/MS +knockwurst's +knoll/MDSG +Knopf/M +Knossos/M +knothole/SM +knot/MS +knotted +knottiness/M +knotting/M +knotty/TPR +knowable/U +knower/M +know/GRBSJ +knowhow +knowingly/U +knowing/RYT +knowings/U +knowledgeableness/M +knowledgeable/P +knowledgeably +knowledge/SM +Knowles +known/SU +Knox/M +Knoxville/M +knuckleball/R +knuckle/DSMG +knuckleduster +knucklehead/MS +Knudsen/M +Knudson/M +knurl/DSG +Knuth/M +Knutsen/M +Knutson/M +KO +koala/SM +Kobayashi/M +Kobe/M +Kochab/M +Koch/M +Kodachrome/M +Kodak/SM +Kodaly/M +Kodiak/M +Koenig/M +Koenigsberg/M +Koenraad/M +Koestler/M +Kohinoor/M +Kohler/M +Kohl/MR +kohlrabies +kohlrabi/M +kola/SM +Kolyma/M +Kommunizma/M +Kong/M +Kongo/M +Konrad/M +Konstance/M +Konstantine/M +Konstantin/M +Konstanze/M +kookaburra/SM +kook/GDMS +kookiness/S +kooky/PRT +Koo/M +Koontz/M +kopeck/MS +Koppers/M +Koralle/M +Koral/M +Kora/M +Koranic +Koran/SM +Kordula/M +Korea/M +Korean/S +Korella/M +Kore/M +Koren/M +Koressa/M +Korey/M +Korie/M +Kori/M +Kornberg/M +Korney/M +Korrie/M +Korry/M +Kort/M +Kory/M +Korzybski/M +Kosciusko/M +kosher/DGS +Kossuth/M +Kosygin/M +Kovacs/M +Kowalewski/M +Kowalski/M +Kowloon/M +kowtow/SGD +KP +kph +kraal/SMDG +Kraemer/M +kraft/M +Kraft/M +Krakatau's +Krakatoa/M +Krakow/M +Kramer/M +Krasnodar/M +Krasnoyarsk/M +Krause/M +kraut/S! +Krebs/M +Kremlin/M +Kremlinologist/MS +Kremlinology/MS +Kresge/M +Krieger/M +kriegspiel/M +krill/MS +Kringle/M +Krisha/M +Krishnah/M +Krishna/M +Kris/M +Krispin/M +Krissie/M +Krissy/M +Kristal/M +Krista/M +Kristan/M +Kristel/M +Kriste/M +Kristen/M +Kristian/M +Kristie/M +Kristien/M +Kristi/MN +Kristina/M +Kristine/M +Kristin/M +Kristofer/M +Kristoffer/M +Kristofor/M +Kristoforo/M +Kristo/MS +Kristopher/M +Kristy/M +Kristyn/M +Kr/M +Kroc/M +Kroger/M +krna/M +Kronecker/M +krone/RM +kronor +krnur +Kropotkin/M +Krueger/M +Kruger/M +Krugerrand/S +Krupp/M +Kruse/M +krypton/SM +Krystalle/M +Krystal/M +Krysta/M +Krystle/M +Krystyna/M +ks +K's +KS +k's/IE +kt +Kublai/M +Kubrick/M +kuchen/MS +kudos/M +kudzu/SM +Kuenning/M +Kuhn/M +Kuibyshev/M +Ku/M +Kumar/M +kumquat/SM +Kunming/M +Kuomintang/M +Kurdish/M +Kurdistan/SM +Kurd/SM +Kurosawa/M +Kurtis/M +Kurt/M +kurtosis/M +Kusch/M +Kuwaiti/SM +Kuwait/M +Kuznetsk/M +Kuznets/M +kvetch/DSG +kw +kW +Kwakiutl/M +Kwangchow's +Kwangju/M +Kwanzaa/S +kWh +KY +Kyla/M +kyle/M +Kyle/M +Kylen/M +Kylie/M +Kylila/M +Kylynn/M +Ky/MH +Kym/M +Kynthia/M +Kyoto/M +Kyrgyzstan +Kyrstin/M +Kyushu/M +L +LA +Laban/M +labeled/U +labeler/M +label/GAZRDS +labellings/A +label's +labial/YS +labia/M +labile +labiodental +labium/M +laboratory/MS +laboredness/M +labored/PMY +labored's/U +laborer/M +laboring/MY +laborings/U +laboriousness/MS +laborious/PY +labor/RDMJSZG +laborsaving +Labradorean/S +Labrador/SM +lab/SM +Lab/SM +laburnum/SM +labyrinthine +labyrinth/M +labyrinths +laced/U +Lacee/M +lace/MS +lacerate/NGVXDS +laceration/M +lacer/M +laces/U +lacewing/MS +Lacey/M +Lachesis/M +lachrymal/S +lachrymose +Lacie/M +lacing/M +lackadaisic +lackadaisical/Y +Lackawanna/M +lacker/M +lackey/SMDG +lack/GRDMS +lackluster/S +Lac/M +laconic +laconically +lacquerer/M +lacquer/ZGDRMS +lacrosse/MS +lac/SGMDR +lactate/MNGSDX +lactational/Y +lactation/M +lacteal +lactic +lactose/MS +lacunae +lacuna/M +Lacy/M +lacy/RT +ladder/GDMS +laddie/MS +laded/U +ladened +ladening +laden/U +lade/S +lading/M +ladle/SDGM +Ladoga/M +Ladonna/M +lad/XGSJMND +ladybird/SM +ladybug/MS +ladyfinger/SM +ladylike/U +ladylove/MS +Ladyship/MS +ladyship/SM +lady/SM +Lady/SM +Laetitia/M +laetrile/S +Lafayette/M +Lafitte/M +lager/DMG +laggard/MYSP +laggardness/M +lagged +lagging/MS +lagniappe/SM +lagoon/MS +Lagos/M +Lagrange/M +Lagrangian/M +Laguerre/M +Laguna/M +lag/ZSR +Lahore/M +laid/AI +Laidlaw/M +lain +Laina/M +Lainey/M +Laird/M +laird/MS +lair/GDMS +laissez +laity/SM +Laius/M +lake/DSRMG +Lakehurst/M +Lakeisha/M +laker/M +lakeside +Lakewood/M +Lakisha/M +Lakshmi/M +lallygagged +lallygagging +lallygag/S +Lalo/M +La/M +Lamaism/SM +Lamarck/M +Lamar/M +lamasery/MS +lama/SM +Lamaze +lambada/S +lambaste/SDG +lambda/SM +lambency/MS +lambent/Y +Lambert/M +lambkin/MS +Lamb/M +Lamborghini/M +lambskin/MS +lamb/SRDMG +lambswool +lamebrain/SM +lamed/M +lameness/MS +lamentableness/M +lamentable/P +lamentably +lamentation/SM +lament/DGSB +lamented/U +lame/SPY +la/MHLG +laminae +lamina/M +laminar +laminate/XNGSD +lamination/M +lam/MDRSTG +lammed +lammer +lamming +Lammond/M +Lamond/M +Lamont/M +L'Amour +lampblack/SM +lamplighter/M +lamplight/ZRMS +lampooner/M +lampoon/RDMGS +Lamport/M +lamppost/SM +lamprey/MS +lamp/SGMRD +lampshade/MS +LAN +Lanae/M +Lanai/M +lanai/SM +Lana/M +Lancashire/M +Lancaster/M +Lancelot/M +Lance/M +lancer/M +lance/SRDGMZ +lancet/MS +landau/MS +lander/I +landfall/SM +landfill/DSG +landforms +landholder/M +landhold/JGZR +landing/M +Landis/M +landlady/MS +landless +landlines +landlocked +landlord/MS +landlubber/SM +Land/M +landmark/GSMD +landmass/MS +Landon/M +landowner/MS +landownership/M +landowning/SM +Landry/M +Landsat +landscape/GMZSRD +landscaper/M +lands/I +landslide/MS +landslid/G +landslip +landsman/M +landsmen +land/SMRDJGZ +Landsteiner/M +landward/S +Landwehr/M +Lane/M +lane/SM +Lanette/M +Laney/M +Langeland/M +Lange/M +Langerhans/M +Langford/M +Langland/M +Langley/M +Lang/M +Langmuir/M +Langsdon/M +Langston/M +language/MS +languidness/MS +languid/PY +languisher/M +languishing/Y +languish/SRDG +languorous/Y +languor/SM +Lanie/M +Lani/M +Lanita/M +lankiness/SM +lankness/MS +lank/PTYR +lanky/PRT +Lanna/M +Lannie/M +Lanni/M +Lanny/M +lanolin/MS +Lansing/M +lantern/GSDM +lanthanide/M +lanthanum/MS +lanyard/MS +Lanzhou +Laocoon/M +Lao/SM +Laotian/MS +lapboard/MS +lapdog/S +lapel/MS +lapidary/MS +lapin/MS +Laplace/M +Lapland/ZMR +lapped +lappet/MS +lapping +Lapp/SM +lapsed/A +lapse/KSDMG +lapser/MA +lapses/A +lapsing/A +lap/SM +laps/SRDG +laptop/SM +lapwing/MS +Laraine/M +Lara/M +Laramie/M +larboard/MS +larcenist/S +larcenous +larceny/MS +larch/MS +larder/M +lard/MRDSGZ +Lardner/M +lardy/RT +Laredo/M +largehearted +largemouth +largeness/SM +large/SRTYP +largess/SM +largish +largo/S +lariat/MDGS +Lari/M +Larina/M +Larine/M +Larisa/M +Larissa/M +larker/M +lark/GRDMS +Lark/M +larkspur/MS +Larousse/M +Larry/M +Larsen/M +Lars/NM +Larson/M +larvae +larval +larva/M +laryngeal/YS +larynges +laryngitides +laryngitis/M +larynx/M +Laryssa/M +lasagna/S +lasagne's +Lascaux/M +lasciviousness/MS +lascivious/YP +lase +laser/M +lashed/U +lasher/M +lashing/M +lash/JGMSRD +Lassa/M +Lassen/M +Lassie/M +lassie/SM +lassitude/MS +lassoer/M +lasso/GRDMS +las/SRZG +lass/SM +laster/M +lastingness/M +lasting/PY +last/JGSYRD +Laszlo/M +Latasha/M +Latashia/M +latching/M +latchkey/SM +latch's +latch/UGSD +latecomer/SM +lated/A +late/KA +lately +latency/MS +lateness/MS +latent/YS +later/A +lateral/GDYS +lateralization +Lateran/M +latest/S +LaTeX/M +latex/MS +lathe/M +latherer/M +lather/RDMG +lathery +lathing/M +lath/MSRDGZ +Lathrop/M +laths +Latia/M +latices/M +Latina/SM +Latinate +Latino/S +Latin/RMS +latish +Latisha/M +latitude/SM +latitudinal/Y +latitudinarian/S +latitudinary +Lat/M +Latonya/M +Latoya/M +Latrena/M +Latrina/M +latrine/MS +Latrobe/M +lat/SDRT +latter/YM +latte/SR +lattice/SDMG +latticework/MS +latticing/M +Lattimer/M +Latvia/M +Latvian/S +laudably +laudanum/MS +laudatory +Lauderdale/M +lauder/M +Lauder/M +Laud/MR +laud/RDSBG +lauds/M +Laue/M +laughableness/M +laughable/P +laughably +laugh/BRDZGJ +laugher/M +laughing/MY +laughingstock/SM +laughs +laughter/MS +Laughton/M +Launce/M +launch/AGSD +launcher/MS +launching/S +launchpad/S +laundered/U +launderer/M +launderette/MS +launder/SDRZJG +laundress/MS +laundrette/S +laundromat/S +Laundromat/SM +laundryman/M +laundrymen +laundry/MS +laundrywoman/M +laundrywomen +Lauraine/M +Lauralee/M +Laural/M +laura/M +Laura/M +Laurasia/M +laureate/DSNG +laureateship/SM +Lauree/M +Laureen/M +Laurella/M +Laurel/M +laurel/SGMD +Laure/M +Laurena/M +Laurence/M +Laurene/M +Lauren/SM +Laurentian +Laurent/M +Lauretta/M +Laurette/M +Laurianne/M +Laurice/M +Laurie/M +Lauri/M +Lauritz/M +Lauryn/M +Lausanne/M +lavage/MS +lavaliere/MS +Laval/M +lava/SM +lavatory/MS +lave/GDS +Lavena/M +lavender/MDSG +Laverna/M +Laverne/M +Lavern/M +Lavina/M +Lavinia/M +Lavinie/M +lavishness/MS +lavish/SRDYPTG +Lavoisier/M +Lavonne/M +Lawanda/M +lawbreaker/SM +lawbreaking/MS +Lawford/M +lawfulness/SMU +lawful/PUY +lawgiver/MS +lawgiving/M +lawlessness/MS +lawless/PY +Law/M +lawmaker/MS +lawmaking/SM +lawman/M +lawmen +lawnmower/S +lawn/SM +Lawrence/M +Lawrenceville/M +lawrencium/SM +Lawry/M +law/SMDG +Lawson/M +lawsuit/MS +Lawton/M +lawyer/DYMGS +laxativeness/M +laxative/PSYM +laxer/A +laxes/A +laxity/SM +laxness/SM +lax/PTSRY +layabout/MS +Layamon/M +layaway/S +lay/CZGSR +layered/C +layer/GJDM +layering/M +layer's/IC +layette/SM +Layla/M +Lay/M +layman/M +laymen +Layne/M +Layney/M +layoff/MS +layout/SM +layover/SM +laypeople +layperson/S +lays/AI +Layton/M +layup/MS +laywoman/M +laywomen +Lazare/M +Lazar/M +Lazaro/M +Lazarus/M +laze/DSG +lazily +laziness/MS +lazuli/M +lazybones/M +lazy/PTSRDG +lb +LBJ/M +lbs +LC +LCD +LCM +LDC +leachate +Leach/M +leach/SDG +Leadbelly/M +leaded/U +leadenness/M +leaden/PGDY +leaderless +leader/M +leadership/MS +lead/SGZXJRDN +leadsman/M +leadsmen +leafage/MS +leaf/GSDM +leafhopper/M +leafiness/M +leafless +leaflet/SDMG +leafstalk/SM +leafy/PTR +leaguer/M +league/RSDMZG +Leah/M +leakage/SM +leaker/M +Leakey/M +leak/GSRDM +leakiness/MS +leaky/PRT +Lea/M +lea/MS +Leander/M +Leandra/M +leaner/M +leaning/M +Lean/M +Leanna/M +Leanne/M +leanness/MS +Leann/M +Leanora/M +Leanor/M +lean/YRDGTJSP +leaper/M +leapfrogged +leapfrogging +leapfrog/SM +leap/RDGZS +Lear/M +learnedly +learnedness/M +learned/UA +learner/M +learning/M +learns/UA +learn/SZGJRD +Leary/M +lease/ARSDG +leaseback/MS +leaseholder/M +leasehold/SRMZ +leaser/MA +lease's +leash's +leash/UGSD +leasing/M +leas/SRDGZ +least/S +leastwise +leatherette/S +leather/MDSG +leathern +leatherneck/SM +leathery +leaven/DMJGS +leavened/U +leavening/M +Leavenworth/M +leaver/M +leaves/M +leave/SRDJGZ +leaving/M +Lebanese +Lebanon/M +Lebbie/M +lebensraum +Lebesgue/M +Leblanc/M +lecher/DMGS +lecherousness/MS +lecherous/YP +lechery/MS +lecithin/SM +lectern/SM +lecturer/M +lecture/RSDZMG +lectureship/SM +led +Leda/M +Lederberg/M +ledger/DMG +ledge/SRMZ +LED/SM +Leeanne/M +Leeann/M +leech/MSDG +Leeds/M +leek/SM +Leelah/M +Leela/M +Leeland/M +Lee/M +lee/MZRS +Leena/M +leer/DG +leeriness/MS +leering/Y +leery/PTR +Leesa/M +Leese/M +Leeuwenhoek/M +Leeward/M +leeward/S +leeway/MS +leftism/SM +leftist/SM +leftmost +leftover/MS +Left/S +left/TRS +leftward/S +Lefty/M +lefty/SM +legacy/MS +legalese/MS +legalism/SM +legalistic +legality/MS +legalization/MS +legalize/DSG +legalized/U +legal/SY +legate/AXCNGSD +legatee/MS +legate's/C +legation/AMC +legato/SM +legendarily +legendary/S +Legendre/M +legend/SM +legerdemain/SM +Leger/SM +legged +legginess/MS +legging/MS +leggy/PRT +leghorn/SM +Leghorn/SM +legibility/MS +legible +legibly +legionary/S +legionnaire/SM +legion/SM +legislate/SDXVNG +legislation/M +legislative/SY +legislator/SM +legislature/MS +legitimacy/MS +legitimate/SDNGY +legitimation/M +legitimatize/SDG +legitimization/MS +legitimize/RSDG +legit/S +legless +legman/M +legmen +leg/MS +Lego/M +Legra/M +Legree/M +legroom/MS +legstraps +legume/SM +leguminous +legwork/SM +Lehigh/M +Lehman/M +Leia/M +Leibniz/M +Leicester/SM +Leiden/M +Leif/M +Leigha/M +Leigh/M +Leighton/M +Leilah/M +Leila/M +lei/MS +Leipzig/M +Leisha/M +leisureliness/MS +leisurely/P +leisure/SDYM +leisurewear +leitmotif/SM +leitmotiv/MS +Lek/M +Lelah/M +Lela/M +Leland/M +Lelia/M +Lemaitre/M +Lemar/M +Lemke/M +Lem/M +lemma/MS +lemme/GJ +Lemmie/M +lemming/M +Lemmy/M +lemonade/SM +lemon/GSDM +lemony +Lemuel/M +Lemuria/M +lemur/MS +Lena/M +Lenard/M +Lenci/M +lender/M +lend/SRGZ +Lenee/M +Lenette/M +lengthener/M +lengthen/GRD +lengthily +lengthiness/MS +length/MNYX +lengths +lengthwise +lengthy/TRP +lenience/S +leniency/MS +lenient/SY +Leningrad/M +Leninism/M +Leninist +Lenin/M +lenitive/S +Lenka/M +Len/M +Le/NM +Lenna/M +Lennard/M +Lennie/M +Lennon/M +Lenny/M +Lenoir/M +Leno/M +Lenora/M +Lenore/M +lens/SRDMJGZ +lent/A +lenticular +lentil/SM +lento/S +Lent/SMN +Leodora/M +Leoine/M +Leola/M +Leoline/M +Leo/MS +Leona/M +Leonanie/M +Leonard/M +Leonardo/M +Leoncavallo/M +Leonelle/M +Leonel/M +Leone/M +Leonerd/M +Leonhard/M +Leonidas/M +Leonid/M +Leonie/M +leonine +Leon/M +Leonora/M +Leonore/M +Leonor/M +Leontine/M +Leontyne/M +leopardess/SM +leopard/MS +leopardskin +Leopold/M +Leopoldo/M +Leopoldville/M +Leora/M +leotard/MS +leper/SM +Lepidus/M +Lepke/M +leprechaun/SM +leprosy/MS +leprous +lepta +lepton/SM +Lepus/M +Lerner/M +Leroi/M +Leroy/M +Lesa/M +lesbianism/MS +lesbian/MS +Leshia/M +lesion/DMSG +Lesley/M +Leslie/M +Lesli/M +Lesly/M +Lesotho/M +lessee/MS +lessen/GDS +Lesseps/M +lesser +lesses +Lessie/M +lessing +lesson/DMSG +lessor/MS +less/U +Lester/M +lest/R +Les/Y +Lesya/M +Leta/M +letdown/SM +lethality/M +lethal/YS +Letha/M +lethargic +lethargically +lethargy/MS +Lethe/M +Lethia/M +Leticia/M +Letisha/M +let/ISM +Letitia/M +Letizia/M +Letta/M +letterbox/S +lettered/U +letterer/M +letterhead/SM +lettering/M +letter/JSZGRDM +letterman/M +Letterman/M +lettermen +letterpress/MS +Lettie/M +Letti/M +letting/S +lettuce/SM +Letty/M +letup/MS +leukemia/SM +leukemic/S +leukocyte/MS +Leupold/M +Levant/M +leveeing +levee/SDM +leveled/U +leveler/M +levelheadedness/S +levelheaded/P +leveling/U +levelness/SM +level/STZGRDYP +leverage/MGDS +lever/SDMG +Levesque/M +Levey/M +Leviathan +leviathan/MS +levier/M +Levi/MS +Levine/M +Levin/M +levitate/XNGDS +levitation/M +Leviticus/M +Levitt/M +levity/MS +Lev/M +Levon/M +Levy/M +levy/SRDZG +lewdness/MS +lewd/PYRT +Lewellyn/M +Lewes +Lewie/M +Lewinsky/M +lewis/M +Lewis/M +Lewiss +Lew/M +lex +lexeme/MS +lexical/Y +lexicographer/MS +lexicographic +lexicographical/Y +lexicography/SM +lexicon/SM +Lexie/M +Lexi/MS +Lexine/M +Lexington/M +Lexus/M +Lexy/M +Leyden/M +Leyla/M +Lezley/M +Lezlie/M +lg +Lhasa/SM +Lhotse/M +liability/SAM +liable/AP +liaise/GSD +liaison/SM +Lia/M +Liam/M +Liana/M +Liane/M +Lian/M +Lianna/M +Lianne/M +liar/MS +libation/SM +libbed +Libbey/M +Libbie/M +Libbi/M +libbing +Libby/M +libeler/M +libel/GMRDSZ +libelous/Y +Liberace/M +liberalism/MS +liberality/MS +liberalization/SM +liberalized/U +liberalize/GZSRD +liberalizer/M +liberalness/MS +liberal/YSP +liberate/NGDSCX +liberationists +liberation/MC +liberator/SCM +Liberia/M +Liberian/S +libertarianism/M +libertarian/MS +libertine/MS +liberty/MS +libidinal +libidinousness/M +libidinous/PY +libido/MS +Lib/M +lib/MS +librarian/MS +library/MS +Libra/SM +libretoes +libretos +librettist/MS +libretto/MS +Libreville/M +Librium/M +Libya/M +Libyan/S +lice/M +licensed/AU +licensee/SM +license/MGBRSD +licenser/M +licenses/A +licensing/A +licensor/M +licentiate/MS +licentiousness/MS +licentious/PY +Licha/M +lichee's +lichen/DMGS +Lichtenstein/M +Lichter/M +licit/Y +licked/U +lickerish +licker/M +lick/GRDSJ +licking/M +licorice/SM +Lida/M +lidded +lidding +Lidia/M +lidless +lid/MS +lido/MS +Lieberman/M +Liebfraumilch/M +Liechtenstein/RMZ +lied/MR +lie/DRS +Lief/M +liefs/A +lief/TSR +Liege/M +liege/SR +Lie/M +lien/SM +lier/IMA +lies/A +Liesa/M +lieu/SM +lieut +lieutenancy/MS +lieutenant/SM +Lieut/M +lifeblood/SM +lifeboat/SM +lifebuoy/S +lifeforms +lifeguard/MDSG +lifelessness/SM +lifeless/PY +lifelikeness/M +lifelike/P +lifeline/SM +lifelong +life/MZR +lifer/M +lifesaver/SM +lifesaving/S +lifespan/S +lifestyle/S +lifetaking +lifetime/MS +lifework/MS +LIFO +lifter/M +lift/GZMRDS +liftoff/MS +ligament/MS +ligand/MS +ligate/XSDNG +ligation/M +ligature/DSGM +light/ADSCG +lighted/U +lightener/M +lightening/M +lighten/ZGDRS +lighter/CM +lightered +lightering +lighters +lightest +lightface/SDM +lightheaded +lightheartedness/MS +lighthearted/PY +lighthouse/MS +lighting/MS +lightly +lightness/MS +lightning/SMD +lightproof +light's +lightship/SM +lightweight/S +ligneous +lignite/MS +lignum +likability/MS +likableness/MS +likable/P +likeability's +liked/E +likelihood/MSU +likely/UPRT +likeness/MSU +liken/GSD +liker/E +liker's +likes/E +likest +like/USPBY +likewise +liking/SM +lilac/MS +Lilah/M +Lila/SM +Lilia/MS +Liliana/M +Liliane/M +Lilian/M +Lilith/M +Liliuokalani/M +Lilla/M +Lille/M +Lillian/M +Lillie/M +Lilli/MS +lilliputian/S +Lilliputian/SM +Lilliput/M +Lilllie/M +Lilly/M +Lil/MY +Lilongwe/M +lilting/YP +lilt/MDSG +Lilyan/M +Lily/M +lily/MSD +Lima/M +Limbaugh/M +limbered/U +limberness/SM +limber/RDYTGP +limbers/U +limbic +limbless +Limbo +limbo/GDMS +limb/SGZRDM +Limburger/SM +limeade/SM +lime/DSMG +limekiln/M +limelight/DMGS +limerick/SM +limestone/SM +limitability +limitably +limitation/MCS +limit/CSZGRD +limitedly/U +limitedness/M +limited/PSY +limiter/M +limiting/S +limitlessness/SM +limitless/PY +limit's +limn/GSD +Limoges/M +limo/S +limousine/SM +limper/M +limpet/SM +limpidity/MS +limpidness/SM +limpid/YP +limpness/MS +Limpopo/M +limp/SGTPYRD +Li/MY +limy/TR +linage/MS +Lina/M +linchpin/MS +Linc/M +Lincoln/SM +Linda/M +Lindbergh/M +Lindberg/M +linden/MS +Lindholm/M +Lindie/M +Lindi/M +Lind/M +Lindon/M +Lindquist/M +Lindsay/M +Lindsey/M +Lindstrom/M +Lindsy/M +Lindy/M +line/AGDS +lineage/SM +lineal/Y +Linea/M +lineament/MS +linearity/MS +linearize/SDGNB +linear/Y +linebacker/SM +lined/U +linefeed +Linell/M +lineman/M +linemen +linen/SM +liner/SM +line's +linesman/M +linesmen +Linet/M +Linette/M +lineup/S +lingerer/M +lingerie/SM +lingering/Y +linger/ZGJRD +lingoes +lingo/M +lingual/SY +lingua/M +linguine +linguini's +linguistically +linguistic/S +linguistics/M +linguist/SM +ling/ZR +liniment/MS +lining/SM +linkable +linkage/SM +linked/A +linker/S +linking/S +Link/M +link's +linkup/S +link/USGD +Lin/M +Linnaeus/M +Linnea/M +Linnell/M +Linnet/M +linnet/SM +Linnie/M +Linn/M +Linoel/M +linoleum/SM +lino/M +Linotype/M +linseed/SM +lintel/SM +linter/M +Linton/M +lint/SMR +linty/RST +Linus/M +Linux/M +Linwood/M +Linzy/M +Lionello/M +Lionel/M +lioness/SM +lionhearted +lionization/SM +lionizer/M +lionize/ZRSDG +Lion/M +lion/MS +lipase/M +lipid/MS +lip/MS +liposuction/S +lipped +lipper +Lippi/M +lipping +Lippmann/M +lippy/TR +lipread/GSRJ +Lipschitz/M +Lipscomb/M +lipstick/MDSG +Lipton/M +liq +liquefaction/SM +liquefier/M +liquefy/DRSGZ +liqueur/DMSG +liquidate/GNXSD +liquidation/M +liquidator/SM +liquidity/SM +liquidizer/M +liquidize/ZGSRD +liquidness/M +liquid/SPMY +liquorice/SM +liquorish +liquor/SDMG +lira/M +Lira/M +lire +Lisabeth/M +Lisa/M +Lisbeth/M +Lisbon/M +Lise/M +Lisetta/M +Lisette/M +Lisha/M +Lishe/M +Lisle/M +lisle/SM +lisper/M +lisp/MRDGZS +Lissajous/M +Lissa/M +Lissie/M +Lissi/M +Liss/M +lissomeness/M +lissome/P +lissomness/M +Lissy/M +listed/U +listener/M +listen/ZGRD +Listerine/M +lister/M +Lister/M +listing/M +list/JMRDNGZXS +listlessness/SM +listless/PY +Liston/M +Liszt/M +Lita/M +litany/MS +litchi/SM +literacy/MS +literalism/M +literalistic +literalness/MS +literal/PYS +literariness/SM +literary/P +literate/YNSP +literati +literation/M +literature/SM +liter/M +lite/S +litheness/SM +lithe/PRTY +lithesome +lithium/SM +lithograph/DRMGZ +lithographer/M +lithographic +lithographically +lithographs +lithography/MS +lithology/M +lithosphere/MS +lithospheric +Lithuania/M +Lithuanian/S +litigant/MS +litigate/NGXDS +litigation/M +litigator/SM +litigiousness/MS +litigious/PY +litmus/SM +litotes/M +lit/RZS +littrateur/S +litterbug/SM +litter/SZGRDM +Little/M +littleneck/M +littleness/SM +little/RSPT +Littleton/M +Litton/M +littoral/S +liturgical/Y +liturgic/S +liturgics/M +liturgist/MS +liturgy/SM +Liuka/M +livability/MS +livableness/M +livable/U +livably +Liva/M +lived/A +livelihood/SM +liveliness/SM +livelong/S +lively/RTP +liveness/M +liven/SDG +liver/CSGD +liveried +liverish +Livermore/M +Liverpool/M +Liverpudlian/MS +liver's +liverwort/SM +liverwurst/SM +livery/CMS +liveryman/MC +liverymen/C +lives/A +lives's +livestock/SM +live/YHZTGJDSRPB +Livia/M +lividness/M +livid/YP +livingness/M +Livingstone/M +Livingston/M +living/YP +Liv/M +Livonia/M +Livvie/M +Livvy/M +Livvyy/M +Livy/M +Lizabeth/M +Liza/M +lizard/MS +Lizbeth/M +Lizette/M +Liz/M +Lizzie/M +Lizzy/M +l/JGVXT +Ljubljana/M +LL +llama/SM +llano/SM +LLB +ll/C +LLD +Llewellyn/M +Lloyd/M +Llywellyn/M +LNG +lo +loadable +loaded/A +loader/MU +loading/MS +load's/A +loads/A +loadstar's +loadstone's +load/SURDZG +loafer/M +Loafer/S +loaf/SRDMGZ +loam/SMDG +loamy/RT +loaner/M +loaning/M +loan/SGZRDMB +loansharking/S +loanword/S +loathe +loather/M +loathing/M +loath/JPSRDYZG +loathness/M +loathsomeness/MS +loathsome/PY +loaves/M +Lobachevsky/M +lobar +lobbed +lobber/MS +lobbing +lobby/GSDM +lobbyist/MS +lobe/SM +lob/MDSG +lobotomist +lobotomize/GDS +lobotomy/MS +lobster/MDGS +lobularity +lobular/Y +lobule/SM +locale/MS +localisms +locality/MS +localization/MS +localized/U +localizer/M +localizes/U +localize/ZGDRS +local/SGDY +locatable +locate/AXESDGN +locater/M +locational/Y +location/EMA +locative/S +locator's +Lochinvar/M +loch/M +lochs +loci/M +lockable +Lockean/M +locked/A +Locke/M +locker/SM +locket/SM +Lockhart/M +Lockheed/M +Lockian/M +locking/S +lockjaw/SM +Lock/M +locknut/M +lockout/MS +lock's +locksmithing/M +locksmith/MG +locksmiths +lockstep/S +lock/UGSD +lockup/MS +Lockwood/M +locomotion/SM +locomotive/YMS +locomotor +locomotory +loco/SDMG +locoweed/MS +locus/M +locust/SM +locution/MS +lode/SM +lodestar/MS +lodestone/MS +lodged/E +lodge/GMZSRDJ +Lodge/M +lodgepole +lodger/M +lodges/E +lodging/M +lodgment/M +Lodovico/M +Lodowick/M +Lodz +Loeb/M +Loella/M +Loewe/M +Loewi/M +lofter/M +loftily +loftiness/SM +loft/SGMRD +lofty/PTR +loganberry/SM +Logan/M +logarithmic +logarithmically +logarithm/MS +logbook/MS +loge/SMNX +logged/U +loggerhead/SM +logger/SM +loggia/SM +logging/MS +logicality/MS +logicalness/M +logical/SPY +logician/SM +logic/SM +login/S +logion/M +logistical/Y +logistic/MS +logjam/SM +LOGO +logo/SM +logotype/MS +logout +logrolling/SM +log's/K +log/SM +logy/RT +Lohengrin/M +loincloth/M +loincloths +loin/SM +Loire/M +Loise/M +Lois/M +loiterer/M +loiter/RDJSZG +Loki/M +Lola/M +Loleta/M +Lolita/M +loller/M +lollipop/MS +loll/RDGS +Lolly/M +lolly/SM +Lombardi/M +Lombard/M +Lombardy/M +Lomb/M +Lome +Lona/M +Londonderry/M +Londoner/M +London/RMZ +Lonee/M +loneliness/SM +lonely/TRP +loneness/M +lone/PYZR +loner/M +lonesomeness/MS +lonesome/PSY +longboat/MS +longbow/SM +longed/K +longeing +longer/K +longevity/MS +Longfellow/M +longhair/SM +longhand/SM +longhorn/SM +longing/MY +longish +longitude/MS +longitudinal/Y +long/JGTYRDPS +Long/M +longness/M +longshoreman/M +longshoremen +longsighted +longs/K +longstanding +Longstreet/M +longsword +longterm +longtime +Longueuil/M +longueur/SM +longways +longword/SM +Loni/M +Lon/M +Lonna/M +Lonnard/M +Lonnie/M +Lonni/M +Lonny/M +loofah/M +loofahs +lookahead +lookalike/S +looker/M +look/GZRDS +lookout/MS +lookup/SM +looming/M +Loomis/M +loom/MDGS +loon/MS +loony/SRT +looper/M +loophole/MGSD +loop/MRDGS +loopy/TR +loosed/U +looseleaf +loosener/M +looseness/MS +loosen/UDGS +loose/SRDPGTY +looses/U +loosing/M +looter/M +loot/MRDGZS +loper/M +lope/S +Lopez/M +lopped +lopper/MS +lopping +lop/SDRG +lopsidedness/SM +lopsided/YP +loquaciousness/MS +loquacious/YP +loquacity/SM +Loraine/M +Lorain/M +Loralee/M +Loralie/M +Loralyn/M +Lora/M +Lorant/M +lording/M +lordliness/SM +lordly/PTR +Lord/MS +lord/MYDGS +lordship/SM +Lordship/SM +Loree/M +Loreen/M +Lorelei/M +Lorelle/M +lore/MS +Lorena/M +Lorene/M +Loren/SM +Lorentzian/M +Lorentz/M +Lorenza/M +Lorenz/M +Lorenzo/M +Loretta/M +Lorette/M +lorgnette/SM +Loria/M +Lorianna/M +Lorianne/M +Lorie/M +Lorilee/M +Lorilyn/M +Lori/M +Lorinda/M +Lorine/M +Lorin/M +loris/SM +Lorita/M +lorn +Lorna/M +Lorne/M +Lorraine/M +Lorrayne/M +Lorre/M +Lorrie/M +Lorri/M +Lorrin/M +lorryload/S +Lorry/M +lorry/SM +Lory/M +Los +loser/M +lose/ZGJBSR +lossage +lossless +loss/SM +lossy/RT +lost/P +Lothaire/M +Lothario/MS +lotion/MS +Lot/M +lot/MS +Lotta/M +lotted +Lotte/M +lotter +lottery/MS +Lottie/M +Lotti/M +lotting +Lott/M +lotto/MS +Lotty/M +lotus/SM +louden/DG +loudhailer/S +loudly/RT +loudmouth/DM +loudmouths +loudness/MS +loudspeaker/SM +loudspeaking +loud/YRNPT +Louella/M +Louie/M +Louisa/M +Louise/M +Louisette/M +Louisiana/M +Louisianan/S +Louisianian/S +Louis/M +Louisville/M +Lou/M +lounger/M +lounge/SRDZG +Lourdes/M +lour/GSD +louse/CSDG +louse's +lousewort/M +lousily +lousiness/MS +lousy/PRT +loutishness/M +loutish/YP +Loutitia/M +lout/SGMD +louver/DMS +L'Ouverture +Louvre/M +lovableness/MS +lovable/U +lovably +lovebird/SM +lovechild +Lovecraft/M +love/DSRMYZGJB +loved/U +Lovejoy/M +Lovelace/M +Loveland/M +lovelessness/M +loveless/YP +lovelies +lovelinesses +loveliness/UM +Lovell/M +lovelornness/M +lovelorn/P +lovely/URPT +Love/M +lovemaking/SM +lover/YMG +lovesick +lovestruck +lovingly +lovingness/M +loving/U +lowborn +lowboy/SM +lowbrow/MS +lowdown/S +Lowell/M +Lowe/M +lowercase/GSD +lower/DG +lowermost +Lowery/M +lowish +lowland/RMZS +Lowlands/M +lowlife/SM +lowlight/MS +lowliness/MS +lowly/PTR +lowness/MS +low/PDRYSZTG +Lowrance/M +lox/MDSG +loyaler +loyalest +loyal/EY +loyalism/SM +loyalist/SM +loyalty/EMS +Loyang/M +Loydie/M +Loyd/M +Loy/M +Loyola/M +lozenge/SDM +LP +LPG +LPN/S +Lr +ls +l's +L's +LSD +ltd +Ltd/M +Lt/M +Luanda/M +Luann/M +luau/MS +lubber/YMS +Lubbock/M +lube/DSMG +lubricant/SM +lubricate/VNGSDX +lubrication/M +lubricator/MS +lubricious/Y +lubricity/SM +Lubumbashi/M +Lucais/M +Luca/MS +Luce/M +lucent/Y +Lucerne/M +Lucho/M +Lucia/MS +Luciana/M +Lucian/M +Luciano/M +lucidity/MS +lucidness/MS +lucid/YP +Lucie/M +Lucien/M +Lucienne/M +Lucifer/M +Lucila/M +Lucile/M +Lucilia/M +Lucille/M +Luci/MN +Lucina/M +Lucinda/M +Lucine/M +Lucio/M +Lucita/M +Lucite/MS +Lucius/M +luck/GSDM +luckier/U +luckily/U +luckiness/UMS +luckless +Lucknow/M +Lucky/M +lucky/RSPT +lucrativeness/SM +lucrative/YP +lucre/MS +Lucretia/M +Lucretius/M +lucubrate/GNSDX +lucubration/M +Lucy/M +Luddite/SM +Ludhiana/M +ludicrousness/SM +ludicrous/PY +Ludlow/M +Ludmilla/M +ludo/M +Ludovico/M +Ludovika/M +Ludvig/M +Ludwig/M +Luella/M +Luelle/M +luff/GSDM +Lufthansa/M +Luftwaffe/M +luge/MC +Luger/M +luggage/SM +lugged +lugger/SM +lugging +Lugosi/M +lug/RS +lugsail/SM +lugubriousness/MS +lugubrious/YP +Luigi/M +Luisa/M +Luise/M +Luis/M +Lukas/M +Luke/M +lukewarmness/SM +lukewarm/PY +Lula/M +Lulita/M +lullaby/GMSD +lull/SDG +lulu/M +Lulu/M +Lu/M +lumbago/SM +lumbar/S +lumberer/M +lumbering/M +lumberjack/MS +lumberman/M +lumbermen +lumber/RDMGZSJ +lumberyard/MS +lumen/M +Lumire/M +luminance/M +luminary/MS +luminescence/SM +luminescent +luminosity/MS +luminousness/M +luminous/YP +lummox/MS +lumper/M +lumpiness/MS +lumpishness/M +lumpish/YP +lump/SGMRDN +lumpy/TPR +lunacy/MS +Luna/M +lunar/S +lunary +lunate/YND +lunatic/S +lunation/M +luncheonette/SM +luncheon/SMDG +luncher/M +lunch/GMRSD +lunchpack +lunchroom/MS +lunchtime/MS +Lundberg/M +Lund/M +Lundquist/M +lune/M +lunge/MS +lunger/M +lungfish/SM +lungful +lung/SGRDM +lunkhead/SM +Lupe/M +lupine/SM +Lupus/M +lupus/SM +Lura/M +lurcher/M +lurch/RSDG +lure/DSRG +lurer/M +Lurette/M +lurex +Luria/M +luridness/SM +lurid/YP +lurker/M +lurk/GZSRD +Lurleen/M +Lurlene/M +Lurline/M +Lusaka/M +Lusa/M +lusciousness/MS +luscious/PY +lushness/MS +lush/YSRDGTP +Lusitania/M +luster/GDM +lustering/M +lusterless +lustfulness/M +lustful/PY +lustily +lustiness/MS +lust/MRDGZS +lustrousness/M +lustrous/PY +lusty/PRT +lutanist/MS +lute/DSMG +lutenist/MS +Lutero/M +lutetium/MS +Lutheranism/MS +Lutheran/SM +Luther/M +luting/M +Lutz +Luxembourgian +Luxembourg/RMZ +Luxemburg's +luxe/MS +luxuriance/MS +luxuriant/Y +luxuriate/GNSDX +luxuriation/M +luxuriousness/SM +luxurious/PY +luxury/MS +Luz/M +Luzon/M +L'vov +Lyallpur/M +lyceum/MS +lychee's +lycopodium/M +Lycra/S +Lycurgus/M +Lyda/M +Lydia/M +Lydian/S +Lydie/M +Lydon/M +lye/JSMG +Lyell/M +lying/Y +Lyle/M +Lyly/M +Lyman/M +Lyme/M +lymphatic/S +lymph/M +lymphocyte/SM +lymphoid +lymphoma/MS +lymphs +Ly/MY +Lynchburg/M +lyncher/M +lynching/M +Lynch/M +lynch/ZGRSDJ +Lynda/M +Lyndell/M +Lyndel/M +Lynde/M +Lyndon/M +Lyndsay/M +Lyndsey/M +Lyndsie/M +Lyndy/M +Lynea/M +Lynelle/M +Lynette/M +Lynett/M +Lyn/M +Lynna/M +Lynnea/M +Lynnelle/M +Lynnell/M +Lynne/M +Lynnet/M +Lynnette/M +Lynnett/M +Lynn/M +Lynsey/M +lynx/MS +Lyon/SM +Lyra/M +lyrebird/MS +lyre/SM +lyricalness/M +lyrical/YP +lyricism/SM +lyricist/SM +lyric/S +Lysenko/M +lysine/M +Lysistrata/M +Lysol/M +Lyssa/M +LyX/M +MA +Maalox/M +ma'am +Mabelle/M +Mabel/M +Mable/M +Mab/M +macabre/Y +macadamize/SDG +macadam/SM +Macao/M +macaque/SM +macaroni/SM +macaroon/MS +Macarthur/M +MacArthur/M +Macaulay/M +macaw/SM +Macbeth/M +Maccabees/M +Maccabeus/M +Macdonald/M +MacDonald/M +MacDraw/M +Macedonia/M +Macedonian/S +Macedon/M +mace/MS +Mace/MS +macerate/DSXNG +maceration/M +macer/M +Macgregor/M +MacGregor/M +machete/SM +Machiavellian/S +Machiavelli/M +machinate/SDXNG +machination/M +machinelike +machine/MGSDB +machinery/SM +machinist/MS +machismo/SM +Mach/M +macho/S +Machs +Macias/M +Macintosh/M +MacIntosh/M +macintosh's +Mackenzie/M +MacKenzie/M +mackerel/SM +Mackinac/M +Mackinaw +mackinaw/SM +mackintosh/SM +mack/M +Mack/M +MacLeish/M +Macmillan/M +MacMillan/M +Macon/SM +MacPaint/M +macram/S +macrobiotic/S +macrobiotics/M +macrocosm/MS +macrodynamic +macroeconomic/S +macroeconomics/M +macromolecular +macromolecule/SM +macron/MS +macrophage/SM +macroscopic +macroscopically +macrosimulation +macro/SM +macrosocioeconomic +Mac/SGMD +mac/SGMDR +Macy/M +Madagascan/SM +Madagascar/M +Madalena/M +Madalyn/M +Mada/M +madame/M +Madame/MS +madam/SM +madcap/S +Maddalena/M +madded +madden/GSD +maddening/Y +Madden/M +madder/MS +maddest +Maddie/M +Maddi/M +madding +Maddox/M +Maddy/M +made/AU +Madeira/SM +Madelaine/M +Madeleine/M +Madelena/M +Madelene/M +Madelina/M +Madeline/M +Madelin/M +Madella/M +Madelle/M +Madel/M +Madelon/M +Madelyn/M +mademoiselle/MS +Madge/M +madhouse/SM +Madhya/M +Madison/M +Madlen/M +Madlin/M +madman/M +madmen +madness/SM +Madonna/MS +mad/PSY +Madras +madras/SM +Madrid/M +madrigal/MSG +Madsen/M +Madurai/M +madwoman/M +madwomen +Mady/M +Maegan/M +Maelstrom/M +maelstrom/SM +Mae/M +maestro/MS +Maeterlinck/M +Mafia/MS +mafia/S +mafiosi +mafioso/M +Mafioso/S +MAG +magazine/DSMG +Magdaia/M +Magdalena/M +Magdalene/M +Magdalen/M +Magda/M +Magellanic +Magellan/M +magenta/MS +magged +Maggee/M +Maggie/M +Maggi/M +magging +maggot/MS +maggoty/RT +Maggy/M +magi +magical/Y +magician/MS +magicked +magicking +magic/SM +Magill/M +Magi/M +Maginot/M +magisterial/Y +magistracy/MS +magistrate/MS +Mag/M +magma/SM +magnanimity/SM +magnanimosity +magnanimous/PY +magnate/SM +magnesia/MS +magnesite/M +magnesium/SM +magnetically +magnetic/S +magnetics/M +magnetism/SM +magnetite/SM +magnetizable +magnetization/ASCM +magnetize/CGDS +magnetized/U +magnetodynamics +magnetohydrodynamical +magnetohydrodynamics/M +magnetometer/MS +magneto/MS +magnetosphere/M +magnetron/M +magnet/SM +magnification/M +magnificence/SM +magnificent/Y +magnified/U +magnify/DRSGNXZ +magniloquence/MS +magniloquent +Magnitogorsk/M +magnitude/SM +magnolia/SM +Magnum +magnum/SM +Magnuson/M +Magog/M +Magoo/M +magpie/SM +Magritte/M +Magruder/M +mag/S +Magsaysay/M +Maguire/SM +Magus/M +Magyar/MS +Mahabharata +Mahala/M +Mahalia/M +maharajah/M +maharajahs +maharanee's +maharani/MS +Maharashtra/M +maharishi/SM +mahatma/SM +Mahavira/M +Mahayana/M +Mahayanist +Mahdi/M +Mahfouz/M +Mahican/SM +mahjong's +Mahler/M +Mahmoud/M +Mahmud/M +mahogany/MS +Mahomet's +mahout/SM +Maia/M +Maible/M +maidenhair/MS +maidenhead/SM +maidenhood/SM +maidenly/P +maiden/YM +maidservant/MS +maid/SMNX +maier +Maier/M +Maiga/M +Maighdiln/M +Maigret/M +mailbag/MS +mailbox/MS +mail/BSJGZMRD +mailer/M +Mailer/M +Maillol/M +maillot/SM +mailman/M +mailmen +Maiman/M +maimedness/M +maimed/P +maimer/M +Maimonides/M +Mai/MR +maim/SGZRD +mainbrace/M +Maine/MZR +Mainer/M +mainframe/MS +mainlander/M +mainland/SRMZ +mainliner/M +mainline/RSDZG +mainly +mainmast/SM +main/SA +mainsail/SM +mains/M +mainspring/SM +mainstay/MS +mainstream/DRMSG +maintainability +maintainable/U +maintain/BRDZGS +maintained/U +maintainer/M +maintenance/SM +maintop/SM +maiolica's +Maire/M +Mair/M +Maisey/M +Maisie/M +maisonette/MS +Maison/M +Maitilde/M +maize/MS +Maj +Maje/M +majestic +majestically +majesty/MS +Majesty/MS +majolica/SM +Majorca/M +major/DMGS +majordomo/S +majorette/SM +majority/SM +Major/M +Majuro/M +makable +Makarios/M +makefile/S +makeover/S +Maker/M +maker/SM +makeshift/S +make/UGSA +makeup/MS +making/SM +Malabar/M +Malabo/M +Malacca/M +Malachi/M +malachite/SM +maladapt/DV +maladjust/DLV +maladjustment/MS +maladministration +maladroitness/MS +maladroit/YP +malady/MS +Malagasy/M +malaise/SM +Mala/M +Malamud/M +malamute/SM +Malanie/M +malaprop +malapropism/SM +Malaprop/M +malarial +malaria/MS +malarious +malarkey/SM +malathion/S +Malawian/S +Malawi/M +Malayalam/M +Malaya/M +Malayan/MS +Malaysia/M +Malaysian/S +Malay/SM +Malchy/M +Malcolm/M +malcontentedness/M +malcontented/PY +malcontent/SMD +Maldive/SM +Maldivian/S +Maldonado/M +maledict +malediction/MS +malefaction/MS +malefactor/MS +malefic +maleficence/MS +maleficent +Male/M +Malena/M +maleness/MS +male/PSM +malevolence/S +malevolencies +malevolent/Y +malfeasance/SM +malfeasant +malformation/MS +malformed +malfunction/SDG +Malia/M +Malian/S +Malibu/M +malice/MGSD +maliciousness/MS +malicious/YU +malignancy/SM +malignant/YS +malign/GSRDYZ +malignity/MS +Mali/M +Malina/M +Malinda/M +Malinde/M +malingerer/M +malinger/GZRDS +Malinowski/M +Malissa/M +Malissia/M +mallard/SM +Mallarm/M +malleability/SM +malleableness/M +malleable/P +mallet/MS +Mallissa/M +Mallorie/M +Mallory/M +mallow/MS +mall/SGMD +Mal/M +malnourished +malnutrition/SM +malocclusion/MS +malodorous +Malone/M +Malorie/M +Malory/M +malposed +malpractice/SM +Malraux/M +Malta/M +malted/S +Maltese +Malthusian/S +Malthus/M +malting/M +maltose/SM +maltreat/GDSL +maltreatment/S +malt/SGMD +malty/RT +Malva/M +Malvina/M +Malvin/M +Malynda/M +mama/SM +mamba/SM +mambo/GSDM +Mame/M +Mamet/M +ma/MH +Mamie/M +mammalian/SM +mammal/SM +mammary +mamma's +mammogram/S +mammography/S +Mammon's +mammon/SM +mammoth/M +mammoths +mammy/SM +Mamore/M +manacle/SDMG +manageability/S +manageableness +manageable/U +managed/U +management/SM +manageress/M +managerial/Y +manager/M +managership/M +manage/ZLGRSD +Managua/M +Manama/M +maana/M +mananas +Manasseh/M +manatee/SM +Manaus's +Manchester/M +Manchu/MS +Manchuria/M +Manchurian/S +Mancini/M +manciple/M +Mancunian/MS +mandala/SM +Mandalay/M +Manda/M +mandamus/GMSD +Mandarin +mandarin/MS +mandate/SDMG +mandatory/S +Mandela +Mandelbrot/M +Mandel/M +mandible/MS +mandibular +Mandie/M +Mandi/M +Mandingo/M +mandolin/MS +mandrake/MS +mandrel/SM +mandrill/SM +Mandy/M +mange/GSD +mane/MDS +Manet/M +maneuverability/MS +maneuverer/M +maneuver/MRDSGB +Manfred/M +manful/Y +manganese/MS +mange/GMSRDZ +manger/M +manginess/S +mangler/M +mangle/RSDG +mangoes +mango/M +mangrove/MS +mangy/PRT +manhandle/GSD +Manhattan/SM +manhole/MS +manhood/MS +manhunt/SM +maniacal/Y +maniac/SM +mania/SM +manically +Manichean/M +manic/S +manicure/MGSD +manicurist/SM +manifestation/SM +manifesto/GSDM +manifest/YDPGS +manifolder/M +manifold/GPYRDMS +manifoldness/M +manikin/MS +Manila/MS +manila/S +manilla's +Mani/M +manioc/SM +manipulability +manipulable +manipulate/SDXBVGN +manipulative/PM +manipulator/MS +manipulatory +Manitoba/M +Manitoulin/M +Manitowoc/M +mankind/M +Mankowski/M +Manley/M +manlike +manliness/SM +manliness's/U +manly/URPT +manna/MS +manned/U +mannequin/MS +mannered/U +mannerism/SM +mannerist/M +mannerliness/MU +mannerly/UP +manner/SDYM +Mann/GM +Mannheim/M +Mannie/M +mannikin's +Manning/M +manning/U +mannishness/SM +mannish/YP +Manny/M +Manolo/M +Mano/M +manometer/SM +Manon/M +manorial +manor/MS +manpower/SM +manqu/M +man's +mansard/SM +manservant/M +manse/XNM +Mansfield/M +mansion/M +manslaughter/SM +Man/SM +Manson/M +mans/S +manta/MS +Mantegna/M +mantelpiece/MS +mantel/SM +mantes +mantilla/MS +mantissa/SM +mantis/SM +mantle/ESDG +Mantle/M +mantle's +mantling/M +mantra/MS +mantrap/SM +manual/SMY +Manuela/M +Manuel/M +manufacture/JZGDSR +manufacturer/M +manumission/MS +manumit/S +manumitted +manumitting +manure/RSDMZG +manuscript/MS +man/USY +Manville/M +Manx +many +Manya/M +Maoism/MS +Maoist/S +Mao/M +Maori/SM +Maplecrest/M +maple/MS +mapmaker/S +mappable +mapped/UA +mapper/S +mapping/MS +Mapplethorpe/M +maps/AU +map/SM +Maputo/M +Marabel/M +marabou/MS +marabout's +Maracaibo/M +maraca/MS +Mara/M +maraschino/SM +Marathi +marathoner/M +Marathon/M +marathon/MRSZ +Marat/M +marauder/M +maraud/ZGRDS +marbleize/GSD +marble/JRSDMG +marbler/M +marbling/M +Marceau/M +Marcela/M +Marcelia/M +Marcelino/M +Marcella/M +Marcelle/M +Marcellina/M +Marcelline/M +Marcello/M +Marcellus/M +Marcel/M +Marcelo/M +Marchall/M +Marchelle/M +marcher/M +marchioness/SM +March/MS +march/RSDZG +Marcia/M +Marciano/M +Marcie/M +Marcile/M +Marcille/M +Marci/M +Marc/M +Marconi/M +Marco/SM +Marcotte/M +Marcus/M +Marcy/M +Mardi/SM +Marduk/M +Mareah/M +mare/MS +Marena/M +Maren/M +Maressa/M +Margalit/M +Margalo/M +Marga/M +Margareta/M +Margarete/M +Margaretha/M +Margarethe/M +Margaret/M +Margaretta/M +Margarette/M +margarine/MS +Margarita/M +margarita/SM +Margarito/M +Margaux/M +Margeaux/M +Marge/M +Margery/M +Marget/M +Margette/M +Margie/M +Margi/M +marginalia +marginality +marginalization +marginalize/SDG +marginal/YS +margin/GSDM +Margit/M +Margo/M +Margot/M +Margrethe/M +Margret/M +Marguerite/M +Margy/M +mariachi/SM +maria/M +Maria/M +Mariam/M +Mariana/SM +Marian/MS +Marianna/M +Marianne/M +Mariann/M +Mariano/M +Maribelle/M +Maribel/M +Maribeth/M +Maricela/M +Marice/M +Maridel/M +Marieann/M +Mariejeanne/M +Mariele/M +Marielle/M +Mariellen/M +Mariel/M +Marie/M +Marietta/M +Mariette/M +Marigold/M +marigold/MS +Marijn/M +Marijo/M +marijuana/SM +Marika/M +Marilee/M +Marilin/M +Marillin/M +Marilyn/M +marimba/SM +Mari/MS +marinade/MGDS +Marina/M +marina/MS +marinara/SM +marinate/NGXDS +marination/M +mariner/M +Marine/S +marine/ZRS +Marin/M +Marinna/M +Marino/M +Mario/M +marionette/MS +Marion/M +Mariquilla/M +Marisa/M +Mariska/M +Marisol/M +Marissa/M +Maritain/M +marital/Y +Marita/M +maritime/R +Maritsa/M +Maritza/M +Mariupol/M +Marius/M +Mariya/M +Marja/M +Marje/M +Marjie/M +Marji/M +Marj/M +marjoram/SM +Marjorie/M +Marjory/M +Marjy/M +Markab/M +markdown/SM +marked/AU +markedly +marker/M +marketability/SM +marketable/U +Marketa/M +marketeer/S +marketer/M +market/GSMRDJBZ +marketing/M +marketplace/MS +mark/GZRDMBSJ +Markham/M +marking/M +Markism/M +markkaa +markka/M +Mark/MS +Markos +Markov +Markovian +Markovitz/M +marks/A +marksman/M +marksmanship/S +marksmen +markup/SM +Markus/M +Marla/M +Marlane/M +Marlboro/M +Marlborough/M +Marleah/M +Marlee/M +Marleen/M +Marlena/M +Marlene/M +Marley/M +Marlie/M +Marline/M +marlinespike/SM +Marlin/M +marlin/SM +marl/MDSG +Marlo/M +Marlon/M +Marlowe/M +Marlow/M +Marlyn/M +Marmaduke/M +marmalade/MS +Marmara/M +marmoreal +marmoset/MS +marmot/SM +Marna/M +Marne/M +Marney/M +Marnia/M +Marnie/M +Marni/M +maroon/GRDS +marquee/MS +Marquesas/M +marque/SM +marquess/MS +marquetry/SM +Marquette/M +Marquez/M +marquise/M +marquisette/MS +Marquis/M +marquis/SM +Marquita/M +Marrakesh/M +marred/U +marriageability/SM +marriageable +marriage/ASM +married/US +Marrilee/M +marring +Marriott/M +Marris/M +Marrissa/M +marrowbone/MS +marrow/GDMS +marry/SDGA +mar/S +Marseillaise/SM +Marseilles +Marseille's +marshal/GMDRSZ +Marshalled/M +marshaller +Marshall/GDM +Marshalling/M +marshallings +Marshal/M +Marsha/M +marshiness/M +marshland/MS +Marsh/M +marshmallow/SM +marsh/MS +marshy/PRT +Marsiella/M +Mar/SMN +marsupial/MS +Martainn/M +Marta/M +Martelle/M +Martel/M +marten/M +Marten/M +Martguerita/M +Martha/M +Marthe/M +Marthena/M +Martial +martial/Y +Martian/S +Martica/M +Martie/M +Marti/M +Martina/M +martinet/SM +Martinez/M +martingale/MS +martini/MS +Martinique/M +Martin/M +Martino/M +martin/SM +Martinson/M +Martita/M +mart/MDNGXS +Mart/MN +Marty/M +Martyn/M +Martynne/M +martyrdom/SM +martyr/GDMS +Marva/M +marvel/DGS +Marvell/M +marvelous/PY +Marve/M +Marven/M +Marvin/M +Marv/NM +Marwin/M +Marxian/S +Marxism/SM +Marxist/SM +Marx/M +Marya/M +Maryanna/M +Maryanne/M +Maryann/M +Marybelle/M +Marybeth/M +Maryellen/M +Maryjane/M +Maryjo/M +Maryland/MZR +Marylee/M +Marylinda/M +Marylin/M +Maryl/M +Marylou/M +Marylynne/M +Mary/M +Maryrose/M +Marys +Marysa/M +marzipan/SM +Masada/M +Masai/M +Masaryk/M +masc +Mascagni/M +mascara/SGMD +mascot/SM +masculineness/M +masculine/PYS +masculinity/SM +Masefield/M +maser/M +Maseru/M +MASH +Masha/M +Mashhad/M +mash/JGZMSRD +m/ASK +masked/U +masker/M +mask/GZSRDMJ +masks/U +masochism/MS +masochistic +masochistically +masochist/MS +masonic +Masonic +Masonite/M +masonry/MS +mason/SDMG +Mason/SM +masquerader/M +masquerade/RSDGMZ +masquer/M +masque/RSMZ +Massachusetts/M +massacre/DRSMG +massager/M +massage/SRDMG +Massasoit/M +Massenet/M +masseur/MS +masseuse/SM +Massey/M +massif/SM +Massimiliano/M +Massimo/M +massing/R +massiveness/SM +massive/YP +massless +mas/SRZ +Mass/S +mass/VGSD +mastectomy/MS +masterclass +mastered/A +masterfulness/M +masterful/YP +master/JGDYM +masterliness/M +masterly/P +mastermind/GDS +masterpiece/MS +mastership/M +Master/SM +masterstroke/MS +masterwork/S +mastery/MS +mast/GZSMRD +masthead/SDMG +masticate/SDXGN +mastication/M +mastic/SM +mastiff/MS +mastodon/MS +mastoid/S +masturbate/SDNGX +masturbation/M +masturbatory +matador/SM +Mata/M +matchable/U +match/BMRSDZGJ +matchbook/SM +matchbox/SM +matched/UA +matcher/M +matches/A +matchless/Y +matchlock/MS +matchmake/GZJR +matchmaker/M +matchmaking/M +matchplay +match's/A +matchstick/MS +matchwood/SM +mated/U +mate/IMS +Matelda/M +Mateo/M +materialism/SM +materialistic +materialistically +materialist/SM +materiality/M +materialization/SM +materialize/CDS +materialized/A +materializer/SM +materializes/A +materializing +materialness/M +material/SPYM +matriel/MS +mater/M +maternal/Y +maternity/MS +mates/U +mathematical/Y +Mathematica/M +mathematician/SM +mathematic/S +mathematics/M +Mathematik/M +Mather/M +Mathe/RM +Mathew/MS +Mathewson/M +Mathian/M +Mathias +Mathieu/M +Mathilda/M +Mathilde/M +Mathis +math/M +maths +Matias/M +Matilda/M +Matilde/M +matine/S +mating/M +matins/M +Matisse/SM +matriarchal +matriarch/M +matriarchs +matriarchy/MS +matrices +matricidal +matricide/MS +matriculate/XSDGN +matriculation/M +matrimonial/Y +matrimony/SM +matrix/M +matron/YMS +mat/SJGMDR +Matsumoto/M +matte/JGMZSRD +Mattel/M +Matteo/M +matter/GDM +Matterhorn/M +Matthaeus/M +Mattheus/M +Matthew/MS +Matthias +Matthieu/M +Matthiew/M +Matthus/M +Mattias/M +Mattie/M +Matti/M +matting/M +mattins's +Matt/M +mattock/MS +mattress/MS +matt's +Matty/M +maturate/DSNGVX +maturational +maturation/M +matureness/M +maturer/M +mature/RSDTPYG +maturity/MS +matzo/SHM +matzot +Maude/M +Maudie/M +maudlin/Y +Maud/M +Maugham/M +Maui/M +mauler/M +maul/RDGZS +maunder/GDS +Maupassant/M +Maura/M +Maureene/M +Maureen/M +Maure/M +Maurene/M +Mauriac/M +Maurice/M +Mauricio/M +Maurie/M +Maurine/M +Maurise/M +Maurita/M +Mauritania/M +Mauritanian/S +Mauritian/S +Mauritius/M +Maurits/M +Maurizia/M +Maurizio/M +Maurois/M +Mauro/M +Maury/M +Mauser/M +mausoleum/SM +mauve/SM +maven/S +maverick/SMDG +mavin's +Mavis/M +Mavra/M +mawkishness/SM +mawkish/PY +Mawr/M +maw/SGMD +max/GDS +Maxie/M +maxillae +maxilla/M +maxillary/S +Maxi/M +maximality +maximal/SY +maxima's +Maximilian/M +Maximilianus/M +Maximilien/M +maximization/SM +maximizer/M +maximize/RSDZG +Maxim/M +Maximo/M +maxim/SM +maximum/MYS +Maxine/M +maxi/S +Max/M +Maxtor/M +Maxwellian +maxwell/M +Maxwell/M +Maxy/M +Maya/MS +Mayan/S +Maybelle/M +maybe/S +mayday/S +may/EGS +Maye/M +mayer +Mayer/M +mayest +Mayfair/M +Mayflower/M +mayflower/SM +mayfly/MS +mayhap +mayhem/MS +Maynard/M +Mayne/M +Maynord/M +mayn't +Mayo/M +mayonnaise/MS +mayoral +mayoralty/MS +mayoress/MS +Mayor/M +mayor/MS +mayorship/M +mayo/S +maypole/MS +Maypole/SM +Mayra/M +May/SMR +mayst +Mazama/M +Mazarin/M +Mazatlan/M +Mazda/M +mazedness/SM +mazed/YP +maze/MGDSR +mazurka/SM +Mazzini/M +Mb +MB +MBA +Mbabane/M +Mbini/M +MC +McAdam/MS +McAllister/M +McBride/M +McCabe/M +McCain/M +McCall/M +McCarthyism/M +McCarthy/M +McCartney/M +McCarty/M +McCauley/M +McClain/M +McClellan/M +McClure/M +McCluskey/M +McConnell/M +McCormick/M +McCoy/SM +McCracken/M +McCray/M +McCullough/M +McDaniel/M +McDermott/M +McDonald/M +McDonnell/M +McDougall/M +McDowell/M +McElhaney/M +McEnroe/M +McFadden/M +McFarland/M +McGee/M +McGill/M +McGovern/M +McGowan/M +McGrath/M +McGraw/M +McGregor/M +McGuffey/M +McGuire/M +MCI/M +McIntosh/M +McIntyre/M +McKay/M +McKee/M +McKenzie/M +McKesson/M +McKinley/M +McKinney/M +McKnight/M +McLanahan/M +McLaughlin/M +McLean/M +McLeod/M +McLuhan/M +McMahon/M +McMartin/M +McMillan/M +McNamara/M +McNaughton/M +McNeil/M +McPherson/M +MD +Md/M +mdse +MDT +ME +Meade/M +Mead/M +meadowland +meadowlark/SM +meadow/MS +Meadows +meadowsweet/M +mead/SM +Meagan/M +meagerness/SM +meager/PY +Meaghan/M +meagres +mealiness/MS +meal/MDGS +mealtime/MS +mealybug/S +mealymouthed +mealy/PRST +meander/JDSG +meaneing +meanie/MS +meaningfulness/SM +meaningful/YP +meaninglessness/SM +meaningless/PY +meaning/M +meanness/S +means/M +meantime/SM +meant/U +meanwhile/S +Meany/M +mean/YRGJTPS +meany's +Meara/M +measle/SD +measles/M +measly/TR +measurable/U +measurably +measure/BLMGRSD +measured/Y +measureless +measurement/SM +measurer/M +measures/A +measuring/A +meas/Y +meataxe +meatball/MS +meatiness/MS +meatless +meatloaf +meatloaves +meat/MS +meatpacking/S +meaty/RPT +Mecca/MS +mecca/S +mechanical/YS +mechanic/MS +mechanism/SM +mechanistic +mechanistically +mechanist/M +mechanization/SM +mechanized/U +mechanizer/M +mechanize/RSDZGB +mechanizes/U +mechanochemically +Mechelle/M +med +medalist/MS +medallion/MS +medal/SGMD +Medan/M +meddle/GRSDZ +meddlesome +Medea/M +Medellin +Medfield/M +mediaeval's +medial/AY +medials +median/YMS +media/SM +mediateness/M +mediate/PSDYVNGX +mediation/ASM +mediator/SM +Medicaid/SM +medical/YS +medicament/MS +Medicare/MS +medicate/DSXNGV +medication/M +Medici/MS +medicinal/SY +medicine/DSMG +medico/SM +medic/SM +medievalist/MS +medieval/YMS +Medina/M +mediocre +mediocrity/MS +meditate/NGVXDS +meditation/M +meditativeness/M +meditative/PY +Mediterranean/MS +mediumistic +medium/SM +medley/SM +medulla/SM +Medusa/M +meed/MS +meekness/MS +meek/TPYR +meerschaum/MS +meeter/M +meetinghouse/S +meeting/M +meet/JGSYR +me/G +mega +megabit/MS +megabuck/S +megabyte/S +megacycle/MS +megadeath/M +megadeaths +megahertz/M +megalithic +megalith/M +megaliths +megalomaniac/SM +megalomania/SM +megalopolis/SM +Megan/M +megaphone/SDGM +megaton/MS +megavolt/M +megawatt/SM +megaword/S +Megen/M +Meggie/M +Meggi/M +Meggy/M +Meghan/M +Meghann/M +Meg/MN +megohm/MS +Mehetabel/M +Meier/M +Meighen/M +Meiji/M +Mei/MR +meioses +meiosis/M +meiotic +Meir/M +Meister/M +Meistersinger/M +Mejia/M +Mekong/M +Mela/M +Melamie/M +melamine/SM +melancholia/SM +melancholic/S +melancholy/MS +Melanesia/M +Melanesian/S +melange/S +Melania/M +Melanie/M +melanin/MS +melanoma/SM +Melantha/M +Melany/M +Melba/M +Melbourne/M +Melcher/M +Melchior/M +meld/SGD +mle/MS +Melendez/M +Melesa/M +Melessa/M +Melicent/M +Melina/M +Melinda/M +Melinde/M +meliorate/XSDVNG +melioration/M +Melisa/M +Melisande/M +Melisandra/M +Melisenda/M +Melisent/M +Melissa/M +Melisse/M +Melita/M +Melitta/M +Mella/M +Mellicent/M +Mellie/M +mellifluousness/SM +mellifluous/YP +Melli/M +Mellisa/M +Mellisent/M +Melloney/M +Mellon/M +mellowness/MS +mellow/TGRDYPS +Melly/M +Mel/MY +Melodee/M +melodically +melodic/S +Melodie/M +melodiousness/S +melodious/YP +melodrama/SM +melodramatically +melodramatic/S +Melody/M +melody/MS +Melonie/M +melon/MS +Melony/M +Melosa/M +Melpomene/M +meltdown/S +melter/M +melting/Y +Melton/M +melt/SAGD +Melva/M +Melville/M +Melvin/M +Melvyn/M +Me/M +member/DMS +membered/AE +members/EA +membership/SM +membrane/MSD +membranous +memento/SM +Memling/M +memoir/MS +memorabilia +memorability/SM +memorableness/M +memorable/P +memorably +memorandum/SM +memorialize/DSG +memorialized/U +memorial/SY +memoriam +memorization/MS +memorized/U +memorizer/M +memorize/RSDZG +memorizes/A +memoryless +memory/MS +memo/SM +Memphis/M +menace/GSD +menacing/Y +menagerie/SM +menage/S +Menander/M +menarche/MS +Menard/M +Mencius/M +Mencken/M +mendaciousness/M +mendacious/PY +mendacity/MS +Mendeleev/M +mendelevium/SM +Mendelian +Mendel/M +Mendelssohn/M +mender/M +Mendez/M +mendicancy/MS +mendicant/S +Mendie/M +mending/M +Mendocino/M +Mendoza/M +mend/RDSJGZ +Mendy/M +Menelaus/M +Menes/M +menfolk/S +menhaden/M +menial/YS +meningeal +meninges +meningitides +meningitis/M +meninx +menisci +meniscus/M +Menkalinan/M +Menkar/M +Menkent/M +Menlo/M +men/MS +Mennonite/SM +Menominee +menopausal +menopause/SM +menorah/M +menorahs +Menotti/M +Mensa/M +Mensch/M +mensch/S +menservants/M +mens/SDG +menstrual +menstruate/NGDSX +menstruation/M +mensurable/P +mensuration/MS +menswear/M +mentalist/MS +mentality/MS +mental/Y +mentholated +menthol/SM +mentionable/U +mentioned/U +mentioner/M +mention/ZGBRDS +mentor/DMSG +Menuhin/M +menu/SM +Menzies/M +meow/DSG +Mephistopheles/M +Merak/M +Mercado/M +mercantile +Mercator/M +Mercedes +mercenariness/M +mercenary/SMP +mercerize/SDG +Mercer/M +mercer/SM +merchandiser/M +merchandise/SRDJMZG +merchantability +merchantman/M +merchantmen +merchant/SBDMG +Mercie/M +mercifully/U +mercifulness/M +merciful/YP +mercilessness/SM +merciless/YP +Merci/M +Merck/M +mercurial/SPY +mercuric +Mercurochrome/M +mercury/MS +Mercury/MS +Mercy/M +mercy/SM +Meredeth/M +Meredithe/M +Meredith/M +Merell/M +meretriciousness/SM +meretricious/YP +mere/YS +merganser/MS +merger/M +merge/SRDGZ +Meridel/M +meridian/MS +meridional +Meridith/M +Meriel/M +Merilee/M +Merill/M +Merilyn/M +meringue/MS +merino/MS +Meris +Merissa/M +merited/U +meritocracy/MS +meritocratic +meritocrats +meritoriousness/MS +meritorious/PY +merit/SCGMD +Meriwether/M +Merla/M +Merle/M +Merlina/M +Merline/M +merlin/M +Merlin/M +Merl/M +mermaid/MS +merman/M +mermen +Merna/M +Merola/M +meromorphic +Merralee/M +Merrel/M +Merriam/M +Merrick/M +Merridie/M +Merrielle/M +Merrie/M +Merrilee/M +Merrile/M +Merrili/M +Merrill/M +merrily +Merrily/M +Merrimack/M +Merrimac/M +merriment/MS +merriness/S +Merritt/M +Merry/M +merrymaker/MS +merrymaking/SM +merry/RPT +Mersey/M +mer/TGDR +Merton/M +Mervin/M +Merv/M +Merwin/M +Merwyn/M +Meryl/M +Mesa +Mesabi/M +mesa/SM +mescaline/SM +mescal/SM +mesdames/M +mesdemoiselles/M +Meshed's +meshed/U +mesh/GMSD +mesmeric +mesmerism/SM +mesmerized/U +mesmerizer/M +mesmerize/SRDZG +Mesolithic/M +mesomorph/M +mesomorphs +meson/MS +Mesopotamia/M +Mesopotamian/S +mesosphere/MS +mesozoic +Mesozoic +mesquite/MS +mes/S +message/SDMG +messeigneurs +messenger/GSMD +Messerschmidt/M +mess/GSDM +Messiaen/M +messiah +Messiah/M +messiahs +Messiahs +messianic +Messianic +messieurs/M +messily +messiness/MS +messmate/MS +Messrs/M +messy/PRT +mestizo/MS +meta +metabolic +metabolically +metabolism/MS +metabolite/SM +metabolize/GSD +metacarpal/S +metacarpi +metacarpus/M +metacircular +metacircularity +metalanguage/MS +metalization/SM +metalized +metallic/S +metalliferous +metallings +metallography/M +metalloid/M +metallurgic +metallurgical/Y +metallurgist/S +metallurgy/MS +metal/SGMD +metalsmith/MS +metalworking/M +metalwork/RMJGSZ +Meta/M +metamathematical +metamorphic +metamorphism/SM +metamorphose/GDS +metamorphosis/M +metaphoric +metaphorical/Y +metaphor/MS +metaphosphate/M +metaphysical/Y +metaphysic/SM +metastability/M +metastable +metastases +metastasis/M +metastasize/DSG +metastatic +metatarsal/S +metatarsi +metatarsus/M +metatheses +metathesis/M +metathesized +metathesizes +metathesizing +metavariable +metempsychoses +metempsychosis/M +meteoric +meteorically +meteorite/SM +meteoritic/S +meteoritics/M +meteoroid/SM +meteorologic +meteorological +meteorologist/S +meteorology/MS +meteor/SM +meter/GDM +mete/ZDGSR +methadone/SM +methane/MS +methanol/SM +methinks +methionine/M +methodicalness/SM +methodical/YP +methodism +Methodism/SM +methodist/MS +Methodist/MS +method/MS +methodological/Y +methodologists +methodology/MS +methought +Methuen/M +Methuselah/M +Methuselahs +methylated +methylene/M +methyl/SM +meticulousness/MS +meticulous/YP +mtier/S +metonymy/M +Metrecal/M +metrical/Y +metricate/SDNGX +metricize/GSD +metrics/M +metric/SM +metronome/MS +metropolis/SM +metropolitanization +metropolitan/S +metro/SM +mets +Metternich/M +mettle/SDM +mettlesome +met/U +Metzler/M +Meuse/M +mewl/GSD +mew/SGD +mews/SM +Mex +Mexicali/M +Mexican/S +Mexico/M +Meyerbeer/M +Meyer/SM +mezzanine/MS +mezzo/S +MFA +mfg +mfr/S +mg +M/GB +Mg/M +MGM/M +mgr +Mgr +MHz +MI +MIA +Mia/M +Miami/SM +Miaplacidus/M +miasmal +miasma/SM +Micaela/M +Micah/M +mica/MS +micelles +mice/M +Michaela/M +Michaelangelo/M +Michaelina/M +Michaeline/M +Michaella/M +Michaelmas/MS +Michael/SM +Michaelson/M +Michail/M +Michale/M +Michal/M +Micheal/M +Micheil/M +Michelangelo/M +Michele/M +Michelina/M +Micheline/M +Michelin/M +Michelle/M +Michell/M +Michel/M +Michelson/M +Michigander/S +Michiganite/S +Michigan/M +Mich/M +Mickelson/M +Mickey/M +mickey/SM +Mickie/M +Micki/M +Mick/M +Micky/M +Mic/M +Micmac/M +micra's +microamp +microanalysis/M +microanalytic +microbe/MS +microbial +microbicidal +microbicide/M +microbiological +microbiologist/MS +microbiology/SM +microbrewery/S +microchemistry/M +microchip/S +microcircuit/MS +microcode/GSD +microcomputer/MS +microcosmic +microcosm/MS +microdensitometer +microdot/MS +microeconomic/S +microeconomics/M +microelectronic/S +microelectronics/M +microfiber/S +microfiche/M +microfilm/DRMSG +microfossils +micrography/M +microgroove/MS +microhydrodynamics +microinstruction/SM +microjoule +microlevel +microlight/S +micromanage/GDSL +micromanagement/S +micrometeorite/MS +micrometeoritic +micrometer/SM +Micronesia/M +Micronesian/S +micron/MS +microorganism/SM +microphone/SGM +Microport/M +microprocessing +microprocessor/SM +microprogrammed +microprogramming +microprogram/SM +micro/S +microscope/SM +microscopic +microscopical/Y +microscopy/MS +microsecond/MS +microsimulation/S +Microsystems +micros/M +Microsoft/M +microsomal +microstore +microsurgery/SM +MicroVAXes +MicroVAX/M +microvolt/SM +microwaveable +microwave/BMGSD +microword/S +midair/MS +midas +Midas/M +midband/M +midday/MS +midden/SM +middest +middlebrow/SM +Middlebury/M +middle/GJRSD +middleman/M +middlemen +middlemost +Middlesex/M +Middleton/M +Middletown/M +middleweight/SM +middling/Y +middy/SM +Mideastern +Mideast/M +midfield/RM +Midge/M +midge/SM +midget/MS +midi/S +midland/MRS +Midland/MS +midlife +midlives +midmorn/G +midmost/S +midnight/SYM +midpoint/MS +midrange +midrib/MS +midriff/MS +mid/S +midscale +midsection/M +midshipman/M +midshipmen +midship/S +midspan +midstream/MS +midst/SM +midsummer/MS +midterm/MS +midtown/MS +Midway/M +midway/S +midweek/SYM +Midwesterner/M +Midwestern/ZR +Midwest/M +midwicket +midwifery/SM +midwife/SDMG +midwinter/YMS +midwives +midyear/MS +mien/M +miff/GDS +mightily +mightiness/MS +mightn't +might/S +mighty/TPR +mignon +mignonette/SM +Mignon/M +Mignonne/M +migraine/SM +migrant/MS +migrate/ASDG +migration/MS +migrative +migratory/S +MIG/S +Miguela/M +Miguelita/M +Miguel/M +mikado/MS +Mikaela/M +Mikael/M +mike/DSMG +Mikel/M +Mike/M +Mikey/M +Mikhail/M +Mikkel/M +Mikol/M +Mikoyan/M +milady/MS +Milagros/M +Milanese +Milan/M +milch/M +mildew/DMGS +mildness/MS +Mildred/M +Mildrid/M +mild/STYRNP +mileage/SM +Milena/M +milepost/SM +miler/M +mile/SM +Mile/SM +milestone/MS +Milford/M +Milicent/M +milieu/SM +Milissent/M +militancy/MS +militantness/M +militant/YPS +militarily +militarism/SM +militaristic +militarist/MS +militarization/SCM +militarize/SDCG +military +militate/SDG +militiaman/M +militiamen +militia/SM +Milka/M +Milken/M +milker/M +milk/GZSRDM +milkiness/MS +milkmaid/SM +milkman/M +milkmen +milkshake/S +milksop/SM +milkweed/MS +milky/RPT +millage/S +Millard/M +Millay/M +millenarian +millenarianism/M +millennial +millennialism +millennium/MS +millepede's +miller/M +Miller/M +Millet/M +millet/MS +milliamp +milliampere/S +milliard/MS +millibar/MS +Millicent/M +millidegree/S +Millie/M +milligram/MS +millijoule/S +Millikan/M +milliliter/MS +Milli/M +millimeter/SM +milliner/SM +millinery/MS +milling/M +millionaire/MS +million/HDMS +millionth/M +millionths +millipede/SM +millisecond/MS +Millisent/M +millivoltmeter/SM +millivolt/SM +milliwatt/S +millpond/MS +millrace/SM +mill/SGZMRD +Mill/SMR +millstone/SM +millstream/SM +millwright/MS +Milly/M +mil/MRSZ +Mil/MY +Milne/M +Milo/M +Milquetoast/S +milquetoast/SM +Miltiades/M +Miltie/M +Milt/M +milt/MDSG +Miltonic +Milton/M +Miltown/M +Milty/M +Milwaukee/M +Milzie/M +MIMD +mime/DSRMG +mimeograph/GMDS +mimeographs +mimer/M +mimesis/M +mimetic +mimetically +mimicked +mimicker/SM +mimicking +mimicry/MS +mimic/S +Mimi/M +mi/MNX +Mimosa/M +mimosa/SM +Mina/M +minaret/MS +minatory +mincemeat/MS +mincer/M +mince/SRDGZJ +mincing/Y +Minda/M +Mindanao/M +mind/ARDSZG +mindbogglingly +minded/P +minder/M +mindfully +mindfulness/MS +mindful/U +mindlessness/SM +mindless/YP +Mindoro/M +min/DRZGJ +mind's +mindset/S +Mindy/M +minefield/MS +mineralization/C +mineralized/U +mineralogical +mineralogist/SM +mineralogy/MS +mineral/SM +miner/M +Miner/M +Minerva/M +mineshaft +mine/SNX +minestrone/MS +minesweeper/MS +Minetta/M +Minette/M +mineworkers +mingle/SDG +Ming/M +Mingus/M +miniature/GMSD +miniaturist/SM +miniaturization/MS +miniaturize/SDG +minibike/S +minibus/SM +minicab/M +minicam/MS +minicomputer/SM +minidress/SM +minify/GSD +minimalism/S +minimalistic +minimalist/MS +minimality +minimal/SY +minima's +minimax/M +minimization/MS +minimized/U +minimizer/M +minimize/RSDZG +minim/SM +minimum/MS +mining/M +minion/M +mini/S +miniseries +miniskirt/MS +ministerial/Y +minister/MDGS +ministrant/S +ministration/SM +ministry/MS +minivan/S +miniver/M +minke +mink/SM +Min/MR +Minna/M +Minnaminnie/M +Minneapolis/M +Minne/M +minnesinger/MS +Minnesota/M +Minnesotan/S +Minnie/M +Minni/M +Minn/M +Minnnie/M +minnow/SM +Minny/M +Minoan/S +Minolta/M +minor/DMSG +minority/MS +Minor/M +Minos +Minotaur/M +minotaur/S +Minot/M +minoxidil/S +Minsk/M +Minsky/M +minster/SM +minstrel/SM +minstrelsy/MS +mintage/SM +Mintaka/M +Minta/M +minter/M +mint/GZSMRD +minty/RT +minuend/SM +minuet/SM +Minuit/M +minuscule/SM +minus/S +minuteman +Minuteman/M +minutemen +minuteness/SM +minute/RSDPMTYG +minutiae +minutia/M +minx/MS +Miocene +MIPS +Miquela/M +Mirabeau/M +Mirabella/M +Mirabelle/M +Mirabel/M +Mirach/M +miracle/MS +miraculousness/M +miraculous/PY +mirage/GSDM +Mira/M +Miranda/M +Miran/M +Mireielle/M +Mireille/M +Mirella/M +Mirelle/M +mire/MGDS +Mirfak/M +Miriam/M +Mirilla/M +Mir/M +Mirna/M +Miro +mirror/DMGS +mirthfulness/SM +mirthful/PY +mirthlessness/M +mirthless/YP +mirth/M +mirths +MIRV/DSG +miry/RT +Mirzam/M +misaddress/SDG +misadventure/SM +misalign/DSGL +misalignment/MS +misalliance/MS +misanalysed +misanthrope/MS +misanthropic +misanthropically +misanthropist/S +misanthropy/SM +misapplier/M +misapply/GNXRSD +misapprehend/GDS +misapprehension/MS +misappropriate/GNXSD +misbegotten +misbehaver/M +misbehave/RSDG +misbehavior/SM +misbrand/DSG +misc +miscalculate/XGNSD +miscalculation/M +miscall/SDG +miscarriage/MS +miscarry/SDG +miscast/GS +miscegenation/SM +miscellanea +miscellaneous/PY +miscellany/MS +Mischa/M +mischance/MGSD +mischief/MDGS +mischievousness/MS +mischievous/PY +miscibility/S +miscible/C +misclassification/M +misclassified +misclassifying +miscode/SDG +miscommunicate/NDS +miscomprehended +misconceive/GDS +misconception/MS +misconduct/GSMD +misconfiguration +misconstruction/MS +misconstrue/DSG +miscopying +miscount/DGS +miscreant/MS +miscue/MGSD +misdeal/SG +misdealt +misdeed/MS +misdemeanant/SM +misdemeanor/SM +misdiagnose/GSD +misdid +misdirect/GSD +misdirection/MS +misdirector/S +misdoes +misdo/JG +misdone +miserableness/SM +miserable/SP +miserably +miser/KM +miserliness/MS +miserly/P +misery/MS +mises/KC +misfeasance/MS +misfeature/M +misfield +misfile/SDG +misfire/SDG +misfit/MS +misfitted +misfitting +misfortune/SM +misgauge/GDS +misgiving/MYS +misgovern/LDGS +misgovernment/S +misguidance/SM +misguidedness/M +misguided/PY +misguide/DRSG +misguider/M +Misha/M +mishandle/SDG +mishap/MS +mishapped +mishapping +misheard +mishear/GS +mishitting +mishmash/SM +misidentification/M +misidentify/GNSD +misinformation/SM +misinform/GDS +misinterpretation/MS +misinterpreter/M +misinterpret/RDSZG +misjudge/DSG +misjudging/Y +misjudgment/MS +Miskito +mislabel/DSG +mislaid +mislay/GS +misleader/M +mislead/GRJS +misleading/Y +misled +mismanage/LGSD +mismanagement/MS +mismatch/GSD +misname/GSD +misnomer/GSMD +misogamist/MS +misogamy/MS +misogynistic +misogynist/MS +misogynous +misogyny/MS +misperceive/SD +misplace/GLDS +misplacement/MS +misplay/GSD +mispositioned +misprint/SGDM +misprision/SM +mispronounce/DSG +mispronunciation/MS +misquotation/MS +misquote/GDS +misreader/M +misread/RSGJ +misrelated +misremember/DG +misreport/DGS +misrepresentation/MS +misrepresenter/M +misrepresent/SDRG +misroute/DS +misrule/SDG +missal/ESM +misshape/DSG +misshapenness/SM +misshapen/PY +Missie/M +missile/MS +missilery/SM +mission/AMS +missionary/MS +missioned +missioner/SM +missioning +missis's +Mississauga/M +Mississippian/S +Mississippi/M +missive/MS +Missoula/M +Missourian/S +Missouri/M +misspeak/SG +misspecification +misspecified +misspelling/M +misspell/SGJD +misspend/GS +misspent +misspoke +misspoken +mis/SRZ +miss/SDEGV +Miss/SM +misstate/GLDRS +misstatement/MS +misstater/M +misstep/MS +misstepped +misstepping +missus/SM +Missy/M +mistakable/U +mistake/BMGSR +mistaken/Y +mistaker/M +mistaking/Y +Mistassini/M +mister/GDM +Mister/SM +mistily +Misti/M +mistime/GSD +mistiness/S +mistletoe/MS +mist/MRDGZS +mistook +mistral/MS +mistranslated +mistranslates +mistranslating +mistranslation/SM +mistreat/DGSL +mistreatment/SM +Mistress/MS +mistress/MSY +mistrial/SM +mistruster/M +mistrustful/Y +mistrust/SRDG +Misty/M +mistype/SDGJ +misty/PRT +misunderstander/M +misunderstanding/M +misunderstand/JSRZG +misunderstood +misuser/M +misuse/RSDMG +miswritten +Mitchael/M +Mitchell/M +Mitchel/M +Mitch/M +miterer/M +miter/GRDM +mite/SRMZ +Mitford/M +Mithra/M +Mithridates/M +mitigated/U +mitigate/XNGVDS +mitigation/M +MIT/M +mitoses +mitosis/M +mitotic +MITRE/SM +Mitsubishi/M +mitten/M +Mitterrand/M +mitt/XSMN +Mitty/M +Mitzi/M +mitzvahs +mixable +mix/AGSD +mixed/U +mixer/SM +mixture/SM +Mizar/M +mizzenmast/SM +mizzen/MS +Mk +mks +ml +Mlle/M +mm +MM +MMe +Mme/SM +MN +mnemonically +mnemonics/M +mnemonic/SM +Mnemosyne/M +Mn/M +MO +moan/GSZRDM +moat/SMDG +mobbed +mobber +mobbing +mobcap/SM +Mobile/M +mobile/S +mobility/MS +mobilizable +mobilization/AMCS +mobilize/CGDS +mobilized/U +mobilizer/MS +mobilizes/A +Mobil/M +mob/MS +mobster/MS +Mobutu/M +moccasin/SM +mocha/SM +mockers/M +mockery/MS +mock/GZSRD +mockingbird/MS +mocking/Y +mo/CSK +modality/MS +modal/Y +modeled/A +modeler/M +modeling/M +models/A +model/ZGSJMRD +mode/MS +modem/SM +moderated/U +moderateness/SM +moderate/PNGDSXY +moderation/M +moderator/MS +modernism/MS +modernistic +modernist/S +modernity/SM +modernization/MS +modernized/U +modernizer/M +modernize/SRDGZ +modernizes/U +modernness/SM +modern/PTRYS +Modesta/M +Modestia/M +Modestine/M +Modesto/M +modest/TRY +Modesty/M +modesty/MS +modicum/SM +modifiability/M +modifiableness/M +modifiable/U +modification/M +modified/U +modifier/M +modify/NGZXRSD +Modigliani/M +modishness/MS +modish/YP +mod/TSR +Modula/M +modularity/SM +modularization +modularize/SDG +modular/SY +modulate/ADSNCG +modulation/CMS +modulator/ACSM +module/SM +moduli +modulo +modulus/M +modus +Moe/M +Moen/M +Mogadiscio's +Mogadishu +mogul/MS +Mogul/MS +mohair/SM +Mohamed/M +Mohammad/M +Mohammedanism/MS +Mohammedan/SM +Mohammed's +Mohandas/M +Mohandis/M +Mohawk/MS +Mohegan/S +Mohican's +Moho/M +Mohorovicic/M +Mohr/M +moiety/MS +moil/SGD +Moina/M +Moines/M +Moira/M +moire/MS +Moise/MS +Moiseyev/M +Moishe/M +moistener/M +moisten/ZGRD +moistness/MS +moist/TXPRNY +moisture/MS +moisturize/GZDRS +Mojave/M +molal +molarity/SM +molar/MS +molasses/MS +Moldavia/M +Moldavian/S +moldboard/SM +molder/DG +moldiness/SM +molding/M +mold/MRDJSGZ +Moldova +moldy/PTR +molecularity/SM +molecular/Y +molecule/MS +molehill/SM +mole/MTS +moleskin/MS +molestation/SM +molested/U +molester/M +molest/RDZGS +Moliere +Molina/M +Moline/M +Mollee/M +Mollie/M +mollification/M +mollify/XSDGN +Molli/M +Moll/M +moll/MS +mollusc's +mollusk/S +mollycoddler/M +mollycoddle/SRDG +Molly/M +molly/SM +Molnar/M +Moloch/M +Molokai/M +Molotov/M +molter/M +molt/RDNGZS +Moluccas +molybdenite/M +molybdenum/MS +Mombasa/M +momenta +momentarily +momentariness/SM +momentary/P +moment/MYS +momentousness/MS +momentous/YP +momentum/SM +momma/S +Mommy/M +mommy/SM +Mo/MN +mom/SM +Monaco/M +monadic +monad/SM +Monah/M +Mona/M +monarchic +monarchical +monarchism/MS +monarchistic +monarchist/MS +monarch/M +monarchs +monarchy/MS +Monash/M +monastery/MS +monastical/Y +monasticism/MS +monastic/S +monaural/Y +Mondale/M +Monday/MS +Mondrian/M +Monegasque/SM +Monera/M +monetarily +monetarism/S +monetarist/MS +monetary +monetization/CMA +monetize/CGADS +Monet/M +moneybag/SM +moneychangers +moneyer/M +moneylender/SM +moneymaker/MS +moneymaking/MS +money/SMRD +Monfort/M +monger/SGDM +Mongolia/M +Mongolian/S +Mongolic/M +mongolism/SM +mongoloid/S +Mongoloid/S +Mongol/SM +mongoose/SM +mongrel/SM +Monica/M +monies/M +Monika/M +moniker/MS +Monique/M +monism/MS +monist/SM +monition/SM +monitored/U +monitor/GSMD +monitory/S +monkeyshine/S +monkey/SMDG +monkish +Monk/M +monk/MS +monkshood/SM +Monmouth/M +monochromatic +monochromator +monochrome/MS +monocle/SDM +monoclinic +monoclonal/S +monocotyledonous +monocotyledon/SM +monocular/SY +monodic +monodist/S +monody/MS +monogamist/MS +monogamous/PY +monogamy/MS +monogrammed +monogramming +monogram/MS +monograph/GMDS +monographs +monolingualism +monolingual/S +monolithic +monolithically +monolith/M +monoliths +monologist/S +monologue/GMSD +monomaniacal +monomaniac/MS +monomania/MS +monomeric +monomer/SM +monomial/SM +mono/MS +Monongahela/M +mononuclear +mononucleoses +mononucleosis/M +monophonic +monoplane/MS +monopole/S +monopolistic +monopolist/MS +monopolization/MS +monopolized/U +monopolize/GZDSR +monopolizes/U +monopoly/MS +monorail/SM +monostable +monosyllabic +monosyllable/MS +monotheism/SM +monotheistic +monotheist/S +monotone/SDMG +monotonic +monotonically +monotonicity +monotonousness/MS +monotonous/YP +monotony/MS +monovalent +monoxide/SM +Monroe/M +Monro/M +Monrovia/M +Monsanto/M +monseigneur +monsieur/M +Monsignori +Monsignor/MS +monsignor/S +Mon/SM +monsoonal +monsoon/MS +monster/SM +monstrance/ASM +monstrosity/SM +monstrousness/M +monstrous/YP +montage/SDMG +Montague/M +Montaigne/M +Montana/M +Montanan/MS +Montcalm/M +Montclair/M +Monte/M +Montenegrin +Montenegro/M +Monterey/M +Monterrey/M +Montesquieu/M +Montessori/M +Monteverdi/M +Montevideo/M +Montezuma +Montgomery/M +monthly/S +month/MY +months +Monticello/M +Monti/M +Mont/M +Montmartre/M +Montoya/M +Montpelier/M +Montrachet/M +Montreal/M +Montserrat/M +Monty/M +monumentality/M +monumental/Y +monument/DMSG +mooch/ZSRDG +moodily +moodiness/MS +mood/MS +Moody/M +moody/PTR +Moog +moo/GSD +moonbeam/SM +Mooney/M +moon/GDMS +moonless +moonlight/GZDRMS +moonlighting/M +moonlit +Moon/M +moonscape/MS +moonshiner/M +moonshine/SRZM +moonshot/MS +moonstone/SM +moonstruck +moonwalk/SDG +Moore/M +moor/GDMJS +mooring/M +Moorish +moorland/MS +Moor/MS +moose/M +moot/RDGS +moped/MS +moper/M +mope/S +mopey +mopier +mopiest +mopish +mopped +moppet/MS +mopping +mop/SZGMDR +moraine/MS +morale/MS +Morales/M +moralistic +moralistically +moralist/MS +morality/UMS +moralization/CS +moralize/CGDRSZ +moralled +moraller +moralling +moral/SMY +Mora/M +Moran/M +morass/SM +moratorium/SM +Moravia/M +Moravian +moray/SM +morbidity/SM +morbidness/S +morbid/YP +mordancy/MS +mordant/GDYS +Mordecai/M +Mord/M +Mordred/M +Mordy/M +more/DSN +Moreen/M +Morehouse/M +Moreland/M +morel/SM +More/M +Morena/M +Moreno/M +moreover +Morey/M +Morgana/M +Morganica/M +Morgan/MS +Morganne/M +morgen/M +Morgen/M +morgue/SM +Morgun/M +Moria/M +Moriarty/M +moribundity/M +moribund/Y +Morie/M +Morin/M +morion/M +Morison/M +Morissa/M +Morita/M +Moritz/M +Morlee/M +Morley/M +Morly/M +Mormonism/MS +Mormon/SM +Morna/M +morning/MY +morn/SGJDM +Moroccan/S +Morocco/M +morocco/SM +Moro/M +moronic +moronically +Moroni/M +moron/SM +moroseness/MS +morose/YP +morpheme/DSMG +morphemic/S +Morpheus/M +morph/GDJ +morphia/S +morphine/MS +morphism/MS +morphologic +morphological/Y +morphology/MS +morphophonemic/S +morphophonemics/M +morphs +Morrie/M +morris +Morris/M +Morrison/M +Morristown/M +Morrow/M +morrow/MS +Morry/M +morsel/GMDS +Morse/M +mortality/SM +mortal/SY +mortarboard/SM +mortar/GSDM +Morten/M +mortgageable +mortgagee/SM +mortgage/MGDS +mortgagor/SM +mortice's +mortician/SM +Mortie/M +mortification/M +mortified/Y +mortifier/M +mortify/DRSXGN +Mortimer/M +mortise/MGSD +Mort/MN +Morton/M +mortuary/MS +Morty/M +Mosaic +mosaicked +mosaicking +mosaic/MS +Moscone/M +Moscow/M +Moseley/M +Moselle/M +Mose/MSR +Moser/M +mosey/SGD +Moshe/M +Moslem's +Mosley/M +mosque/SM +mosquitoes +mosquito/M +mos/S +mossback/MS +Mossberg/M +Moss/M +moss/SDMG +mossy/SRT +most/SY +Mosul/M +mote/ASCNK +motel/MS +mote's +motet/SM +mothball/DMGS +motherboard/MS +motherfucker/MS! +motherfucking/! +motherhood/SM +mothering/M +motherland/SM +motherless +motherliness/MS +motherly/P +mother/RDYMZG +moths +moth/ZMR +motif/MS +motile/S +motility/MS +motional/K +motioner/M +motion/GRDMS +motionlessness/S +motionless/YP +motion's/ACK +motions/K +motivated/U +motivate/XDSNGV +motivational/Y +motivation/M +motivator/S +motiveless +motive/MGSD +motley/S +motlier +motliest +mot/MSV +motocross/SM +motorbike/SDGM +motorboat/MS +motorcade/MSDG +motorcar/MS +motorcycle/GMDS +motorcyclist/SM +motor/DMSG +motoring/M +motorist/SM +motorization/SM +motorize/DSG +motorized/U +motorman/M +motormen +motormouth +motormouths +Motorola/M +motorway/SM +Motown/M +mottle/GSRD +mottler/M +Mott/M +mottoes +motto/M +moue/DSMG +moulder/DSG +moult/GSD +mound/GMDS +mountable +mountaineering/M +mountaineer/JMDSG +mountainousness/M +mountainous/PY +mountainside/MS +mountain/SM +mountaintop/SM +Mountbatten/M +mountebank/SGMD +mounted/U +mount/EGACD +mounter/SM +mounties +Mountie/SM +mounting/MS +Mount/M +mounts/AE +mourner/M +mournfuller +mournfullest +mournfulness/S +mournful/YP +mourning/M +mourn/ZGSJRD +mouser/M +mouse/SRDGMZ +mousetrapped +mousetrapping +mousetrap/SM +mousiness/MS +mousing/M +mousse/MGSD +Moussorgsky/M +mousy/PRT +Mouthe/M +mouthful/MS +mouthiness/SM +mouth/MSRDG +mouthorgan +mouthpiece/SM +mouths +mouthwash/SM +mouthwatering +mouthy/PTR +Mouton/M +mouton/SM +movable/ASP +movableness/AM +move/ARSDGZB +moved/U +movement/SM +mover/AM +moviegoer/S +movie/SM +moving/YS +mower/M +Mowgli/M +mowing/M +mow/SDRZG +moxie/MS +Moyer/M +Moyna/M +Moyra/M +Mozambican/S +Mozambique/M +Mozart/M +Mozelle/M +Mozes/M +Mozilla/M +mozzarella/MS +mp +MP +mpg +mph +MPH +MRI +Mr/M +Mrs +ms +M's +MS +MSG +Msgr/M +m's/K +Ms/S +MST +MSW +mt +MT +mtg +mtge +Mt/M +MTS +MTV +Muawiya/M +Mubarak/M +muchness/M +much/SP +mucilage/MS +mucilaginous +mucker/M +muck/GRDMS +muckraker/M +muckrake/ZMDRSG +mucky/RT +mucosa/M +mucous +mucus/SM +mudded +muddily +muddiness/SM +mudding +muddle/GRSDZ +muddleheaded/P +muddlehead/SMD +muddler/M +muddy/TPGRSD +mudflat/S +mudguard/SM +mudlarks +mud/MS +mudroom/S +mudslide/S +mudslinger/M +mudslinging/M +mudsling/JRGZ +Mueller/M +Muenster +muenster/MS +muesli/M +muezzin/MS +muff/GDMS +Muffin/M +muffin/SM +muffler/M +muffle/ZRSDG +Mufi/M +Mufinella/M +mufti/MS +Mugabe/M +mugged +mugger/SM +mugginess/S +mugging/S +muggy/RPT +mugshot/S +mug/SM +mugwump/MS +Muhammadanism/S +Muhammadan/SM +Muhammad/M +Muire/M +Muir/M +Mukden/M +mukluk/SM +mulattoes +mulatto/M +mulberry/MS +mulch/GMSD +mulct/SDG +Mulder/M +mule/MGDS +muleskinner/S +muleteer/MS +mulishness/MS +mulish/YP +mullah/M +mullahs +mullein/MS +Mullen/M +muller/M +Muller/M +mullet/MS +Mulligan/M +mulligan/SM +mulligatawny/SM +Mullikan/M +Mullins +mullion/MDSG +mull/RDSG +Multan/M +multi +Multibus/M +multicellular +multichannel/M +multicollinearity/M +multicolor/SDM +multicolumn +multicomponent +multicomputer/MS +Multics/M +MULTICS/M +multicultural +multiculturalism/S +multidimensional +multidimensionality +multidisciplinary +multifaceted +multifamily +multifariousness/SM +multifarious/YP +multifigure +multiform +multifunction/D +multilateral/Y +multilayer +multilevel/D +multilingual +multilingualism/S +multimedia/S +multimegaton/M +multimeter/M +multimillionaire/SM +multinational/S +multinomial/M +multiphase +multiple/SM +multiplet/SM +multiplex/GZMSRD +multiplexor's +multipliable +multiplicand/SM +multiplication/M +multiplicative/YS +multiplicity/MS +multiplier/M +multiply/ZNSRDXG +multiprocess/G +multiprocessor/MS +multiprogram +multiprogrammed +multiprogramming/MS +multipurpose +multiracial +multistage +multistory/S +multisyllabic +multitasking/S +multitude/MS +multitudinousness/M +multitudinous/YP +multiuser +multivalent +multivalued +multivariate +multiversity/M +multivitamin/S +mu/M +mumbler/M +mumbletypeg/S +mumble/ZJGRSD +Mumford/M +mummed +mummer/SM +mummery/MS +mummification/M +mummify/XSDGN +mumming +mum/MS +mummy/GSDM +mumps/M +muncher/M +Mnchhausen/M +munchies +Munch/M +munch/ZRSDG +Muncie/M +mundane/YSP +Mundt/M +munge/JGZSRD +Munich/M +municipality/SM +municipal/YS +munificence/MS +munificent/Y +munition/SDG +Munmro/M +Munoz/M +Munroe/M +Munro/M +mun/S +Munsey/M +Munson/M +Munster/MS +Muong/M +muon/M +Muppet/M +muralist/SM +mural/SM +Murasaki/M +Murat/M +Murchison/M +Murcia/M +murderer/M +murderess/S +murder/GZRDMS +murderousness/M +murderous/YP +Murdoch/M +Murdock/M +Mureil/M +Murial/M +muriatic +Murielle/M +Muriel/M +Murillo/M +murkily +murkiness/S +murk/TRMS +murky/RPT +Murmansk/M +murmurer/M +murmuring/U +murmurous +murmur/RDMGZSJ +Murphy/M +murrain/SM +Murray/M +Murrow/M +Murrumbidgee/M +Murry/M +Murvyn/M +muscatel/MS +Muscat/M +muscat/SM +musclebound +muscle/SDMG +Muscovite/M +muscovite/MS +Muscovy/M +muscularity/SM +muscular/Y +musculature/SM +muse +Muse/M +muser/M +musette/SM +museum/MS +mus/GJDSR +musher/M +mushiness/MS +mush/MSRDG +mushroom/DMSG +mushy/PTR +Musial/M +musicale/SM +musicality/SM +musicals +musical/YU +musician/MYS +musicianship/MS +musicked +musicking +musicological +musicologist/MS +musicology/MS +music/SM +musing/Y +Muskegon/M +muskeg/SM +muskellunge/SM +musketeer/MS +musketry/MS +musket/SM +musk/GDMS +muskie/M +muskiness/MS +muskmelon/MS +muskox/N +muskrat/MS +musky/RSPT +Muslim/MS +muslin/MS +mussel/MS +Mussolini/MS +Mussorgsky/M +muss/SDG +mussy/RT +mustache/DSM +mustachio/MDS +mustang/MS +mustard/MS +muster/GD +mustily +mustiness/MS +mustn't +must/RDGZS +must've +musty/RPT +mutability/SM +mutableness/M +mutable/P +mutably +mutagen/SM +mutant/MS +mutate/XVNGSD +mutational/Y +mutation/M +mutator/S +muted/Y +muteness/S +mute/PDSRBYTG +mutilate/XDSNG +mutilation/M +mutilator/MS +mutineer/SMDG +mutinous/Y +mutiny/MGSD +Mutsuhito/M +mutterer/M +mutter/GZRDJ +muttonchops +mutton/SM +mutt/ZSMR +mutuality/S +mutual/SY +muumuu/MS +muzak +Muzak/SM +Muzo/M +muzzled/U +muzzle/MGRSD +muzzler/M +MVP +MW +Myanmar +Mycah/M +Myca/M +Mycenaean +Mycenae/M +Mychal/M +mycologist/MS +mycology/MS +myelitides +myelitis/M +Myer/MS +myers +mylar +Mylar/S +Myles/M +Mylo/M +My/M +myna/SM +Mynheer/M +myocardial +myocardium/M +myopia/MS +myopically +myopic/S +Myrah/M +Myra/M +Myranda/M +Myrdal/M +myriad/S +Myriam/M +Myrilla/M +Myrle/M +Myrlene/M +myrmidon/S +Myrna/M +Myron/M +myrrh/M +myrrhs +Myrta/M +Myrtia/M +Myrtice/M +Myrtie/M +Myrtle/M +myrtle/SM +Myrvyn/M +Myrwyn/M +mys +my/S +myself +Mysore/M +mysteriousness/MS +mysterious/YP +mystery/MDSG +mystical/Y +mysticism/MS +mystic/SM +mystification/M +mystifier/M +mystify/CSDGNX +mystifying/Y +mystique/MS +Myst/M +mythic +mythical/Y +myth/MS +mythographer/SM +mythography/M +mythological/Y +mythologist/MS +mythologize/CSDG +mythology/SM +myths +N +NAACP +nabbed +nabbing +Nabisco/M +nabob/SM +Nabokov/M +nab/S +nacelle/SM +nacho/S +NaCl/M +nacre/MS +nacreous +Nada/M +Nadean/M +Nadeen/M +Nader/M +Nadia/M +Nadine/M +nadir/SM +Nadiya/M +Nadya/M +Nady/M +nae/VM +Nagasaki/M +nagged +nagger/S +nagging/Y +nag/MS +Nagoya/M +Nagpur/M +Nagy/M +Nahuatl/SM +Nahum/M +naiad/SM +naifs +nailbrush/SM +nailer/M +nail/SGMRD +Naipaul/M +Nair/M +Nairobi/M +Naismith/M +naive/SRTYP +naivet/SM +naivety/MS +Nakamura/M +Nakayama/M +nakedness/MS +naked/TYRP +Nakoma/M +Nalani/M +Na/M +Namath/M +nameable/U +name/ADSG +namedrop +namedropping +named's +named/U +nameless/PY +namely +nameplate/MS +namer/SM +name's +namesake/SM +Namibia/M +Namibian/S +naming/M +Nam/M +Nanak/M +Nana/M +Nananne/M +Nancee/M +Nance/M +Nancey/M +Nanchang/M +Nancie/M +Nanci/M +Nancy/M +Nanete/M +Nanette/M +Nanice/M +Nani/M +Nanine/M +Nanjing +Nanking's +Nan/M +Nannette/M +Nannie/M +Nanni/M +Nanny/M +nanny/SDMG +nanometer/MS +Nanon/M +Nanook/M +nanosecond/SM +Nansen/M +Nantes/M +Nantucket/M +Naoma/M +Naomi/M +napalm/MDGS +nape/SM +Naphtali/M +naphthalene/MS +naphtha/SM +Napier/M +napkin/SM +Naples/M +napless +Nap/M +Napoleonic +napoleon/MS +Napoleon/MS +napped +napper/MS +Nappie/M +napping +Nappy/M +nappy/TRSM +nap/SM +Nara/M +Narbonne/M +narc/DGS +narcissism/MS +narcissistic +narcissist/MS +narcissus/M +Narcissus/M +narcoleptic +narcoses +narcosis/M +narcotic/SM +narcotization/S +narcotize/GSD +Nariko/M +Nari/M +nark's +Narmada/M +Narragansett/M +narrate/VGNSDX +narration/M +narrative/MYS +narratology +narrator/SM +narrowing/P +narrowness/SM +narrow/RDYTGPS +narwhal/MS +nary +nasality/MS +nasalization/MS +nasalize/GDS +nasal/YS +NASA/MS +nascence/ASM +nascent/A +NASDAQ +Nash/M +Nashua/M +Nashville/M +Nassau/M +Nasser/M +nastily +nastiness/MS +nasturtium/SM +nasty/TRSP +natal +Natala/M +Natalee/M +Natale/M +Natalia/M +Natalie/M +Natalina/M +Nataline/M +natalist +natality/M +Natal/M +Natalya/M +Nata/M +Nataniel/M +Natasha/M +Natassia/M +Natchez +natch/S +Nate/XMN +Nathalia/M +Nathalie/M +Nathanael/M +Nathanial/M +Nathaniel/M +Nathanil/M +Nathan/MS +nationalism/SM +nationalistic +nationalistically +nationalist/MS +nationality/MS +nationalization/MS +nationalize/CSDG +nationalized/AU +nationalizer/SM +national/YS +nationhood/SM +nation/MS +nationwide +nativeness/M +native/PYS +Natividad/M +Nativity/M +nativity/MS +Natka/M +natl +Nat/M +NATO/SM +natter/SGD +nattily +nattiness/SM +Natty/M +natty/TRP +naturalism/MS +naturalistic +naturalist/MS +naturalization/SM +naturalized/U +naturalize/GSD +naturalness/US +natural/PUY +naturals +nature/ASDCG +nature's +naturist +Naugahyde/S +naughtily +naughtiness/SM +naught/MS +naughty/TPRS +Naur/M +Nauru/M +nausea/SM +nauseate/DSG +nauseating/Y +nauseousness/SM +nauseous/P +nautical/Y +nautilus/MS +Navaho's +Navajoes +Navajo/S +naval/Y +Navarro/M +navel/MS +nave/SM +navigability/SM +navigableness/M +navigable/P +navigate/DSXNG +navigational +navigation/M +navigator/MS +Navona/M +Navratilova/M +navvy/M +Navy/S +navy/SM +nay/MS +naysayer/S +Nazarene/MS +Nazareth/M +Nazi/SM +Nazism/S +NB +NBA +NBC +Nb/M +NBS +NC +NCAA +NCC +NCO +NCR +ND +N'Djamena +Ndjamena/M +Nd/M +Ne +NE +Neala/M +Neale/M +Neall/M +Neal/M +Nealon/M +Nealson/M +Nealy/M +Neanderthal/S +neap/DGS +Neapolitan/SM +nearby +nearly/RT +nearness/MS +nearside/M +nearsightedness/S +nearsighted/YP +near/TYRDPSG +neaten/DG +neath +neatness/MS +neat/YRNTXPS +Neb/M +Nebraska/M +Nebraskan/MS +Nebr/M +Nebuchadnezzar/MS +nebulae +nebula/M +nebular +nebulousness/SM +nebulous/PY +necessaries +necessarily/U +necessary/U +necessitate/DSNGX +necessitation/M +necessitous +necessity/SM +neckband/M +neckerchief/MS +neck/GRDMJS +necking/M +necklace/DSMG +neckline/MS +necktie/MS +necrology/SM +necromancer/MS +necromancy/MS +necromantic +necrophiliac/S +necrophilia/M +necropolis/SM +necropsy/M +necroses +necrosis/M +necrotic +nectarine/SM +nectarous +nectar/SM +nectary/MS +Neda/M +Nedda/M +Neddie/M +Neddy/M +Nedi/M +Ned/M +ne +needed/U +needer/M +needful/YSP +Needham/M +neediness/MS +needlecraft/M +needle/GMZRSD +needlepoint/SM +needlessness/S +needless/YP +needlewoman/M +needlewomen +needlework/RMS +needn't +need/YRDGS +needy/TPR +Neel/M +Neely/M +ne'er +nefariousness/MS +nefarious/YP +Nefen/M +Nefertiti/M +negated/U +negater/M +negate/XRSDVNG +negation/M +negativeness/SM +negative/PDSYG +negativism/MS +negativity/MS +negator/MS +Negev/M +neglecter/M +neglectfulness/SM +neglectful/YP +neglect/SDRG +negligee/SM +negligence/MS +negligent/Y +negligibility/M +negligible +negligibly +negotiability/MS +negotiable/A +negotiant/M +negotiate/ASDXGN +negotiation/MA +negotiator/MS +Negress/MS +negritude/MS +Negritude/S +Negroes +negroid +Negroid/S +Negro/M +neg/S +Nehemiah/M +Nehru/M +neighbored/U +neighborer/M +neighborhood/SM +neighborlinesses +neighborliness/UM +neighborly/UP +neighbor/SMRDYZGJ +neigh/MDG +neighs +Neila/M +Neile/M +Neilla/M +Neille/M +Neill/M +Neil/SM +neither +Nelda/M +Nelia/M +Nelie/M +Nelle/M +Nellie/M +Nelli/M +Nell/M +Nelly/M +Nelsen/M +Nels/N +Nelson/M +nelson/MS +nematic +nematode/SM +Nembutal/M +nemeses +nemesis +Nemesis/M +neoclassical +neoclassicism/MS +neoclassic/M +neocolonialism/MS +neocortex/M +neodymium/MS +Neogene +neolithic +Neolithic/M +neologism/SM +neomycin/M +neonatal/Y +neonate/MS +neon/DMS +neophyte/MS +neoplasm/SM +neoplastic +neoprene/SM +Nepalese +Nepali/MS +Nepal/M +nepenthe/MS +nephew/MS +nephrite/SM +nephritic +nephritides +nephritis/M +nepotism/MS +nepotist/S +Neptune/M +neptunium/MS +nerd/S +nerdy/RT +Nereid/M +Nerf/M +Nerissa/M +Nerita/M +Nero/M +Neron/M +Nerta/M +Nerte/M +Nertie/M +Nerti/M +Nert/M +Nerty/M +Neruda/M +nervelessness/SM +nerveless/YP +nerve's +nerve/UGSD +nerviness/SM +nerving/M +nervousness/SM +nervous/PY +nervy/TPR +Nessa/M +Nessie/M +Nessi/M +Nessy/M +Nesta/M +nester/M +Nester/M +Nestle/M +nestler/M +nestle/RSDG +nestling/M +Nestorius/M +Nestor/M +nest/RDGSBM +netball/M +nether +Netherlander/SM +Netherlands/M +nethermost +netherworld/S +Netscape/M +net/SM +Netta/M +Nettie/M +Netti/M +netting/M +nett/JGRDS +Nettle/M +nettle/MSDG +nettlesome +Netty/M +network/SJMDG +Netzahualcoyotl/M +Neumann/M +neuralgia/MS +neuralgic +neural/Y +neurasthenia/MS +neurasthenic/S +neuritic/S +neuritides +neuritis/M +neuroanatomy +neurobiology/M +neurological/Y +neurologist/MS +neurology/SM +neuromuscular +neuronal +neurone/S +neuron/MS +neuropathology/M +neurophysiology/M +neuropsychiatric +neuroses +neurosis/M +neurosurgeon/MS +neurosurgery/SM +neurotically +neurotic/S +neurotransmitter/S +neuter/JZGRD +neutralise's +neutralism/MS +neutralist/S +neutrality/MS +neutralization/MS +neutralized/U +neutralize/GZSRD +neutral/PYS +neutrino/MS +neutron/MS +neut/ZR +Nevada/M +Nevadan/S +Nevadian/S +Neva/M +never +nevermore +nevertheless +nevi +Nevile/M +Neville/M +Nevil/M +Nevin/SM +Nevis/M +Nev/M +Nevsa/M +Nevsky/M +nevus/M +Newark/M +newbie/S +newborn/S +Newbury/M +Newburyport/M +Newcastle/M +newcomer/MS +newed/A +Newell/M +newel/MS +newer/A +newfangled +newfound +newfoundland +Newfoundlander/M +Newfoundland/SRMZ +newish +newline/SM +newlywed/MS +Newman/M +newness/MS +Newport/M +news/A +newsagent/MS +newsboy/SM +newscaster/M +newscasting/M +newscast/SRMGZ +newsdealer/MS +newsed +newses +newsflash/S +newsgirl/S +newsgroup/SM +newsing +newsletter/SM +NeWS/M +newsman/M +newsmen +newspaperman/M +newspapermen +newspaper/SMGD +newspaperwoman/M +newspaperwomen +newsprint/MS +new/SPTGDRY +newsreader/MS +newsreel/SM +newsroom/S +news's +newsstand/MS +Newsweekly/M +newsweekly/S +Newsweek/MY +newswire +newswoman/M +newswomen +newsworthiness/SM +newsworthy/RPT +newsy/TRS +newt/MS +Newtonian +Newton/M +newton/SM +Nexis/M +next +nexus/SM +Neysa/M +NF +NFC +NFL +NFS +Ngaliema/M +Nguyen/M +NH +NHL +niacin/SM +Niagara/M +Niall/M +Nial/M +Niamey/M +nibbed +nibbing +nibbler/M +nibble/RSDGZ +Nibelung/M +nib/SM +Nicaean +Nicaragua/M +Nicaraguan/S +Niccolo/M +Nice/M +Nicene +niceness/MS +nicety/MS +nice/YTPR +niche/SDGM +Nicholas +Nichole/M +Nicholle/M +Nichol/MS +Nicholson/M +nichrome +nickelodeon/SM +nickel/SGMD +nicker/GD +Nickey/M +nick/GZRDMS +Nickie/M +Nicki/M +Nicklaus/M +Nick/M +nicknack's +nickname/MGDRS +nicknamer/M +Nickolai/M +Nickola/MS +Nickolaus/M +Nicko/M +Nicky/M +Nicobar/M +Nicodemus/M +Nicolai/MS +Nicola/MS +Nicolea/M +Nicole/M +Nicolette/M +Nicoli/MS +Nicolina/M +Nicoline/M +Nicolle/M +Nicol/M +Nico/M +Nicosia/M +nicotine/MS +Niebuhr/M +niece/MS +Niel/MS +Nielsen/M +Niels/N +Nielson/M +Nietzsche/M +Nieves/M +nifty/TRS +Nigel/M +Nigeria/M +Nigerian/S +Nigerien +Niger/M +niggardliness/SM +niggardly/P +niggard/SGMDY +nigger/SGDM! +niggler/M +niggle/RSDGZJ +niggling/Y +nigh/RDGT +nighs +nightcap/SM +nightclothes +nightclubbed +nightclubbing +nightclub/MS +nightdress/MS +nightfall/SM +nightgown/MS +nighthawk/MS +nightie/MS +Nightingale/M +nightingale/SM +nightlife/MS +nightlong +nightmare/MS +nightmarish/Y +nightshade/SM +nightshirt/MS +night/SMYDZ +nightspot/MS +nightstand/SM +nightstick/S +nighttime/S +nightwear/M +nighty's +NIH +nihilism/MS +nihilistic +nihilist/MS +Nijinsky/M +Nikaniki/M +Nike/M +Niki/M +Nikita/M +Nikkie/M +Nikki/M +Nikko/M +Nikolai/M +Nikola/MS +Nikolaos/M +Nikolaus/M +Nikolayev's +Nikoletta/M +Nikolia/M +Nikolos/M +Niko/MS +Nikon/M +Nile/SM +nilled +nilling +Nil/MS +nil/MYS +nilpotent +Nilsen/M +Nils/N +Nilson/M +Nilsson/M +Ni/M +nimbi +nimbleness/SM +nimble/TRP +nimbly +nimbus/DM +NIMBY +Nimitz/M +Nimrod/MS +Nina/M +nincompoop/MS +ninefold +nine/MS +ninepence/M +ninepin/S +ninepins/M +nineteen/SMH +nineteenths +ninetieths +Ninetta/M +Ninette/M +ninety/MHS +Nineveh/M +ninja/S +Ninnetta/M +Ninnette/M +ninny/SM +Ninon/M +Nintendo/M +ninth +ninths +Niobe/M +niobium/MS +nipped +nipper/DMGS +nippiness/S +nipping/Y +nipple/GMSD +Nipponese +Nippon/M +nippy/TPR +nip/S +Nirenberg/M +nirvana/MS +Nirvana/S +nisei +Nisei/MS +Nissa/M +Nissan/M +Nisse/M +Nissie/M +Nissy/M +Nita/M +niter/M +nitpick/DRSJZG +nitrate/MGNXSD +nitration/M +nitric +nitride/MGS +nitriding/M +nitrification/SM +nitrite/MS +nitrocellulose/MS +nitrogenous +nitrogen/SM +nitroglycerin/MS +nitrous +nitwit/MS +nit/ZSMR +Niven/M +nixer/M +nix/GDSR +Nixie/M +Nixon/M +NJ +Nkrumah/M +NLRB +nm +NM +no/A +NOAA +Noach/M +Noah/M +Noak/M +Noami/M +Noam/M +Nobelist/SM +nobelium/MS +Nobel/M +Nobe/M +Nobie/M +nobility/MS +Noble/M +nobleman/M +noblemen +nobleness/SM +noblesse/M +noble/TPSR +noblewoman +noblewomen +nob/MY +nobody/MS +Noby/M +nocturnal/SY +nocturne/SM +nodal/Y +nodded +nodding +noddle/MSDG +noddy/M +node/MS +NoDoz/M +nod/SM +nodular +nodule/SM +Noelani/M +Noella/M +Noelle/M +Noell/M +Noellyn/M +Noel/MS +noel/S +Noelyn/M +Noe/M +Noemi/M +noes/S +noggin/SM +nohow +noise/GMSD +noiselessness/SM +noiseless/YP +noisemaker/M +noisemake/ZGR +noisily +noisiness/MS +noisome +noisy/TPR +Nola/M +Nolana/M +Noland/M +Nolan/M +Nolie/M +Nollie/M +Noll/M +Nolly/M +No/M +nomadic +nomad/SM +Nome/M +nomenclature/MS +Nomi/M +nominalized +nominal/K +nominally +nominals +nominate/CDSAXNG +nomination/MAC +nominative/SY +nominator/CSM +nominee/MS +non +nonabrasive +nonabsorbent/S +nonacademic/S +nonacceptance/MS +nonacid/MS +nonactive +nonadaptive +nonaddictive +nonadhesive +nonadjacent +nonadjustable +nonadministrative +nonage/MS +nonagenarian/MS +nonaggression/SM +nonagricultural +Nonah/M +nonalcoholic/S +nonaligned +nonalignment/SM +nonallergic +Nona/M +nonappearance/MS +nonassignable +nonathletic +nonattendance/SM +nonautomotive +nonavailability/SM +nonbasic +nonbeliever/SM +nonbelligerent/S +nonblocking +nonbreakable +nonburnable +nonbusiness +noncaloric +noncancerous +noncarbohydrate/M +nonce/MS +nonchalance/SM +nonchalant/YP +nonchargeable +nonclerical/S +nonclinical +noncollectable +noncombatant/MS +noncombustible/S +noncommercial/S +noncommissioned +noncommittal/Y +noncom/MS +noncommunicable +noncompeting +noncompetitive +noncompliance/MS +noncomplying/S +noncomprehending +nonconducting +nonconductor/MS +nonconforming +nonconformist/SM +nonconformity/SM +nonconsecutive +nonconservative +nonconstructive +noncontagious +noncontiguous +noncontinuous +noncontributing +noncontributory +noncontroversial +nonconvertible +noncooperation/SM +noncorroding/S +noncorrosive +noncredit +noncriminal/S +noncritical +noncrystalline +noncumulative +noncustodial +noncyclic +nondairy +nondecreasing +nondeductible +nondelivery/MS +nondemocratic +nondenominational +nondepartmental +nondepreciating +nondescript/YS +nondestructive/Y +nondetachable +nondeterminacy +nondeterminate/Y +nondeterminism +nondeterministic +nondeterministically +nondisciplinary +nondisclosure/SM +nondiscrimination/SM +nondiscriminatory +nondramatic +nondrinker/SM +nondrying +nondurable +noneconomic +noneducational +noneffective/S +nonelastic +nonelectrical +nonelectric/S +nonemergency +nonempty +nonenforceable +nonentity/MS +nonequivalence/M +nonequivalent/S +none/S +nones/M +nonessential/S +nonesuch/SM +nonetheless +nonevent/MS +nonexchangeable +nonexclusive +nonexempt +nonexistence/MS +nonexistent +nonexplosive/S +nonextensible +nonfactual +nonfading +nonfat +nonfatal +nonfattening +nonferrous +nonfictional +nonfiction/SM +nonflammable +nonflowering +nonfluctuating +nonflying +nonfood/M +nonfreezing +nonfunctional +nongovernmental +nongranular +nonhazardous +nonhereditary +nonhuman +nonidentical +Nonie/M +Noni/M +noninclusive +nonindependent +nonindustrial +noninfectious +noninflammatory +noninflationary +noninflected +nonintellectual/S +noninteracting +noninterchangeable +noninterference/MS +nonintervention/SM +nonintoxicating +nonintuitive +noninvasive +nonionic +nonirritating +nonjudgmental +nonjudicial +nonlegal +nonlethal +nonlinearity/MS +nonlinear/Y +nonlinguistic +nonliterary +nonliving +nonlocal +nonmagical +nonmagnetic +nonmalignant +nonmember/SM +nonmetallic +nonmetal/MS +nonmigratory +nonmilitant/S +nonmilitary +Nonnah/M +Nonna/M +nonnarcotic/S +nonnative/S +nonnegative +nonnegotiable +nonnuclear +nonnumerical/S +nonobjective +nonobligatory +nonobservance/MS +nonobservant +nonoccupational +nonoccurence +nonofficial +nonogenarian +nonoperational +nonoperative +nonorthogonal +nonorthogonality +nonparallel/S +nonparametric +nonpareil/SM +nonparticipant/SM +nonparticipating +nonpartisan/S +nonpaying +nonpayment/SM +nonperformance/SM +nonperforming +nonperishable/S +nonperson/S +nonperturbing +nonphysical/Y +nonplus/S +nonplussed +nonplussing +nonpoisonous +nonpolitical +nonpolluting +nonporous +nonpracticing +nonprejudicial +nonprescription +nonprocedural/Y +nonproductive +nonprofessional/S +nonprofit/SB +nonprogrammable +nonprogrammer +nonproliferation/SM +nonpublic +nonpunishable +nonracial +nonradioactive +nonrandom +nonreactive +nonreciprocal/S +nonreciprocating +nonrecognition/SM +nonrecoverable +nonrecurring +nonredeemable +nonreducing +nonrefillable +nonrefundable +nonreligious +nonrenewable +nonrepresentational +nonresidential +nonresident/SM +nonresidual +nonresistance/SM +nonresistant/S +nonrespondent/S +nonresponse +nonrestrictive +nonreturnable/S +nonrhythmic +nonrigid +nonsalaried +nonscheduled +nonscientific +nonscoring +nonseasonal +nonsectarian +nonsecular +nonsegregated +nonsense/MS +nonsensicalness/M +nonsensical/PY +nonsensitive +nonsexist +nonsexual +nonsingular +nonskid +nonslip +nonsmoker/SM +nonsmoking +nonsocial +nonspeaking +nonspecialist/MS +nonspecializing +nonspecific +nonspiritual/S +nonstaining +nonstandard +nonstarter/SM +nonstick +nonstop +nonstrategic +nonstriking +nonstructural +nonsuccessive +nonsupervisory +nonsupport/GS +nonsurgical +nonsustaining +nonsympathizer/M +nontarnishable +nontaxable/S +nontechnical/Y +nontenured +nonterminal/MS +nonterminating +nontermination/M +nontheatrical +nonthinking/S +nonthreatening +nontoxic +nontraditional +nontransferable +nontransparent +nontrivial +nontropical +nonuniform +nonunion/S +nonuser/SM +nonvenomous +nonverbal/Y +nonveteran/MS +nonviable +nonviolence/SM +nonviolent/Y +nonvirulent +nonvocal +nonvocational +nonvolatile +nonvolunteer/S +nonvoter/MS +nonvoting +nonwhite/SM +nonworking +nonyielding +nonzero +noodle/GMSD +nook/MS +noonday/MS +noon/GDMS +nooning/M +noontide/MS +noontime/MS +noose/SDGM +nope/S +NORAD/M +noradrenalin +noradrenaline/M +Norah/M +Nora/M +Norbert/M +Norberto/M +Norbie/M +Norby/M +Nordhoff/M +Nordic/S +Nordstrom/M +Norean/M +Noreen/M +Norene/M +Norfolk/M +nor/H +Norina/M +Norine/M +normalcy/MS +normality/SM +normalization/A +normalizations +normalization's +normalized/AU +normalizes/AU +normalize/SRDZGB +normal/SY +Norma/M +Normand/M +Normandy/M +Norman/SM +normativeness/M +normative/YP +Normie/M +norm/SMGD +Normy/M +Norplant +Norrie/M +Norri/SM +Norristown/M +Norry/M +Norse +Norseman/M +Norsemen +Northampton/M +northbound +northeastern +northeaster/YM +Northeast/SM +northeastward/S +northeast/ZSMR +northerly/S +norther/MY +Northerner/M +northernmost +northern/RYZS +Northfield/M +northing/M +northland +North/M +northmen +north/MRGZ +Northrop/M +Northrup/M +norths +Norths +Northumberland/M +northward/S +northwestern +northwester/YM +northwest/MRZS +Northwest/MS +northwestward/S +Norton/M +Norwalk/M +Norway/M +Norwegian/S +Norwich/M +Norw/M +nosebag/M +nosebleed/SM +nosecone/S +nosedive/DSG +nosed/V +nosegay/MS +nose/M +Nosferatu/M +nos/GDS +nosh/MSDG +nosily +nosiness/MS +nosing/M +nostalgia/SM +nostalgically +nostalgic/S +Nostradamus/M +Nostrand/M +nostril/SM +nostrum/SM +nosy/SRPMT +notability/SM +notableness/M +notable/PS +notably +notarial +notarization/S +notarize/DSG +notary/MS +notate/VGNXSD +notational/CY +notation/CMSF +notative/CF +notch/MSDG +not/DRGB +notebook/MS +note/CSDFG +notedness/M +noted/YP +notepad/S +notepaper/MS +note's +noteworthiness/SM +noteworthy/P +nothingness/SM +nothing/PS +noticeable/U +noticeably +noticeboard/S +noticed/U +notice/MSDG +notifiable +notification/M +notifier/M +notify/NGXSRDZ +notional/Y +notion/MS +notoriety/S +notoriousness/M +notorious/YP +Notre/M +Nottingham/M +notwithstanding +Nouakchott/M +nougat/MS +Noumea/M +noun/SMK +nourish/DRSGL +nourished/U +nourisher/M +nourishment/SM +nous/M +nouveau +nouvelle +novae +Novak/M +Nova/M +nova/MS +novelette/SM +Novelia/M +novelist/SM +novelization/S +novelize/GDS +Novell/SM +novella/SM +novel/SM +novelty/MS +November/SM +novena/SM +novene +Novgorod/M +novice/MS +novitiate/MS +Nov/M +Novocaine/M +Novocain/S +Novokuznetsk/M +Novosibirsk/M +NOW +nowadays +noway/S +Nowell/M +nowhere/S +nowise +now/S +noxiousness/M +noxious/PY +Noyce/M +Noyes/M +nozzle/MS +Np +NP +NRA +nroff/M +N's +NS +n's/CI +NSF +n/T +NT +nth +nuance/SDM +nubbin/SM +nubby/RT +Nubia/M +Nubian/M +nubile +nub/MS +nuclear/K +nuclease/M +nucleated/A +nucleate/DSXNG +nucleation/M +nucleic +nuclei/M +nucleoli +nucleolus/M +nucleon/MS +nucleotide/MS +nucleus/M +nuclide/M +nude/CRS +nudely +nudeness/M +nudest +nudge/GSRD +nudger/M +nudism/MS +nudist/MS +nudity/MS +nugatory +Nugent/M +nugget/SM +nuisance/MS +nuke/DSMG +Nukualofa +null/DSG +nullification/M +nullifier/M +nullify/RSDXGNZ +nullity/SM +nu/M +numbered/UA +numberer/M +numberless +numberplate/M +number/RDMGJ +numbers/A +Numbers/M +numbing/Y +numbness/MS +numb/SGZTYRDP +numbskull's +numerable/IC +numeracy/SI +numeral/YMS +numerate/SDNGX +numerates/I +numeration/M +numerator/MS +numerical/Y +numeric/S +numerological +numerologist/S +numerology/MS +numerousness/M +numerous/YP +numinous/S +numismatic/S +numismatics/M +numismatist/MS +numskull/SM +Nunavut/M +nuncio/SM +Nunez/M +Nunki/M +nun/MS +nunnery/MS +nuptial/S +Nuremberg/M +Nureyev/M +nursemaid/MS +nurser/M +nurseryman/M +nurserymen +nursery/MS +nurse/SRDJGMZ +nursling/M +nurturer/M +nurture/SRDGZM +nus +nutate/NGSD +nutation/M +nutcracker/M +nutcrack/RZ +nuthatch/SM +nutmeat/SM +nutmegged +nutmegging +nutmeg/MS +nut/MS +nutpick/MS +Nutrasweet/M +nutria/SM +nutrient/MS +nutriment/MS +nutritional/Y +nutritionist/MS +nutrition/SM +nutritiousness/MS +nutritious/PY +nutritive/Y +nutshell/MS +nutted +nuttiness/SM +nutting +nutty/TRP +nuzzle/GZRSD +NV +NW +NWT +NY +Nyasa/M +NYC +Nydia/M +Nye/M +Nyerere/M +nylon/SM +nymphet/MS +nymph/M +nympholepsy/M +nymphomaniac/S +nymphomania/MS +nymphs +Nyquist/M +NYSE +Nyssa/M +NZ +o +O +oafishness/S +oafish/PY +oaf/MS +Oahu/M +Oakland/M +Oakley/M +Oakmont/M +oak/SMN +oakum/MS +oakwood +oar/GSMD +oarlock/MS +oarsman/M +oarsmen +oarswoman +oarswomen +OAS +oases +oasis/M +oatcake/MS +oater/M +Oates/M +oath/M +oaths +oatmeal/SM +oat/SMNR +Oaxaca/M +ob +OB +Obadiah/M +Obadias/M +obbligato/S +obduracy/S +obdurateness/S +obdurate/PDSYG +Obediah/M +obedience/EMS +obedient/EY +Obed/M +obeisance/MS +obeisant/Y +obelisk/SM +Oberlin/M +Oberon/M +obese +obesity/MS +obey/EDRGS +obeyer/EM +obfuscate/SRDXGN +obfuscation/M +obfuscatory +Obidiah/M +Obie/M +obi/MDGS +obit/SMR +obituary/SM +obj +objectify/GSDXN +objectionableness/M +objectionable/U +objectionably +objection/SMB +objectiveness/MS +objective/PYS +objectivity/MS +objector/SM +object/SGVMD +objurgate/GNSDX +objurgation/M +oblate/NYPSX +oblation/M +obligate/NGSDXY +obligational +obligation/M +obligatorily +obligatory +obliged/E +obliger/M +obliges/E +oblige/SRDG +obligingness/M +obliging/PY +oblique/DSYGP +obliqueness/S +obliquity/MS +obliterate/VNGSDX +obliteration/M +obliterative/Y +oblivion/MS +obliviousness/MS +oblivious/YP +oblongness/M +oblong/SYP +obloquies +obloquy/M +Ob/MD +obnoxiousness/MS +obnoxious/YP +oboe/SM +oboist/S +obos +O'Brien/M +obs +obscene/RYT +obscenity/MS +obscurantism/MS +obscurantist/MS +obscuration +obscureness/M +obscure/YTPDSRGL +obscurity/MS +obsequies +obsequiousness/S +obsequious/YP +obsequy +observability/M +observable/SU +observably +observance/MS +observantly +observants +observant/U +observational/Y +observation/MS +observatory/MS +observed/U +observer/M +observe/ZGDSRB +observing/Y +obsess/GVDS +obsessional +obsession/MS +obsessiveness/S +obsessive/PYS +obsidian/SM +obsolesce/GSD +obsolescence/S +obsolescent/Y +obsolete/GPDSY +obsoleteness/M +obstacle/SM +obstetrical +obstetrician/SM +obstetric/S +obstetrics/M +obstinacy/SM +obstinateness/M +obstinate/PY +obstreperousness/SM +obstreperous/PY +obstructed/U +obstructer/M +obstructionism/SM +obstructionist/MS +obstruction/SM +obstructiveness/MS +obstructive/PSY +obstruct/RDVGS +obtainable/U +obtainably +obtain/LSGDRB +obtainment/S +obtrude/DSRG +obtruder/M +obtrusion/S +obtrusiveness/MSU +obtrusive/UPY +obtuseness/S +obtuse/PRTY +obverse/YS +obviate/XGNDS +obviousness/SM +obvious/YP +Oby/M +ocarina/MS +O'Casey +Occam/M +occasional/Y +occasion/MDSJG +Occidental/S +occidental/SY +occident/M +Occident/SM +occipital/Y +occlude/GSD +occlusion/MS +occlusive/S +occulter/M +occultism/SM +occult/SRDYG +occupancy/SM +occupant/MS +occupational/Y +occupation/SAM +occupied/AU +occupier/M +occupies/A +occupy/RSDZG +occur/AS +occurred/A +occurrence/SM +occurring/A +oceanfront/MS +oceangoing +Oceania/M +oceanic +ocean/MS +oceanographer/SM +oceanographic +oceanography/SM +oceanology/MS +oceanside +Oceanside/M +Oceanus/M +ocelot/SM +ocher/DMGS +Ochoa/M +o'clock +O'Clock +O'Connell/M +O'Connor/M +Oconomowoc/M +OCR +octagonal/Y +octagon/SM +octahedral +octahedron/M +octal/S +octane/MS +octant/M +octave/MS +Octavia/M +Octavian/M +Octavio/M +Octavius/M +octavo/MS +octennial +octet/SM +octile +octillion/M +Oct/M +October/MS +octogenarian/MS +octopi +octopus/SM +octoroon/M +ocular/S +oculist/SM +OD +odalisque/SM +oddball/SM +oddity/MS +oddment/MS +oddness/MS +odd/TRYSPL +Odele/M +Odelia/M +Odelinda/M +Odella/M +Odelle/M +Odell/M +O'Dell/M +ode/MDRS +Ode/MR +Oderberg/MS +Oder/M +Odessa/M +Odets/M +Odetta/M +Odette/M +Odey/M +Odie/M +Odilia/M +Odille/M +Odin/M +odiousness/MS +odious/PY +Odis/M +odium/MS +Odo/M +odometer/SM +Odom/M +O'Donnell/M +odor/DMS +odoriferous +odorless +odorous/YP +ODs +O'Dwyer/M +Ody/M +Odysseus/M +Odyssey/M +odyssey/S +OE +OED +oedipal +Oedipal/Y +Oedipus/M +OEM/M +OEMS +oenology/MS +oenophile/S +o'er +O'Er +Oersted/M +oesophagi +oeuvre/SM +Ofelia/M +Ofella/M +offal/MS +offbeat/MS +offcuts +Offenbach/M +offender/M +offend/SZGDR +offense/MSV +offensively/I +offensiveness/MSI +offensive/YSP +offerer/M +offering/M +offer/RDJGZ +offertory/SM +offhand/D +offhandedness/S +offhanded/YP +officeholder/SM +officemate/S +officer/GMD +officership/S +office/SRMZ +officialdom/SM +officialism/SM +officially/U +official/PSYM +officiant/SM +officiate/XSDNG +officiation/M +officiator/MS +officio +officiousness/MS +officious/YP +offing/M +offish +offload/GDS +offprint/GSDM +offramp +offset/SM +offsetting +offshoot/MS +offshore +offside/RS +offspring/M +offstage/S +off/SZGDRJ +offtrack +Ofilia/M +of/K +often/RT +oftentimes +oft/NRT +ofttimes +Ogbomosho/M +Ogdan/M +Ogden/M +Ogdon/M +Ogilvy/M +ogive/M +Oglethorpe/M +ogle/ZGDSR +ogreish +ogre/MS +ogress/S +oh +OH +O'Hara +O'Hare/M +O'Higgins +Ohioan/S +Ohio/M +ohmic +ohmmeter/MS +ohm/SM +oho/S +ohs +OHSA/M +oilcloth/M +oilcloths +oiler/M +oilfield/MS +oiliness/SM +oilman/M +oil/MDRSZG +oilmen +oilseed/SM +oilskin/MS +oily/TPR +oink/GDS +ointment/SM +Oise/M +OJ +Ojibwa/SM +Okamoto/M +okapi/SM +Okayama/M +okay/M +Okeechobee/M +O'Keeffe +Okefenokee +Okhotsk/M +Okinawa/M +Okinawan/S +Oklahoma/M +Oklahoman/SM +Okla/M +OK/MDG +okra/MS +OKs +Oktoberfest +Olaf/M +Olag/M +Ola/M +Olav/M +Oldenburg/M +olden/DG +Oldfield/M +oldie/MS +oldish +oldness/S +Oldsmobile/M +oldster/SM +Olduvai/M +old/XTNRPS +ol +oleaginous +oleander/SM +O'Leary/M +olefin/M +Oleg/M +Ole/MV +Olenek/M +Olenka/M +Olen/M +Olenolin/M +oleomargarine/SM +oleo/S +oles +olfactory +Olga/M +Olia/M +oligarchic +oligarchical +oligarch/M +oligarchs +oligarchy/SM +Oligocene +oligopolistic +oligopoly/MS +Olimpia/M +Olin/M +olive/MSR +Olive/MZR +Oliver/M +Olivero/M +Olivette/M +Olivetti/M +Olivia/M +Olivier/M +Olivie/RM +Oliviero/M +Oliy/M +Ollie/M +Olly/M +Olmec +Olmsted/M +Olsen/M +Olson/M +Olva/M +Olvan/M +Olwen/M +Olympe/M +Olympiad/MS +Olympian/S +Olympia/SM +Olympic/S +Olympie/M +Olympus/M +Omaha/SM +Oman/M +Omar/M +ombudsman/M +ombudsmen +Omdurman/M +omega/MS +omelet/SM +omelette's +omen/DMG +Omero/M +omicron/MS +ominousness/SM +ominous/YP +omission/MS +omit/S +omitted +omitting +omnibus/MS +omni/M +omnipotence/SM +Omnipotent +omnipotent/SY +omnipresence/MS +omnipresent/Y +omniscience/SM +omniscient/YS +omnivore/MS +omnivorousness/MS +omnivorous/PY +oms +Omsk/M +om/XN +ON +onanism/M +Onassis/M +oncer/M +once/SR +oncogene/S +oncologist/S +oncology/SM +oncoming/S +Ondrea/M +Oneal/M +Onega/M +Onegin/M +Oneida/SM +O'Neil +O'Neill +oneness/MS +one/NPMSX +oner/M +onerousness/SM +onerous/YP +oneself +onetime +oneupmanship +Onfre/M +Onfroi/M +ongoing/S +Onida/M +onion/GDM +onionskin/MS +onlooker/MS +onlooking +only/TP +Onofredo/M +Ono/M +onomatopoeia/SM +onomatopoeic +onomatopoetic +Onondaga/MS +onrush/GMS +on/RY +ons +Onsager/M +onset/SM +onsetting +onshore +onside +onslaught/MS +Ontarian/S +Ontario/M +Ont/M +onto +ontogeny/SM +ontological/Y +ontology/SM +onus/SM +onward/S +onyx/MS +oodles +ooh/GD +oohs +oolitic +Oona/M +OOo/M +oops/S +Oort/M +ooze/GDS +oozy/RT +opacity/SM +opalescence/S +opalescent/Y +Opalina/M +Opaline/M +Opal/M +opal/SM +opaque/GTPYRSD +opaqueness/SM +opcode/MS +OPEC +Opel/M +opencast +opened/AU +opener/M +openhandedness/SM +openhanded/P +openhearted +opening/M +openness/S +OpenOffice.org/M +opens/A +openwork/MS +open/YRDJGZTP +operable/I +operandi +operand/SM +operant/YS +opera/SM +operate/XNGVDS +operatically +operatic/S +operationalization/S +operationalize/D +operational/Y +operation/M +operative/IP +operatively +operativeness/MI +operatives +operator/SM +operetta/MS +ope/S +Ophelia/M +Ophelie/M +Ophiuchus/M +ophthalmic/S +ophthalmologist/SM +ophthalmology/MS +opiate/GMSD +opine/XGNSD +opinionatedness/M +opinionated/PY +opinion/M +opioid +opium/MS +opossum/SM +opp +Oppenheimer/M +opponent/MS +opportune/IY +opportunism/SM +opportunistic +opportunistically +opportunist/SM +opportunity/MS +oppose/BRSDG +opposed/U +opposer/M +oppositeness/M +opposite/SXYNP +oppositional +opposition/M +oppress/DSGV +oppression/MS +oppressiveness/MS +oppressive/YP +oppressor/MS +opprobrious/Y +opprobrium/SM +Oprah/M +ops +opt/DSG +opthalmic +opthalmologic +opthalmology +optical/Y +optician/SM +optic/S +optics/M +optima +optimality +optimal/Y +optimise's +optimism/SM +optimistic +optimistically +optimist/SM +optimization/SM +optimize/DRSZG +optimized/U +optimizer/M +optimizes/U +optimum/SM +optionality/M +optional/YS +option/GDMS +optoelectronic +optometric +optometrist/MS +optometry/SM +opulence/SM +opulent/Y +opus/SM +op/XGDN +OR +oracle/GMSD +oracular +Oralee/M +Oralia/M +Oralie/M +Oralla/M +Oralle/M +oral/YS +Ora/M +orangeade/MS +Orange/M +orange/MS +orangery/SM +orangutan/MS +Oranjestad/M +Oran/M +orate/SDGNX +oration/M +oratorical/Y +oratorio/MS +orator/MS +oratory/MS +Orazio/M +Orbadiah/M +orbicular +orbiculares +orbital/MYS +orbit/MRDGZS +orb/SMDG +orchard/SM +orchestral/Y +orchestra/MS +orchestrate/GNSDX +orchestrater's +orchestration/M +orchestrator/M +orchid/SM +ordainer/M +ordainment/MS +ordain/SGLDR +ordeal/SM +order/AESGD +ordered/U +orderer +ordering/S +orderless +orderliness/SE +orderly/PS +order's/E +ordinal/S +ordinance/MS +ordinarily +ordinariness/S +ordinary/RSPT +ordinated +ordinate/I +ordinates +ordinate's +ordinating +ordination/SM +ordnance/SM +Ordovician +ordure/MS +oregano/SM +Oreg/M +Oregonian/S +Oregon/M +Orelee/M +Orelia/M +Orelie/M +Orella/M +Orelle/M +Orel/M +Oren/M +Ore/NM +ore/NSM +Oreo +Orestes +organdie's +organdy/MS +organelle/MS +organically/I +organic/S +organismic +organism/MS +organist/MS +organizable/UMS +organizational/MYS +organization/MEAS +organize/AGZDRS +organized/UE +organizer/MA +organizes/E +organizing/E +organ/MS +organometallic +organza/SM +orgasm/GSMD +orgasmic +orgiastic +orgy/SM +Oriana/M +oriel/MS +orientable +Oriental/S +oriental/SY +orientated/A +orientate/ESDXGN +orientates/A +orientation/AMES +orienteering/M +orienter +orient/GADES +orient's +Orient/SM +orifice/MS +orig +origami/MS +originality/SM +originally +original/US +originate/VGNXSD +origination/M +originative/Y +originator/SM +origin/MS +Orin/M +Orinoco/M +oriole/SM +Orion/M +orison/SM +Oriya/M +Orizaba/M +Orkney/M +Orland/M +Orlando/M +Orlan/M +Orleans +Orlick/M +Orlon/SM +Orly/M +ormolu/SM +or/MY +ornamental/SY +ornamentation/SM +ornament/GSDM +ornateness/SM +ornate/YP +orneriness/SM +ornery/PRT +ornithological +ornithologist/SM +ornithology/MS +orographic/M +orography/M +Orono/M +orotund +orotundity/MS +orphanage/MS +orphanhood/M +orphan/SGDM +Orpheus/M +Orphic +Orran/M +Orren/M +Orrin/M +orris/SM +Orr/MN +ors +Orsa/M +Orsola/M +Orson/M +Ortega/M +Ortensia/M +orthodontia/S +orthodontic/S +orthodontics/M +orthodontist/MS +orthodoxies +orthodoxly/U +Orthodox/S +orthodoxy's +orthodox/YS +orthodoxy/U +orthogonality/M +orthogonalization/M +orthogonalized +orthogonal/Y +orthographic +orthographically +orthography/MS +orthonormal +orthopedic/S +orthopedics/M +orthopedist/SM +orthophosphate/MS +orthorhombic +Ortiz/M +Orton/M +Orval/M +Orville/M +Orv/M +Orwellian +Orwell/M +o's +Osage/SM +Osaka/M +Osbert/M +Osborne/M +Osborn/M +Osbourne/M +Osbourn/M +Oscar/SM +Osceola/M +oscillate/SDXNG +oscillation/M +oscillator/SM +oscillatory +oscilloscope/SM +osculate/XDSNG +osculation/M +Osgood/M +OSHA +Oshawa/M +O'Shea/M +Oshkosh/M +osier/MS +Osiris/M +Oslo/M +Os/M +OS/M +Osman/M +osmium/MS +Osmond/M +osmoses +osmosis/M +osmotic +Osmund/M +osprey/SM +osseous/Y +Ossie/M +ossification/M +ossify/NGSDX +ostensible +ostensibly +ostentation/MS +ostentatiousness/M +ostentatious/PY +osteoarthritides +osteoarthritis/M +osteology/M +osteopathic +osteopath/M +osteopaths +osteopathy/MS +osteoporoses +osteoporosis/M +ostracise's +ostracism/MS +ostracize/GSD +Ostrander/M +ostrich/MS +Ostrogoth/M +Ostwald/M +O'Sullivan/M +Osvaldo/M +Oswald/M +Oswell/M +OT +OTB +OTC +Otes +Otha/M +Othelia/M +Othella/M +Othello/M +otherness/M +other/SMP +otherwise +otherworldly/P +otherworld/Y +Othilia/M +Othilie/M +Otho/M +otiose +Otis/M +OTOH +Ottawa/MS +otter/DMGS +Ottilie/M +Otto/M +Ottoman +ottoman/MS +Ouagadougou/M +oubliette/SM +ouch/SDG +oughtn't +ought/SGD +Ouija/MS +ounce/MS +our/S +ourself +ourselves +ouster/M +oust/RDGZS +outage/MS +outargue/GDS +outback/MRS +outbalance/GDS +outbidding +outbid/S +outboard/S +outboast/GSD +outbound/S +outbreak/SMG +outbroke +outbroken +outbuilding/SM +outburst/MGS +outcast/GSM +outclass/SDG +outcome/SM +outcropped +outcropping/S +outcrop/SM +outcry/MSDG +outdated/P +outdid +outdistance/GSD +outdoes +outdo/G +outdone +outdoor/S +outdoorsy +outdraw/GS +outdrawn +outdrew +outermost +outerwear/M +outface/SDG +outfall/MS +outfielder/M +outfield/RMSZ +outfight/SG +outfit/MS +outfitted +outfitter/MS +outfitting +outflank/SGD +outflow/SMDG +outfought +outfox/GSD +outgeneraled +outgoes +outgo/GJ +outgoing/P +outgrew +outgrip +outgrow/GSH +outgrown +outgrowth/M +outgrowths +outguess/SDG +outhit/S +outhitting +outhouse/SM +outing/M +outlaid +outlander/M +outlandishness/MS +outlandish/PY +outland/ZR +outlast/GSD +outlawry/M +outlaw/SDMG +outlay/GSM +outlet/SM +outliers +outline/SDGM +outlive/GSD +outlook/MDGS +outlying +outmaneuver/GSD +outmatch/SDG +outmigration +outmoded +outness/M +outnumber/GDS +outpaced +outpatient/SM +outperform/DGS +out/PJZGSDR +outplacement/S +outplay/GDS +outpoint/GDS +outpost/SM +outpouring/M +outpour/MJG +outproduce/GSD +output/SM +outputted +outputting +outrace/GSD +outrage/GSDM +outrageousness/M +outrageous/YP +outran +outrank/GSD +outr +outreach/SDG +outrider/MS +outrigger/SM +outright/Y +outrunning +outrun/S +outscore/GDS +outsell/GS +outset/MS +outsetting +outshine/SG +outshone +outshout/GDS +outsider/PM +outside/ZSR +outsize/S +outskirt/SM +outsmart/SDG +outsold +outsource/SDJG +outspend/SG +outspent +outspoke +outspokenness/SM +outspoken/YP +outspread/SG +outstanding/Y +outstate/NX +outstation/M +outstay/SDG +outstretch/GSD +outstripped +outstripping +outstrip/S +outtake/S +outvote/GSD +outwardness/M +outward/SYP +outwear/SG +outweigh/GD +outweighs +outwit/S +outwitted +outwitting +outwore +outwork/SMDG +outworn +ouzo/SM +oval/MYPS +ovalness/M +ova/M +ovarian +ovary/SM +ovate/SDGNX +ovation/GMD +ovenbird/SM +oven/MS +overabundance/MS +overabundant +overachieve/SRDGZ +overact/DGVS +overage/S +overaggressive +overallocation +overall/SM +overambitious +overanxious +overarching +overarm/GSD +overate +overattentive +overawe/GDS +overbalance/DSG +overbear/GS +overbearingness/M +overbearing/YP +overbidding +overbid/S +overbite/MS +overblown +overboard +overbold +overbook/SDG +overbore +overborne +overbought +overbuild/GS +overbuilt +overburdening/Y +overburden/SDG +overbuy/GS +overcame +overcapacity/M +overcapitalize/DSG +overcareful +overcast/GS +overcasting/M +overcautious +overcerebral +overcharge/DSG +overcloud/DSG +overcoating/M +overcoat/SMG +overcomer/M +overcome/RSG +overcommitment/S +overcompensate/XGNDS +overcompensation/M +overcomplexity/M +overcomplicated +overconfidence/MS +overconfident/Y +overconscientious +overconsumption/M +overcook/SDG +overcooled +overcorrection +overcritical +overcrowd/DGS +overcurious +overdecorate/SDG +overdependent +overdetermined +overdevelop/SDG +overdid +overdoes +overdo/G +overdone +overdose/DSMG +overdraft/SM +overdraw/GS +overdrawn +overdress/GDS +overdrew +overdrive/GSM +overdriven +overdrove +overdubbed +overdubbing +overdub/S +overdue +overeagerness/M +overeager/PY +overeater/M +overeat/GNRS +overeducated +overemotional +overemphases +overemphasis/M +overemphasize/GZDSR +overenthusiastic +overestimate/DSXGN +overestimation/M +overexcite/DSG +overexercise/SDG +overexert/GDS +overexertion/SM +overexploitation +overexploited +overexpose/GDS +overexposure/SM +overextend/DSG +overextension +overfall/M +overfed +overfeed/GS +overfill/GDS +overfishing +overflew +overflight/SM +overflow/DGS +overflown +overfly/GS +overfond +overfull +overgeneralize/GDS +overgenerous +overgraze/SDG +overgrew +overground +overgrow/GSH +overgrown +overgrowth/M +overgrowths +overhand/DGS +overhang/GS +overhasty +overhaul/GRDJS +overhead/S +overheard +overhearer/M +overhear/SRG +overheat/SGD +overhung +overincredulous +overindulgence/SM +overindulgent +overindulge/SDG +overinflated +overjoy/SGD +overkill/SDMG +overladed +overladen +overlaid +overlain +overland/S +overlap/MS +overlapped +overlapping +overlarge +overlay/GS +overleaf +overlie +overload/SDG +overlong +overlook/DSG +overlord/DMSG +overloud +overly/GRS +overmanning +overmaster/GSD +overmatching +overmodest +overmuch/S +overnice +overnight/SDRGZ +overoptimism/SM +overoptimistic +overpaid +overparticular +overpass/GMSD +overpay/LSG +overpayment/M +overplay/SGD +overpopulate/DSNGX +overpopulation/M +overpopulous +overpower/GSD +overpowering/Y +overpraise/DSG +overprecise +overpressure +overprice/SDG +overprint/DGS +overproduce/SDG +overproduction/S +overprotect/GVDS +overprotection/M +overqualified +overran +overrate/DSG +overreach/DSRG +overreaction/SM +overreact/SGD +overred +overrefined +overrepresented +overridden +overrider/M +override/RSG +overripe +overrode +overrule/GDS +overrunning +overrun/S +oversample/DG +oversaturate +oversaw +oversea/S +overseeing +overseen +overseer/M +oversee/ZRS +oversell/SG +oversensitiveness/S +oversensitive/P +oversensitivity +oversexed +overshadow/GSD +overshoe/SM +overshoot/SG +overshot/S +oversight/SM +oversimple +oversimplification/M +oversimplify/GXNDS +oversize/GS +oversleep/GS +overslept +oversoftness/M +oversoft/P +oversold +overspecialization/MS +overspecialize/GSD +overspend/SG +overspent +overspill/DMSG +overspread/SG +overstaffed +overstatement/SM +overstate/SDLG +overstay/GSD +overstepped +overstepping +overstep/S +overstimulate/DSG +overstock/SGD +overstraining +overstressed +overstretch/D +overstrict +overstrike/GS +overstrung +overstuffed +oversubscribe/SDG +oversubtle +oversupply/MDSG +oversuspicious +overtaken +overtake/RSZG +overtax/DSG +overthrew +overthrow/GS +overthrown +overtightened +overtime/MGDS +overtire/DSG +overtone/MS +overtook +overt/PY +overture/DSMG +overturn/SDG +overuse/DSG +overvalue/GSD +overview/MS +overweening +overweight/GSD +overwhelm/GDS +overwhelming/Y +overwinter/SDG +overwork/GSD +overwrap +overwrite/SG +overwritten +overwrote +overwrought +over/YGS +overzealousness/M +overzealous/P +Ovid/M +oviduct/SM +oviform +oviparous +ovoid/S +ovular +ovulate/GNXDS +ovulatory +ovule/MS +ovum/MS +ow/DYG +Owen/MS +owe/S +owlet/SM +owl/GSMDR +owlishness/M +owlish/PY +owned/U +own/EGDS +ownership/MS +owner/SM +oxalate/M +oxalic +oxaloacetic +oxblood/S +oxbow/SM +oxcart/MS +oxen/M +oxford/MS +Oxford/MS +oxidant/SM +oxidate/NVX +oxidation/M +oxidative/Y +oxide/SM +oxidization/MS +oxidized/U +oxidize/JDRSGZ +oxidizer/M +oxidizes/A +ox/MNS +Oxnard +Oxonian +oxtail/M +Oxus/M +oxyacetylene/MS +oxygenate/XSDMGN +oxygenation/M +oxygen/MS +oxyhydroxides +oxymora +oxymoron/M +oyster/GSDM +oystering/M +oz +Ozark/SM +Oz/M +ozone/SM +Ozymandias/M +Ozzie/M +Ozzy/M +P +PA +Pablo/M +Pablum/M +pablum/S +Pabst/M +pabulum/SM +PAC +pace/DRSMZG +Pace/M +pacemaker/SM +pacer/M +pacesetter/MS +pacesetting +Pacheco/M +pachyderm/MS +pachysandra/MS +pacific +pacifically +pacification/M +Pacific/M +pacifier/M +pacifism/MS +pacifistic +pacifist/MS +pacify/NRSDGXZ +package/ARSDG +packaged/U +packager/S +package's +packages/U +packaging/SM +Packard/SM +packed/AU +packer/MUS +packet/MSDG +pack/GZSJDRMB +packhorse/M +packinghouse/S +packing/M +packsaddle/SM +Packston/M +packs/UA +Packwood/M +Paco/M +Pacorro/M +pact/SM +Padang/M +padded/U +Paddie/M +padding/SM +paddle/MZGRSD +paddler/M +paddock/SDMG +Paddy/M +paddy/SM +Padget/M +Padgett/M +Padilla/M +padlock/SGDM +pad/MS +Padraic/M +Padraig/M +padre/MS +Padrewski/M +Padriac/M +paean/MS +paediatrician/MS +paediatrics/M +paedophilia's +paella/SM +paeony/M +Paganini/M +paganism/MS +pagan/SM +pageantry/SM +pageant/SM +pageboy/SM +paged/U +pageful +Page/M +page/MZGDRS +pager/M +paginate/DSNGX +Paglia/M +pagoda/MS +Pahlavi/M +paid/AU +Paige/M +pailful/SM +Pail/M +pail/SM +Paine/M +painfuller +painfullest +painfulness/MS +painful/YP +pain/GSDM +painkiller/MS +painkilling +painlessness/S +painless/YP +painstaking/SY +paint/ADRZGS +paintbox/M +paintbrush/SM +painted/U +painterly/P +painter/YM +painting/SM +paint's +paintwork +paired/UA +pair/JSDMG +pairs/A +pairwise +paisley/MS +pajama/MDS +Pakistani/S +Pakistan/M +palace/MS +paladin/MS +palaeolithic +palaeontologists +palaeontology/M +palanquin/MS +palatability/M +palatableness/M +palatable/P +palatalization/MS +palatalize/SDG +palatal/YS +palate/BMS +palatial/Y +palatinate/SM +Palatine +palatine/S +palaver/GSDM +paleface/SM +Palembang/M +paleness/S +Paleocene +Paleogene +paleographer/SM +paleography/SM +paleolithic +Paleolithic +paleontologist/S +paleontology/MS +Paleozoic +Palermo/M +pale/SPY +Palestine/M +Palestinian/S +Palestrina/M +palette/MS +Paley/M +palfrey/MS +palimony/S +palimpsest/MS +palindrome/MS +palindromic +paling/M +palisade/MGSD +Palisades/M +palish +Palladio/M +palladium/SM +pallbearer/SM +palletized +pallet/SMGD +pall/GSMD +palliate/SDVNGX +palliation/M +palliative/SY +pallidness/MS +pallid/PY +Pall/M +pallor/MS +palmate +palmer/M +Palmer/M +Palmerston/M +palmetto/MS +palm/GSMDR +palmist/MS +palmistry/MS +Palm/MR +Palmolive/M +palmtop/S +Palmyra/M +palmy/RT +Palo/M +Paloma/M +Palomar/M +palomino/MS +palpable +palpably +palpate/SDNGX +palpation/M +palpitate/NGXSD +palpitation/M +pal/SJMDRYTG +palsy/GSDM +paltriness/SM +paltry/TRP +paludal +Pa/M +Pamela/M +Pamelina/M +Pamella/M +pa/MH +Pamirs +Pam/M +Pammie/M +Pammi/M +Pammy/M +pampas/M +pamperer/M +pamper/RDSG +Pampers +pamphleteer/DMSG +pamphlet/SM +panacea/MS +panache/MS +Panama/MS +Panamanian/S +panama/S +pancake/MGSD +Panchito/M +Pancho/M +panchromatic +pancreas/MS +pancreatic +panda/SM +pandemic/S +pandemonium/SM +pander/ZGRDS +Pandora/M +panegyric/SM +pane/KMS +paneling/M +panelist/MS +panelization +panelized +panel/JSGDM +Pangaea/M +pang/GDMS +pangolin/M +panhandle/RSDGMZ +panicked +panicking +panicky/RT +panic/SM +panier's +panjandrum/M +Pankhurst/M +Pan/M +Panmunjom/M +panned +pannier/SM +panning +panoply/MSD +panorama/MS +panoramic +panpipes +Pansie/M +pan/SMD +Pansy/M +pansy/SM +Pantagruel/M +Pantaloon/M +pantaloons +pant/GDS +pantheism/MS +pantheistic +pantheist/S +pantheon/MS +panther/SM +pantie/SM +pantiled +pantograph/M +pantomime/SDGM +pantomimic +pantomimist/SM +pantry/SM +pantsuit/SM +pantyhose +pantyliner +pantywaist/SM +Panza/M +Paola/M +Paoli/M +Paolina/M +Paolo/M +papacy/SM +Papagena/M +Papageno/M +papal/Y +papa/MS +paparazzi +papaw/SM +papaya/MS +paperback/GDMS +paperboard/MS +paperboy/SM +paperer/M +papergirl/SM +paper/GJMRDZ +paperhanger/SM +paperhanging/SM +paperiness/M +paperless +paperweight/MS +paperwork/SM +papery/P +papillae +papilla/M +papillary +papist/MS +papoose/SM +Pappas/M +papped +papping +pappy/RST +paprika/MS +pap/SZMNR +papyri +papyrus/M +Paquito/M +parable/MGSD +parabola/MS +parabolic +paraboloidal/M +paraboloid/MS +Paracelsus/M +paracetamol/M +parachuter/M +parachute/RSDMG +parachutist/MS +Paraclete/M +parader/M +parade/RSDMZG +paradigmatic +paradigm/SM +paradisaic +paradisaical +Paradise/M +paradise/MS +paradoxic +paradoxicalness/M +paradoxical/YP +paradox/MS +paraffin/GSMD +paragon/SGDM +paragrapher/M +paragraph/MRDG +paragraphs +Paraguayan/S +Paraguay/M +parakeet/MS +paralegal/S +paralinguistic +parallax/SM +parallel/DSG +paralleled/U +parallelepiped/MS +parallelism/SM +parallelization/MS +parallelize/ZGDSR +parallelogram/MS +paralysis/M +paralytically +paralytic/S +paralyzedly/S +paralyzed/Y +paralyzer/M +paralyze/ZGDRS +paralyzingly/S +paralyzing/Y +paramagnetic +paramagnet/M +Paramaribo/M +paramecia +paramecium/M +paramedical/S +paramedic/MS +parameterization/SM +parameterize/BSDG +parameterized/U +parameterless +parameter/SM +parametric +parametrically +parametrization +parametrize/DS +paramilitary/S +paramount/S +paramour/MS +para/MS +Paramus/M +Paran +paranoiac/S +paranoia/SM +paranoid/S +paranormal/SY +parapet/SMD +paraphernalia +paraphrase/GMSRD +paraphraser/M +paraplegia/MS +paraplegic/S +paraprofessional/SM +parapsychologist/S +parapsychology/MS +paraquat/S +parasite/SM +parasitically +parasitic/S +parasitism/SM +parasitologist/M +parasitology/M +parasol/SM +parasympathetic/S +parathion/SM +parathyroid/S +paratrooper/M +paratroop/RSZ +paratyphoid/S +parboil/DSG +parceled/U +parceling/M +parcel/SGMD +Parcheesi/M +parch/GSDL +parchment/SM +PARC/M +pardonableness/M +pardonable/U +pardonably/U +pardoner/M +pardon/ZBGRDS +paregoric/SM +parentage/MS +parental/Y +parenteral +parentheses +parenthesis/M +parenthesize/GSD +parenthetic +parenthetical/Y +parenthood/MS +parent/MDGJS +pare/S +paresis/M +pares/S +Pareto/M +parfait/SM +pariah/M +pariahs +parietal/S +parimutuel/S +paring/M +parishioner/SM +parish/MS +Parisian/SM +Paris/M +parity/ESM +parka/MS +Parke/M +Parker/M +Parkersburg/M +park/GJZDRMS +Parkhouse/M +parking/M +Parkinson/M +parkish +parkland/M +parklike +Parkman +Park/RMS +parkway/MS +parlance/SM +parlay/DGS +parley/MDSG +parliamentarian/SM +parliamentary/U +parliament/MS +Parliament/MS +parlor/SM +parlous +Parmesan/S +parmigiana +Parnassus/SM +Parnell/M +parochialism/SM +parochiality +parochial/Y +parodied/U +parodist/SM +parody/SDGM +parolee/MS +parole/MSDG +paroxysmal +paroxysm/MS +parquetry/SM +parquet/SMDG +parrakeet's +parred +parricidal +parricide/MS +parring +Parrish/M +Parr/M +Parrnell/M +parrot/GMDS +parrotlike +parry/GSD +Parry/M +parse +parsec/SM +parsed/U +Parsee's +parser/M +Parsifal/M +parsimonious/Y +parsimony/SM +pars/JDSRGZ +parsley/MS +parsnip/MS +parsonage/MS +parson/MS +Parsons/M +partaken +partaker/M +partake/ZGSR +part/CDGS +parterre/MS +parter/S +parthenogeneses +parthenogenesis/M +Parthenon/M +Parthia/M +partiality/MS +partial/SY +participant/MS +participate/NGVDSX +participation/M +participator/S +participatory +participial/Y +participle/MS +particleboard/S +particle/MS +particolored +particularistic +particularity/SM +particularization/MS +particularize/GSD +particular/SY +particulate/S +parting/MS +partisanship/SM +partisan/SM +partition/AMRDGS +partitioned/U +partitioner/M +partitive/S +partizan's +partly +partner/DMGS +partnership/SM +partook +partridge/MS +part's +parturition/SM +partway +party/RSDMG +parvenu/SM +par/ZGSJBMDR +Pasadena/M +PASCAL +Pascale/M +Pascal/M +pascal/SM +paschal/S +pasha/MS +Paso/M +Pasquale/M +pas/S +passably +passage/MGSD +passageway/MS +Passaic/M +passband +passbook/MS +passel/MS +pass/M +passenger/MYS +passerby +passer/M +passersby +passim +passing/Y +passionated +passionate/EYP +passionateness/EM +passionates +passionating +passioned +passionflower/MS +passioning +passionless +passion/SEM +Passion/SM +passivated +passiveness/S +passive/SYP +passivity/S +pass/JGVBZDSR +passkey/SM +passmark +passover +Passover/MS +passport/SM +password/SDM +pasta/MS +pasteboard/SM +pasted/UA +pastel/MS +paste/MS +Pasternak/M +pastern/SM +pasteup +pasteurization/MS +pasteurized/U +pasteurizer/M +pasteurize/RSDGZ +Pasteur/M +pastiche/MS +pastille/SM +pastime/SM +pastiness/SM +pastoralization/M +pastoral/SPY +pastorate/MS +pastor/GSDM +past/PGMDRS +pastrami/MS +pastry/SM +past's/A +pasts/A +pasturage/SM +pasture/MGSRD +pasturer/M +pasty/PTRS +Patagonia/M +Patagonian/S +patch/EGRSD +patcher/EM +patchily +patchiness/S +patch's +patchwork/RMSZ +patchy/PRT +patellae +patella/MS +Patel/M +Pate/M +paten/M +Paten/M +patentee/SM +patent/ZGMRDYSB +paterfamilias/SM +pater/M +paternalism/MS +paternalist +paternalistic +paternal/Y +paternity/SM +paternoster/SM +Paterson/M +pate/SM +pathetic +pathetically +pathfinder/MS +pathless/P +path/M +pathname/SM +pathogenesis/M +pathogenic +pathogen/SM +pathologic +pathological/Y +pathologist/MS +pathology/SM +pathos/SM +paths +pathway/MS +Patience/M +patience/SM +patient/MRYTS +patient's/I +patients/I +patina/SM +patine +Patin/M +patio/MS +Pat/MN +pat/MNDRS +Patna/M +patois/M +Paton/M +patresfamilias +patriarchal +patriarchate/MS +patriarch/M +patriarchs +patriarchy/MS +Patrica/M +Patrice/M +Patricia/M +patrician/MS +patricide/MS +Patricio/M +Patrick/M +Patric/M +patrimonial +patrimony/SM +patriotically +patriotic/U +patriotism/SM +patriot/SM +patristic/S +Patrizia/M +Patrizio/M +Patrizius/M +patrolled +patrolling +patrolman/M +patrolmen +patrol/MS +patrolwoman +patrolwomen +patronage/MS +patroness/S +patronization +patronized/U +patronize/GZRSDJ +patronizer/M +patronizes/A +patronizing's/U +patronizing/YM +patronymically +patronymic/S +patron/YMS +patroon/MS +patsy/SM +Patsy/SM +patted +Patten/M +patten/MS +patterer/M +pattern/GSDM +patternless +patter/RDSGJ +Patterson/M +Pattie/M +Patti/M +patting +Pattin/M +Patton/M +Patty/M +patty/SM +paucity/SM +Paula/M +Paule/M +Pauletta/M +Paulette/M +Paulie/M +Pauli/M +Paulina/M +Pauline +Pauling/M +Paulita/M +Paul/MG +Paulo/M +Paulsen/M +Paulson/M +Paulus/M +Pauly/M +paunch/GMSD +paunchiness/M +paunchy/RTP +pauperism/SM +pauperize/SDG +pauper/SGDM +pause/DSG +Pavarotti +paved/UA +pave/GDRSJL +Pavel/M +pavement/SGDM +paver/M +paves/A +Pavia/M +pavilion/SMDG +paving/A +paving's +Pavla/M +Pavlova/MS +Pavlovian +Pavlov/M +pawl/SM +paw/MDSG +pawnbroker/SM +pawnbroking/S +Pawnee/SM +pawner/M +pawn/GSDRM +pawnshop/MS +pawpaw's +Pawtucket/M +paxes +Paxon/M +Paxton/M +payable/S +pay/AGSLB +payback/S +paycheck/SM +payday/MS +payed +payee/SM +payer/SM +payload/SM +paymaster/SM +payment/ASM +Payne/SM +payoff/MS +payola/MS +payout/S +payroll/MS +payslip/S +Payson/M +Payton/M +Paz/M +Pb/M +PBS +PBX +PCB +PC/M +PCP +PCs +pct +pd +PD +Pd/M +PDP +PDQ +PDT +PE +Peabody/M +peaceableness/M +peaceable/P +peaceably +peacefuller +peacefullest +peacefulness/S +peaceful/PY +peace/GMDS +peacekeeping/S +Peace/M +peacemaker/MS +peacemaking/MS +peacetime/MS +peach/GSDM +Peachtree/M +peachy/RT +peacock/SGMD +Peadar/M +peafowl/SM +peahen/MS +peaked/P +peakiness/M +peak/SGDM +peaky/P +pealed/A +Peale/M +peal/MDSG +peals/A +pea/MS +peanut/SM +Pearce/M +Pearla/M +Pearle/M +pearler/M +Pearlie/M +Pearline/M +Pearl/M +pearl/SGRDM +pearly/TRS +Pearson/M +pear/SYM +peartrees +Peary/M +peasanthood +peasantry/SM +peasant/SM +peashooter/MS +peats/A +peat/SM +peaty/TR +pebble/MGSD +pebbling/M +pebbly/TR +Pebrook/M +pecan/SM +peccadilloes +peccadillo/M +peccary/MS +Pechora/M +pecker/M +peck/GZSDRM +Peckinpah/M +Peck/M +Pecos/M +pectic +pectin/SM +pectoral/S +peculate/NGDSX +peculator/S +peculiarity/MS +peculiar/SY +pecuniary +pedagogical/Y +pedagogic/S +pedagogics/M +pedagogue/SDGM +pedagogy/MS +pedal/SGRDM +pedantic +pedantically +pedantry/MS +pedant/SM +peddler/M +peddle/ZGRSD +pederast/SM +pederasty/SM +Peder/M +pedestal/GDMS +pedestrianization +pedestrianize/GSD +pedestrian/MS +pediatrician/SM +pediatric/S +pedicab/SM +pedicure/DSMG +pedicurist/SM +pedigree/DSM +pediment/DMS +pedlar's +pedometer/MS +pedophile/S +pedophilia +Pedro/M +peduncle/MS +peeing +peekaboo/SM +peek/GSD +peeler/M +peeling/M +Peel/M +peel/SJGZDR +peen/GSDM +peeper/M +peephole/SM +peep/SGZDR +peepshow/MS +peepy +peerage/MS +peer/DMG +peeress/MS +peerlessness/M +peerless/PY +peeve/GZMDS +peevers/M +peevishness/SM +peevish/YP +peewee/S +pee/ZDRS +Pegasus/MS +pegboard/SM +Pegeen/M +pegged +Peggie/M +Peggi/M +pegging +Peggy/M +Peg/M +peg/MS +peignoir/SM +Pei/M +Peiping/M +Peirce/M +pejoration/SM +pejorative/SY +peke/MS +Pekinese's +pekingese +Pekingese/SM +Peking/SM +pekoe/SM +pelagic +Pelee/M +Pele/M +pelf/SM +Pelham/M +pelican/SM +pellagra/SM +pellet/SGMD +pellucid +Peloponnese/M +pelter/M +pelt/GSDR +pelvic/S +pelvis/SM +Pembroke/M +pemmican/SM +penalization/SM +penalized/U +penalize/SDG +penalty/MS +penal/Y +Pena/M +penance/SDMG +pence/M +penchant/MS +pencil/SGJMD +pendant/SM +pend/DCGS +pendent/CS +Penderecki/M +Pendleton/M +pendulous +pendulum/MS +Penelopa/M +Penelope/M +penetrability/SM +penetrable +penetrate/SDVGNX +penetrating/Y +penetration/M +penetrativeness/M +penetrative/PY +penetrator/MS +penguin/MS +penicillin/SM +penile +peninsular +peninsula/SM +penis/MS +penitence/MS +penitential/YS +penitentiary/MS +penitent/SY +penknife/M +penknives +penlight/MS +pen/M +Pen/M +penman/M +penmanship/MS +penmen +Penna +pennant/SM +penned +Penney/M +Pennie/M +penniless +Penni/M +penning +Pennington/M +pennis +Penn/M +pennon/SM +Pennsylvania/M +Pennsylvanian/S +Penny/M +penny/SM +pennyweight/SM +pennyworth/M +penologist/MS +penology/MS +Penrod/M +Pensacola/M +pensioner/M +pension/ZGMRDBS +pensiveness/S +pensive/PY +pens/V +pentacle/MS +pentagonal/SY +Pentagon/M +pentagon/SM +pentagram/MS +pentameter/SM +pent/AS +Pentateuch/M +pentathlete/S +pentathlon/MS +pentatonic +pentecostal +Pentecostalism/S +Pentecostal/S +Pentecost/SM +penthouse/SDGM +Pentium/M +penuche/SM +penultimate/SY +penumbrae +penumbra/MS +penuriousness/MS +penurious/YP +penury/SM +peonage/MS +peon/MS +peony/SM +people/SDMG +Peoria/M +Pepe/M +Pepillo/M +Pepi/M +Pepin/M +Pepita/M +Pepito/M +pepped +peppercorn/MS +pepperer/M +peppergrass/M +peppermint/MS +pepperoni/S +pepper/SGRDM +peppery +peppiness/SM +pepping +peppy/PRT +Pepsico/M +PepsiCo/M +Pepsi/M +pepsin/SM +pep/SM +peptic/S +peptidase/SM +peptide/SM +peptizing +Pepys/M +Pequot/M +peradventure/S +perambulate/DSNGX +perambulation/M +perambulator/MS +percale/MS +perceivably +perceive/DRSZGB +perceived/U +perceiver/M +percentage/MS +percentile/SM +percent/MS +perceptible +perceptibly +perceptional +perception/MS +perceptiveness/MS +perceptive/YP +perceptual/Y +percept/VMS +Perceval/M +perchance +perch/GSDM +perchlorate/M +perchlorination +percipience/MS +percipient/S +Percival/M +percolate/NGSDX +percolation/M +percolator/MS +percuss/DSGV +percussionist/MS +percussion/SAM +percussiveness/M +percussive/PY +percutaneous/Y +Percy/M +perdition/MS +perdurable +peregrinate/XSDNG +peregrination/M +peregrine/S +Perelman/M +peremptorily +peremptory/P +perennial/SY +pres +perestroika/S +Perez/M +perfecta/S +perfect/DRYSTGVP +perfecter/M +perfectibility/MS +perfectible +perfectionism/MS +perfectionist/MS +perfection/MS +perfectiveness/M +perfective/PY +perfectness/MS +perfidiousness/M +perfidious/YP +perfidy/MS +perforated/U +perforate/XSDGN +perforation/M +perforce +performance/MS +performed/U +performer/M +perform/SDRZGB +perfumer/M +perfumery/SM +perfume/ZMGSRD +perfunctorily +perfunctoriness/M +perfunctory/P +perfused +perfusion/M +Pergamon/M +pergola/SM +perhaps/S +Peria/M +pericardia +pericardium/M +Perice/M +Periclean +Pericles/M +perigee/SM +perihelia +perihelion/M +peril/GSDM +Perilla/M +perilousness/M +perilous/PY +Peri/M +perimeter/MS +perinatal +perinea +perineum/M +periodic +periodical/YMS +periodicity/MS +period/MS +periodontal/Y +periodontics/M +periodontist/S +peripatetic/S +peripheral/SY +periphery/SM +periphrases +periphrasis/M +periphrastic +periscope/SDMG +perishable/SM +perish/BZGSRD +perishing/Y +peristalses +peristalsis/M +peristaltic +peristyle/MS +peritoneal +peritoneum/SM +peritonitis/MS +periwigged +periwigging +periwig/MS +periwinkle/SM +perjurer/M +perjure/SRDZG +perjury/MS +per/K +perk/GDS +perkily +perkiness/S +Perkin/SM +perky/TRP +Perla/M +Perle/M +Perl/M +permafrost/MS +permalloy/M +Permalloy/M +permanence/SM +permanency/MS +permanentness/M +permanent/YSP +permeability/SM +permeableness/M +permeable/P +permeate/NGVDSX +Permian +permissibility/M +permissibleness/M +permissible/P +permissibly +permission/SM +permissiveness/MS +permissive/YP +permit/SM +permitted +permitting +Perm/M +perm/MDGS +permutation/MS +permute/SDG +Pernell/M +perniciousness/MS +pernicious/PY +Pernod/M +Peron/M +peroration/SM +Perot/M +peroxidase/M +peroxide/MGDS +perpend/DG +perpendicularity/SM +perpendicular/SY +perpetrate/NGXSD +perpetration/M +perpetrator/SM +perpetual/SY +perpetuate/NGSDX +perpetuation/M +perpetuity/MS +perplex/DSG +perplexed/Y +perplexity/MS +perquisite/SM +Perren/M +Perri/M +Perrine/M +Perry/MR +persecute/XVNGSD +persecution/M +persecutor/MS +persecutory +Perseid/M +Persephone/M +Perseus/M +perseverance/MS +persevere/GSD +persevering/Y +Pershing/M +Persia/M +Persian/S +persiflage/MS +persimmon/SM +Persis/M +persist/DRSG +persistence/SM +persistent/Y +persnickety +personableness/M +personable/P +personae +personage/SM +personality/SM +personalization/CMS +personalize/CSDG +personalized/U +personalty/MS +personal/YS +persona/M +person/BMS +personification/M +personifier/M +personify/XNGDRS +personnel/SM +person's/U +persons/U +perspective/YMS +perspex +perspicaciousness/M +perspicacious/PY +perspicacity/S +perspicuity/SM +perspicuousness/M +perspicuous/YP +perspiration/MS +perspire/DSG +persuaded/U +persuader/M +persuade/ZGDRSB +persuasion/SM +persuasively +persuasiveness/MS +persuasive/U +pertain/GSD +Perth/M +pertinaciousness/M +pertinacious/YP +pertinacity/MS +pertinence/S +pertinent/YS +pertness/MS +perturbation/MS +perturbed/U +perturb/GDS +pertussis/SM +pert/YRTSP +peruke/SM +Peru/M +perusal/SM +peruser/M +peruse/RSDZG +Peruvian/S +pervade/SDG +pervasion/M +pervasiveness/MS +pervasive/PY +perverseness/SM +perverse/PXYNV +perversion/M +perversity/MS +pervert/DRSG +perverted/YP +perverter/M +perviousness +peseta/SM +Peshawar/M +peskily +peskiness/S +pesky/RTP +peso/MS +pessimal/Y +pessimism/SM +pessimistic +pessimistically +pessimist/SM +pester/DG +pesticide/MS +pestiferous +pestilence/SM +pestilential/Y +pestilent/Y +pestle/SDMG +pesto/S +pest/RZSM +PET +Ptain/M +petal/SDM +Peta/M +petard/MS +petcock/SM +Pete/M +peter/GD +Peter/M +Petersburg/M +Petersen/M +Peters/N +Peterson/M +Peterus/M +Petey/M +pethidine/M +petiole/SM +petiteness/M +petite/XNPS +petitioner/M +petition/GZMRD +petition's/A +petitions/A +petits +Petkiewicz/M +Pet/MRZ +Petra/M +Petrarch/M +petrel/SM +petri +petrifaction/SM +petrify/NDSG +Petrina/M +Petr/M +petrochemical/SM +petrodollar/MS +petroglyph/M +petrolatum/MS +petroleum/MS +petrolled +petrolling +petrol/MS +petrologist/MS +petrology/MS +Petronella/M +Petronia/M +Petronilla/M +Petronille/M +pet/SMRZ +petted +petter/MS +Pettibone/M +petticoat/SMD +pettifogged +pettifogger/SM +pettifogging +pettifog/S +pettily +pettiness/S +petting +pettis +pettishness/M +pettish/YP +Petty/M +petty/PRST +petulance/MS +petulant/Y +Petunia/M +petunia/SM +Peugeot/M +Pewaukee/M +pewee/MS +pewit/MS +pew/SM +pewter/SRM +peyote/SM +Peyter/M +Peyton/M +pf +Pfc +PFC +pfennig/SM +Pfizer/M +pg +PG +Phaedra/M +Phaethon/M +phaeton/MS +phage/M +phagocyte/SM +Phaidra/M +phalanger/MS +phalanges +phalanx/SM +phalli +phallic +phallus/M +Phanerozoic +phantasmagoria/SM +phantasmal +phantasm/SM +phantasy's +phantom/MS +pharaoh +Pharaoh/M +pharaohs +Pharaohs +pharisaic +Pharisaic +Pharisaical +pharisee/S +Pharisee/SM +pharmaceutical/SY +pharmaceutic/S +pharmaceutics/M +pharmacist/SM +pharmacological/Y +pharmacologist/SM +pharmacology/SM +pharmacopoeia/SM +pharmacy/SM +pharyngeal/S +pharynges +pharyngitides +pharyngitis/M +pharynx/M +phase/DSRGZM +phaseout/S +PhD +pheasant/SM +Phebe/M +Phedra/M +Phekda/M +Phelia/M +Phelps/M +phenacetin/MS +phenobarbital/SM +phenolic +phenol/MS +phenolphthalein/M +phenomenal/Y +phenomena/SM +phenomenological/Y +phenomenology/MS +phenomenon/SM +phenotype/MS +phenylalanine/M +phenyl/M +pheromone/MS +phew/S +phialled +phialling +phial/MS +Phidias/M +Philadelphia/M +philanderer/M +philander/SRDGZ +philanthropic +philanthropically +philanthropist/MS +philanthropy/SM +philatelic +philatelist/MS +philately/SM +Philbert/M +Philco/M +philharmonic/S +Philipa/M +Philip/M +Philippa/M +Philippe/M +Philippians/M +philippic/SM +Philippine/SM +Philis/M +philistine/S +Philistine/SM +philistinism/S +Phillida/M +Phillie/M +Phillipa/M +Phillipe/M +Phillip/MS +Phillipp/M +Phillis/M +Philly/SM +Phil/MY +philodendron/MS +philological/Y +philologist/MS +philology/MS +Philomena/M +philosopher/MS +philosophic +philosophical/Y +philosophized/U +philosophizer/M +philosophizes/U +philosophize/ZDRSG +philosophy/MS +philter/SGDM +philtre/DSMG +Phineas/M +Phip/M +Phipps/M +phi/SM +phlebitides +phlebitis/M +phlegmatic +phlegmatically +phlegm/SM +phloem/MS +phlox/M +pH/M +Ph/M +phobia/SM +phobic/S +Phobos/M +Phoebe/M +phoebe/SM +Phoenicia/M +Phoenician/SM +Phoenix/M +phoenix/MS +phone/DSGM +phoneme/SM +phonemically +phonemic/S +phonemics/M +phonetically +phonetician/SM +phonetic/S +phonetics/M +phonically +phonic/S +phonics/M +phoniness/MS +phonographer/M +phonographic +phonograph/RM +phonographs +phonologic +phonological/Y +phonologist/MS +phonology/MS +phonon/M +phony/PTRSDG +phooey/S +phosphatase/M +phosphate/MS +phosphide/M +phosphine/MS +phosphoresce +phosphorescence/SM +phosphorescent/Y +phosphoric +phosphor/MS +phosphorous +phosphorus/SM +photocell/MS +photochemical/Y +photochemistry/M +photocopier/M +photocopy/MRSDZG +photoelectric +photoelectrically +photoelectronic +photoelectrons +photoengraver/M +photoengrave/RSDJZG +photoengraving/M +photofinishing/MS +photogenic +photogenically +photograph/AGD +photographer/SM +photographic +photographically +photograph's +photographs/A +photography/MS +photojournalism/SM +photojournalist/SM +photoluminescence/M +photolysis/M +photolytic +photometer/SM +photometric +photometrically +photometry/M +photomicrograph/M +photomicrography/M +photomultiplier/M +photon/MS +photorealism +photosensitive +photo/SGMD +photosphere/M +photostatic +Photostat/MS +Photostatted +Photostatting +photosyntheses +photosynthesis/M +photosynthesize/DSG +photosynthetic +phototypesetter +phototypesetting/M +phrasal +phrase/AGDS +phrasebook +phrasemaking +phraseology/MS +phrase's +phrasing/SM +phrenological/Y +phrenologist/MS +phrenology/MS +phylactery/MS +phylae +phyla/M +Phylis/M +Phyllida/M +Phyllis/M +Phyllys/M +phylogeny/MS +phylum/M +Phylys/M +phys +physicality/M +physical/PYS +physician/SM +physicist/MS +physicked +physicking +physic/SM +physiochemical +physiognomy/SM +physiography/MS +physiologic +physiological/Y +physiologist/SM +physiology/MS +physiotherapist/MS +physiotherapy/SM +physique/MSD +phytoplankton/M +Piaf/M +Piaget/M +Pia/M +pianism/M +pianissimo/S +pianistic +pianist/SM +pianoforte/MS +pianola +Pianola/M +piano/SM +piaster/MS +piazza/SM +pibroch/M +pibrochs +picador/MS +picaresque/S +pica/SM +Picasso/M +picayune/S +Piccadilly/M +piccalilli/MS +piccolo/MS +pickaback's +pickaxe's +pickax/GMSD +pickerel/MS +Pickering/M +picker/MG +picketer/M +picket/MSRDZG +Pickett/M +Pickford/M +pick/GZSJDR +pickle/SDMG +Pickman/M +pickoff/S +pickpocket/GSM +pickup/SM +Pickwick/M +picky/RT +picnicked +picnicker/MS +picnicking +picnic/SM +picofarad/MS +picojoule +picoseconds +picot/DMGS +Pict/M +pictograph/M +pictographs +pictorialness/M +pictorial/PYS +picture/MGSD +picturesqueness/SM +picturesque/PY +piddle/GSD +piddly +pidgin/SM +piebald/S +piece/GMDSR +piecemeal +piecer/M +piecewise +pieceworker/M +piecework/ZSMR +piedmont +Piedmont/M +pieing +pie/MS +Pierce/M +piercer/M +pierce/RSDZGJ +piercing/Y +Pierette/M +pier/M +Pier/M +Pierre/M +Pierrette/M +Pierrot/M +Pierson/M +Pieter/M +Pietra/M +Pietrek/M +Pietro/M +piety/SM +piezoelectric +piezoelectricity/M +piffle/MGSD +pigeon/DMGS +pigeonhole/SDGM +pigged +piggery/M +pigging +piggishness/SM +piggish/YP +piggyback/MSDG +Piggy/M +piggy/RSMT +pigheadedness/S +pigheaded/YP +piglet/MS +pigmentation/MS +pigment/MDSG +pig/MLS +Pigmy's +pigpen/SM +pigroot +pigskin/MS +pigsty/SM +pigswill/M +pigtail/SMD +Pike/M +pike/MZGDRS +piker/M +pikestaff/MS +pilaf/MS +pilaster/SM +Pilate/M +pilau's +pilchard/SM +Pilcomayo/M +pile/JDSMZG +pileup/MS +pilferage/SM +pilferer/M +pilfer/ZGSRD +Pilgrim +pilgrimage/DSGM +pilgrim/MS +piling/M +pillage/RSDZG +pillar/DMSG +pillbox/MS +pill/GSMD +pillion/DMGS +pillory/MSDG +pillowcase/SM +pillow/GDMS +pillowslip/S +Pillsbury/M +pilot/DMGS +pilothouse/SM +piloting/M +pimento/MS +pimiento/SM +pimpernel/SM +pimp/GSMYD +pimple/SDM +pimplike +pimply/TRM +PIN +pinafore/MS +piata/S +Pinatubo/M +pinball/MS +Pincas/M +pincer/GSD +Pinchas/M +pincher/M +pinch/GRSD +pincushion/SM +Pincus/M +Pindar/M +pineapple/MS +pined/A +Pinehurst/M +pine/MNGXDS +pines/A +pinfeather/SM +ping/GDRM +pinheaded/P +pinhead/SMD +pinhole/SM +pining/A +pinion/DMG +Pinkerton/M +pinkeye/MS +pink/GTYDRMPS +pinkie/SM +pinkish/P +pinkness/S +pinko/MS +pinky's +pinnacle/MGSD +pinnate +pinned/U +pinning/S +Pinocchio/M +Pinochet/M +pinochle/SM +pion/S +pinpoint/SDG +pinprick/MDSG +pin's +pinsetter/SM +Pinsky/M +pinstripe/SDM +pintail/SM +Pinter/M +pint/MRS +pinto/S +pinup/MS +pin/US +pinwheel/DMGS +pinyin +Pinyin +piny/RT +pioneer/SDMG +pion/M +Piotr/M +piousness/MS +pious/YP +pipeline/DSMG +pipe/MS +piper/M +Piper/M +Pipestone/M +pipet's +pipette/MGSD +pipework +piping/YM +pipit/MS +pip/JSZMGDR +Pip/MR +Pippa/M +pipped +pipping +pippin/SM +Pippo/M +Pippy/M +pipsqueak/SM +piquancy/MS +piquantness/M +piquant/PY +pique/GMDS +piracy/MS +Piraeus/M +Pirandello/M +piranha/SM +pirate/MGSD +piratical/Y +pirogi +pirogies +pirouette/MGSD +pis +Pisa/M +piscatorial +Pisces/M +Pisistratus/M +pismire/SM +Pissaro/M +piss/DSRG! +pistachio/MS +piste/SM +pistillate +pistil/MS +pistoleers +pistole/M +pistol/SMGD +piston/SM +pitapat/S +pitapatted +pitapatting +pita/SM +Pitcairn/M +pitchblende/SM +pitcher/M +pitchfork/GDMS +pitching/M +pitchman/M +pitchmen +pitch/RSDZG +pitchstone/M +piteousness/SM +piteous/YP +pitfall/SM +pithily +pithiness/SM +pith/MGDS +piths +pithy/RTP +pitiableness/M +pitiable/P +pitiably +pitier/M +pitifuller +pitifullest +pitifulness/M +pitiful/PY +pitilessness/SM +pitiless/PY +pitman/M +pit/MS +Pitney/M +piton/SM +pittance/SM +pitted +pitting +Pittman/M +Pittsburgh/ZM +Pittsfield/M +Pitt/SM +Pittston/M +pituitary/SM +pitying/Y +pity/ZDSRMG +Pius/M +pivotal/Y +pivot/DMSG +pivoting/M +pix/DSG +pixel/SM +pixie/MS +pixiness +pixmap/SM +Pizarro/M +pizazz/S +pi/ZGDRH +pizza/SM +pizzeria/SM +pizzicati +pizzicato +pj's +PJ's +pk +pkg +pkt +pkwy +Pkwy +pl +placard/DSMG +placate/NGVXDRS +placatory +placeable/A +placebo/SM +placed/EAU +place/DSRJLGZM +placeholder/S +placekick/DGS +placeless/Y +placement/AMES +placental/S +placenta/SM +placer/EM +places/EA +placidity/SM +placidness/M +placid/PY +placing/AE +placket/SM +plagiarism/MS +plagiarist/MS +plagiarize/GZDSR +plagiary/SM +plagued/U +plague/MGRSD +plaguer/M +plaice/M +plaid/DMSG +plainclothes +plainclothesman +plainclothesmen +Plainfield/M +plainness/MS +plainsman/M +plainsmen +plainsong/SM +plainspoken +plain/SPTGRDY +plaintiff/MS +plaintiveness/M +plaintive/YP +plaint/VMS +Plainview/M +plaiting/M +plait/SRDMG +planar +planarity +Planck/M +plan/DRMSGZ +planeload +planer/M +plane's +plane/SCGD +planetarium/MS +planetary +planetesimal/M +planet/MS +planetoid/SM +plangency/S +plangent +planking/M +plank/SJMDG +plankton/MS +planned/U +planner/SM +planning +Plano +planoconcave +planoconvex +Plantagenet/M +plantain/MS +plantar +plantation/MS +planter/MS +planting/S +plantlike +plant's +plant/SADG +plaque/MS +plash/GSDM +plasma/MS +plasmid/S +plasm/M +plasterboard/MS +plasterer/M +plastering/M +plaster/MDRSZG +plasterwork/M +plastically +plasticine +Plasticine/M +plasticity/SM +plasticize/GDS +plastic/MYS +plateau/GDMS +plateful/S +platelet/SM +platen/M +plater/M +plate/SM +platform/SGDM +Plath/M +plating/M +platinize/GSD +platinum/MS +platitude/SM +platitudinous/Y +plat/JDNRSGXZ +Plato/M +platonic +Platonic +Platonism/M +Platonist +platoon/MDSG +platted +Platte/M +platter/MS +Platteville/M +platting +platypus/MS +platys +platy/TR +plaudit/MS +plausibility/S +plausible/P +plausibly +Plautus/M +playability/U +playable/U +playacting/M +playact/SJDG +playback/MS +playbill/SM +Playboy/M +playboy/SM +play/DRSEBG +played/A +player's/E +player/SM +playfellow/S +playfulness/MS +playful/PY +playgirl/SM +playgoer/MS +playground/MS +playgroup/S +playhouse/SM +playing/S +playmate/MS +playoff/S +playpen/SM +playroom/SM +plays/A +Playtex/M +plaything/MS +playtime/SM +playwright/SM +playwriting/M +plaza/SM +pleader/MA +pleading/MY +plead/ZGJRDS +pleasanter +pleasantest +pleasantness/SMU +pleasantry/MS +pleasant/UYP +pleased/EU +pleaser/M +pleases/E +please/Y +pleasingness/M +pleasing/YP +plea/SM +pleas/RSDJG +pleasurableness/M +pleasurable/P +pleasurably +pleasureful +pleasure/MGBDS +pleasure's/E +pleasures/E +pleater/M +pleat/RDMGS +plebeian/SY +plebe/MS +plebiscite/SM +plectra +plectrum/SM +pledger/M +pledge/RSDMG +Pleiads +Pleistocene +plenary/S +plenipotentiary/S +plenitude/MS +plenteousness/M +plenteous/PY +plentifulness/M +plentiful/YP +plenty/SM +plenum/M +pleonasm/MS +plethora/SM +pleurae +pleural +pleura/M +pleurisy/SM +Plexiglas/MS +plexus/SM +pliability/MS +pliableness/M +pliable/P +pliancy/MS +pliantness/M +pliant/YP +plication/MA +plier/MA +plight/GMDRS +plimsolls +plinker/M +plink/GRDS +plinth/M +plinths +Pliny/M +Pliocene/S +PLO +plodded +plodder/SM +plodding/SY +plod/S +plopped +plopping +plop/SM +plosive +plot/SM +plotted/A +plotter/MDSG +plotting +plover/MS +plowed/U +plower/M +plowman/M +plowmen +plow/SGZDRM +plowshare/MS +ploy's +ploy/SCDG +plucker/M +pluckily +pluckiness/SM +pluck/SGRD +plucky/TPR +pluggable +plugged/UA +plugging/AU +plughole +plug's +plug/US +plumage/DSM +plumbago/M +plumbed/U +plumber/M +plumbing/M +plumb/JSZGMRD +plume/SM +plummer +plummest +plummet/DSG +plummy +plumper/M +plumpness/S +plump/RDNYSTGP +plum/SMDG +plumy/TR +plunder/GDRSZ +plunger/M +plunge/RSDZG +plunker/M +plunk/ZGSRD +pluperfect/S +pluralism/MS +pluralistic +pluralist/S +plurality/SM +pluralization/MS +pluralize/GZRSD +pluralizer/M +plural/SY +plushness/MS +plush/RSYMTP +plushy/RPT +plus/S +plussed +plussing +Plutarch/M +plutocracy/MS +plutocratic +plutocrat/SM +Pluto/M +plutonium/SM +pluvial/S +ply/AZNGRSD +Plymouth/M +plywood/MS +pm +PM +Pm/M +PMS +pneumatically +pneumatic/S +pneumatics/M +pneumonia/MS +PO +poacher/M +poach/ZGSRD +Pocahontas/M +pocketbook/SM +pocketful/SM +pocketing/M +pocketknife/M +pocketknives +pocket/MSRDG +pock/GDMS +pockmark/MDSG +Pocono/MS +podded +podding +podge/ZR +Podgorica/M +podiatrist/MS +podiatry/MS +podium/MS +pod/SM +Podunk/M +Poe/M +poem/MS +poesy/GSDM +poetaster/MS +poetess/MS +poetically +poeticalness +poetical/U +poetic/S +poetics/M +poet/MS +poetry/SM +pogo +Pogo/M +pogrom/GMDS +poignancy/MS +poignant/Y +Poincar/M +poinciana/SM +Poindexter/M +poinsettia/SM +pointblank +pointedness/M +pointed/PY +pointer/M +pointillism/SM +pointillist/SM +pointing/M +pointlessness/SM +pointless/YP +point/RDMZGS +pointy/TR +poise/M +pois/GDS +poi/SM +poisoner/M +poisoning/M +poisonous/PY +poison/RDMZGSJ +Poisson/M +poke/DRSZG +Pokemon/M +pokerface/D +poker/M +poky/SRT +Poland/M +Polanski/M +polarimeter/SM +polarimetry +polariscope/M +Polaris/M +polarity/MS +polarization/CMS +polarized/UC +polarize/RSDZG +polarizes/C +polarizing/C +polarogram/SM +polarograph +polarography/M +Polaroid/SM +polar/S +polecat/SM +polemical/Y +polemicist/S +polemic/S +polemics/M +pole/MS +Pole/MS +poler/M +polestar/S +poleward/S +pol/GMDRS +policeman/M +policemen/M +police/MSDG +policewoman/M +policewomen +policyholder/MS +policymaker/S +policymaking +policy/SM +poliomyelitides +poliomyelitis/M +polio/SM +Polish +polished/U +polisher/M +polish/RSDZGJ +polis/M +Politburo/M +politburo/S +politeness/MS +polite/PRTY +politesse/SM +politically +political/U +politician/MS +politicization/S +politicize/CSDG +politicked +politicking/SM +politico/SM +politic/S +politics/M +polity/MS +polka/SDMG +Polk/M +pollack/SM +Pollard/M +polled/U +pollen/GDM +pollinate/XSDGN +pollination/M +pollinator/MS +polliwog/SM +poll/MDNRSGX +pollock's +Pollock/SM +pollster/MS +pollutant/MS +polluted/U +polluter/M +pollute/RSDXZVNG +pollution/M +Pollux/M +Pollyanna/M +Polly/M +pollywog's +Pol/MY +Polo/M +polo/MS +polonaise/MS +polonium/MS +poltergeist/SM +poltroon/MS +polyandrous +polyandry/MS +polyatomic +polybutene/MS +polycarbonate +polychemicals +polychrome +polyclinic/MS +polycrystalline +polyelectrolytes +polyester/SM +polyether/S +polyethylene/SM +polygamist/MS +polygamous/Y +polygamy/MS +polyglot/S +polygonal/Y +polygon/MS +polygraph/MDG +polygraphs +polygynous +polyhedral +polyhedron/MS +Polyhymnia/M +polyisobutylene +polyisocyanates +polymath/M +polymaths +polymerase/S +polymeric +polymerization/SM +polymerize/SDG +polymer/MS +polymorphic +polymorphism/MS +polymorph/M +polymyositis +Polynesia/M +Polynesian/S +polynomial/YMS +Polyphemus/M +polyphonic +polyphony/MS +polyphosphate/S +polyp/MS +polypropylene/MS +polystyrene/SM +polysyllabic +polysyllable/SM +polytechnic/MS +polytheism/SM +polytheistic +polytheist/SM +polythene/M +polytonal/Y +polytopes +polyunsaturated +polyurethane/SM +polyvinyl/MS +Po/M +pomade/MGSD +pomander/MS +pomegranate/SM +Pomerania/M +Pomeranian +pommel/GSMD +Pomona/M +Pompadour/M +pompadour/MDS +pompano/SM +Pompeian/S +Pompeii/M +Pompey/M +pompom/SM +pompon's +pomposity/MS +pompousness/S +pompous/YP +pomp/SM +ponce/M +Ponce/M +Ponchartrain/M +poncho/MS +ponderer/M +ponderousness/MS +ponderous/PY +ponder/ZGRD +pond/SMDRGZ +pone/SM +pongee/MS +poniard/GSDM +pons/M +Pontchartrain/M +Pontiac/M +Pontianak/M +pontiff/MS +pontifical/YS +pontificate/XGNDS +pontoon/SMDG +pony/DSMG +ponytail/SM +pooch/GSDM +poodle/MS +poof/MS +pooh/DG +Pooh/M +poohs +Poole/M +pool/MDSG +poolroom/MS +poolside +Poona/M +poop/MDSG +poorboy +poorhouse/MS +poorness/MS +poor/TYRP +popcorn/MS +Popek/MS +pope/SM +Pope/SM +Popeye/M +popgun/SM +popinjay/MS +poplar/SM +poplin/MS +Popocatepetl/M +popover/SM +poppa/MS +popped +Popper/M +popper/SM +poppet/M +popping +Poppins/M +poppycock/MS +Poppy/M +poppy/SDM +poppyseed +Popsicle/MS +pop/SM +populace/MS +popularism +popularity/UMS +popularization/SM +popularize/A +popularized +popularizer/MS +popularizes/U +popularizing +popular/YS +populate/CXNGDS +populated/UA +populates/A +populating/A +population/MC +populism/S +populist/SM +populousness/MS +populous/YP +porcelain/SM +porch/SM +porcine +porcupine/MS +pore/ZGDRS +Porfirio/M +porgy/SM +poring/Y +porker/M +porky/TSR +pork/ZRMS +pornographer/SM +pornographic +pornographically +pornography/SM +porno/S +porn/S +porosity/SM +porousness/MS +porous/PY +porphyritic +porphyry/MS +porpoise/DSGM +porridge/MS +Porrima/M +porringer/MS +Porsche/M +portability/S +portables +portable/U +portably +port/ABSGZMRD +portage/ASM +portaged +portaging +portal/SM +portamento/M +portcullis/MS +ported/CE +Porte/M +portend/SDG +portentousness/M +portentous/PY +portent/SM +porterage/M +porter/DMG +porterhouse/SM +Porter/M +porter's/A +portfolio/MS +porthole/SM +Portia/M +porticoes +portico/M +Portie/M +portire/SM +porting/E +portion/KGSMD +Portland/M +portliness/SM +portly/PTR +portmanteau/SM +Port/MR +Prto/M +portraitist/SM +portrait/MS +portraiture/MS +portrayal/SM +portrayer/M +portray/GDRS +ports/CE +Portsmouth/M +Portugal/M +Portuguese/M +portulaca/MS +Porty/M +posed/CA +Poseidon/M +poser/KME +poses/CA +poseur/MS +pose/ZGKDRSE +posh/DSRGT +posing/CA +positifs +positionable +positional/KY +position/KGASMD +position's/EC +positions/EC +positiveness/S +positive/RSPYT +positivism/M +positivist/S +positivity +positron/SM +posit/SCGD +Posner/M +posse/M +possess/AGEDS +possessed/PY +possession/AEMS +possessional +possessiveness/MS +possessive/PSMY +possessor/MS +possibility/SM +possible/TRS +possibly +poss/S +possum/MS +postage/MS +postal/S +post/ASDRJG +postbag/M +postbox/SM +postcard/SM +postcode/SM +postcondition/S +postconsonantal +postdate/DSG +postdoctoral +posteriori +posterior/SY +posterity/SM +poster/MS +postfix/GDS +postgraduate/SM +posthaste/S +posthumousness/M +posthumous/YP +posthypnotic +postilion/MS +postindustrial +posting/M +postlude/MS +Post/M +postman/M +postmarital +postmark/GSMD +postmaster/SM +postmen +postmeridian +postmistress/MS +postmodern +postmodernist +postmortem/S +postnasal +postnatal +postoperative/Y +postorder +postpaid +postpartum +postpone/GLDRS +postponement/S +postpositions +postprandial +post's +postscript/SM +postsecondary +postulate/XGNSD +postulation/M +postural +posture/MGSRD +posturer/M +postvocalic +postwar +posy/SM +potability/SM +potableness/M +potable/SP +potage/M +potash/MS +potassium/MS +potatoes +potato/M +potbelly/MSD +potboiler/M +potboil/ZR +pot/CMS +Potemkin/M +potency/MS +potentate/SM +potentiality/MS +potential/SY +potentiating +potentiometer/SM +potent/YS +potful/SM +pothead/MS +potherb/MS +pother/GDMS +potholder/MS +pothole/SDMG +potholing/M +pothook/SM +potion/SM +potlatch/SM +potluck/MS +Potomac/M +potpie/SM +potpourri/SM +Potsdam/M +potsherd/MS +potshot/S +pottage/SM +Pottawatomie/M +potted +Potter/M +potter/RDMSG +pottery/MS +potting +Potts/M +potty/SRT +pouch/SDMG +Poughkeepsie/M +Poul/M +poulterer/MS +poultice/DSMG +poultry/MS +pounce/SDG +poundage/MS +pounder/MS +pound/KRDGS +Pound/M +pour/DSG +pourer's +Poussin/MS +pouter/M +pout/GZDRS +poverty/MS +POW +powderpuff +powder/RDGMS +powdery +Powell/M +powerboat/MS +powerfulness/M +powerful/YP +power/GMD +powerhouse/MS +powerlessness/SM +powerless/YP +Powers +Powhatan/M +pow/RZ +powwow/GDMS +pox/GMDS +Poznan/M +pp +PP +ppm +ppr +PPS +pr +PR +practicability/S +practicable/P +practicably +practicality/SM +practicalness/M +practical/YPS +practice/BDRSMG +practiced/U +practicer/M +practicum/SM +practitioner/SM +Pradesh/M +Prado/M +Praetorian +praetorian/S +praetor/MS +pragmatical/Y +pragmatic/S +pragmatics/M +pragmatism/MS +pragmatist/MS +Prague/M +Praia +prairie/MS +praise/ESDG +praiser/S +praise's +praiseworthiness/MS +praiseworthy/P +praising/Y +Prakrit/M +praline/MS +pram/MS +prancer/M +prance/ZGSRD +prancing/Y +prank/SMDG +prankster/SM +praseodymium/SM +Pratchett/M +prate/DSRGZ +prater/M +pratfall/MS +prating/Y +prattle/DRSGZ +prattler/M +prattling/Y +Pratt/M +Prattville/M +Pravda/M +prawn/MDSG +praxes +praxis/M +Praxiteles/M +pray/DRGZS +prayerbook +prayerfulness/M +prayerful/YP +prayer/M +PRC +preach/DRSGLZJ +preacher/M +preaching/Y +preachment/MS +preachy/RT +preadolescence/S +Preakness/M +preallocate/XGNDS +preallocation/M +preallocator/S +preamble/MGDS +preamp +preamplifier/M +prearrange/LSDG +prearrangement/SM +preassign/SDG +preauthorize +prebendary/M +Precambrian +precancel/DGS +precancerous +precariousness/MS +precarious/PY +precautionary +precaution/SGDM +precede/DSG +precedence/SM +precedented/U +precedent/SDM +preceptive/Y +preceptor/MS +precept/SMV +precess/DSG +precession/M +precinct/MS +preciosity/MS +preciousness/S +precious/PYS +precipice/MS +precipitable +precipitant/S +precipitateness/M +precipitate/YNGVPDSX +precipitation/M +precipitousness/M +precipitous/YP +preciseness/SM +precise/XYTRSPN +precision/M +prcis/MDG +preclude/GDS +preclusion/S +precociousness/MS +precocious/YP +precocity/SM +precode/D +precognition/SM +precognitive +precollege/M +precolonial +precomputed +preconceive/GSD +preconception/SM +precondition/GMDS +preconscious +precook/GDS +precursor/SM +precursory +precut +predate/NGDSX +predation/CMS +predator/SM +predatory +predecease/SDG +predecessor/MS +predeclared +predecline +predefine/GSD +predefinition/SM +predesignate/GDS +predestination/SM +predestine/SDG +predetermination/MS +predeterminer/M +predetermine/ZGSRD +predicable/S +predicament/SM +predicate/VGNXSD +predication/M +predicator +predictability/UMS +predictable/U +predictably/U +predict/BSDGV +predicted/U +prediction/MS +predictive/Y +predictor/MS +predigest/GDS +predilect +predilection/SM +predispose/SDG +predisposition/MS +predoctoral +predominance/SM +predominant/Y +predominate/YSDGN +predomination/M +preemie/MS +preeminence/SM +preeminent/Y +preemployment/M +preempt/GVSD +preemption/SM +preemptive/Y +preemptor/M +preener/M +preen/SRDG +preexist/DSG +preexistence/SM +preexistent +prefabbed +prefabbing +prefab/MS +prefabricate/XNGDS +prefabrication/M +preface/DRSGM +prefacer/M +prefatory +prefect/MS +prefecture/MS +preferableness/M +preferable/P +preferably +prefer/BL +preference/MS +preferential/Y +preferment/SM +preferred +preferring +prefiguration/M +prefigure/SDG +prefix/MDSG +preflight/SGDM +preform/DSG +pref/RZ +pregnancy/SM +pregnant/Y +preheat/GDS +prehensile +prehistoric +prehistorical/Y +prehistory/SM +preindustrial +preinitialize/SDG +preinterview/M +preisolated +prejudge/DRSG +prejudger/M +prejudgment/SM +prejudiced/U +prejudice/MSDG +prejudicial/PY +prekindergarten/MS +prelacy/MS +prelate/SM +preliminarily +preliminary/S +preliterate/S +preloaded +prelude/GMDRS +preluder/M +premarital/Y +premarket +prematureness/M +premature/SPY +prematurity/M +premedical +premeditated/Y +premeditate/XDSGNV +premeditation/M +premed/S +premenstrual +premiere/MS +premier/GSDM +premiership/SM +Preminger/M +premise/GMDS +premiss's +premium/MS +premix/GDS +premolar/S +premonition/SM +premonitory +prenatal/Y +Pren/M +Prenticed/M +Prentice/MGD +Prenticing/M +Prentiss/M +Prent/M +prenuptial +preoccupation/MS +preoccupy/DSG +preoperative +preordain/DSLG +prepackage/GSD +prepaid +preparation/SM +preparative/SYM +preparatory +preparedly +preparedness/USM +prepared/UP +prepare/ZDRSG +prepay/GLS +prepayment/SM +prepender/S +prepends +preplanned +preponderance/SM +preponderant/Y +preponderate/DSYGN +prepositional/Y +preposition/SDMG +prepossess/GSD +prepossessing/U +prepossession/MS +preposterousness/M +preposterous/PY +prepped +prepping +preppy/RST +preprepared +preprint/SGDM +preprocessed +preprocessing +preprocessor/S +preproduction +preprogrammed +prep/SM +prepubescence/S +prepubescent/S +prepublication/M +prepuce/SM +prequel/S +preradiation +prerecord/DGS +preregister/DSG +preregistration/MS +prerequisite/SM +prerogative/SDM +Pres +presage/GMDRS +presager/M +presbyopia/MS +presbyterian +Presbyterianism/S +Presbyterian/S +presbyter/MS +presbytery/MS +preschool/RSZ +prescience/SM +prescient/Y +Prescott/M +prescribed/U +prescriber/M +prescribe/RSDG +prescription/SM +prescriptive/Y +prescript/SVM +preselect/SGD +presence/SM +presentableness/M +presentable/P +presentably/A +presentational/A +presentation/AMS +presented/A +presenter/A +presentiment/MS +presentment/SM +presents/A +present/SLBDRYZGP +preservationist/S +preservation/SM +preservative/SM +preserve/DRSBZG +preserved/U +preserver/M +preset/S +presetting +preshrank +preshrink/SG +preshrunk +preside/DRSG +presidency/MS +presidential/Y +president/SM +presider/M +presidia +presidium/M +Presley/M +presoaks +presort/GDS +pres/S +press/ACDSG +pressed/U +presser/MS +pressingly/C +pressing/YS +pressman/M +pressmen +pressure/DSMG +pressurization/MS +pressurize/DSRGZ +pressurized/U +prestidigitate/NX +prestidigitation/M +prestidigitatorial +prestidigitator/M +prestige/MS +prestigious/PY +Preston/M +presto/S +presumably +presume/BGDRS +presumer/M +presuming/Y +presumption/MS +presumptive/Y +presumptuousness/SM +presumptuous/YP +presuppose/GDS +presupposition/S +pretax +preteen/S +pretended/Y +pretender/M +pretending/U +pretend/SDRZG +pretense/MNVSX +pretension/GDM +pretentiousness/S +pretentious/UYP +preterite's +preterit/SM +preternatural/Y +pretest/SDG +pretext/SMDG +Pretoria/M +pretreated +pretreatment/S +pretrial +prettify/SDG +prettily +prettiness/SM +pretty/TGPDRS +pretzel/SM +prevailing/Y +prevail/SGD +prevalence/MS +prevalent/SY +prevaricate/DSXNG +prevaricator/MS +preventable/U +preventably +preventative/S +prevent/BSDRGV +preventer/M +prevention/MS +preventiveness/M +preventive/SPY +preview/ZGSDRM +previous/Y +prevision/SGMD +prewar +prexes +preyer's +prey/SMDG +Priam/M +priapic +Pribilof/M +price/AGSD +priced/U +priceless +Price/M +pricer/MS +price's +pricey +pricier +priciest +pricker/M +pricking/M +prickle/GMDS +prickliness/S +prickly/RTP +prick/RDSYZG +prideful/Y +pride/GMDS +prier/M +priestess/MS +priesthood/SM +Priestley/M +priestliness/SM +priestly/PTR +priest/SMYDG +prigged +prigging +priggishness/S +priggish/PYM +prig/SM +primacy/MS +primal +primarily +primary/MS +primate/MS +primed/U +primely/M +primeness/M +prime/PYS +primer/M +Prime's +primeval/Y +priming/M +primitiveness/SM +primitive/YPS +primitivism/M +primmed +primmer +primmest +primming +primness/MS +primogenitor/MS +primogeniture/MS +primordial/YS +primp/DGS +primrose/MGSD +prim/SPJGZYDR +princedom/MS +princeliness/SM +princely/PRT +Prince/M +prince/SMY +princess/MS +Princeton/M +principality/MS +principal/SY +Principe/M +Principia/M +principled/U +principle/SDMG +printable/U +printably +print/AGDRS +printed/U +printer/AM +printers +printing/SM +printmaker/M +printmake/ZGR +printmaking/M +printout/S +Prinz/M +prioress/MS +priori +prioritize/DSRGZJ +priority/MS +prior/YS +priory/SM +Pris +Prisca/M +Priscella/M +Priscilla/M +prised +prise/GMAS +prismatic +prism/MS +prison/DRMSGZ +prisoner/M +Prissie/M +prissily +prissiness/SM +prissy/RSPT +pristine/Y +prithee/S +privacy/MS +privateer/SMDG +privateness/M +private/NVYTRSXP +privation/MCS +privative/Y +privatization/S +privatize/GSD +privet/SM +privileged/U +privilege/SDMG +privily +privy/SRMT +prized/A +prize/DSRGZM +prizefighter/M +prizefighting/M +prizefight/SRMGJZ +prizewinner/S +prizewinning +Pr/MN +PRO +proactive +probabilist +probabilistic +probabilistically +probability/SM +probable/S +probably +probated/A +probate/NVMX +probates/A +probating/A +probational +probationary/S +probationer/M +probation/MRZ +probation's/A +probative/A +prober/M +probity/SM +problematical/UY +problematic/S +problem/SM +proboscis/MS +prob/RBJ +procaine/MS +procedural/SY +procedure/MS +proceeder/M +proceeding/M +proceed/JRDSG +process/BSDMG +processed/UA +processes/A +processional/YS +procession/GD +processor/MS +proclamation/MS +proclivity/MS +proconsular +procrastinate/XNGDS +procrastination/M +procrastinator/MS +procreational +procreatory +procrustean +Procrustean +Procrustes/M +proctor/GSDM +proctorial +procurable/U +procure/L +procurement/MS +Procyon/M +prodded +prodding +prodigality/S +prodigal/SY +prodigiousness/M +prodigious/PY +prodigy/MS +prod/S +produce/AZGDRS +producer/AM +producible/A +production/ASM +productively/UA +productiveness/MS +productive/PY +productivities +productivity/A +productivity's +productize/GZRSD +product/V +Prof +profanation/S +profaneness/MS +profane/YPDRSG +profanity/MS +professed/Y +professionalism/SM +professionalize/GSD +professional/USY +profession/SM +professorial/Y +professorship/SM +professor/SM +proffer/GSD +proficiency/SM +proficient/YS +profitability/MS +profitableness/MU +profitable/UP +profitably/U +profiteer/GSMD +profiterole/MS +profit/GZDRB +profitless +profligacy/S +profligate/YS +proforma/S +profoundity +profoundness/SM +profound/PTYR +prof/S +profundity/MS +profuseness/MS +profuse/YP +progenitor/SM +progeny/M +progesterone/SM +prognathous +prognoses +prognosis/M +prognosticate/NGVXDS +prognostication/M +prognosticator/S +prognostic/S +program/CSA +programed +programing +programmability +programmable/S +programmed/CA +programmer/ASM +programming/CA +programmings +progression/SM +progressiveness/SM +progressive/SPY +progressivism +progress/MSDVG +prohibiter/M +prohibitionist/MS +prohibition/MS +Prohibition/MS +prohibitiveness/M +prohibitive/PY +prohibitory +prohibit/VGSRD +projected/AU +projectile/MS +projectionist/MS +projection/MS +projective/Y +project/MDVGS +projector/SM +Prokofieff/M +Prokofiev/M +prolegomena +proletarianization/M +proletarianized +proletarian/S +proletariat/SM +proliferate/GNVDSX +proliferation/M +prolifically +prolific/P +prolixity/MS +prolix/Y +prologize +prologue/MGSD +prologuize +prolongate/NGSDX +prolongation/M +prolonger/M +prolong/G +promenade/GZMSRD +promenader/M +Promethean +Prometheus/M +promethium/SM +prominence/MS +prominent/Y +promiscuity/MS +promiscuousness/M +promiscuous/PY +promise/GD +promising/UY +promissory +promontory/MS +promote/GVZBDR +promoter/M +promotiveness/M +promotive/P +prompted/U +prompter/M +promptitude/SM +promptness/MS +prompt/SGJTZPYDR +pro/MS +promulgate/NGSDX +promulgation/M +promulgator/MS +pron +proneness/MS +prone/PY +pronghorn/SM +prong/SGMD +pronominalization +pronominalize +pronounceable/U +pronouncedly +pronounced/U +pronounce/GLSRD +pronouncement/SM +pronouncer/M +pronto +pronunciation/SM +proofed/A +proofer +proofing/M +proofreader/M +proofread/GZSR +proof/SEAM +propaganda/SM +propagandistic +propagandist/SM +propagandize/DSG +propagated/U +propagate/SDVNGX +propagation/M +propagator/MS +propellant/MS +propelled +propeller/MS +propelling +propel/S +propensity/MS +properness/M +proper/PYRT +propertied/U +property/SDM +prophecy/SM +prophesier/M +prophesy/GRSDZ +prophetess/S +prophetic +prophetical/Y +prophet/SM +prophylactic/S +prophylaxes +prophylaxis/M +propinquity/MS +propionate/M +propitiate/GNXSD +propitiatory +propitiousness/M +propitious/YP +proponent/MS +proportionality/M +proportional/SY +proportionate/YGESD +proportioner/M +proportion/ESGDM +proportionment/M +proposal/SM +propped +propping +proprietary/S +proprietorial +proprietorship/SM +proprietor/SM +proprietress/MS +propriety/MS +proprioception +proprioceptive +prop/SZ +propulsion/MS +propulsive +propylene/M +prorogation/SM +prorogue +prosaic +prosaically +proscenium/MS +prosciutti +prosciutto/SM +proscription/SM +proscriptive +pros/DSRG +prosecute/SDBXNG +prosecution/M +prosecutor/MS +proselyte/SDGM +proselytism/MS +proselytize/ZGDSR +prose/M +proser/M +Proserpine/M +prosodic/S +prosody/MS +prospect/DMSVG +prospection/SM +prospectiveness/M +prospective/SYP +prospector/MS +prospectus/SM +prosper/GSD +prosperity/MS +prosperousness/M +prosperous/PY +prostate +prostheses +prosthesis/M +prosthetic/S +prosthetics/M +prostitute/DSXNGM +prostitution/M +prostrate/SDXNG +prostration/M +prosy/RT +protactinium/MS +protagonist/SM +Protagoras/M +protean/S +protease/M +protect/DVGS +protected/UY +protectionism/MS +protectionist/MS +protection/MS +protectiveness/S +protective/YPS +protectorate/SM +protector/MS +protges +protg/SM +protein/MS +proteolysis/M +proteolytic +Proterozoic/M +protestantism +Protestantism/MS +protestant/S +Protestant/SM +protestation/MS +protest/G +protesting/Y +Proteus/M +protocol/DMGS +protoplasmic +protoplasm/MS +prototype/SDGM +prototypic +prototypical/Y +protozoa +protozoan/MS +protozoic +protozoon's +protract/DG +protrude/SDG +protrusile +protrusion/MS +protrusive/PY +protuberance/S +protuberant +Proudhon/M +proud/TRY +Proust/M +provabilities +provability's +provability/U +provableness/M +provable/P +provably +prov/DRGZB +proved/U +proven/U +prove/ESDAG +provenance/SM +Provenal +Provencals +Provence/M +provender/SDG +provenience/SM +provenly +proverb/DG +proverbial/Y +Proverbs/M +prover/M +provide/DRSBGZ +provided/U +providence/SM +Providence/SM +providential/Y +provident/Y +provider/M +province/SM +provincialism/SM +provincial/SY +provisional/YS +provisioner/M +provision/R +proviso/MS +provocateur/S +provocativeness/SM +provocative/P +provoked/U +provoke/GZDRS +provoking/Y +provolone/SM +Provo/M +provost/MS +prowess/SM +prowler/M +prowl/RDSZG +prow/TRMS +proximal/Y +proximateness/M +proximate/PY +proximity/MS +Proxmire/M +proxy/SM +Prozac +prude/MS +Prudence/M +prudence/SM +Prudential/M +prudential/SY +prudent/Y +prudery/MS +Prudi/M +prudishness/SM +prudish/YP +Prudy/M +Prue/M +Pruitt/M +Pru/M +prune/DSRGZM +pruner/M +prurience/MS +prurient/Y +Prussia/M +Prussian/S +prussic +Prut/M +Pryce/M +pry/DRSGTZ +pryer's +prying/Y +P's +PS +p's/A +psalmist/SM +psalm/SGDM +Psalms/M +psalter +Psalter/SM +psaltery/MS +psephologist/M +pseudonymous +pseudonym/SM +pseudopod +pseudo/S +pseudoscience/S +pshaw/SDG +psi/S +psittacoses +psittacosis/M +psoriases +psoriasis/M +psst/S +PST +psychedelically +psychedelic/S +psyche/M +Psyche/M +psychiatric +psychiatrist/SM +psychiatry/MS +psychical/Y +psychic/MS +psychoacoustic/S +psychoacoustics/M +psychoactive +psychoanalysis/M +psychoanalyst/S +psychoanalytic +psychoanalytical +psychoanalyze/SDG +psychobabble/S +psychobiology/M +psychocultural +psychodrama/MS +psychogenic +psychokinesis/M +psycholinguistic/S +psycholinguistics/M +psycholinguists +psychological/Y +psychologist/MS +psychology/MS +psychometric/S +psychometrics/M +psychometry/M +psychoneuroses +psychoneurosis/M +psychopathic/S +psychopath/M +psychopathology/M +psychopaths +psychopathy/SM +psychophysical/Y +psychophysic/S +psychophysics/M +psychophysiology/M +psychosis/M +psycho/SM +psychosocial/Y +psychosomatic/S +psychosomatics/M +psychos/S +psychotherapeutic/S +psychotherapist/MS +psychotherapy/SM +psychotically +psychotic/S +psychotropic/S +psychs +psych/SDG +PT +PTA +Ptah/M +ptarmigan/MS +pt/C +pterodactyl/SM +Pt/M +PTO +Ptolemaic +Ptolemaists +Ptolemy/MS +ptomaine/MS +Pu +pubbed +pubbing +pubertal +puberty/MS +pubes +pubescence/S +pubescent +pubic +pubis/M +publican/AMS +publication/AMS +publicist/SM +publicity/SM +publicized/U +publicize/SDG +publicness/M +publics/A +public/YSP +publishable/U +published/UA +publisher/ASM +publishes/A +publishing/M +publish/JDRSBZG +pub/MS +Puccini/M +puce/SM +pucker/DG +Puckett/M +puck/GZSDRM +puckishness/S +puckish/YP +Puck/M +pudding/MS +puddle/JMGRSD +puddler/M +puddling/M +puddly +pudenda +pudendum/M +pudginess/SM +pudgy/PRT +Puebla/M +Pueblo/MS +pueblo/SM +puerile/Y +puerility/SM +puerperal +puers +Puerto/M +puffball/SM +puffer/M +puffery/M +puffiness/S +puffin/SM +Puff/M +puff/SGZDRM +puffy/PRT +Puget/M +pugged +pugging +Pugh/M +pugilism/SM +pugilistic +pugilist/S +pug/MS +pugnaciousness/MS +pugnacious/YP +pugnacity/SM +puissant/Y +puke/GDS +pukka +Pulaski/SM +pulchritude/SM +pulchritudinous/M +pule/GDS +Pulitzer/SM +pullback/S +pull/DRGZSJ +pullet/SM +pulley/SM +Pullman/MS +pullout/S +pullover/SM +pulmonary +pulpiness/S +pulpit/MS +pulp/MDRGS +pulpwood/MS +pulpy/PTR +pulsar/MS +pulsate/NGSDX +pulsation/M +pulse/ADSG +pulser +pulse's +pulverable +pulverization/MS +pulverized/U +pulverize/GZSRD +pulverizer/M +pulverizes/UA +puma/SM +pumice/SDMG +pummel/SDG +pumpernickel/SM +pump/GZSMDR +pumping/M +pumpkin/MS +punchbowl/M +punched/U +puncheon/MS +puncher/M +punch/GRSDJBZ +punchline/S +Punch/M +punchy/RT +punctilio/SM +punctiliousness/SM +punctilious/PY +punctualities +punctuality/UM +punctualness/M +punctual/PY +punctuate/SDXNG +punctuational +punctuation/M +puncture/SDMG +punditry/S +pundit/SM +pungency/MS +pungent/Y +Punic +puniness/MS +punished/U +punisher/M +punishment/MS +punish/RSDGBL +punitiveness/M +punitive/YP +Punjabi/M +Punjab/M +punk/TRMS +punky/PRS +pun/MS +punned +punning +punster/SM +punter/M +punt/GZMDRS +puny/PTR +pupae +pupal +pupa/M +pupate/NGSD +pupillage/M +pupil/SM +pup/MS +pupped +puppeteer/SM +puppetry/MS +puppet/SM +pupping +puppy/GSDM +puppyish +purblind +Purcell/M +purchasable +purchase/GASD +purchaser/MS +purdah/M +purdahs +Purdue/M +purebred/S +puree/DSM +pureeing +pureness/MS +pure/PYTGDR +purgation/M +purgative/MS +purgatorial +purgatory/SM +purge/GZDSR +purger/M +purify/GSRDNXZ +Purim/SM +Purina/M +purine/SM +purism/MS +puristic +purist/MS +puritanic +puritanical/Y +Puritanism/MS +puritanism/S +puritan/SM +Puritan/SM +purity/SM +purlieu/SM +purl/MDGS +purloin/DRGS +purloiner/M +purple/MTGRSD +purplish +purport/DRSZG +purported/Y +purposefulness/S +purposeful/YP +purposelessness/M +purposeless/PY +purpose/SDVGYM +purposiveness/M +purposive/YP +purr/DSG +purring/Y +purse/DSRGZM +purser/M +pursuance/MS +pursuant +pursuer/M +pursue/ZGRSD +pursuit/MS +purulence/MS +purulent +Purus +purveyance/MS +purvey/DGS +purveyor/MS +purview/SM +Pusan/M +Pusey/M +pushbutton/S +pushcart/SM +pushchair/SM +pushdown +push/DSRBGZ +pusher/M +pushily +pushiness/MS +Pushkin/M +pushover/SM +Pushtu/M +pushy/PRT +pusillanimity/MS +pusillanimous/Y +pus/SM +puss/S +pussycat/S +pussyfoot/DSG +pussy/TRSM +pustular +pustule/MS +putative/Y +Putin/M +put/IS +Putnam/M +Putnem/M +putout/S +putrefaction/SM +putrefactive +putrefy/DSG +putrescence/MS +putrescent +putridity/M +putridness/M +putrid/YP +putsch/S +putted/I +puttee/MS +putter/RDMGZ +putting/I +putt/SGZMDR +puttying/M +putty/SDMG +puzzle/JRSDZLG +puzzlement/MS +puzzler/M +PVC +pvt +Pvt/M +PW +PX +p/XTGJ +Pygmalion/M +pygmy/SM +Pygmy/SM +Pyhrric/M +pyknotic +Pyle/M +pylon/SM +pylori +pyloric +pylorus/M +Pym/M +Pynchon/M +Pyongyang/M +pyorrhea/SM +Pyotr/M +pyramidal/Y +pyramid/GMDS +pyre/MS +Pyrenees +Pyrex/SM +pyridine/M +pyrimidine/SM +pyrite/MS +pyroelectric +pyroelectricity/SM +pyrolysis/M +pyrolyze/RSM +pyromaniac/SM +pyromania/MS +pyrometer/MS +pyrometry/M +pyrophosphate/M +pyrotechnical +pyrotechnic/S +pyrotechnics/M +pyroxene/M +pyroxenite/M +Pyrrhic +Pythagoras/M +Pythagorean/S +Pythias +Python/M +python/MS +pyx/MDSG +q +Q +QA +Qaddafi/M +Qantas/M +Qatar/M +QB +QC +QED +Qingdao +Qiqihar/M +QM +Qom/M +qr +q's +Q's +qt +qty +qua +Quaalude/M +quackery/MS +quackish +quack/SDG +quadded +quadding +quadrangle/MS +quadrangular/M +quadrant/MS +quadraphonic/S +quadrapole +quadratical/Y +quadratic/SM +quadrature/MS +quadrennial/SY +quadrennium/MS +quadric +quadriceps/SM +quadrilateral/S +quadrille/XMGNSD +quadrillion/MH +quadripartite/NY +quadriplegia/SM +quadriplegic/SM +quadrivia +quadrivium/M +quadrupedal +quadruped/MS +quadruple/GSD +quadruplet/SM +quadruplicate/GDS +quadruply/NX +quadrupole +quad/SM +quadword/MS +quaffer/M +quaff/SRDG +quagmire/DSMG +quahog/MS +quail/GSDM +quaintness/MS +quaint/PTYR +quake/GZDSR +Quakeress/M +Quakerism/S +Quaker/SM +quaky/RT +qualification/ME +qualified/UY +qualifier/SM +qualify/EGXSDN +qualitative/Y +quality/MS +qualmish +qualm/SM +quandary/MS +quangos +quanta/M +Quantico/M +quantifiable/U +quantified/U +quantifier/M +quantify/GNSRDZX +quantile/S +quantitativeness/M +quantitative/PY +quantity/MS +quantization/MS +quantizer/M +quantize/ZGDRS +quantum/M +quarantine/DSGM +quark/SM +quarreler/M +quarrellings +quarrelsomeness/MS +quarrelsome/PY +quarrel/SZDRMG +quarrier/M +quarryman/M +quarrymen +quarry/RSDGM +quarterback/SGMD +quarterdeck/MS +quarterer/M +quarterfinal/MS +quartering/M +quarterly/S +quartermaster/MS +quarter/MDRYG +quarterstaff/M +quarterstaves +quartet/SM +quartic/S +quartile/SM +quarto/SM +quart/RMSZ +quartzite/M +quartz/SM +quasar/SM +quash/GSD +quasi +quasilinear +Quasimodo/M +Quaternary +quaternary/S +quaternion/SM +quatrain/SM +quaver/GDS +quavering/Y +quavery +Quayle/M +quayside/M +quay/SM +queasily +queasiness/SM +queasy/TRP +Quebec/M +Quechua/M +Queenie/M +queenly/RT +queen/SGMDY +Queensland/M +Queen/SM +queerness/S +queer/STGRDYP +queller/M +quell/SRDG +Que/M +quenchable/U +quenched/U +quencher/M +quench/GZRSDB +quenchless +Quentin/M +Quent/M +Querida/M +quern/M +querulousness/S +querulous/YP +query/MGRSD +quested/A +quester/AS +quester's +quest/FSIM +questing +questionableness/M +questionable/P +questionably/U +questioned/UA +questioner/M +questioning/UY +questionnaire/MS +question/SMRDGBZJ +quests/A +Quetzalcoatl/M +queued/C +queue/GZMDSR +queuer/M +queues/C +queuing/C +Quezon/M +quibble/GZRSD +quibbler/M +quiche/SM +quicken/RDG +quickie/MS +quicklime/SM +quickness/MS +quick/RNYTXPS +quicksand/MS +quicksilver/GDMS +quickstep/SM +quid/SM +quiesce/D +quiescence/MS +quiescent/YP +quieted/E +quieten/SGD +quieter/E +quieter's +quieting/E +quietly/E +quietness/MS +quiets/E +quietude/IEMS +quietus/MS +quiet/UTGPSDRY +Quillan/M +quill/GSDM +Quill/M +quilter/M +quilting/M +quilt/SZJGRDM +quincentenary/M +quince/SM +Quincey/M +quincy/M +Quincy/M +quinine/MS +Quinlan/M +Quinn/M +quinquennial/Y +quinsy/SM +Quinta/M +Quintana/M +quintessence/SM +quintessential/Y +quintet/SM +quintic +quintile/SM +Quintilian/M +Quintilla/M +quintillion/MH +quintillionth/M +Quintina/M +Quintin/M +Quint/M +quint/MS +Quinton/M +quintuple/SDG +quintuplet/MS +Quintus/M +quip/MS +quipped +quipper +quipping +quipster/SM +quired/AI +quire/MDSG +quires/AI +Quirinal/M +quiring/IA +quirkiness/SM +quirk/SGMD +quirky/PTR +quirt/SDMG +Quisling/M +quisling/SM +quitclaim/GDMS +quit/DGS +quite/SADG +Quito/M +quittance/SM +quitter/SM +quitting +quiver/GDS +quivering/Y +quivery +Quixote/M +quixotic +quixotically +Quixotism/M +quiz/M +quizzed +quizzer/SM +quizzes +quizzical/Y +quizzing +quo/H +quoin/SGMD +quoit/GSDM +quondam +quonset +Quonset +quorate/I +quorum/MS +quotability/S +quota/MS +quotation/SM +quoter/M +quote/UGSD +quot/GDRB +quotidian/S +quotient/SM +qwerty +qwertys +Rabat/M +rabbet/GSMD +Rabbi/M +rabbi/MS +rabbinate/MS +rabbinic +rabbinical/Y +rabbiter/M +rabbit/MRDSG +rabble/GMRSD +rabbler/M +Rabelaisian +Rabelais/M +rabidness/SM +rabid/YP +rabies +Rabi/M +Rabin/M +rabis +Rab/M +raccoon/SM +racecourse/MS +racegoers +racehorse/SM +raceme/MS +race/MZGDRSJ +racer/M +racetrack/SMR +raceway/SM +Rachael/M +Rachele/M +Rachelle/M +Rachel/M +Rachmaninoff/M +racialism/MS +racialist/MS +racial/Y +racily +Racine/M +raciness/MS +racism/S +racist/MS +racketeer/MDSJG +racket/SMDG +rackety +rack/GDRMS +raconteur/SM +racoon's +racquetball/S +racquet's +racy/RTP +radarscope/MS +radar/SM +Radcliffe/M +radded +radder +raddest +Raddie/M +radding +Raddy/M +radial/SY +radiance/SM +radian/SM +radiant/YS +radiate/XSDYVNG +radiation/M +radiative/Y +radiator/MS +radicalism/MS +radicalization/S +radicalize/GSD +radicalness/M +radical/SPY +radices's +radii/M +radioactive/Y +radioactivity/MS +radioastronomical +radioastronomy +radiocarbon/MS +radiochemical/Y +radiochemistry/M +radiogalaxy/S +radiogram/SM +radiographer/MS +radiographic +radiography/MS +radioisotope/SM +radiologic +radiological/Y +radiologist/MS +radiology/MS +radioman/M +radiomen +radiometer/SM +radiometric +radiometry/MS +radionics +radionuclide/M +radiopasteurization +radiophone/MS +radiophysics +radioscopy/SM +radio/SMDG +radiosonde/SM +radiosterilization +radiosterilized +radiotelegraph +radiotelegraphs +radiotelegraphy/MS +radiotelephone/SM +radiotherapist/SM +radiotherapy/SM +radish/MS +radium/MS +radius/M +radix/SM +Rad/M +radon/SM +rad/S +Raeann/M +Rae/M +RAF +Rafaela/M +Rafaelia/M +Rafaelita/M +Rafaellle/M +Rafaello/M +Rafael/M +Rafa/M +Rafe/M +Raffaello/M +Raffarty/M +Rafferty/M +raffia/SM +raffishness/SM +raffish/PY +raffle/MSDG +Raff/M +Rafi/M +Raf/M +rafter/DM +raft/GZSMDR +raga/MS +ragamuffin/MS +ragbag/SM +rage/MS +raggedness/SM +ragged/PRYT +raggedy/TR +ragging +rag/GSMD +raging/Y +raglan/MS +Ragnar/M +Ragnark +ragout/SMDG +ragtag/MS +ragtime/MS +ragweed/MS +ragwort/M +Rahal/M +rah/DG +Rahel/M +rahs +raider/M +raid/MDRSGZ +railbird/S +rail/CDGS +railer/SM +railhead/SM +railing/MS +raillery/MS +railroader/M +railroading/M +railroad/SZRDMGJ +rail's +railwaymen +railway/MS +raiment/SM +Raimondo/M +Raimund/M +Raimundo/M +Raina/M +rainbow/MS +raincloud/S +raincoat/SM +raindrop/SM +Raine/MR +Rainer/M +rainfall/SM +rainforest's +rain/GSDM +Rainier/M +rainless +rainmaker/SM +rainmaking/MS +rainproof/GSD +rainstorm/SM +rainwater/MS +rainy/RT +raise/DSRGZ +raiser/M +raising/M +raisin/MS +rajah/M +rajahs +Rajive/M +raj/M +Rakel/M +rake/MGDRS +raker/M +rakishness/MS +rakish/PY +Raleigh/M +Ralf/M +Ralina/M +rally/GSD +Ralph/M +Ralston/M +Ra/M +Ramada/M +Ramadan/SM +Ramakrishna/M +Rama/M +Raman/M +Ramayana/M +ramble/JRSDGZ +rambler/M +rambling/Y +Rambo/M +rambunctiousness/S +rambunctious/PY +ramekin/SM +ramie/MS +ramification/M +ramify/XNGSD +Ramirez/M +Ramiro/M +ramjet/SM +Ram/M +rammed +ramming +Ramo/MS +Ramona/M +Ramonda/M +Ramon/M +rampage/SDG +rampancy/S +rampant/Y +rampart/SGMD +ramp/GMDS +ramrodded +ramrodding +ramrod/MS +RAM/S +Ramsay/M +Ramses/M +Ramsey/M +ramshackle +ram/SM +rams/S +ran/A +Rana/M +Rancell/M +Rance/M +rancher/M +rancho/SM +ranch/ZRSDMJG +rancidity/MS +rancidness/SM +rancid/P +rancorous/Y +rancor/SM +Randall/M +Randal/M +Randa/M +Randee/M +Randell/M +Randene/M +Randie/M +Randi/M +randiness/S +Rand/M +rand/MDGS +Randolf/M +Randolph/M +randomization/SM +randomize/SRDG +randomness/SM +random/PYS +Randy/M +randy/PRST +Ranee/M +ranee/SM +ranged/C +rangeland/S +ranger/M +ranges/C +range/SM +rang/GZDR +ranginess/S +ranging/C +Rangoon/M +rangy/RPT +Rania/M +Ranice/M +Ranier/M +Rani/MR +Ranique/M +rani's +ranked/U +ranker/M +rank/GZTYDRMPJS +Rankine/M +ranking/M +Rankin/M +rankle/SDG +rankness/MS +Ranna/M +ransacker/M +ransack/GRDS +Ransell/M +ransomer/M +Ransom/M +ransom/ZGMRDS +ranter/M +rant/GZDRJS +ranting/Y +Raoul/M +rapaciousness/MS +rapacious/YP +rapacity/MS +rapeseed/M +rape/SM +Raphaela/M +Raphael/M +rapidity/MS +rapidness/S +rapid/YRPST +rapier/SM +rapine/SM +rapist/MS +rap/MDRSZG +rapped +rappelled +rappelling +rappel/S +rapper/SM +rapping/M +rapporteur/SM +rapport/SM +rapprochement/SM +rapscallion/MS +raptness/S +rapture/MGSD +rapturousness/M +rapturous/YP +rapt/YP +Rapunzel/M +Raquela/M +Raquel/M +rarebit/MS +rarefaction/MS +rarefy/GSD +rareness/MS +rare/YTPGDRS +rarity/SM +Rasalgethi/M +Rasalhague/M +rascal/SMY +rasher/M +rashness/S +rash/PZTYSR +Rasia/M +Rasla/M +Rasmussen/M +raspberry/SM +rasper/M +rasping/Y +rasp/SGJMDR +Rasputin/M +raspy/RT +Rastaban/M +Rastafarian/M +raster/MS +Rastus/M +ratchet/MDSG +rateable +rated/U +rate/KNGSD +ratepayer/SM +rater/M +rate's +Ratfor/M +rather +Rather/M +rathskeller/SM +ratifier/M +ratify/ZSRDGXN +rating/M +ratiocinate/VNGSDX +ratiocination/M +ratio/MS +rationale/SM +rationalism/SM +rationalistic +rationalist/S +rationality/MS +rationalization/SM +rationalizer/M +rationalize/ZGSRD +rationalness/M +rational/YPS +ration/DSMG +Ratliff/M +ratlike +ratline/SM +rat/MDRSJZGB +rattail +rattan/MS +ratted +ratter/MS +ratting +rattlebrain/DMS +rattle/RSDJGZ +rattlesnake/MS +rattletrap/MS +rattling/Y +rattly/TR +rattrap/SM +ratty/RT +raucousness/SM +raucous/YP +Raul/M +raunchily +raunchiness/S +raunchy/RTP +ravage/GZRSD +ravager/M +raveling/S +Ravel/M +ravel/UGDS +raven/JGMRDS +Raven/M +ravenous/YP +raver/M +rave/ZGDRSJ +Ravid/M +Ravi/M +ravine/SDGM +ravioli/SM +ravisher/M +ravishing/Y +ravish/LSRDZG +ravishment/SM +Raviv/M +Rawalpindi/M +rawboned +rawhide/SDMG +Rawley/M +Rawlings/M +Rawlins/M +Rawlinson/M +rawness/SM +raw/PSRYT +Rawson/M +Rayburn/M +Raychel/M +Raye/M +ray/GSMD +Rayleigh/M +Ray/M +Raymond/M +Raymondville/M +Raymund/M +Raymundo/M +Rayna/M +Raynard/M +Raynell/M +Rayner/M +Raynor/M +rayon/SM +Rayshell/M +Raytheon/M +raze/DRSG +razer/M +razorback/SM +razorblades +razor/MDGS +razz/GDS +razzmatazz/S +Rb +RBI/S +RC +RCA +rcpt +RCS +rd +RD +RDA +Rd/M +reabbreviate +reachability +reachable/U +reachably +reached/U +reacher/M +reach/GRB +reacquisition +reactant/SM +reacted/U +reaction +reactionary/SM +reactivity +readability/MS +readable/P +readably +readdress/G +Reade/M +reader/M +readership/MS +Read/GM +readied +readies +readily +readinesses +readiness/UM +reading/M +Reading/M +read/JGZBR +readopt/G +readout/MS +reads/A +readying +ready/TUPR +Reagan/M +Reagen/M +realisms +realism's +realism/U +realistically/U +realistic/U +realist/SM +reality/USM +realizability/MS +realizableness/M +realizable/SMP +realizably/S +realization/MS +realized/U +realize/JRSDBZG +realizer/M +realizes/U +realizing/MY +realm/M +realness/S +realpolitik/SM +real/RSTP +realtor's +Realtor/S +realty/SM +Rea/M +reamer/M +ream/MDRGZ +Reamonn/M +reanimate +reaper/M +reappraise/G +reap/SGZ +rear/DRMSG +rearguard/MS +rearmost +rearrange/L +rearward/S +reasonableness/SMU +reasonable/UP +reasonably/U +Reasoner/M +reasoner/SM +reasoning/MS +reasonless +reasons +reason/UBDMG +reassess/GL +reassuringly/U +reattach/GSL +reawakening/M +Reba/M +rebate/M +Rebbecca/M +Rebeca/M +Rebecca's +Rebecka/M +Rebekah/M +Rebeka/M +Rebekkah/M +rebeller +rebellion/SM +rebelliousness/MS +rebellious/YP +rebel/MS +Rebe/M +rebid +rebidding +rebind/G +rebirth +reboil/G +rebook +reboot/ZR +rebound/G +rebroadcast/MG +rebuke/RSDG +rebuking/Y +rebus +rebuttal/SM +rebutting +rec +recalcitrance/SM +recalcitrant/S +recalibrate/N +recantation/S +recant/G +recap +recappable +recapping +recast/G +recd +rec'd +recede +receipt/SGDM +receivable/S +received/U +receiver/M +receivership/SM +receive/ZGRSDB +recency/M +recension/M +recentness/SM +recent/YPT +receptacle/SM +receptionist/MS +reception/MS +receptiveness/S +receptive/YP +receptivity/S +receptor/MS +recessional/S +recessionary +recessiveness/M +recessive/YPS +recess/SDMVG +rechargeable +recheck/G +recherch +recherches +recidivism/MS +recidivist/MS +Recife/M +recipe/MS +recipiency +recipient/MS +reciprocal/SY +reciprocate/NGXVDS +reciprocation/M +reciprocity/MS +recitalist/S +recital/MS +recitative/MS +reciter/M +recite/ZR +recked +recking +recklessness/S +reckless/PY +reckoner/M +reckoning/M +reckon/SGRDJ +reclaim/B +reclamation/SM +recliner/M +recline/RSDZG +recluse/MVNS +reclusion/M +recode/G +recognizability +recognizable/U +recognizably +recognize/BZGSRD +recognizedly/S +recognized/U +recognizer/M +recognizingly/S +recognizing/UY +recoilless +recoinage +recolor/GD +recombinant +recombine +recommended/U +recompense/GDS +recompute/B +reconciled/U +reconciler/M +reconcile/SRDGB +reconditeness/M +recondite/YP +reconfigurability +reconfigure/R +reconnaissance/MS +reconnect/R +reconnoiter/GSD +reconquer/G +reconsecrate +reconstitute +reconstructed/U +Reconstruction/M +reconsult/G +recontact/G +recontaminate/N +recontribute +recook/G +recopy/G +recorded/AU +records/A +record/ZGJ +recourse +recoverability +recoverable/U +recover/B +recovery/MS +recreant/S +recreational +recriminate/GNVXDS +recrimination/M +recriminatory +recross/G +recrudesce/GDS +recrudescence/MS +recrudescent +recruiter/M +recruitment/MS +recruit/ZSGDRML +recrystallize +rectal/Y +rectangle/SM +rectangular/Y +recta's +rectifiable +rectification/M +rectifier/M +rectify/DRSGXZN +rectilinear/Y +rectitude/MS +recto/MS +rector/SM +rectory/MS +rectum/SM +recumbent/Y +recuperate/VGNSDX +recuperation/M +recur +recurrence/MS +recurrent +recurse/NX +recursion/M +recusant/M +recuse +recyclable/S +recycle/BZ +redact/DGS +redaction/SM +redactor/MS +redbird/SM +redbreast/SM +redbrick/M +redbud/M +redcap/MS +redcoat/SM +redcurrant/M +redden/DGS +redder +reddest +redding +reddish/P +Redd/M +redeclaration +redecorate +redeemable/U +redeem/BRZ +redeemed/U +redeemer/M +Redeemer/M +redemptioner/M +redemption/RMS +redemptive +redeposit/M +redetermination +Redford/M +Redgrave/M +redhead/DRMS +Redhook/M +redial/G +redirect/G +redirection +redlining/S +Redmond/M +redneck/SMD +redness/MS +redo/G +redolence/MS +redolent +Redondo/M +redouble/S +redoubtably +redound/GDS +red/PYS +redshift/S +redskin/SM +Redstone/M +reduced/U +reducer/M +reduce/RSDGZ +reducibility/M +reducible +reducibly +reductionism/M +reductionist/S +reduction/SM +reduct/V +redundancy/SM +redundant/Y +redwood/SM +redye +redyeing +Reeba/M +Reebok/M +Reece/M +reecho/G +reed/GMDR +reediness/SM +reeding/M +Reed/M +Reedville/M +reedy/PTR +reefer/M +reef/GZSDRM +reeker/M +reek/GSR +reeler/M +reel's +reel/USDG +Ree/MDS +Reena/M +reenforcement +reentrant +Reese/M +reestimate/M +Reeta/M +Reeva/M +reeve/G +Reeves +reexamine +refection/SM +refectory/SM +refer/B +refereed/U +refereeing +referee/MSD +reference/CGSRD +referenced/U +reference's +referencing/U +referendum/MS +referentiality +referential/YM +referent/SM +referral/SM +referred +referrer/S +referring +reffed +reffing +refile +refinance +refined/U +refine/LZ +refinement/MS +refinish/G +refit +reflectance/M +reflected/U +reflectional +reflection/SM +reflectiveness/M +reflective/YP +reflectivity/M +reflector/MS +reflect/SDGV +reflexion/MS +reflexiveness/M +reflexive/PSY +reflexivity/M +reflex/YV +reflooring +refluent +reflux/G +refocus/G +refold/G +reforestation +reforge/G +reformatory/SM +reform/B +reformed/U +reformer/M +reformism/M +reformist/S +refract/DGVS +refractiveness/M +refractive/PY +refractometer/MS +refractoriness/M +refractory/PS +refrain/DGS +refreshed/U +refreshing/Y +refresh/LB +refreshment/MS +refrigerant/MS +refrigerated/U +refrigerate/XDSGN +refrigeration/M +refrigerator/MS +refrozen +refry/GS +refugee/MS +refuge/SDGM +Refugio/M +refulgence/SM +refulgent +refund/B +refunder/M +refurbish/L +refurbishment/S +refusal/SM +refuse/R +refuser/M +refutation/MS +refute/GZRSDB +refuter/M +ref/ZS +reg +regale/L +regalement/S +regal/GYRD +regalia/M +Regan/M +regard/EGDS +regardless/PY +regather/G +regatta/MS +regency/MS +regeneracy/MS +regenerately +regenerateness/M +regenerate/U +Regen/M +reggae/SM +Reggie/M +Reggi/MS +Reggy/M +regicide/SM +regime/MS +regimen/MS +regimental/S +regimentation/MS +regiment/SDMG +Reginae +Reginald/M +Regina/M +Reginauld/M +Regine/M +regionalism/MS +regional/SY +region/SM +Regis/M +register's +register/UDSG +registrable +registrant/SM +registrar/SM +registration/AM +registrations +registry/MS +Reg/MN +regnant +Regor/M +regress/DSGV +regression/MS +regressiveness/M +regressive/PY +regressors +regretfulness/M +regretful/PY +regret/S +regrettable +regrettably +regretted +regretting +reground +regroup/G +regrow/G +regularity/MS +regularization/MS +regularize/SDG +regular/YS +regulate/CSDXNG +regulated/U +regulation/M +regulative +regulator/SM +regulatory +Regulus/M +regurgitate/XGNSD +regurgitation/M +rehabbed +rehabbing +rehabilitate/SDXVGN +rehabilitation/M +rehab/S +rehang/G +rehear/GJ +rehearsal/SM +rehearse +rehearsed/U +rehearser/M +rehears/R +reheat/G +reheating/M +Rehnquist +rehydrate +Reichenberg/M +Reich/M +Reichstags +Reichstag's +Reidar/M +Reider/M +Reid/MR +reign/MDSG +Reiko/M +Reilly/M +reimburse/GSDBL +reimbursement/MS +Reinald/M +Reinaldo/MS +Reina/M +reindeer/M +Reine/M +reinforced/U +reinforce/GSRDL +reinforcement/MS +reinforcer/M +rein/GDM +Reinhard/M +Reinhardt/M +Reinhold/M +Reinold/M +reinstate/L +reinstatement/MS +reinsurance +Reinwald/M +reissue +REIT +reiterative/SP +rejecter/M +rejecting/Y +rejection/SM +rejector/MS +reject/RDVGS +rejigger +rejoice/RSDJG +rejoicing/Y +rejoinder/SM +rejuvenate/NGSDX +rejuvenatory +relapse +relatedly +relatedness/MS +related/U +relater/M +relate/XVNGSZ +relational/Y +relation/M +relationship/MS +relativeness/M +relative/SPY +relativism/M +relativistic +relativistically +relativist/MS +relativity/MS +relator's +relaxant/SM +relaxation/MS +relaxedness/M +relaxed/YP +relax/GZD +relaxing/Y +relay/GDM +relearn/G +releasable/U +release/B +released/U +relenting/U +relentlessness/SM +relentless/PY +relent/SDG +relevance/SM +relevancy/MS +relevant/Y +reliability/UMS +reliables +reliable/U +reliably/U +reliance/MS +reliant/Y +relicense/R +relic/MS +relict/C +relict's +relief/M +relievedly +relieved/U +reliever/M +relieve/RSDZG +religionists +religion/SM +religiosity/M +religiousness/MS +religious/PY +relink/G +relinquish/GSDL +relinquishment/SM +reliquary/MS +relish/GSD +relive/GB +reload/GR +relocate/B +reluctance/MS +reluctant/Y +rel/V +rely/DG +rem +Re/M +remade/S +remainder/SGMD +remain/GD +remake/M +remand/DGS +remap +remapping +remarkableness/S +remarkable/U +remarkably +remark/BG +remarked/U +Remarque/M +rematch/G +Rembrandt/M +remeasure/D +remediableness/M +remediable/P +remedy/SDMG +remembered/U +rememberer/M +remember/GR +remembrance/MRS +remembrancer/M +Remington/M +reminisce/GSD +reminiscence/SM +reminiscent/Y +remissness/MS +remiss/YP +remit/S +remittance/MS +remitted +remitting/U +Rem/M +remnant/MS +remodel/G +remolding +remonstrant/MS +remonstrate/SDXVNG +remonstration/M +remonstrative/Y +remorsefulness/M +remorseful/PY +remorselessness/MS +remorseless/YP +remorse/SM +remoteness/MS +remote/RPTY +remoulds +removal/MS +REM/S +remunerated/U +remunerate/VNGXSD +remuneration/M +remunerativeness/M +remunerative/YP +Remus/M +Remy/M +Renado/M +Renae/M +renaissance/S +Renaissance/SM +renal +Renaldo/M +Rena/M +Renard/M +Renascence/SM +Renata/M +Renate/M +Renato/M +renaturation +Renaud/M +Renault/MS +rend +renderer/M +render/GJRD +rendering/M +rendezvous/DSMG +rendition/GSDM +rend/RGZS +Renee/M +renegade/SDMG +renege/GZRSD +reneger/M +Renelle/M +Renell/M +Rene/M +renewal/MS +renew/BG +renewer/M +Renie/M +rennet/MS +Rennie/M +rennin/SM +Renoir/M +Reno/M +renounce/LGRSD +renouncement/MS +renouncer/M +renovate/NGXSD +renovation/M +renovator/SM +renown/SGDM +Rensselaer/M +rentaller +rental/SM +renter/M +rent/GZMDRS +renumber/G +renumeration +renunciate/VNX +renunciation/M +Renville/M +reoccupy/G +reopen/G +reorganized/U +repack/G +repairable/U +repair/BZGR +repairer/M +repairman/M +repairmen +repairs/E +repaper +reparable +reparation/SM +reparteeing +repartee/MDS +repartition/Z +repast/G +repatriate/SDXNG +repave +repealer/M +repeal/GR +repeatability/M +repeatable/U +repeatably +repeated/Y +repeater/M +repeat/RDJBZG +repelled +repellent/SY +repelling/Y +repel/S +repentance/SM +repentant/SY +repent/RDG +repertoire/SM +repertory/SM +repetition +repetitiousness/S +repetitious/YP +repetitiveness/MS +repetitive/PY +repine/R +repiner/M +replace/RL +replay/GM +replenish/LRSDG +replenishment/S +repleteness/MS +replete/SDPXGN +repletion/M +replica/SM +replicate/SDVG +replicator/S +replug +reply/X +Rep/M +repopulate +reported/Y +reportorial/Y +reposeful +repose/M +repository/MS +reprehend/GDS +reprehensibility/MS +reprehensibleness/M +reprehensible/P +reprehensibly +reprehension/MS +representable/U +representational/Y +representativeness/M +Representative/S +representative/SYMP +representativity +represented/U +represent/GB +repression/SM +repressiveness/M +repressive/YP +repress/V +reprieve/GDS +reprimand/SGMD +reprint/M +reprisal/MS +reproacher/M +reproachfulness/M +reproachful/YP +reproach/GRSDB +reproaching/Y +reprobate/N +reprocess/G +reproducibility/MS +reproducible/S +reproducibly +reproductive/S +reproof/G +reprove/R +reproving/Y +rep/S +reptile/SM +reptilian/S +Republicanism/S +republicanism/SM +Republican/S +republic/M +republish/G +repudiate/XGNSD +repudiation/M +repudiator/S +repugnance/MS +repugnant/Y +repulse/VNX +repulsion/M +repulsiveness/MS +repulsive/PY +reputability/SM +reputably/E +reputation/SM +reputed/Y +repute/ESB +reputing +requested/U +request/G +Requiem/MS +requiem/SM +require/LR +requirement/MS +requisiteness/M +requisite/PNXS +requisitioner/M +requisition/GDRM +requital/MS +requited/U +requiter/M +requite/RZ +reread/G +rerecord/G +rerouteing +rerunning +res/C +rescale +rescind/SDRG +rescission/SM +rescue/GZRSD +reseal/BG +research/MB +reselect/G +resemblant +resemble/DSG +resend/G +resent/DSLG +resentfulness/SM +resentful/PY +resentment/MS +reserpine/MS +reservation/MS +reservednesses +reservedness/UM +reserved/UYP +reservist/SM +reservoir/MS +reset/RDG +resettle/L +reshipping +reshow/G +reshuffle/M +reside/G +residence/MS +residency/SM +residential/Y +resident/SM +resider/M +residua +residual/YS +residuary +residue/SM +residuum/M +resignation/MS +resigned/YP +resilience/MS +resiliency/S +resilient/Y +resin/D +resinlike +resinous +resiny +resistance/SM +Resistance/SM +resistantly +resistants +resistant/U +resisted/U +resistible +resistibly +resisting/U +resistiveness/M +resistive/PY +resistivity/M +resistless +resistor/MS +resist/RDZVGS +resize/G +resold +resole/G +resoluble +resoluteness/MS +resolute/PYTRV +resolvability/M +resolvable/U +resolved/U +resolvent +resonance/SM +resonant/YS +resonate/DSG +resonator/MS +resorption/MS +resort/R +resound/G +resourcefulness/SM +resourceful/PY +resp +respectability/SM +respectable/SP +respectably +respect/BSDRMZGV +respected/E +respectful/EY +respectfulness/SM +respecting/E +respectiveness/M +respective/PY +respect's/E +respects/E +respell/G +respiration/MS +respirator/SM +respiratory/M +resplendence/MS +resplendent/Y +respondent/MS +respond/SDRZG +responser/M +response/RSXMV +responsibility/MS +responsibleness/M +responsible/P +responsibly +responsiveness/MSU +responsive/YPU +respray/G +restart/B +restate/L +restaurant/SM +restaurateur/SM +rest/DRSGVM +rested/U +rester/M +restfuller +restfullest +restfulness/MS +restful/YP +restitution/SM +restiveness/SM +restive/PY +restlessness/MS +restless/YP +restorability +Restoration/M +restoration/MS +restorative/PYS +restorer/M +restore/Z +restrained/UY +restraint/MS +restrict/DVGS +restricted/YU +restriction/SM +restrictively +restrictiveness/MS +restrictives +restrictive/U +restroom/SM +restructurability +restructure +rest's/U +rests/U +restudy/M +restyle +resubstitute +resultant/YS +result/SGMD +resume/SDBG +resumption/MS +resurface +resurgence/MS +resurgent +resurrect/GSD +resurrection/SM +resurvey/G +resuscitate/XSDVNG +resuscitation/M +resuscitator/MS +retail/Z +retainer/M +retain/LZGSRD +retake +retaliate/VNGXSD +retaliation/M +retaliatory +Reta/M +retardant/SM +retardation/SM +retarder/M +retard/ZGRDS +retch/SDG +retention/SM +retentiveness/S +retentive/YP +retentivity/M +retest/G +Retha/M +rethought +reticence/S +reticent/Y +reticle/SM +reticular +reticulate/GNYXSD +reticulation/M +reticule/MS +reticulum/M +retinal/S +retina/SM +retinue/MS +retiredness/M +retiree/MS +retire/L +retirement/SM +retiring/YP +retort/GD +retract/DG +retractile +retrench/L +retrenchment/MS +retributed +retribution/MS +retributive +retrieval/SM +retriever/M +retrieve/ZGDRSB +retroactive/Y +retrofire/GMSD +retrofit/S +retrofitted +retrofitting +retroflection +retroflex/D +retroflexion/M +retrogradations +retrograde/GYDS +retrogression/MS +retrogressive/Y +retrogress/SDVG +retrorocket/MS +retro/SM +retrospection/MS +retrospective/SY +retrospect/SVGMD +retrovirus/S +retrovision +retry/G +retsina/SM +returnable/S +returned/U +returnee/SM +retype +Reube/M +Reuben/M +Reub/NM +Reunion/M +reuse/B +Reuters +Reuther/M +reutilization +Reuven/M +Reva/M +revanchist +revealed/U +revealingly +revealing/U +reveal/JBG +reveille/MS +revelation/MS +Revelation/MS +revelatory +revelry/MS +revel/SJRDGZ +revenge/MGSRD +revenger/M +revenuer/M +revenue/ZR +reverberant +reverberate/XVNGSD +reverberation/M +revere/GSD +Revere/M +reverencer/M +reverence/SRDGM +Reverend +reverend/SM +reverential/Y +reverent/Y +reverie/SM +reversal/MS +reverser/M +reverse/Y +reversibility/M +reversible/S +reversibly +reversioner/M +reversion/R +revers/M +reverter/M +revertible +revert/RDVGS +revet/L +revetment/SM +review/G +revile/GZSDL +revilement/MS +reviler/M +revise/BRZ +revised/U +revisionary +revisionism/SM +revisionist/SM +revitalize/ZR +revivalism/MS +revivalist/MS +revival/SM +reviver/M +revive/RSDG +revivification/M +revivify/X +Revkah/M +Revlon/M +Rev/M +revocable +revoke/GZRSD +revolter/M +revolt/GRD +revolting/Y +revolutionariness/M +revolutionary/MSP +revolutionist/MS +revolutionize/GDSRZ +revolutionizer/M +revolution/SM +revolve/BSRDZJG +revolver/M +revue/MS +revulsion/MS +revved +revving +rev/ZM +rewarded/U +rewarding/Y +rewarm/G +reweave +rewedding +reweigh/G +rewind/BGR +rewire/G +rework/G +rexes +Rex/M +Reyes +Reykjavik/M +re/YM +Rey/M +Reynaldo/M +Reyna/M +Reynard/M +Reynold/SM +rezone +Rf +RF +RFC +RFD +R/G +rhapsodic +rhapsodical +rhapsodize/GSD +rhapsody/SM +Rhea/M +rhea/SM +Rheba/M +Rhee/M +Rheims/M +Rheinholdt/M +Rhenish +rhenium/MS +rheology/M +rheostat/MS +rhesus/S +Rheta/M +rhetorical/YP +rhetorician/MS +rhetoric/MS +Rhetta/M +Rhett/M +rheumatically +rheumatic/S +rheumatics/M +rheumatism/SM +rheumatoid +rheum/MS +rheumy/RT +Rhiamon/M +Rhianna/M +Rhiannon/M +Rhianon/M +Rhinelander/M +Rhineland/RM +Rhine/M +rhinestone/SM +rhinitides +rhinitis/M +rhinoceros/MS +rhino/MS +rhinotracheitis +rhizome/MS +Rh/M +Rhoda/M +Rhodes +Rhodesia/M +Rhodesian/S +Rhodia/M +Rhodie/M +rhodium/MS +rhododendron/SM +rhodolite/M +rhodonite/M +Rhody/M +rhombic +rhomboidal +rhomboid/SM +rhombus/SM +rho/MS +Rhona/M +Rhonda/M +Rhone +rhubarb/MS +rhyme/DSRGZM +rhymester/MS +Rhys/M +rhythmical/Y +rhythmic/S +rhythmics/M +rhythm/MS +RI +rial/MS +Riane/M +Riannon/M +Rianon/M +ribaldry/MS +ribald/S +ribbed +Ribbentrop/M +ribber/S +ribbing/M +ribbon/DMSG +ribcage +rib/MS +riboflavin/MS +ribonucleic +ribosomal +ribosome/MS +Rica/M +Rican/SM +Ricard/M +Ricardo/M +Ricca/M +Riccardo/M +rice/DRSMZG +Rice/M +ricer/M +Richard/MS +Richardo/M +Richardson/M +Richart/M +Richelieu/M +richen/DG +Richey/M +Richfield/M +Richie/M +Richland/M +Rich/M +Richmond/M +Richmound/M +richness/MS +Richter/M +Richthofen/M +Richy/M +rich/YNSRPT +Rici/M +Rickard/M +Rickenbacker/M +Rickenbaugh/M +Rickert/M +rickets/M +rickety/RT +Rickey/M +rick/GSDM +Rickie/M +Ricki/M +Rick/M +Rickover/M +rickrack/MS +rickshaw/SM +Ricky/M +Ric/M +ricochet/GSD +Rico/M +Ricoriki/M +ricotta/MS +riddance/SM +ridden +ridding +riddle/GMRSD +Riddle/M +ride/CZSGR +Ride/M +rider/CM +riderless +ridership/S +ridge/DSGM +Ridgefield/M +ridgepole/SM +Ridgway/M +ridgy/RT +ridicule/MGDRS +ridiculer/M +ridiculousness/MS +ridiculous/PY +riding/M +rid/ZGRJSB +Riemann/M +Riesling/SM +rife/RT +riff/GSDM +riffle/SDG +riffraff/SM +rifled/U +rifle/GZMDSR +rifleman/M +riflemen +rifler/M +rifling/M +rift/GSMD +Riga/M +rigamarole's +rigatoni/M +Rigel/M +rigged +rigger/SM +rigging/MS +Riggs/M +righteousnesses/U +righteousness/MS +righteous/PYU +rightfulness/MS +rightful/PY +rightism/SM +rightist/S +rightmost +rightness/MS +Right/S +right/SGTPYRDN +rightsize/SDG +rights/M +rightward/S +rigidify/S +rigidity/S +rigidness/S +rigid/YP +rigmarole/MS +rig/MS +Rigoberto/M +Rigoletto/M +rigor/MS +rigorousness/S +rigorous/YP +Riki/M +Rikki/M +Rik/M +rile/DSG +Riley/M +Rilke/M +rill/GSMD +Rimbaud/M +rime/MS +rimer/M +rim/GSMDR +rimless +rimmed +rimming +Rinaldo/M +Rina/M +rind/MDGS +Rinehart/M +ringer/M +ring/GZJDRM +ringing/Y +ringleader/MS +ringlet/SM +ringlike +Ringling/M +Ring/M +ringmaster/MS +Ringo/M +ringside/ZMRS +ringworm/SM +rink/GDRMS +rinse/DSRG +Riobard/M +Rio/MS +Riordan/M +rioter/M +riotousness/M +riotous/PY +riot/SMDRGZJ +RIP +riparian/S +ripcord/SM +ripened/U +ripenesses +ripeness/UM +ripen/RDG +ripe/PSY +riper/U +ripest/U +Ripley/M +Rip/M +rip/NDRSXTG +ripoff/S +riposte/SDMG +ripped +ripper/SM +ripping +rippler/M +ripple/RSDGM +ripply/TR +ripsaw/GDMS +riptide/SM +Risa/M +RISC +risen +riser/M +rise/RSJZG +risibility/SM +risible/S +rising/M +risker/M +risk/GSDRM +riskily +riskiness/MS +risky/RTP +risotto/SM +risqu +rissole/M +Ritalin +Rita/M +Ritchie/M +rite/DSM +Ritter/M +ritualism/SM +ritualistic +ritualistically +ritualized +ritual/MSY +Ritz/M +ritzy/TR +rivaled/U +Rivalee/M +rivalry/MS +rival/SGDM +Riva/MS +rive/CSGRD +Rivera/M +riverbank/SM +riverbed/S +riverboat/S +river/CM +riverfront +riverine +Rivers +Riverside/M +riverside/S +Riverview/M +riveter/M +rivet/GZSRDM +riveting/Y +Riviera/MS +Rivi/M +Rivkah/M +rivulet/SM +Rivy/M +riv/ZGNDR +Riyadh/M +riyal/SM +rm +RMS +RN +RNA +Rn/M +roach/GSDM +Roach/M +roadbed/MS +roadblock/SMDG +roadhouse/SM +roadie/S +roadkill/S +road/MIS +roadrunner/MS +roadshow/S +roadside/S +roadsigns +roadster/SM +roadsweepers +roadway/SM +roadwork/SM +roadworthy +roam/DRGZS +Roana/M +Roanna/M +Roanne/M +Roanoke/M +roan/S +roar/DRSJGZ +roarer/M +roaring/T +Roarke/M +roaster/M +roast/SGJZRD +robbed +robber/SM +Robbert/M +robbery/SM +Robbie/M +Robbi/M +robbing +Robbin/MS +Robb/M +Robby/M +Robbyn/M +robe/ESDG +Robena/M +Robenia/M +Robers/M +Roberson/M +Roberta/M +Robert/MS +Roberto/M +Robertson/SM +robe's +Robeson/M +Robespierre/M +Robina/M +Robinet/M +Robinetta/M +Robinette/M +Robinett/M +Robinia/M +Robin/M +robin/MS +Robinson/M +Robinsonville/M +Robles/M +Rob/MZ +robotic/S +robotism +robotize/GDS +robot/MS +rob/SDG +Robson/M +Robt/M +robustness/SM +robust/RYPT +Roby/M +Robyn/M +Rocco/M +Rocha/M +Rochambeau/M +Rochella/M +Rochelle/M +Rochell/M +Roche/M +Rochester/M +Rochette/M +Roch/M +rockabilly/MS +rockabye +Rockaway/MS +rockbound +Rockefeller/M +rocker/M +rocketry/MS +rocket/SMDG +Rockey/M +rockfall/S +Rockford/M +rock/GZDRMS +Rockie/M +rockiness/MS +Rockland/M +Rock/M +Rockne/M +Rockville/M +Rockwell/M +Rocky/SM +rocky/SRTP +rococo/MS +Roda/M +rodded +Roddenberry/M +rodder +Roddie/M +rodding +Rodd/M +Roddy/M +rodent/MS +rodeo/SMDG +Roderich/M +Roderick/M +Roderic/M +Roderigo/M +rode/S +Rodger/M +Rodge/ZMR +Rodie/M +Rodi/M +Rodina/M +Rodin/M +Rod/M +Rodney/M +Rodolfo/M +Rodolphe/M +Rodolph/M +Rodrick/M +Rodrigo/M +Rodriguez/M +Rodrique/M +Rodriquez/M +rod/SGMD +roebuck/SM +Roentgen's +roentgen/SM +roe/SM +ROFL +Rogelio/M +roger/GSD +Rogerio/M +Roger/M +Roget/M +Rog/MRZ +rogued/K +rogue/GMDS +roguery/MS +rogues/K +roguing/K +roguishness/SM +roguish/PY +roil/SGD +Roi/SM +roisterer/M +roister/SZGRD +Rojas/M +Roland/M +Rolando/M +Roldan/M +role/MS +Roley/M +Rolfe/M +Rolf/M +Rolland/M +rollback/SM +rolled/A +Rollerblade/S +rollerskating +roller/SM +rollick/DGS +rollicking/Y +Rollie/M +rolling/S +Rollin/SM +Rollo/M +rollover/S +roll/UDSG +Rolodex +Rolph/M +Rolvaag/M +ROM +romaine/MS +Romain/M +Roma/M +romancer/M +romance/RSDZMG +Romanesque/S +Romania/M +Romanian/SM +Romano/MS +Romanov/M +roman/S +Romansh/M +Romans/M +Roman/SM +romantically/U +romanticism/MS +Romanticism/S +romanticist/S +romanticize/SDG +romantic/MS +Romany/SM +Romeo/MS +romeo/S +Romero/M +Rome/SM +Rommel/M +Romney/M +Romola/M +Romona/M +Romonda/M +romper/M +romp/GSZDR +Rom/SM +Romulus/M +Romy/M +Ronalda/M +Ronald/M +Rona/M +Ronda/M +rondo/SM +Ronica/M +Ron/M +Ronna/M +Ronnica/M +Ronnie/M +Ronni/M +Ronny/M +Ronstadt/M +Rontgen +Roobbie/M +rood/MS +roof/DRMJGZS +roofer/M +roofgarden +roofing/M +roofless +rooftop/S +rookery/MS +rook/GDMS +rookie/SRMT +roomer/M +roomette/SM +roomful/MS +roominess/MS +roommate/SM +room/MDRGZS +roomy/TPSR +Rooney/M +Rooseveltian +Roosevelt/M +rooster/M +roost/SGZRDM +rooted/P +rooter/M +rootlessness/M +rootless/P +rootlet/SM +Root/M +root/MGDRZS +rootstock/M +rope/DRSMZG +roper/M +roping/M +Roquefort/MS +Roquemore/M +Rora/M +Rorie/M +Rori/M +Rorke/M +Rorschach +Rory/M +Rosabella/M +Rosabelle/M +Rosabel/M +Rosaleen/M +Rosales/M +Rosalia/M +Rosalie/M +Rosalinda/M +Rosalinde/M +Rosalind/M +Rosaline/M +Rosalynd/M +Rosalyn/M +Rosa/M +Rosamond/M +Rosamund/M +Rosana/M +Rosanna/M +Rosanne/M +Rosario/M +rosary/SM +Roscoe/M +Rosco/M +Roseanna/M +Roseanne/M +Roseann/M +roseate/Y +Roseau +rosebud/MS +rosebush/SM +Rosecrans/M +Roseland/M +Roselia/M +Roseline/M +Roselin/M +Rosella/M +Roselle/M +Rose/M +Rosemaria/M +Rosemarie/M +Rosemary/M +rosemary/MS +rose/MGDS +Rosemonde/M +Rosenberg/M +Rosenblum/M +Rosendo/M +Rosene/M +Rosen/M +Rosenthal/M +Rosenzweig/M +Rosetta/M +Rosette/M +rosette/SDMG +rosewater +rosewood/SM +Roshelle/M +Rosicrucian/M +Rosie/M +rosily +Rosina/M +rosiness/MS +rosin/SMDG +Rosita/M +Roslyn/M +Rosmunda/M +Ros/N +Ross +Rossetti/M +Rossie/M +Rossi/M +Rossini/M +Rossy/M +Rostand/M +roster/DMGS +Rostov/M +rostra's +rostrum/SM +Roswell/M +Rosy/M +rosy/RTP +rota/MS +Rotarian/SM +rotary/S +rotated/U +rotate/VGNXSD +rotational/Y +rotation/M +rotative/Y +rotator/SM +rotatory +ROTC +rote/MS +rotgut/MS +Roth/M +Rothschild/M +rotisserie/MS +rotogravure/SM +rotor/MS +rototill/RZ +rot/SDG +rotted +rottenness/S +rotten/RYSTP +Rotterdam/M +rotter/M +rotting +rotunda/SM +rotundity/S +rotundness/S +rotund/SDYPG +Rouault/M +rou/MS +rouge/GMDS +roughage/SM +roughen/DG +rougher/M +roughhouse/GDSM +roughish +roughneck/MDSG +roughness/MS +roughs +roughshod +rough/XPYRDNGT +roulette/MGDS +roundabout/PSM +roundedness/M +rounded/P +roundelay/SM +roundels +rounder/M +roundhead/D +roundheadedness/M +roundheaded/P +roundhouse/SM +roundish +roundness/MS +roundoff +roundup/MS +roundworm/MS +round/YRDSGPZT +Rourke/M +rouse/DSRG +rouser/M +Rousseau/M +roustabout/SM +roust/SGD +route/ASRDZGJ +router/M +route's +rout/GZJMDRS +routine/SYM +routing/M +routinize/GSD +Rouvin/M +rover/M +Rover/M +rove/ZGJDRS +roving/M +Rowan/M +rowboat/SM +rowdily +rowdiness/MS +rowdyism/MS +rowdy/PTSR +rowel/DMSG +Rowe/M +Rowena/M +rowen/M +Rowen/M +rower/M +Rowland/M +Rowley/M +Row/MN +Rowney/M +row/SJZMGNDR +Roxana/M +Roxane/M +Roxanna/M +Roxanne/M +Roxie/M +Roxi/M +Roxine/M +Roxy/M +royalist/SM +Royall/M +Royal/M +royal/SY +royalty/MS +Royce/M +Roy/M +Rozalie/M +Rozalin/M +Rozamond/M +Rozanna/M +Rozanne/M +Rozele/M +Rozella/M +Rozelle/M +Roze/M +Rozina/M +Roz/M +RP +rpm +RPM +rps +RR +Rriocard/M +rs +r's +R's +RSFSR +RSI +RSV +RSVP +RSX +rt +rte +Rte +RTFM +r/TGVJ +Rubaiyat/M +rubato/MS +rubbed +rubberize/GSD +rubberneck/DRMGSZ +rubber/SDMG +rubbery/TR +rubbing/M +rubbish/DSMG +rubbishy +rubble/GMSD +rubdown/MS +rubella/MS +Rube/M +Ruben/MS +rube/SM +Rubetta/M +Rubia/M +Rubicon/SM +rubicund +rubidium/SM +Rubie/M +Rubik/M +Rubi/M +Rubina/M +Rubin/M +Rubinstein/M +ruble/MS +rubout +rubric/MS +rub/S +Ruby/M +ruby/MTGDSR +Ruchbah/M +ruck/M +rucksack/SM +ruckus/SM +ruction/SM +rudderless +rudder/MS +Ruddie/M +ruddiness/MS +Rudd/M +Ruddy/M +ruddy/PTGRSD +rudeness/MS +rude/PYTR +Rudie/M +Rudiger/M +rudimentariness/M +rudimentary/P +rudiment/SM +Rudolf/M +Rudolfo/M +Rudolph/M +Rudyard/M +Rudy/M +ruefulness/S +rueful/PY +rue/GDS +Rufe/M +ruff/GSYDM +ruffian/GSMDY +ruffled/U +ruffler/M +ruffle/RSDG +ruffly/TR +Rufus/M +Rugby's +rugby/SM +ruggedness/S +rugged/PYRT +Ruggiero/M +rugging +rug/MS +Ruhr/M +ruination/MS +ruiner/M +ruin/MGSDR +ruinousness/M +ruinous/YP +Ruiz/M +rulebook/S +ruled/U +rule/MZGJDRS +ruler/GMD +ruling/M +Rumanian's +Rumania's +rumba/GDMS +rumble/JRSDG +rumbler/M +rumbustious +rumen/M +Rumford/M +Ru/MH +ruminant/YMS +ruminate/VNGXSD +ruminative/Y +rummage/GRSD +rummager/M +Rummel/M +rummer +rummest +rummy/TRSM +rumored/U +rumorer/M +rumormonger/SGMD +rumor/ZMRDSG +Rumpelstiltskin/M +rump/GMYDS +rumple/SDG +rumply/TR +rumpus/SM +rum/XSMN +runabout/SM +runaround/S +run/AS +runaway/S +rundown/SM +rune/MS +Runge/M +rung/MS +runic +runlet/SM +runnable +runnel/SM +runner/MS +running/S +Runnymede/M +runny/RT +runoff/MS +runtime +runtiness/M +runt/MS +runty/RPT +runway/MS +Runyon/M +rupee/MS +Ruperta/M +Rupert/M +Ruperto/M +rupiah/M +rupiahs +Ruppert/M +Ruprecht/M +rupture/GMSD +rurality/M +rural/Y +Rurik/M +ruse/MS +Rushdie/M +rush/DSRGZ +rusher/M +rushes/I +rushing/M +Rush/M +Rushmore/M +rushy/RT +Ruskin/M +rusk/MS +Russell/M +Russel/M +russet/MDS +russetting +Russia/M +Russian/SM +Russo/M +Russ/S +Rustbelt/M +rustically +rusticate/GSD +rustication/M +rusticity/S +rustic/S +Rustie/M +rustiness/MS +Rustin/M +rustler/M +rustle/RSDGZ +rust/MSDG +rustproof/DGS +Rusty/M +rusty/XNRTP +rutabaga/SM +Rutger/SM +Ruthanne/M +Ruthann/M +Ruthe/M +ruthenium/MS +rutherfordium/SM +Rutherford/M +Ruthie/M +Ruthi/M +ruthlessness/MS +ruthless/YP +Ruth/M +Ruthy/M +Rutland/M +Rutledge/M +rut/MS +rutted +Rutter/M +Ruttger/M +rutting +rutty/RT +Ruy/M +RV +RVs +Rwandan/S +Rwanda/SM +Rwy/M +Rx/M +Ryan/M +Ryann/M +Rycca/M +Rydberg/M +Ryder/M +rye/MS +Ryley/M +Ry/M +Ryon/M +Ryukyu/M +Ryun/M +S +SA +Saab/M +Saar/M +Saba/M +sabbath +Sabbath/M +Sabbaths +sabbatical/S +sabered/U +saber/GSMD +Sabik/M +Sabina/M +Sabine/M +Sabin/M +sable/GMDS +sabotage/DSMG +saboteur/SM +sabot/MS +Sabra/M +sabra/MS +Sabrina/M +SAC +Sacajawea/M +saccharides +saccharine +saccharin/MS +Sacco/M +sacerdotal +Sacha/M +sachem/MS +sachet/SM +Sachs/M +sackcloth/M +sackcloths +sacker/M +sackful/MS +sack/GJDRMS +sacking/M +sacral +sacra/L +sacramental/S +sacrament/DMGS +Sacramento/M +sacredness/S +sacred/PY +sacrificer/M +sacrifice/RSDZMG +sacrificial/Y +sacrilege/MS +sacrilegious/Y +sacristan/SM +sacristy/MS +sacroiliac/S +sacrosanctness/MS +sacrosanct/P +sacrum/M +sac/SM +Sada/M +Sadat/M +Saddam/M +sadden/DSG +sadder +saddest +saddlebag/SM +saddler/M +saddle's +saddle/UGDS +Sadducee/M +Sadella/M +Sade/M +sades +Sadie/M +sadism/MS +sadistic +sadistically +sadist/MS +sadness/SM +sadomasochism/MS +sadomasochistic +sadomasochist/S +sad/PY +Sadr/M +Sadye/M +safari/GMDS +safeguard/MDSG +safekeeping/MS +safeness/MS +safeness's/U +safes +safety/SDMG +safe/URPTY +safflower/SM +saffron/MS +sagaciousness/M +sagacious/YP +sagacity/MS +saga/MS +Sagan/M +sagebrush/SM +sage/MYPS +sagged +sagger +sagging +saggy/RT +Saginaw/M +Sagittarius/MS +sago/MS +sag/TSR +saguaro/SM +Sahara/M +Saharan/M +Sahel +sahib/MS +Saidee/M +saids +said/U +Saigon/M +sailboard/DGS +sailboat/SRMZG +sailcloth/M +sailcloths +sailer/M +sailfish/SM +sail/GJMDRS +sailing/M +sailor/YMS +sailplane/SDMG +sainthood/MS +saintlike +saintliness/MS +saintly/RTP +saint/YDMGS +Saiph/M +saith +saiths +Sakai/M +sake/MRS +saker/M +Sakhalin/M +Sakharov/M +Saki/M +saki's +salaam/GMDS +salable/U +salaciousness/MS +salacious/YP +salacity/MS +Saladin/M +Salado/M +salad/SM +Salaidh/M +salamander/MS +salami/MS +salary/SDMG +Salas/M +Salazar/M +saleability/M +sale/ABMS +Saleem/M +Salem/M +Salerno/M +salesclerk/SM +salesgirl/SM +saleslady/S +salesman/M +salesmanship/SM +salesmen +salespeople/M +salesperson/MS +salesroom/M +saleswoman +saleswomen +salience/MS +saliency +salient/SY +Salim/M +Salina/MS +saline/S +salinger +Salinger/M +salinity/MS +Salisbury/M +Salish/M +saliva/MS +salivary +salivate/XNGSD +salivation/M +Salk/M +Sallee/M +Salle/M +Sallie/M +Salli/M +sallowness/MS +sallow/TGRDSP +Sallust/M +Sallyanne/M +Sallyann/M +sally/GSDM +Sally/M +salmonellae +salmonella/M +Salmon/M +salmon/SM +Sal/MY +Saloma/M +Salome/M +Salomi/M +Salomo/M +Salomone/M +Salomon/M +Salonika/M +salon/SM +saloonkeeper +saloon/MS +salsa/MS +salsify/M +SALT +saltcellar/SM +salted/UC +salter/M +salt/GZTPMDRS +saltine/MS +saltiness/SM +saltness/M +Salton/M +saltpeter/SM +salts/C +saltshaker/S +saltwater +salty/RSPT +salubriousness/M +salubrious/YP +salubrity/M +salutariness/M +salutary/P +salutation/SM +salutatory/S +saluter/M +salute/RSDG +Salvadoran/S +Salvadorian/S +Salvador/M +salvageable +salvage/MGRSD +salvager/M +salvation/MS +Salvatore/M +salve/GZMDSR +salver/M +Salvidor/M +salvo/GMDS +Salween/M +Salyut/M +Salz/M +SAM +Samantha/M +Samara/M +Samaria/M +Samaritan/MS +samarium/MS +Samarkand/M +samba/GSDM +sameness/MS +same/SP +Sam/M +Sammie/M +Sammy/M +Samoa +Samoan/S +Samoset/M +samovar/SM +Samoyed/M +sampan/MS +sampler/M +sample/RSDJGMZ +sampling/M +Sampson/M +Samsonite/M +Samson/M +Samuele/M +Samuel/SM +Samuelson/M +samurai/M +San'a +Sana/M +sanatorium/MS +Sanborn/M +Sanchez/M +Sancho/M +sanctification/M +sanctifier/M +sanctify/RSDGNX +sanctimoniousness/MS +sanctimonious/PY +sanctimony/MS +sanctioned/U +sanction/SMDG +sanctity/SM +sanctuary/MS +sanctum/SM +sandal/MDGS +sandalwood/SM +sandbagged +sandbagging +sandbag/MS +sandbank/SM +sandbar/S +sandblaster/M +sandblast/GZSMRD +sandbox/MS +Sandburg/M +sandcastle/S +Sande/M +Sanderling/M +sander/M +Sander/M +Sanderson/M +sandhill +sandhog/SM +Sandia/M +Sandie/M +Sandi/M +sandiness/S +Sandinista +sandlot/SM +sandlotter/S +sandman/M +sandmen +Sand/MRZ +Sandor/M +Sandoval/M +sandpaper/DMGS +sandpile +sandpiper/MS +sandpit/M +Sandra/M +Sandro/M +sand/SMDRGZ +sandstone/MS +sandstorm/SM +Sandusky/M +sandwich/SDMG +Sandye/M +Sandy/M +sandy/PRT +saned +sane/IRYTP +saneness/MS +saneness's/I +sanes +Sanford/M +Sanforized +Sanger/M +sangfroid/S +sangria/SM +Sang/RM +sang/S +sanguinary +sanguined +sanguine/F +sanguinely +sanguineness/M +sanguineous/F +sanguines +sanguining +Sanhedrin/M +saning +sanitarian/S +sanitarium/SM +sanitary/S +sanitate/NX +sanitation/M +sanitizer/M +sanitize/RSDZG +sanity/SIM +sank +Sankara/M +San/M +sans +sanserif +Sanskritic +Sanskritize/M +Sanskrit/M +Sansone/M +Sanson/M +Santa/M +Santana/M +Santayana/M +Santeria +Santiago/M +Santo/MS +sapience/MS +sapient +sapless +sapling/SM +sap/MS +sapped +sapper/SM +Sapphira/M +Sapphire/M +sapphire/MS +Sappho/M +sappiness/SM +sapping +Sapporo/M +sappy/RPT +saprophyte/MS +saprophytic +sapsucker/SM +sapwood/SM +Saraann/M +Saracen/MS +Saragossa/M +Sarah/M +Sarajane/M +Sarajevo/M +Sara/M +Saran/M +saran/SM +sarape's +Sarasota/M +Saratoga/M +Saratov/M +Sarawak/M +sarcasm/MS +sarcastic +sarcastically +sarcoma/MS +sarcophagi +sarcophagus/M +sardine/SDMG +Sardinia/M +sardonic +sardonically +Saree/M +Sarena/M +Sarene/M +Sarette/M +Sargasso/M +Sarge/M +Sargent/M +sarge/SM +Sargon/M +Sari/M +sari/MS +Sarina/M +Sarine/M +Sarita/M +Sarnoff/M +sarong/MS +Saroyan/M +sarsaparilla/MS +Sarto/M +sartorial/Y +sartorius/M +Sartre/M +Sascha/M +SASE +Sasha/M +sashay/GDS +Sashenka/M +sash/GMDS +Saskatchewan/M +Saskatoon/M +Sask/M +sassafras/MS +sass/GDSM +Sassoon/M +sassy/TRS +SAT +satanic +satanical/Y +Satanism/M +satanism/S +Satanist/M +satanist/S +Satan/M +satchel/SM +sat/DG +sateen/MS +satellite/GMSD +sate/S +satiable/I +satiate/GNXSD +satiation/M +satiety/MS +satin/MDSG +satinwood/MS +satiny +satire/SM +satiric +satirical/Y +satirist/SM +satirize/DSG +satirizes/U +satisfaction/ESM +satisfactorily/U +satisfactoriness/MU +satisfactory/UP +satisfiability/U +satisfiable/U +satisfied/UE +satisfier/M +satisfies/E +satisfy/GZDRS +satisfying/EU +satisfyingly +Sat/M +satori/SM +satrap/SM +saturated/CUA +saturater/M +saturates/A +saturate/XDRSNG +saturation/M +Saturday/MS +saturnalia +Saturnalia/M +saturnine/Y +Saturn/M +Satyanarayanan/M +satyriases +satyriasis/M +satyric +satyr/MS +sauce/DSRGZM +saucepan/SM +saucer/M +saucily +sauciness/S +saucy/TRP +Saudi/S +Saud/M +Saudra/M +sauerkraut/SM +Saukville/M +Saul/M +Sault/M +sauna/DMSG +Sauncho/M +Saunder/SM +Saunderson/M +Saundra/M +saunter/DRSG +saurian/S +sauropod/SM +sausage/MS +Saussure/M +saut/DGS +Sauternes/M +Sauveur/M +savage/GTZYPRSD +Savage/M +savageness/SM +savagery/MS +Savannah/M +savanna/MS +savant/SM +saved/U +saveloy/M +saver/M +save/ZGJDRSB +Savina/M +Savior/M +savior/SM +Saviour/M +Savonarola/M +savored/U +savorer/M +savorier +savoriest +savoriness/S +savoringly/S +savoring/Y +savor/SMRDGZ +savory/UMPS +Savoyard/M +Savoy/M +savoy/SM +savvy/GTRSD +sawbones/M +sawbuck/SM +sawdust/MDSG +sawer/M +sawfly/SM +sawhorse/MS +Saw/M +sawmill/SM +saw/SMDRG +sawtooth +Sawyere/M +Sawyer/M +sawyer/MS +Saxe/M +saxifrage/SM +Sax/M +sax/MS +Saxon/SM +Saxony/M +saxophone/MS +saxophonist/SM +Saxton/M +Sayer/M +sayer/SM +sayest +saying/MS +Sayre/MS +says/M +say/USG +Say/ZMR +SBA +Sb/M +SC +scabbard/SGDM +scabbed +scabbiness/SM +scabbing +scabby/RTP +scabies/M +scabrousness/M +scabrous/YP +scab/SM +scad/SM +scaffolding/M +scaffold/JGDMS +scalability +Scala/M +scalar/SM +scalawag/SM +scald/GJRDS +scaled/AU +scale/JGZMBDSR +scaleless +scalene +scaler/M +scales/A +scaliness/MS +scaling/A +scallion/MS +scalloper/M +scallop/GSMDR +scalloping/M +scalpel/SM +scalper/M +scalp/GZRDMS +scalping/M +scaly/TPR +scammed +scamming +scamper/GD +scampi/M +scamp/RDMGZS +scam/SM +Scan +scan/AS +scandal/GMDS +scandalized/U +scandalize/GDS +scandalmonger/SM +scandalousness/M +scandalous/YP +Scandinavia/M +Scandinavian/S +scandium/MS +scanned/A +scanner/SM +scanning/A +scansion/SM +scant/CDRSG +scantest +scantily +scantiness/MS +scantly +scantness/MS +scanty/TPRS +scapegoat/SGDM +scapegrace/MS +scape/M +scapulae +scapula/M +scapular/S +scarab/SM +Scaramouch/M +Scarborough/M +scarceness/SM +scarce/RTYP +scarcity/MS +scar/DRMSG +scarecrow/MS +scaremongering/M +scaremonger/SGM +scarer/M +scare/S +scarface +Scarface/M +scarf/SDGM +scarification/M +scarify/DRSNGX +scarily +scariness/S +scarlatina/MS +Scarlatti/M +Scarlet/M +scarlet/MDSG +Scarlett/M +scarp/SDMG +scarred +scarring +scarves/M +scary/PTR +scathe/DG +scathed/U +scathing/Y +scatological +scatology/SM +scat/S +scatted +scatterbrain/MDS +scatter/DRJZSG +scatterer/M +scattergun +scattering/YM +scatting +scavenge/GDRSZ +scavenger/M +SCCS +scenario/SM +scenarist/MS +scene/GMDS +scenery/SM +scenically +scenic/S +scented/U +scent/GDMS +scentless +scent's/C +scents/C +scepter/DMSG +scepters/U +sceptically +sch +Schaefer/M +Schaeffer/M +Schafer/M +Schaffner/M +Schantz/M +Schapiro/M +Scheat/M +Schedar/M +schedule/ADSRG +scheduled/U +scheduler/MS +schedule's +Scheherazade/M +Scheherezade/M +Schelling/M +schema/M +schemata +schematically +schematic/S +scheme/JSRDGMZ +schemer/M +schemta +Schenectady/M +scherzo/MS +Schick/M +Schiller/M +schilling/SM +schismatic/S +schism/SM +schist/SM +schizoid/S +schizomycetes +schizophrenia/SM +schizophrenically +schizophrenic/S +schizo/S +schlemiel/MS +schlepped +schlepping +schlep/S +Schlesinger/M +Schliemann/M +Schlitz/M +schlock/SM +schlocky/TR +Schloss/M +schmaltz/MS +schmaltzy/TR +Schmidt/M +Schmitt/M +schmoes +schmo/M +schmooze/GSD +schmuck/MS +Schnabel/M +schnapps/M +schnauzer/MS +Schneider/M +schnitzel/MS +schnook/SM +schnoz/S +schnozzle/MS +Schoenberg/M +Schofield/M +scholarship/MS +scholar/SYM +scholastically +scholastic/S +schoolbag/SM +schoolbook/SM +schoolboy/MS +schoolchild/M +schoolchildren +schooldays +schooled/U +schoolfellow/S +schoolfriend +schoolgirlish +schoolgirl/MS +schoolhouse/MS +schooling/M +schoolmarmish +schoolmarm/MS +schoolmaster/SGDM +schoolmate/MS +schoolmistress/MS +schoolroom/SM +schoolteacher/MS +schoolwork/SM +schoolyard/SM +school/ZGMRDJS +schooner/SM +Schopenhauer/M +Schottky/M +Schrieffer/M +Schrdinger/M +Schroeder/M +Schroedinger/M +Schubert/M +Schultz/M +Schulz/M +Schumacher/M +Schuman/M +Schumann/M +schussboomer/S +schuss/SDMG +Schuster/M +Schuyler/M +Schuylkill/M +Schwab/M +Schwartzkopf/M +Schwartz/M +Schwarzenegger/M +schwa/SM +Schweitzer/M +Schweppes/M +Schwinger/M +Schwinn/M +sci +sciatica/SM +sciatic/S +science/FMS +scientifically/U +scientific/U +scientist/SM +Scientology/M +scimitar/SM +scintilla/MS +scintillate/GNDSX +scintillation/M +scintillator/SM +scion/SM +Scipio/M +scissor/SGD +scleroses +sclerosis/M +sclerotic/S +Sc/M +scoffer/M +scofflaw/MS +scoff/RDGZS +scolder/M +scold/GSJRD +scolioses +scoliosis/M +scollop's +sconce/SDGM +scone/SM +scooper/M +scoop/SRDMG +scooter/M +scoot/SRDGZ +scope/DSGM +Scopes/M +scops +scorbutic +scorcher/M +scorching/Y +scorch/ZGRSD +scoreboard/MS +scorecard/MS +scored/M +scorekeeper/SM +scoreless +scoreline +score/ZMDSRJG +scorner/M +scornfulness/M +scornful/PY +scorn/SGZMRD +scorpion/SM +Scorpio/SM +Scorpius/M +Scorsese/M +Scotchgard/M +Scotchman/M +Scotchmen +scotch/MSDG +scotchs +Scotch/S +Scotchwoman +Scotchwomen +Scotia/M +Scotian/M +Scotland/M +Scot/MS +Scotsman/M +Scotsmen +Scotswoman +Scotswomen +Scottie/SM +Scotti/M +Scottish +Scott/M +Scottsdale/M +Scotty's +scoundrel/YMS +scourer/M +scourge/MGRSD +scourger/M +scouring/M +scour/SRDGZ +scouter/M +scouting/M +scoutmaster/SM +Scout's +scout/SRDMJG +scow/DMGS +scowler/M +scowl/SRDG +scrabble/DRSZG +scrabbler/M +Scrabble/SM +scragged +scragging +scraggly/TR +scraggy/TR +scrag/SM +scrambler/MS +scrambler's/U +scramble/UDSRG +scrammed +scramming +scram/S +Scranton/M +scrapbook/SM +scraper/M +scrape/S +scrapheap/SM +scrapped +scrapper/SM +scrapping +scrappy/RT +scrap/SGZJRDM +scrapyard/S +scratched/U +scratcher/M +scratches/M +scratchily +scratchiness/S +scratch/JDRSZG +scratchy/TRP +scrawler/M +scrawl/GRDS +scrawly/RT +scrawniness/MS +scrawny/TRP +screamer/M +screaming/Y +scream/ZGSRD +screecher/M +screech/GMDRS +screechy/TR +screed/MS +scree/DSM +screened/U +screening/M +screenplay/MS +screen/RDMJSG +screenwriter/MS +screwball/SM +screwdriver/SM +screwer/M +screw/GUSD +screwiness/S +screw's +screwup +screwworm/MS +screwy/RTP +Scriabin/M +scribal +scribble/JZDRSG +scribbler/M +scribe/CDRSGIK +scriber/MKIC +scribe's +Scribner/MS +scrimmager/M +scrimmage/RSDMG +scrimp/DGS +scrimshaw/GSDM +scrim/SM +Scripps/M +scrip/SM +scripted/U +script/FGMDS +scriptural/Y +scripture/MS +Scripture/MS +scriptwriter/SM +scriptwriting/M +scrivener/M +scriven/ZR +scrod/M +scrofula/MS +scrofulous +scrollbar/SM +scroll/GMDSB +Scrooge/MS +scrooge/SDMG +scrota +scrotal +scrotum/M +scrounge/ZGDRS +scroungy/TR +scrubbed +scrubber/MS +scrubbing +scrubby/TR +scrub/S +scruffily +scruffiness/S +scruff/SM +scruffy/PRT +Scruggs/M +scrummage/MG +scrum/MS +scrumptious/Y +scrunch/DSG +scrunchy/S +scruple/SDMG +scrupulosity/SM +scrupulousness's +scrupulousness/US +scrupulous/UPY +scrutable/I +scrutinized/U +scrutinizer/M +scrutinize/RSDGZ +scrutinizingly/S +scrutinizing/UY +scrutiny/MS +SCSI +scuba/SDMG +scudded +scudding +Scud/M +scud/S +scuff/GSD +scuffle/SDG +sculler/M +scullery/MS +Sculley/M +scullion/MS +scull/SRDMGZ +sculptor/MS +sculptress/MS +sculpt/SDG +sculptural/Y +sculpture/SDGM +scumbag/S +scummed +scumming +scum/MS +scummy/TR +scupper/SDMG +scurf/MS +scurfy/TR +scurrility/MS +scurrilousness/MS +scurrilous/PY +scurry/GJSD +scurvily +scurviness/M +scurvy/SRTP +scutcheon/SM +scuttlebutt/MS +scuttle/MGSD +scuzzy/RT +Scylla/M +scythe/SDGM +Scythia/M +SD +SDI +SE +seabed/S +seabird/S +seaboard/MS +Seaborg/M +seaborne +Seabrook/M +seacoast/MS +seafare/JRZG +seafarer/M +seafood/MS +seafront/MS +Seagate/M +seagoing +Seagram/M +seagull/S +seahorse/S +sealant/MS +sealed/AU +sealer/M +seal/MDRSGZ +sealskin/SM +seals/UA +seamail +seamanship/SM +seaman/YM +seamer/M +seaminess/M +seamlessness/M +seamless/PY +seam/MNDRGS +seams/I +seamstress/MS +Seamus/M +sea/MYS +seamy/TRP +Seana/M +sance/SM +Sean/M +seaplane/SM +seaport/SM +seaquake/M +Seaquarium/M +searcher/AM +searching/YS +searchlight/SM +search/RSDAGZ +sear/DRSJGT +searing/Y +Sears/M +seascape/SM +seashell/MS +seashore/SM +seasickness/SM +seasick/P +seaside/SM +seasonableness/M +seasonable/UP +seasonably/U +seasonality +seasonal/Y +seasoned/U +seasoner/M +seasoning/M +season/JRDYMBZSG +seatbelt +seated/A +seater/M +seating/SM +SEATO +seat's +Seattle/M +seat/UDSG +seawall/S +seaward/S +seawater/S +seaway/MS +seaweed/SM +seaworthinesses +seaworthiness/MU +seaworthy/TRP +sebaceous +Sebastian/M +Sebastiano/M +Sebastien/M +seborrhea/SM +SEC +secant/SM +secede/GRSD +secessionist/MS +secession/MS +secludedness/M +secluded/YP +seclude/GSD +seclusion/SM +seclusive +Seconal +secondarily +secondary/PS +seconder/M +secondhand +second/RDYZGSL +secrecy/MS +secretarial +secretariat/MS +secretaryship/MS +secretary/SM +secrete/XNS +secretion/M +secretiveness/S +secretive/PY +secretory +secret/TVGRDYS +sec/S +sectarianism/MS +sectarian/S +sectary/MS +sectionalism/MS +sectionalized +sectional/SY +section/ASEM +sectioned +sectioning +sect/ISM +sectoral +sectored +sector/EMS +sectoring +sects/E +secularism/MS +secularist/MS +secularity/M +secularization/MS +secularized/U +secularize/GSD +secular/SY +secured/U +securely/I +secure/PGTYRSDJ +security/MSI +secy +sec'y +sedan/SM +sedateness/SM +sedate/PXVNGTYRSD +sedation/M +sedative/S +sedentary +Seder/SM +sedge/SM +Sedgwick/M +sedgy/RT +sedimentary +sedimentation/SM +sediment/SGDM +sedition/SM +seditiousness/M +seditious/PY +seducer/M +seduce/RSDGZ +seduction/MS +seductiveness/MS +seductive/YP +seductress/SM +sedulous/Y +Seebeck/M +seed/ADSG +seedbed/MS +seedcase/SM +seeded/U +seeder/MS +seediness/MS +seeding/S +seedless +seedling/SM +seedpod/S +seed's +seedy/TPR +seeings +seeing's +seeing/U +seeker/M +seek/GZSR +seeking/Y +Seeley/M +See/M +seem/GJSYD +seeming/Y +seemliness's +seemliness/US +seemly/UTPR +seen/U +seepage/MS +seep/GSD +seer/SM +seersucker/MS +sees +seesaw/DMSG +seethe/SDGJ +see/U +segmental/Y +segmentation/SM +segmented/U +segment/SGDM +Segovia/M +segregant +segregated/U +segregate/XCNGSD +segregation/CM +segregationist/SM +segregative +Segre/M +segue/DS +segueing +Segundo/M +Se/H +Seidel/M +seigneur/MS +seignior/SM +Seiko/M +seine/GZMDSR +Seine/M +seiner/M +Seinfeld/M +seismic +seismically +seismographer/M +seismographic +seismographs +seismography/SM +seismograph/ZMR +seismologic +seismological +seismologist/MS +seismology/SM +seismometer/S +seize/BJGZDSR +seizer/M +seizing/M +seizin/MS +seizor/MS +seizure/MS +Seka/M +Sela/M +Selassie/M +Selby/M +seldom +selected/UAC +selectional +selection/MS +selectiveness/M +selective/YP +selectivity/MS +selectman/M +selectmen +selectness/SM +selector/SM +select/PDSVGB +Selectric/M +selects/A +Selena/M +selenate/M +Selene/M +selenite/M +selenium/MS +selenographer/SM +selenography/MS +Selestina/M +Seleucid/M +Seleucus/M +self/GPDMS +selfishness/SU +selfish/PUY +selflessness/MS +selfless/YP +selfness/M +Selfridge/M +selfsameness/M +selfsame/P +Selia/M +Selie/M +Selig/M +Selim/M +Selina/M +Selinda/M +Seline/M +Seljuk/M +Selkirk/M +Sella/M +sell/AZGSR +seller/AM +Sellers/M +Selle/ZM +sellout/MS +Selma/M +seltzer/S +selvage/MGSD +selves/M +Selznick/M +semantical/Y +semanticist/SM +semantic/S +semantics/M +semaphore/GMSD +Semarang/M +semblance/ASME +semen/SM +semester/SM +semiannual/Y +semiarid +semiautomated +semiautomatic/S +semicircle/SM +semicircular +semicolon/MS +semiconductor/SM +semiconscious +semidefinite +semidetached +semidrying/M +semifinalist/MS +semifinal/MS +semilogarithmic +semimonthly/S +seminal/Y +seminarian/MS +seminar/SM +seminary/MS +Seminole/SM +semiofficial +semioticians +semiotic/S +semiotics/M +semipermanent/Y +semipermeable +semiprecious +semiprivate +semiprofessional/YS +semipublic +semiquantitative/Y +Semiramis/M +semiretired +semisecret +semiskilled +semi/SM +semisolid/S +semistructured +semisweet +Semite/SM +Semitic/MS +semitic/S +semitone/SM +semitrailer/SM +semitrance +semitransparent +semitropical +semivowel/MS +semiweekly/S +semiyearly +semolina/SM +sempiternal +sempstress/SM +Semtex +sen +Sen +Sena/M +senate/MS +Senate/MS +senatorial +senator/MS +Sendai/M +sender/M +sends/A +send/SRGZ +Seneca/MS +Senegalese +Senegal/M +senescence/SM +senescent +senile/SY +senility/MS +seniority/SM +senior/MS +Senior/S +Sennacherib/M +senna/MS +Sennett/M +Seora/M +senora/S +senorita/S +senor/MS +sensately/I +sensate/YNX +sensationalism/MS +sensationalist/S +sensationalize/GSD +sensational/Y +sensation/M +sens/DSG +senselessness/SM +senseless/PY +sense/M +sensibility/ISM +sensibleness/MS +sensible/PRST +sensibly/I +sensitiveness/MS +sensitiveness's/I +sensitives +sensitive/YIP +sensitivity/ISM +sensitization/CSM +sensitized/U +sensitizers +sensitize/SDCG +sensor/MS +sensory +sensualist/MS +sensuality/MS +sensual/YF +sensuousness/S +sensuous/PY +Sensurround/M +sentence/SDMG +sentential/Y +sententious/Y +sentience/ISM +sentient/YS +sentimentalism/SM +sentimentalist/SM +sentimentality/SM +sentimentalization/SM +sentimentalize/RSDZG +sentimentalizes/U +sentimental/Y +sentiment/MS +sentinel/GDMS +sentry/SM +sent/UFEA +Seoul/M +sepal/SM +separability/MSI +separableness/MI +separable/PI +separably/I +separateness/MS +separates/M +separate/YNGVDSXP +separation/M +separatism/SM +separatist/SM +separator/SM +Sephardi/M +Sephira/M +sepia/MS +Sepoy/M +sepses +sepsis/M +septa/M +septate/N +September/MS +septennial/Y +septet/MS +septicemia/SM +septicemic +septic/S +septillion/M +sept/M +Sept/M +septuagenarian/MS +Septuagint/MS +septum/M +sepulcher/MGSD +sepulchers/UA +sepulchral/Y +seq +sequel/MS +sequenced/A +sequence/DRSJZMG +sequencer/M +sequence's/F +sequences/F +sequent/F +sequentiality/FM +sequentialize/DSG +sequential/YF +sequester/SDG +sequestrate/XGNDS +sequestration/M +sequin/SDMG +sequitur +Sequoia/M +sequoia/MS +Sequoya/M +Serafin/M +seraglio/SM +serape/S +seraphic +seraphically +seraphim's +seraph/M +seraphs +sera's +Serbia/M +Serbian/S +Serb/MS +Serbo/M +serenade/MGDRS +serenader/M +Serena/M +serendipitous/Y +serendipity/MS +serene/GTYRSDP +Serene/M +sereneness/SM +Serengeti/M +serenity/MS +sere/TGDRS +serfdom/MS +serf/MS +Sergeant/M +sergeant/SM +serge/DSGM +Sergei/M +Serge/M +Sergent/M +Sergio/M +serialization/MS +serialize/GSD +serial/MYS +series/M +serif/SMD +serigraph/M +serigraphs +seriousness/SM +serious/PY +sermonize/GSD +sermon/SGDM +serological/Y +serology/MS +serons +serous +Serpens/M +serpent/GSDM +serpentine/GYS +Serra/M +Serrano/M +serrate/GNXSD +serration/M +serried +serum/MS +servant/SDMG +serve/AGCFDSR +served/U +server/MCF +servers +serviceability/SM +serviceableness/M +serviceable/P +serviced/U +serviceman/M +servicemen +service/MGSRD +service's/E +services/E +servicewoman +servicewomen +serviette/MS +servilely +servileness/M +serviles +servile/U +servility/SM +serving/SM +servitor/SM +servitude/MS +servomechanism/MS +servomotor/MS +servo/S +sesame/MS +sesquicentennial/S +sessile +session/SM +setback/S +Seth/M +Set/M +Seton/M +set's +setscrew/SM +set/SIA +settable/A +sett/BJGZSMR +settee/MS +setter/M +setting/AS +setting's +settle/AUDSG +settlement/ASM +settler/MS +settling/S +setup/MS +Seumas/M +Seurat/M +Seuss/M +Sevastopol/M +sevenfold +sevenpence +seven/SMH +seventeen/HMS +seventeenths +sevenths +seventieths +seventy/MSH +severalfold +severalty/M +several/YS +severance/SM +severed/E +severeness/SM +severe/PY +severing/E +severity/MS +Severn/M +severs/E +sever/SGTRD +Severus/M +Seville/M +sewage/MS +Seward/M +sewerage/SM +sewer/GSMD +sewing/SM +sewn +sew/SAGD +sexagenarian/MS +sex/GMDS +sexily +sexiness/MS +sexism/SM +sexist/SM +sexless +sexologist/SM +sexology/MS +sexpot/SM +Sextans/M +sextant/SM +sextet/SM +sextillion/M +Sexton/M +sexton/MS +sextuple/MDG +sextuplet/MS +sexuality/MS +sexualized +sexual/Y +sexy/RTP +Seychelles +Seyfert +Seymour/M +sf +SF +Sgt +shabbily +shabbiness/SM +shabby/RTP +shack/GMDS +shackler/M +shackle's +Shackleton/M +shackle/UGDS +shad/DRJGSM +shaded/U +shadeless +shade/SM +shadily +shadiness/MS +shading/M +shadowbox/SDG +shadower/M +shadow/GSDRM +shadowiness/M +Shadow/M +shadowy/TRP +shady/TRP +Shae/M +Shafer/M +Shaffer/M +shafting/M +shaft/SDMG +shagged +shagginess/SM +shagging +shaggy/TPR +shag/MS +shah/M +shahs +Shaina/M +Shaine/M +shakable/U +shakably/U +shakeable +shakedown/S +shaken/U +shakeout/SM +shaker/M +Shaker/S +Shakespearean/S +Shakespeare/M +Shakespearian +shake/SRGZB +shakeup/S +shakily +shakiness/S +shaking/M +shaky/TPR +shale/SM +shall +shallot/SM +shallowness/SM +shallow/STPGDRY +Shalna/M +Shalne/M +shalom +Shalom/M +shalt +shamanic +shaman/SM +shamble/DSG +shambles/M +shamefaced/Y +shamefulness/S +shameful/YP +shamelessness/SM +shameless/PY +shame/SM +sham/MDSG +shammed +shammer +shamming +shammy's +shampoo/DRSMZG +shampooer/M +shamrock/SM +Shamus/M +Shana/M +Shanan/M +Shanda/M +Shandee/M +Shandeigh/M +Shandie/M +Shandra/M +shandy/M +Shandy/M +Shane/M +Shanghai/GM +Shanghaiing/M +shanghai/SDG +Shanie/M +Shani/M +shank/SMDG +Shannah/M +Shanna/M +Shannan/M +Shannen/M +Shannon/M +Shanon/M +shan't +Shanta/M +Shantee/M +shantis +Shantung/M +shantung/MS +shanty/SM +shantytown/SM +shape/AGDSR +shaped/U +shapelessness/SM +shapeless/PY +shapeliness/S +shapely/RPT +shaper/S +shape's +Shapiro/M +sharable/U +Sharai/M +Shara/M +shard/SM +shareable +sharecropped +sharecropper/MS +sharecropping +sharecrop/S +share/DSRGZMB +shared/U +shareholder/MS +shareholding/S +sharer/M +shareware/S +Shari'a +Sharia/M +sharia/SM +Shari/M +Sharity/M +shark/SGMD +sharkskin/SM +Sharla/M +Sharleen/M +Sharlene/M +Sharline/M +Sharl/M +Sharona/M +Sharon/M +Sharpe/M +sharpen/ASGD +sharpened/U +sharpener/S +sharper/M +sharpie/SM +Sharp/M +sharpness/MS +sharp/SGTZXPYRDN +sharpshooter/M +sharpshooting/M +sharpshoot/JRGZ +sharpy's +Sharron/M +Sharyl/M +Shasta/M +shat +shatter/DSG +shattering/Y +shatterproof +Shaughn/M +Shaula/M +Shauna/M +Shaun/M +shave/DSRJGZ +shaved/U +shaver/M +Shavian +shaving/M +Shavuot/M +Shawano/M +shawl/SDMG +shaw/M +Shaw/M +Shawna/M +Shawnee/SM +Shawn/M +Shaylah/M +Shayla/M +Shaylyn/M +Shaylynn/M +Shay/M +shay/MS +Shayna/M +Shayne/M +Shcharansky/M +sh/DRS +sheaf/MDGS +Shea/M +shearer/M +shear/RDGZS +sheather/M +sheathe/UGSD +sheath/GJMDRS +sheathing/M +sheaths +sheave/SDG +sheaves/M +Sheba/M +shebang/MS +Shebeli/M +Sheboygan/M +she'd +shedding +Shedir/M +sheds +shed's +shed/U +Sheelagh/M +Sheelah/M +Sheela/M +Sheena/M +sheen/MDGS +sheeny/TRSM +sheepdog/SM +sheepfold/MS +sheepherder/MS +sheepishness/SM +sheepish/YP +sheep/M +sheepskin/SM +Sheeree/M +sheerness/S +sheer/PGTYRDS +sheeting/M +sheetlike +sheet/RDMJSG +Sheetrock +Sheffielder/M +Sheffield/RMZ +Sheffie/M +Sheff/M +Sheffy/M +sheikdom/SM +sheikh's +sheik/SM +Sheilah/M +Sheila/M +shekel/MS +Shelagh/M +Shela/M +Shelba/M +Shelbi/M +Shelby/M +Shelden/M +Sheldon/M +shelf/MDGS +Shelia/M +she'll +shellacked +shellacking/MS +shellac/S +shelled/U +Shelley/M +shellfire/SM +shellfish/SM +Shellie/M +Shelli/M +Shell/M +shell/RDMGS +Shelly/M +Shel/MY +shelter/DRMGS +sheltered/U +shelterer/M +Shelton/M +shelve/JRSDG +shelver/M +shelves/M +shelving/M +she/M +Shem/M +Shena/M +Shenandoah/M +shenanigan/SM +Shenyang/M +Sheol/M +Shepard/M +shepherd/DMSG +shepherdess/S +Shepherd/M +Shep/M +Sheppard/M +Shepperd/M +Sheratan/M +Sheraton/M +sherbet/MS +sherd's +Sheree/M +Sheridan/M +Sherie/M +sheriff/SM +Sherill/M +Sherilyn/M +Sheri/M +Sherline/M +Sherlocke/M +sherlock/M +Sherlock/M +Sher/M +Sherman/M +Shermie/M +Sherm/M +Shermy/M +Sherpa/SM +Sherrie/M +Sherri/M +Sherry/M +sherry/MS +Sherwin/M +Sherwood/M +Sherwynd/M +Sherye/M +Sheryl/M +Shetland/S +Shevardnadze/M +shew/GSD +shewn +shh +shiatsu/S +shibboleth/M +shibboleths +shielded/U +shielder/M +shield/MDRSG +Shields/M +shiftily +shiftiness/SM +shiftlessness/S +shiftless/PY +shift/RDGZS +shifty/TRP +Shi'ite +Shiite/SM +Shijiazhuang +Shikoku/M +shill/DJSG +shillelagh/M +shillelaghs +shilling/M +Shillong/M +Shiloh/M +shimmed +shimmer/DGS +shimmery +shimming +shimmy/DSMG +shim/SM +Shina/M +shinbone/SM +shindig/MS +shiner/M +shine/S +shingle/MDRSG +shingler/M +shinguard +shininess/MS +shining/Y +shinned +shinning +shinny/GDSM +shin/SGZDRM +shinsplints +Shintoism/S +Shintoist/MS +Shinto/MS +shiny/PRT +shipboard/MS +shipborne +shipbuilder/M +shipbuild/RGZJ +shipload/SM +shipman/M +shipmate/SM +shipmen +shipment/AMS +shipowner/MS +shippable +shipped/A +shipper/SM +shipping/MS +ship's +shipshape +ship/SLA +shipwreck/GSMD +shipwright/MS +shipyard/MS +Shiraz/M +shire/MS +shirker/M +shirk/RDGZS +Shirlee/M +Shirleen/M +Shirlene/M +Shirley/M +Shirline/M +Shirl/M +Shir/M +shirr/GJDS +shirtfront/S +shirting/M +shirt/JDMSG +shirtless +shirtmake/R +shirtmaker/M +shirtsleeve/MS +shirttail/S +shirtwaist/SM +shit/S! +shitting/! +shitty/RT! +Shiva/M +shiverer/M +shiver/GDR +shivery +shiv/SZRM +shivved +shivving +shlemiel's +Shmuel/M +shoal/SRDMGT +shoat/SM +shocker/M +shocking/Y +Shockley/M +shockproof +shock/SGZRD +shoddily +shoddiness/SM +shoddy/RSTP +shod/U +shoehorn/GSMD +shoeing +shoelace/MS +shoemaker/M +shoemake/RZ +shoe/MS +shoer's +shoeshine/MS +shoestring/MS +shoetree/MS +shogunate/SM +shogun/MS +Shoji/M +Sholom/M +shone +shoo/DSG +shoofly +shook/SM +shooter/M +shootout/MS +shoot/SJRGZ +shopkeeper/M +shopkeep/RGZ +shoplifter/M +shoplifting/M +shoplift/SRDGZ +shop/MS +shopped/M +shopper/M +shoppe/RSDGZJ +shopping/M +shoptalk/SM +shopworn +shorebird/S +shore/DSRGMJ +shoreline/SM +Shorewood/M +shoring/M +shortage/MS +shortbread/MS +shortcake/SM +shortchange/DSG +shortcoming/MS +shortcrust +shortcut/MS +shortcutting +shortener/M +shortening/M +shorten/RDGJ +shortfall/SM +shorthand/DMS +Shorthorn/M +shorthorn/MS +shortie's +shortish +shortlist/GD +Short/M +shortness/MS +short/SGTXYRDNP +shortsightedness/S +shortsighted/YP +shortstop/MS +shortwave/SM +shorty/SM +Shoshana/M +Shoshanna/M +Shoshone/SM +Shostakovitch/M +shotgunned +shotgunner +shotgunning +shotgun/SM +shot/MS +shotted +shotting +shoulder/GMD +shouldn't +should/TZR +shout/SGZRDM +shove/DSRG +shoveler/M +shovelful/MS +shovel/MDRSZG +shover/M +showbiz +showbizzes +showboat/SGDM +showcase/MGSD +showdown/MS +shower/GDM +showery/TR +show/GDRZJS +showgirl/SM +showily +showiness/MS +showing/M +showman/M +showmanship/SM +showmen +shown +showoff/S +showpiece/SM +showplace/SM +showroom/MS +showy/RTP +shpt +shrank +shrapnel/SM +shredded +shredder/MS +shredding +shred/MS +Shreveport/M +shrewdness/SM +shrewd/RYTP +shrew/GSMD +shrewishness/M +shrewish/PY +shrieker/M +shriek/SGDRMZ +shrift/SM +shrike/SM +shrill/DRTGPS +shrillness/MS +shrilly +shrimp/MDGS +shrine/SDGM +shrinkage/SM +shrinker/M +shrinking/U +shrink/SRBG +shrivel/GSD +shriven +shrive/RSDG +Shropshire/M +shroud/GSMD +shrubbed +shrubbery/SM +shrubbing +shrubby/TR +shrub/SM +shrugged +shrugging +shrug/S +shrunk/N +shtick/S +shucker/M +shuck/SGMRD +shucks/S +shudder/DSG +shuddery +shuffleboard/MS +shuffled/A +shuffle/GDSRZ +shuffles/A +shuffling/A +Shulman/M +Shu/M +shunned +shunning +shun/S +shunter/M +shunt/GSRD +Shurlocke/M +Shurlock/M +Shurwood/M +shush/SDG +shutdown/MS +shuteye/SM +shutoff/M +shutout/SM +shut/S +shutterbug/S +shutter/DMGS +shuttering/M +shutting +shuttlecock/MDSG +shuttle/MGDS +shy/DRSGTZY +shyer +shyest +Shylockian/M +Shylock/M +shyness/SM +shyster/SM +Siamese/M +Siam/M +Siana/M +Sianna/M +Sian's +Sibbie/M +Sibby/M +Sibeal/M +Sibelius/M +Sibella/M +Sibelle/M +Sibel/M +Siberia/M +Siberian/S +sibilance/M +sibilancy/M +sibilant/SY +Sibilla/M +Sibley/M +sibling/SM +Sib/M +Sibylla/M +Sibylle/M +sibylline +Sibyl/M +sibyl/SM +Siciliana/M +Sicilian/S +Sicily/M +sickbay/M +sickbed/S +sickener/M +sickening/Y +sicken/JRDG +sicker/Y +sick/GXTYNDRSP +sickie/SM +sickish/PY +sickle/SDGM +sickliness/M +sickly/TRSDPG +sickness/MS +sicko/S +sickout/S +sickroom/SM +sic/S +sidearm/S +sideband/MS +sidebar/MS +sideboard/SM +sideburns +sidecar/MS +sided/A +sidedness +side/ISRM +sidekick/MS +sidelight/SM +sideline/MGDRS +sidelong +sideman/M +sidemen +sidepiece/S +sidereal +sider/FA +sides/A +sidesaddle/MS +sideshow/MS +sidesplitting +sidestepped +sidestepping +sidestep/S +sidestroke/GMSD +sideswipe/GSDM +sidetrack/SDG +sidewalk/MS +sidewall/MS +sidewards +sideway/SM +sidewinder/SM +siding/SM +sidle/DSG +Sid/M +Sidnee/M +Sidney/M +Sidoney/M +Sidonia/M +Sidonnie/M +SIDS +siege/GMDS +Siegel/M +Siegfried/M +Sieglinda/M +Siegmund/M +Siemens/M +Siena/M +sienna/SM +Sierpinski/M +sierra/SM +siesta/MS +sieve/GZMDS +Siffre/M +sifted/UA +sifter/M +sift/GZJSDR +Sigfrid/M +Sigfried/M +SIGGRAPH/M +sigh/DRG +sigher/M +sighs +sighted/P +sighter/M +sighting/S +sight/ISM +sightless/Y +sightliness/UM +sightly/TURP +sightread +sightseeing/S +sightsee/RZ +Sigismond/M +Sigismondo/M +Sigismund/M +Sigismundo/M +Sig/M +sigma/SM +sigmoid +Sigmund/M +signal/A +signaled +signaler/S +signaling +signalization/S +signalize/GSD +signally +signalman/M +signalmen +signals +signal's +signatory/SM +signature/MS +signboard/MS +signed/FU +signer/SC +signet/SGMD +sign/GARDCS +significance/IMS +significantly/I +significant/YS +signification/M +signify/DRSGNX +signing/S +Signora/M +signora/SM +signore/M +signori +signories +signorina/SM +signorine +Signor/M +signor/SFM +signpost/DMSG +sign's +signs/F +Sigrid/M +Sigurd/M +Sigvard/M +Sihanouk/M +Sikhism/MS +Sikh/MS +Sikhs +Sikkimese +Sikkim/M +Sikorsky/M +silage/GMSD +Silas/M +Sileas/M +siled +Sile/M +silence/MZGRSD +silencer/M +silentness/M +silent/TSPRY +Silesia/M +silhouette/GMSD +silica/SM +silicate/SM +siliceous +silicide/M +silicone/SM +silicon/MS +silicoses +silicosis/M +silken/DG +silk/GXNDMS +silkily +silkiness/SM +silkscreen/SM +silkworm/MS +silky/RSPT +silliness/SM +sill/MS +silly/PRST +silo/GSM +siltation/M +silt/MDGS +siltstone/M +silty/RT +Silurian/S +Silvain/M +Silva/M +Silvana/M +Silvan/M +Silvano/M +Silvanus/M +silverer/M +silverfish/MS +Silverman/M +silver/RDYMGS +silversmith/M +silversmiths +Silverstein/M +silverware/SM +silvery/RTP +Silvester/M +Silvia/M +Silvie/M +Silvio/M +Si/M +SIMD +Simenon/M +Simeon/M +simian/S +similar/EY +similarity/EMS +simile/SM +similitude/SME +Simla/M +simmer/GSD +Simmonds/M +Simmons/M +Simmonsville/M +Sim/MS +Simms/M +Simona/M +Simone/M +Simonette/M +simonize/SDG +Simon/M +Simonne/M +simony/MS +simpatico +simper/GDS +simpleminded/YP +simpleness/S +simple/RSDGTP +simpleton/SM +simplex/S +simplicity/MS +simplified/U +simplify/ZXRSDNG +simplistic +simplistically +simply +Simpson/M +simulacrum/M +Simula/M +SIMULA/M +simulate/XENGSD +simulation/ME +simulative +simulator/SEM +simulcast/GSD +simultaneity/SM +simultaneousness/M +simultaneous/YP +Sinai/M +Sinatra/M +since +sincere/IY +sincereness/M +sincerer +sincerest +sincerity/MIS +Sinclair/M +Sinclare/M +Sindbad/M +Sindee/M +Sindhi/M +sinecure/MS +sinecurist/M +sine/SM +sinew/SGMD +sinewy +sinfulness/SM +sinful/YP +Singaporean/S +Singapore/M +sing/BGJZYDR +Singborg/M +singeing +singer/M +Singer/M +singe/S +singing/Y +singlehanded/Y +singleness/SM +single/PSDG +Singleton/M +singleton/SM +singletree/SM +singlet/SM +singsong/GSMD +singularity/SM +singularization/M +singular/SY +Sinhalese/M +sinisterness/M +sinister/YP +sinistral/Y +sinkable/U +sinker/M +sink/GZSDRB +sinkhole/SM +Sinkiang/M +sinking/M +sinlessness/M +sinless/YP +sin/MAGS +sinned +sinner/MS +sinning +sinter/DM +sinuosity/MS +sinuousities +sinuousness/M +sinuous/PY +sinusitis/SM +sinus/MS +sinusoidal/Y +sinusoid/MS +Siobhan/M +Siouxie/M +Sioux/M +siphon/DMSG +siphons/U +sipped +sipper/SM +sipping +sip/S +sired/C +sire/MS +siren/M +sires/C +siring/C +Sirius/M +sirloin/MS +Sir/MS +sirocco/MS +sirred +sirring +sirup's +sir/XGMNDS +sisal/MS +Sisely/M +Sisile/M +sis/S +Sissie/M +sissified +Sissy/M +sissy/TRSM +sister/GDYMS +sisterhood/MS +sisterliness/MS +sisterly/P +sister's/A +Sistine +Sisyphean +Sisyphus/M +sit/AG +sitarist/SM +sitar/SM +sitcom/SM +site/DSJM +sits +sitter/MS +sitting/SM +situate/GNSDX +situational/Y +situationist +situation/M +situ/S +situs/M +Siusan/M +Siva/M +Siward/M +sixfold +sixgun +six/MRSH +sixpence/MS +sixpenny +sixshooter +sixteen/HRSM +sixteenths +sixths +sixth/Y +sixtieths +sixty/SMH +sizableness/M +sizable/P +sized/UA +size/GJDRSBMZ +sizer/M +sizes/A +sizing/M +sizzler/M +sizzle/RSDG +SJ +Sjaelland/M +SK +ska/S +skateboard/SJGZMDR +skater/M +skate/SM +skat/JMDRGZ +skedaddle/GSD +skeet/RMS +skein/MDGS +skeletal/Y +skeleton/MS +Skell/M +Skelly/M +skeptical/Y +skepticism/MS +skeptic/SM +sketchbook/SM +sketcher/M +sketchily +sketchiness/MS +sketch/MRSDZG +sketchpad +sketchy/PRT +skew/DRSPGZ +skewer/GDM +skewing/M +skewness/M +skidded +skidding +skid/S +skiff/GMDS +skiing/M +skilfully +skill/DMSG +skilled/U +skillet/MS +skillfulnesses +skillfulness/MU +skillful/YUP +skilling/M +skimmed +skimmer/MS +skimming/SM +ski/MNJSG +skimp/GDS +skimpily +skimpiness/MS +skimpy/PRT +skim/SM +skincare +skindive/G +skinflint/MS +skinhead/SM +skinless +skinned +Skinner/M +skinner/SM +skinniness/MS +skinning +skinny/TRSP +skin/SM +skintight +Skip/M +skipped +Skipper/M +skipper/SGDM +Skippie/M +skipping +Skipp/RM +Skippy/M +skip/S +Skipton/M +skirmisher/M +skirmish/RSDMZG +skirter/M +skirting/M +skirt/RDMGS +skit/GSMD +skitter/SDG +skittishness/SM +skittish/YP +skittle/SM +skivvy/GSDM +skoal/SDG +Skopje/M +skulduggery/MS +skulker/M +skulk/SRDGZ +skullcap/MS +skullduggery's +skull/SDM +skunk/GMDS +skycap/MS +skydiver/SM +skydiving/MS +Skye/M +skyhook +skyjacker/M +skyjack/ZSGRDJ +Skylab/M +skylarker/M +skylark/SRDMG +Skylar/M +Skyler/M +skylight/MS +skyline/MS +Sky/M +sky/MDRSGZ +skyrocket/GDMS +skyscraper/M +skyscrape/RZ +skyward/S +skywave +skyway/M +skywriter/MS +skywriting/MS +slabbed +slabbing +slab/MS +slacken/DG +slacker/M +slackness/MS +slack/SPGTZXYRDN +Slade/M +slagged +slagging +slag/MS +slain +slake/DSG +slaked/U +slalom/SGMD +slammed +slammer/S +slamming +slam/S +slander/MDRZSG +slanderousness/M +slanderous/PY +slang/SMGD +slangy/TR +slanting/Y +slant/SDG +slantwise +slapdash/S +slaphappy/TR +slap/MS +slapped +slapper +slapping +slapstick/MS +slash/GZRSD +slashing/Y +slater/M +Slater/M +slate/SM +slather/SMDG +slating/M +slat/MDRSGZ +slatted +slattern/MYS +slatting +slaughterer/M +slaughterhouse/SM +slaughter/SJMRDGZ +slave/DSRGZM +slaveholder/SM +slaver/GDM +slavery/SM +Slavic/M +slavishness/SM +slavish/YP +Slav/MS +Slavonic/M +slaw/MS +slay/RGZS +sleaze/S +sleazily +sleaziness/SM +sleazy/RTP +sledded +sledder/S +sledding +sledgehammer/MDGS +sledge/SDGM +sled/SM +sleekness/S +sleek/PYRDGTS +sleeper/M +sleepily +sleepiness/SM +sleeping/M +sleeplessness/SM +sleepless/YP +sleepover/S +sleep/RMGZS +sleepwalker/M +sleepwalk/JGRDZS +sleepwear/M +sleepyhead/MS +sleepy/PTR +sleet/DMSG +sleety/TR +sleeveless +sleeve/SDGM +sleeving/M +sleigh/GMD +sleighs +sleight/SM +sleken/DG +slenderize/DSG +slenderness/MS +slender/RYTP +slept +Slesinger/M +sleuth/GMD +sleuths +slew/DGS +slice/DSRGZM +sliced/U +slicer/M +slicker/M +slickness/MS +slick/PSYRDGTZ +slider/M +slide/S +slid/GZDR +slight/DRYPSTG +slighter/M +slighting/Y +slightness/S +slime/SM +sliminess/S +slimline +slimmed +slimmer/S +slimmest +slimming/S +slimness/S +slim/SPGYD +slimy/PTR +sling/GMRS +slingshot/MS +slings/U +slink/GS +slinky/RT +slipcase/MS +slipcover/GMDS +slipknot/SM +slippage/SM +slipped +slipper/GSMD +slipperiness/S +slippery/PRT +slipping +slipshod +slip/SM +slipstream/MDGS +slipway/SM +slither/DSG +slithery +slit/SM +slitted +slitter/S +slitting +sliver/GSDM +slivery +Sloane/M +Sloan/M +slobber/SDG +slobbery +slob/MS +Slocum/M +sloe/MS +sloganeer/MG +slogan/MS +slogged +slogging +slog/S +sloop/SM +slop/DRSGZ +sloped/U +slope/S +slopped +sloppily +sloppiness/SM +slopping +sloppy/RTP +slosh/GSDM +slothfulness/MS +slothful/PY +sloth/GDM +sloths +slot/MS +slotted +slotting +slouch/DRSZG +sloucher/M +slouchy/RT +slough/GMD +sloughs +Slovakia/M +Slovakian/S +Slovak/S +Slovene/S +Slovenia/M +Slovenian/S +slovenliness/SM +slovenly/TRP +sloven/YMS +slowcoaches +slowdown/MS +slowish +slowness/MS +slow/PGTYDRS +slowpoke/MS +SLR +sludge/SDGM +sludgy/TR +slue/MGDS +sluggard/MS +slugged +slugger/SM +slugging +sluggishness/SM +sluggish/YP +slug/MS +sluice/SDGM +slumberer/M +slumber/MDRGS +slumberous +slumlord/MS +slummed +slummer +slumming +slum/MS +slummy/TR +slump/DSG +slung/U +slunk +slur/MS +slurp/GSD +slurred +slurried/M +slurring +slurrying/M +slurry/MGDS +slushiness/SM +slush/SDMG +slushy/RTP +slut/MS +sluttish +slutty/TR +Sly/M +slyness/MS +sly/RTY +smacker/M +smack/SMRDGZ +smallholders +smallholding/MS +smallish +Small/M +smallness/S +smallpox/SM +small/SGTRDP +smalltalk +smalltime +Smallwood/M +smarmy/RT +smarten/GD +smartness/S +smartypants +smart/YRDNSGTXP +smasher/M +smash/GZRSD +smashing/Y +smashup/S +smattering/SM +smearer/M +smear/GRDS +smeary/TR +smeller/M +smelliness/MS +smell/SBRDG +smelly/TRP +smelter/M +smelt/SRDGZ +Smetana/M +smidgen/MS +smilax/MS +smile/GMDSR +smiley/M +smilies +smiling/UY +smirch/SDG +smirk/GSMD +Smirnoff/M +smite/GSR +smiter/M +smith/DMG +smithereens +Smithfield/M +Smith/M +smiths +Smithsonian/M +Smithson/M +Smithtown/M +smithy/SM +smitten +Smitty/M +Sm/M +smocking/M +smock/SGMDJ +smoggy/TR +smog/SM +smoke/GZMDSRBJ +smokehouse/MS +smokeless +smoker/M +smokescreen/S +smokestack/MS +Smokey/M +smokiness/S +smoking/M +smoky/RSPT +smoldering/Y +smolder/SGD +Smolensk/M +Smollett/M +smooch/SDG +smoothen/DG +smoother/M +smoothie/SM +smoothness/MS +smooths +smooth/TZGPRDNY +smrgsbord/SM +smote +smother/GSD +SMSA/MS +SMTP +Smucker/M +smudge/GSD +smudginess/M +smudgy/TRP +smugged +smugger +smuggest +smugging +smuggle/JZGSRD +smuggler/M +smugness/MS +smug/YSP +smut/SM +Smuts/M +smutted +smuttiness/SM +smutting +smutty/TRP +Smyrna/M +snack/SGMD +snaffle/GDSM +snafu/DMSG +snagged +snagging +snag/MS +snail/GSDM +Snake +snakebird/M +snakebite/MS +snake/DSGM +snakelike +snakeroot/M +snaky/TR +snapback/M +snapdragon/MS +snapped/U +snapper/SM +snappily +snappiness/SM +snapping/U +snappishness/SM +snappish/PY +snappy/PTR +snapshot/MS +snapshotted +snapshotting +snap/US +snare/DSRGM +snarer/M +snarf/JSGD +snarler/M +snarling/Y +snarl/UGSD +snarly/RT +snatch/DRSZG +snatcher/M +snazzily +snazzy/TR +Snead/M +sneaker/MD +sneakily +sneakiness/SM +sneaking/Y +sneak/RDGZS +sneaky/PRT +Sneed/M +sneerer/M +sneer/GMRDJS +sneering/Y +sneeze/SRDG +Snell/M +snicker/GMRD +snick/MRZ +snideness/M +Snider/M +snide/YTSRP +sniffer/M +sniff/GZSRD +sniffle/GDRS +sniffler/M +sniffles/M +snifter/MDSG +snigger's +sniper/M +snipe/SM +snipped +snipper/SM +snippet/SM +snipping +snippy/RT +snip/SGDRZ +snitch/GDS +snit/SM +sniveler/M +snivel/JSZGDR +Sn/M +snobbery/SM +snobbishness/S +snobbish/YP +snobby/RT +snob/MS +Snodgrass/M +snood/SGDM +snooker/GMD +snook/SMRZ +snooper/M +snoop/SRDGZ +Snoopy/M +snoopy/RT +snootily +snootiness/MS +snoot/SDMG +snooty/TRP +snooze/GSD +snore/DSRGZ +snorkel/ZGSRDM +snorter/M +snort/GSZRD +snot/MS +snotted +snottily +snottiness/SM +snotting +snotty/TRP +snout/SGDM +snowball/SDMG +snowbank/SM +Snowbelt/SM +snowbird/SM +snowblower/S +snowboard/GZDRJS +snowbound +snowcapped +snowdrift/MS +snowdrop/MS +snowfall/MS +snowfield/MS +snowflake/MS +snow/GDMS +snowily +snowiness/MS +Snow/M +snowman/M +snowmen +snowmobile/GMDRS +snowplough/M +snowploughs +snowplow/SMGD +snowshed +snowshoeing +snowshoe/MRS +snowshoer/M +snowstorm/MS +snowsuit/S +snowy/RTP +snubbed +snubber +snubbing +snub/SP +snuffbox/SM +snuffer/M +snuff/GZSYRD +snuffle/GDSR +snuffler/M +snuffly/RT +snugged +snugger +snuggest +snugging +snuggle/GDS +snuggly +snugness/MS +snug/SYP +Snyder/M +so +SO +soaker/M +soak/GDRSJ +soapbox/DSMG +soapiness/S +soap/MDRGS +soapstone/MS +soapsud/S +soapy/RPT +soar/DRJSG +soarer/M +soaring/Y +sobbed +sobbing/Y +soberer/M +soberness/SM +sober/PGTYRD +sobriety/SIM +sobriquet/MS +sob/SZR +Soc +soccer/MS +sociabilities +sociability/IM +sociable/S +sociably/IU +socialism/SM +socialistic +socialist/SM +socialite/SM +sociality/M +socialization/SM +socialized/U +socializer/M +socialize/RSDG +socially/U +social/SY +societal/Y +society/MS +socio +sociobiology/M +sociocultural/Y +sociodemographic +socioeconomically +socioeconomic/S +sociolinguistics/M +sociological/MY +sociologist/SM +sociology/SM +sociometric +sociometry/M +sociopath/M +sociopaths +socket/SMDG +sock/GDMS +Socorro/M +Socrates/M +Socratic/S +soc/S +soda/SM +sodded +sodden/DYPSG +soddenness/M +sodding +Soddy/M +sodium/MS +sod/MS +sodomite/MS +sodomize/GDS +Sodom/M +sodomy/SM +soever +sofa/SM +Sofia/M +Sofie/M +softball/MS +softbound +softener/M +soften/ZGRD +softhearted +softie's +softness/MS +soft/SPXTYNR +software/MS +softwood/SM +softy/SM +soggily +sogginess/S +soggy/RPT +Soho/M +soign +soiled/U +soil/SGMD +soire/SM +sojourn/RDZGSM +solace/GMSRD +solacer/M +solaria +solarium/M +solar/S +solder/RDMSZG +soldier/MDYSG +soldiery/MS +sold/RU +solecism/MS +soled/FA +solemness +solemnify/GSD +solemnity/MS +solemnization/SM +solemnize/GSD +solemnness/SM +solemn/PTRY +solenoid/MS +soler/F +soles/IFA +sole/YSP +sol/GSMDR +solicitation/S +solicited/U +solicitor/MS +solicitousness/S +solicitous/YP +solicit/SDG +solicitude/MS +solidarity/MS +solidi +solidification/M +solidify/NXSDG +solidity/S +solidness/SM +solid/STYRP +solidus/M +soliloquies +soliloquize/DSG +soliloquy/M +soling/NM +solipsism/MS +solipsist/S +Solis/M +solitaire/SM +solitary/SP +solitude/SM +Sollie/M +Solly/M +Sol/MY +solo/DMSG +soloist/SM +Solomon/SM +Solon/M +Soloviev/M +solstice/SM +solubility/IMS +soluble/SI +solute/ENAXS +solute's +solution/AME +solvable/UI +solvating +solve/ABSRDZG +solved/EU +solvency/IMS +solvent/IS +solvently +solvent's +solver/MEA +solves/E +solving/E +Solzhenitsyn/M +Somalia/M +Somalian/S +Somali/MS +soma/M +somatic +somberness/SM +somber/PY +sombre +sombrero/SM +somebody'll +somebody/SM +someday +somehow +someone'll +someone/SM +someplace/M +somersault/DSGM +Somerset/M +somerset/S +somersetted +somersetting +Somerville/M +something/S +sometime/S +someway/S +somewhat/S +somewhere/S +some/Z +sommelier/SM +Somme/M +somnambulism/SM +somnambulist/SM +somnolence/MS +somnolent/Y +Somoza/M +sonar/SM +sonata/MS +sonatina/SM +Sondheim/M +Sondra/M +Sonenberg/M +songbag +songbird/SM +songbook/S +songfest/MS +songfulness/M +songful/YP +Songhai/M +Songhua/M +song/MS +songster/MS +songstress/SM +songwriter/SM +songwriting +Sonia/M +sonic/S +Sonja/M +Son/M +sonnet/MDSG +Sonnie/M +Sonni/M +Sonnnie/M +Sonny/M +sonny/SM +Sonoma/M +Sonora/M +sonority/S +sonorousness/SM +sonorous/PY +son/SMY +Sontag/M +sonuvabitch +Sonya/M +Sony/M +soonish +soon/TR +soothe +soother/M +sooth/GZTYSRDMJ +soothingness/M +soothing/YP +sooths +soothsayer/M +soothsay/JGZR +soot/MGDS +sooty/RT +SOP +Sophey/M +Sophia/SM +Sophie/M +Sophi/M +sophism/SM +sophister/M +sophistical +sophisticatedly +sophisticated/U +sophisticate/XNGDS +sophistication/MU +sophistic/S +sophist/RMS +sophistry/SM +Sophoclean +Sophocles/M +sophomore/SM +sophomoric +Sophronia/M +soporifically +soporific/SM +sopped +sopping/S +soppy/RT +soprano/SM +sop/SM +Sopwith/M +sorbet/SM +Sorbonne/M +sorcerer/MS +sorceress/S +sorcery/MS +Sorcha/M +sordidness/SM +sordid/PY +sorehead/SM +soreness/S +Sorensen/M +Sorenson/M +sore/PYTGDRS +sorghum/MS +sorority/MS +sorrel/SM +Sorrentine/M +sorrily +sorriness/SM +sorrower/M +sorrowfulness/SM +sorrowful/YP +sorrow/GRDMS +sorry/PTSR +sorta +sortable +sorted/U +sorter/MS +sort/FSAGD +sortieing +sortie/MSD +sort's +sos +SOS +Sosa/M +Sosanna/M +Soto/M +sot/SM +sottish +soubriquet's +souffl/MS +sough/DG +soughs +sought/U +soulfulness/MS +soulful/YP +soulless/Y +soul/MDS +sound/AUD +soundboard/MS +sounders +sounder's +sounder/U +soundest +sounding/AY +soundings +sounding's +soundless/Y +soundly/U +soundness/UMS +soundproof/GSD +soundproofing/M +sound's +sounds/A +soundtrack/MS +soupon/SM +soup/GMDS +Souphanouvong/M +soupy/RT +source/ASDMG +sourceless +sourdough +sourdoughs +sourish +sourness/MS +sourpuss/MS +sour/TYDRPSG +Sousa/M +sousaphone/SM +sous/DSG +souse +sou/SMH +Southampton/M +southbound +southeastern +southeaster/YM +Southeast/MS +southeast/RZMS +southeastward/S +southerly/S +souther/MY +southerner/M +Southerner/MS +southernisms +southernmost +southern/PZSYR +Southey/M +Southfield/M +southing/M +southland/M +South/M +southpaw/MS +south/RDMG +souths +Souths +southward/S +southwestern +southwester/YM +Southwest/MS +southwest/RMSZ +southwestward/S +souvenir/SM +sou'wester +sovereignty/MS +sovereign/YMS +soviet/MS +Soviet/S +sow/ADGS +sowbelly/M +sowens/M +sower/DS +Soweto/M +sown/A +sox's +soybean/MS +Soyinka/M +soy/MS +Soyuz/M +Spaatz/M +spacecraft/MS +space/DSRGZMJ +spaceflight/S +spaceman/M +spacemen +spaceport/SM +spacer/M +spaceship/MS +spacesuit/MS +spacewalk/GSMD +Spacewar/M +spacewoman +spacewomen +spacey +spacial +spacier +spaciest +spaciness +spacing/M +spaciousness/SM +spacious/PY +Spackle +spade/DSRGM +spadeful/SM +spader/M +spadework/SM +spadices +spadix/M +Spafford/M +spaghetti/SM +Spahn/M +Spain/M +spake +Spalding/M +Spam/M +spa/MS +Span +spandex/MS +spandrels +spangle/GMDS +Spanglish/S +Spaniard/SM +spanielled +spanielling +spaniel/SM +Spanish/M +spanker/M +spanking/M +spank/SRDJG +span/MS +spanned/U +spanner/SM +spanning +SPARC/M +SPARCstation/M +spar/DRMGTS +spareness/MS +spare/PSY +spareribs +sparer/M +sparing/UY +sparker/M +sparkle/DRSGZ +sparkler/M +Sparkman/M +Sparks +spark/SGMRD +sparky/RT +sparling/SM +sparred +sparrer +sparring/U +sparrow/MS +sparseness/S +sparse/YP +sparsity/S +spars/TR +Spartacus/M +Sparta/M +spartan +Spartan/S +spasm/GSDM +spasmodic +spasmodically +spastic/S +spate/SM +spathe/MS +spatiality/M +spatial/Y +spat/MS +spatted +spatter/DGS +spatterdock/M +spatting +spatula/SM +spavin/DMS +spawner/M +spawn/MRDSG +spay/DGS +SPCA +speakable/U +speakeasy/SM +speaker/M +Speaker's +speakership/M +speaking/U +speak/RBGZJS +spearer/M +spearfish/SDMG +spearhead/GSDM +spearmint/MS +spear/MRDGS +Spears +spec'd +specialism/MS +specialist/MS +specialization/SM +specialized/U +specialize/GZDSR +specializing/U +special/SRYP +specialty/MS +specie/MS +specif +specifiability +specifiable +specifiably +specifically +specification/SM +specificity/S +specific/SP +specified/U +specifier/SM +specifies +specify/AD +specifying +specimen/SM +spec'ing +speciousness/SM +specious/YP +speck/GMDS +speckle/GMDS +spec/SM +spectacle/MSD +spectacular/SY +spectator/SM +specter/DMS +specter's/A +spectralness/M +spectral/YP +spectra/M +spectrogram/MS +spectrographically +spectrograph/M +spectrography/M +spectrometer/MS +spectrometric +spectrometry/M +spectrophotometer/SM +spectrophotometric +spectrophotometry/M +spectroscope/SM +spectroscopic +spectroscopically +spectroscopy/SM +spectrum/M +specularity +specular/Y +speculate/VNGSDX +speculation/M +speculative/Y +speculator/SM +sped +speech/GMDS +speechlessness/SM +speechless/YP +speedboat/GSRM +speedboating/M +speeder/M +speedily +speediness/SM +speedometer/MS +speed/RMJGZS +speedster/SM +speedup/MS +speedway/SM +speedwell/MS +speedy/PTR +speer/M +speleological +speleologist/S +speleology/MS +spellbinder/M +spellbind/SRGZ +spellbound +spelldown/MS +spelled/A +speller/M +spelling/M +spell/RDSJGZ +spells/A +spelunker/MS +spelunking/S +Spencerian +Spencer/M +Spence/RM +spender/M +spend/SBJRGZ +spendthrift/MS +Spenglerian +Spengler/M +Spense/MR +Spenserian +Spenser/M +spent/U +spermatophyte/M +spermatozoa +spermatozoon/M +spermicidal +spermicide/MS +sperm/SM +Sperry/M +spew/DRGZJS +spewer/M +SPF +sphagnum/SM +sphere/SDGM +spherical/Y +spheric/S +spherics/M +spheroidal/Y +spheroid/SM +spherule/MS +sphincter/SM +Sphinx/M +sphinx/MS +Spica/M +spic/DGM +spicebush/M +spice/SM +spicily +spiciness/SM +spicule/MS +spicy/PTR +spider/SM +spiderweb/S +spiderwort/M +spidery/TR +Spiegel/M +Spielberg/M +spiel/GDMS +spier/M +spiffy/TDRSG +spigot/MS +spike/GMDSR +Spike/M +spiker/M +spikiness/SM +spiky/PTR +spillage/SM +Spillane/M +spillover/SM +spill/RDSG +spillway/SM +spinach/MS +spinal/YS +spindle/JGMDRS +spindly/RT +spinelessness/M +spineless/YP +spine/MS +spinet/SM +spininess/M +spinnability/M +spinnaker/SM +spinneret/MS +spinner/SM +spinning/SM +Spinoza/M +spin/S +spinsterhood/SM +spinsterish +spinster/MS +spiny/PRT +spiracle/SM +spiraea's +spiral/YDSG +spire/AIDSGF +spirea/MS +spire's +spiritedness/M +spirited/PY +spirit/GMDS +spiritless +spirits/I +spiritualism/SM +spiritualistic +spiritualist/SM +spirituality/SM +spiritual/SYP +spirituous +spirochete/SM +Spiro/M +spiry/TR +spitball/SM +spite/CSDAG +spitefuller +spitefullest +spitefulness/MS +spiteful/PY +spite's/A +spitfire/SM +spit/SGD +spitted +spitting +spittle/SM +spittoon/SM +Spitz/M +splashdown/MS +splasher/M +splash/GZDRS +splashily +splashiness/MS +splashy/RTP +splat/SM +splatted +splatter/DSG +splatting +splayfeet +splayfoot/MD +splay/SDG +spleen/SM +splendidness/M +splendid/YRPT +splendorous +splendor/SM +splenetic/S +splicer/M +splice/RSDGZJ +spline/MSD +splinter/GMD +splintery +splint/SGZMDR +splits/M +split/SM +splittable +splitter/MS +splitting/S +splodge/SM +splotch/MSDG +splotchy/RT +splurge/GMDS +splutterer/M +splutter/RDSG +Sp/M +Spock/M +spoilables +spoilage/SM +spoil/CSZGDR +spoiled/U +spoiler/MC +spoilsport/SM +Spokane/M +spoke/DSG +spoken/U +spokeshave/MS +spokesman/M +spokesmen +spokespeople +spokesperson/S +spokeswoman/M +spokeswomen +spoliation/MCS +spongecake +sponge/GMZRSD +sponger/M +sponginess/S +spongy/TRP +sponsor/DGMS +sponsorship/S +spontaneity/SM +spontaneousness/M +spontaneous/PY +spoof/SMDG +spookiness/MS +spook/SMDG +spooky/PRT +spool/SRDMGZ +spoonbill/SM +spoonerism/SM +spoonful/MS +spoon/GSMD +spoor/GSMD +sporadically +sporadic/Y +spore/DSGM +sporran/MS +sportiness/SM +sporting/Y +sportiveness/M +sportive/PY +sportscast/RSGZM +sportsmanlike/U +sportsman/MY +sportsmanship/MS +sportsmen +sportswear/M +sportswoman/M +sportswomen +sportswriter/S +sport/VGSRDM +sporty/PRT +Sposato/M +spotlessness/MS +spotless/YP +spotlight/GDMS +spotlit +spot/MSC +spotted/U +spotter/MS +spottily +spottiness/SM +spotting/M +spotty/RTP +spousal/MS +spouse/GMSD +spouter/M +spout/SGRD +sprain/SGD +sprang/S +sprat/SM +sprawl/GSD +sprayed/UA +sprayer/M +spray/GZSRDM +sprays/A +spreadeagled +spreader/M +spread/RSJGZB +spreadsheet/S +spreeing +spree/MDS +sprigged +sprigging +sprightliness/MS +sprightly/PRT +sprig/MS +springboard/MS +springbok/MS +springeing +springer/M +Springfield/M +springily +springiness/SM +springing/M +springlike +spring/SGZR +Springsteen/M +springtime/MS +springy/TRP +sprinkle/DRSJZG +sprinkler/DM +sprinkling/M +Sprint/M +sprint/SGZMDR +sprite/SM +spritz/GZDSR +sprocket/DMGS +sprocketed/U +Sproul/M +sprout/GSD +spruce/GMTYRSDP +spruceness/SM +sprue/M +sprung/U +spryness/S +spry/TRY +SPSS +spudded +spudding +spud/MS +Spuds/M +spume/DSGM +spumone's +spumoni/S +spumy/TR +spun +spunk/GSMD +spunky/SRT +spurge/MS +spuriousness/SM +spurious/PY +spur/MS +spurn/RDSG +spurred +spurring +spurt/SGD +sputa +Sputnik +sputnik/MS +sputter/DRGS +sputum/M +spy/DRSGM +spyglass/MS +sq +sqq +sqrt +squabbed +squabber +squabbest +squabbing +squabbler/M +squabble/ZGDRS +squab/SM +squadded +squadding +squadron/MDGS +squad/SM +squalidness/SM +squalid/PRYT +squaller/M +squall/GMRDS +squally/RT +squalor/SM +squamous/Y +squander/GSRD +Squanto +square/GMTYRSDP +squareness/SM +squarer/M +Squaresville/M +squarish +squash/GSRD +squashiness/M +squashy/RTP +squatness/MS +squat/SPY +squatted +squatter/SMDG +squattest +squatting +squawker/M +squawk/GRDMZS +squaw/SM +squeaker/M +squeakily +squeakiness/S +squeak/RDMGZS +squeaky/RPT +squealer/M +squeal/MRDSGZ +squeamishness/SM +squeamish/YP +squeegee/DSM +squeegeeing +squeeze/GZSRDB +squeezer/M +squelcher/M +squelch/GDRS +squelchy/RT +squibbed +Squibb/GM +squibbing +Squibbing/M +squib/SM +squidded +squidding +squid/SM +squiggle/MGDS +squiggly/RT +squinter/M +squint/GTSRD +squinting/Y +squirehood +squire/SDGM +squirm/SGD +squirmy/TR +squirrel/SGYDM +squirter/M +squirt/GSRD +squish/GSD +squishy/RTP +Sr +Srinagar/M +SRO +S's +SS +SSA +SSE +ssh +s's/KI +SSS +SST +SSW +ST +stabbed +stabber/S +stabbing/S +stability/ISM +stabilizability +stabilization/CS +stabilization's +stabilize/CGSD +stabilizer/MS +stableman/M +stablemate +stablemen +stableness/UM +stable/RSDGMTP +stabler/U +stable's/F +stables/F +stablest/U +stabling/M +stably/U +stab/YS +staccato/S +Stacee/M +Stace/M +Stacey/M +Stacia/M +Stacie/M +Staci/M +stackable +stacker/M +stack's +stack/USDG +Stacy/M +stadias +stadia's +stadium/MS +Stael/M +Stafani/M +staff/ADSG +Staffard/M +staffer/MS +Stafford/M +Staffordshire/M +staffroom +staff's +Staford/M +stag/DRMJSGZ +stagecoach/MS +stagecraft/MS +stagehand/MS +stager/M +stage/SM +stagestruck +stagflation/SM +stagged +staggerer/M +stagger/GSJDR +staggering/Y +staggers/M +stagging +staginess/M +staging/M +stagnancy/SM +stagnant/Y +stagnate/NGDSX +stagnation/M +stagy/PTR +Stahl/M +staidness/MS +staid/YRTP +stained/U +stainer/M +stainless/YS +stain/SGRD +staircase/SM +stair/MS +stairway/SM +stairwell/MS +stake/DSGM +stakeholder/S +stakeout/SM +stalactite/SM +stalag/M +stalagmite/SM +stalemate/SDMG +staleness/MS +stale/PGYTDSR +Staley/M +Stalingrad/M +Stalinist +Stalin/SM +stalker/M +stalk/MRDSGZJ +stall/DMSJG +stalled/I +stallholders +stallion/SM +Stallone/M +stalls/I +stalwartness/M +stalwart/PYS +Sta/M +stamen/MS +Stamford/M +stamina/SM +staminate +stammer/DRSZG +stammerer/M +stammering/Y +stampede/MGDRS +stampeder/M +stamped/U +stamper/M +stamp/RDSGZJ +stance/MIS +stancher/M +stanch/GDRST +stanchion/SGMD +standalone +standardization/AMS +standardized/U +standardize/GZDSR +standardizer/M +standardizes/A +standard/YMS +standby +standbys +standee/MS +Standford/M +standing/M +Standish/M +standoffish +standoff/SM +standout/MS +standpipe/MS +standpoint/SM +stand/SJGZR +standstill/SM +Stanfield/M +Stanford/M +Stanislas/M +Stanislaus/M +Stanislavsky/M +Stanislaw/M +stank/S +Stanleigh/M +Stanley/M +Stanly/M +stannic +stannous +Stanton/M +Stanwood/M +Stan/YMS +stanza/MS +staph/M +staphs +staphylococcal +staphylococci +staphylococcus/M +stapled/U +stapler/M +Stapleton/M +staple/ZRSDGM +starboard/SDMG +starchily +starchiness/MS +starch/MDSG +starchy/TRP +stardom/MS +star/DRMGZS +stardust/MS +stare/S +starfish/SM +Stargate/M +stargaze/ZGDRS +staring/U +Starkey/M +Stark/M +starkness/MS +stark/SPGTYRD +Starla/M +Starlene/M +starless +starlet/MS +starlight/MS +starling/MS +Starlin/M +starlit +Star/M +starred +starring +Starr/M +starry/TR +starship +starstruck +start/ASGDR +starter/MS +startle/GDS +startling/PY +startup/SM +starvation/MS +starveling/M +starver/M +starve/RSDG +stash/GSD +stasis/M +stat/DRSGV +statecraft/MS +stated/U +statehood/MS +statehouse/S +Statehouse's +state/IGASD +statelessness/MS +stateless/P +stateliness/MS +stately/PRT +statement/MSA +Staten/M +stater/M +stateroom/SM +stateside +state's/K +states/K +statesmanlike +statesman/MY +statesmanship/SM +statesmen +stateswoman +stateswomen +statewide +statical/Y +static/S +statics/M +stationarity +stationary/S +stationer/M +stationery/MS +stationmaster/M +station/SZGMDR +statistical/Y +statistician/MS +statistic/MS +Statler/M +stator/SM +statuary/SM +statue/MSD +statuesque/YP +statuette/MS +stature/MS +status/SM +statute/SM +statutorily +statutory/P +Stauffer/M +staunchness/S +staunch/PDRSYTG +stave/DGM +Stavro/MS +stay/DRGZS +stayer/M +std +STD +stdio +steadfastness/MS +steadfast/PY +steadily/U +steadiness's +steadiness/US +steading/M +stead/SGDM +steady/DRSUTGP +steakhouse/SM +steak/SM +stealer/M +stealing/M +steal/SRHG +stealthily +stealthiness/MS +stealth/M +stealths +stealthy/PTR +steamboat/MS +steamer/MDG +steamfitter/S +steamfitting/S +steamily +steaminess/SM +steamroller/DMG +steamroll/GZRDS +steam/SGZRDMJ +steamship/SM +steamy/RSTP +Stearne/M +Stearn/SM +steed/SM +Steele/M +steeliness/SM +steelmaker/M +steel/SDMGZ +steelworker/M +steelwork/ZSMR +steelyard/MS +steely/TPRS +Steen/M +steepen/GD +steeper/M +steeplebush/M +steeplechase/GMSD +steeplejack/MS +steeple/MS +steepness/S +steep/SYRNDPGTX +steerage/MS +steerer/M +steer/SGBRDJ +steersman/M +steersmen +steeves +Stefa/M +Stefania/M +Stefanie/M +Stefan/M +Stefano/M +Steffane/M +Steffen/M +Steffie/M +Steffi/M +stegosauri +stegosaurus/S +Steinbeck/SM +Steinberg/M +Steinem/M +Steiner/M +Steinmetz/M +Stein/RM +stein/SGZMRD +Steinway/M +Stella/M +stellar +stellated +Ste/M +stemless +stemmed/U +stemming +stem/MS +stemware/MS +stench/GMDS +stenciler/M +stencil/GDRMSZ +stencillings +Stendhal/M +Stendler/M +Stengel/M +stenographer/SM +stenographic +stenography/SM +steno/SM +stenotype/M +stentorian +stepbrother/MS +stepchild/M +stepchildren +stepdaughter/MS +stepfather/SM +Stepha/M +Stephana/M +Stephanie/M +Stephani/M +Stephan/M +Stephannie/M +Stephanus/M +Stephenie/M +Stephen/MS +Stephenson/M +Stephie/M +Stephi/M +Stephine/M +stepladder/SM +step/MIS +stepmother/SM +stepparent/SM +stepper/M +steppe/RSDGMZ +steppingstone/S +stepsister/SM +stepson/SM +stepwise +stereographic +stereography/M +stereo/GSDM +stereophonic +stereoscope/MS +stereoscopic +stereoscopically +stereoscopy/M +stereotype/GMZDRS +stereotypic +stereotypical/Y +sterile +sterility/SM +sterilization/SM +sterilized/U +sterilize/RSDGZ +sterilizes/A +Sterling/M +sterling/MPYS +sterlingness/M +sternal +Sternberg/M +Sterne/M +Stern/M +sternness/S +Sterno +stern/SYRDPGT +sternum/SM +steroidal +steroid/MS +stertorous +Stesha/M +stethoscope/SM +stet/MS +stetson/MS +Stetson/SM +stetted +stetting +Steuben/M +Stevana/M +stevedore/GMSD +Steve/M +Stevena/M +Steven/MS +Stevenson/M +Stevie/M +Stevy/M +steward/DMSG +stewardess/SM +Steward/M +stewardship/MS +Stewart/M +stew/GDMS +st/GBJ +sticker/M +stickily +stickiness/SM +stickleback/MS +stickle/GZDR +stickler/M +stick/MRDSGZ +stickpin/SM +stickup/SM +sticky/GPTDRS +Stieglitz/M +stiffen/JZRDG +stiff/GTXPSYRND +stiffness/MS +stifle/GJRSD +stifler/M +stifling/Y +stigma/MS +stigmata +stigmatic/S +stigmatization/C +stigmatizations +stigmatization's +stigmatize/DSG +stigmatized/U +stile/GMDS +stiletto/MDSG +stillbirth/M +stillbirths +stillborn/S +stiller/MI +stillest +Stillman/M +Stillmann/M +stillness/MS +still/RDIGS +Stillwell/M +stilted/PY +stilt/GDMS +Stilton/MS +Stimson/M +stimulant/MS +stimulated/U +stimulate/SDVGNX +stimulation/M +stimulative/S +stimulator/M +stimulatory +stimuli/M +stimulus/MS +Stine/M +stinger/M +sting/GZR +stingily +stinginess/MS +stinging/Y +stingray/MS +stingy/RTP +stinkbug/S +stinker/M +stink/GZRJS +stinking/Y +stinkpot/M +Stinky/M +stinky/RT +stinter/M +stinting/U +stint/JGRDMS +stipendiary +stipend/MS +stipple/JDRSG +stippler/M +stipulate/XNGSD +stipulation/M +Stirling/M +stirred/U +stirrer/SM +stirring/YS +stirrup/SM +stir/S +stitch/ASDG +stitcher/M +stitchery/S +stitching/MS +stitch's +St/M +stoat/SM +stochastic +stochastically +stochasticity +stockade/SDMG +stockbreeder/SM +stockbroker/MS +stockbroking/S +stocker/SM +Stockhausen/M +stockholder/SM +Stockholm/M +stockily +stockiness/SM +stockinet's +stockinette/S +stocking/MDS +stockist/MS +stockpile/GRSD +stockpiler/M +stockpot/MS +stockroom/MS +stock's +stock/SGAD +stocktaking/MS +Stockton/M +stockyard/SM +stocky/PRT +Stoddard/M +stodge/M +stodgily +stodginess/S +stodgy/TRP +stogy/SM +stoical/Y +stoichiometric +stoichiometry/M +stoicism/SM +Stoicism/SM +stoic/MS +Stoic/MS +stoke/DSRGZ +stoker/M +stokes/M +Stokes/M +STOL +stole/MDS +stolen +stolidity/S +stolidness/S +stolid/PTYR +stolon/SM +stomachache/MS +stomacher/M +stomach/RSDMZG +stomachs +stomp/DSG +stonecutter/SM +stone/DSRGM +Stonehenge/M +stoneless +Stone/M +stonemason/MS +stoner/M +stonewall/GDS +stoneware/MS +stonewashed +stonework/SM +stonewort/M +stonily +stoniness/MS +stony/TPR +stood +stooge/SDGM +stool/SDMG +stoop/SDG +stopcock/MS +stopgap/SM +stoplight/SM +stopover/MS +stoppable/U +stoppage/MS +Stoppard/M +stopped/U +stopper/GMDS +stopping/M +stopple/GDSM +stop's +stops/M +stop/US +stopwatch/SM +storage/SM +store/ADSRG +storefront/SM +storehouse/MS +storekeeper/M +storekeep/ZR +storeroom/SM +store's +stork/SM +stormbound +stormer/M +Stormie/M +stormily +Stormi/M +storminess/S +Storm/M +storm/SRDMGZ +stormtroopers +Stormy/M +stormy/PTR +storyboard/MDSG +storybook/MS +story/GSDM +storyline +storyteller/SM +storytelling/MS +Stouffer/M +stoup/SM +stouten/DG +stouthearted +Stout/M +stoutness/MS +stout/STYRNP +stove/DSRGM +stovepipe/SM +stover/M +stowage/SM +stowaway/MS +Stowe/M +stow/GDS +Strabo/M +straddler/M +straddle/ZDRSG +Stradivari/SM +Stradivarius/M +strafe/GRSD +strafer/M +straggle/GDRSZ +straggly/RT +straightaway/S +straightedge/MS +straightener/M +straighten/ZGDR +straightforwardness/MS +straightforward/SYP +straightjacket's +straightness/MS +straight/RNDYSTXGP +straightway/S +strain/ASGZDR +strained/UF +strainer/MA +straining/F +strains/F +straiten/DG +straitjacket/GDMS +straitlaced +straitness/M +strait/XTPSMGYDNR +stranded/P +strand/SDRG +strangeness/SM +strange/PYZTR +stranger/GMD +stranglehold/MS +strangle/JDRSZG +strangles/M +strangulate/NGSDX +strangulation/M +strapless/S +strapped/U +strapping/S +strap's +strap/US +Strasbourg/M +stratagem/SM +strata/MS +strategical/Y +strategic/S +strategics/M +strategist/SM +strategy/SM +Stratford/M +strati +stratification/M +stratified/U +stratify/NSDGX +stratigraphic +stratigraphical +stratigraphy/M +stratosphere/SM +stratospheric +stratospherically +stratum/M +stratus/M +Strauss +Stravinsky/M +strawberry/SM +strawflower/SM +straw/SMDG +strayer/M +stray/GSRDM +streak/DRMSGZ +streaker/M +streaky/TR +streamed/U +streamer/M +stream/GZSMDR +streaming/M +streamline/SRDGM +streetcar/MS +streetlight/SM +street/SMZ +streetwalker/MS +streetwise +Streisand/M +strengthen/AGDS +strengthener/MS +strength/NMX +strengths +strenuousness/SM +strenuous/PY +strep/MS +streptococcal +streptococci +streptococcus/M +streptomycin/SM +stress/DSMG +stressed/U +stressful/YP +stretchability/M +stretchable/U +stretch/BDRSZG +stretcher/DMG +stretchy/TRP +strew/GDHS +strewn +striae +stria/M +striate/DSXGN +striated/U +striation/M +stricken +Strickland/M +strict/AF +stricter +strictest +strictly +strictness/S +stricture/SM +stridden +stridency/S +strident/Y +strider/M +stride/RSGM +strife/SM +strikebreaker/M +strikebreaking/M +strikebreak/ZGR +strikeout/S +striker/M +strike/RSGZJ +striking/Y +Strindberg/M +stringed +stringency/S +stringent/Y +stringer/MS +stringiness/SM +stringing/M +string's +string/SAG +stringy/RTP +striper/M +stripe/SM +strip/GRDMS +stripling/M +stripped/U +stripper/MS +stripping +stripteaser/M +striptease/SRDGZM +stripy/RT +strive/JRSG +striven +striver/M +strobe/SDGM +stroboscope/SM +stroboscopic +strode +stroke/ZRSDGM +stroking/M +stroller/M +stroll/GZSDR +Stromberg/M +Stromboli/M +Strom/M +strongbow +strongbox/MS +Strongheart/M +stronghold/SM +strongish +Strong/M +strongman/M +strongmen +strongroom/MS +strong/YRT +strontium/SM +strophe/MS +strophic +stropped +stropping +strop/SM +strove +struck +structuralism/M +structuralist/SM +structural/Y +structured/AU +structureless +structures/A +structure/SRDMG +structuring/A +strudel/MS +struggle/GDRS +struggler/M +strummed +strumming +strumpet/GSDM +strum/S +strung/UA +strut/S +strutted +strutter/M +strutting +strychnine/MS +Stuart/MS +stubbed/M +stubbing +Stubblefield/MS +stubble/SM +stubbly/RT +stubbornness/SM +stubborn/SGTYRDP +stubby/SRT +stub/MS +stuccoes +stucco/GDM +stuck/U +studbook/SM +studded +studding/SM +Studebaker/M +studentship/MS +student/SM +studiedness/M +studied/PY +studier/SM +studio/MS +studiousness/SM +studious/PY +stud/MS +study/AGDS +stuffily +stuffiness/SM +stuffing/M +stuff/JGSRD +stuffy/TRP +stultify/NXGSD +Stu/M +stumble/GZDSR +stumbling/Y +stumpage/M +stumper/M +stump/RDMSG +stumpy/RT +stung +stunk +stunned +stunner/M +stunning/Y +stun/S +stunted/P +stunt/GSDM +stupefaction/SM +stupefy/DSG +stupendousness/M +stupendous/PY +stupidity/SM +stupidness/M +stupid/PTYRS +stupor/MS +sturdily +sturdiness/SM +sturdy/SRPT +sturgeon/SM +Sturm/M +stutter/DRSZG +Stuttgart/M +Stuyvesant/M +sty/DSGM +Stygian +styled/A +style/GZMDSR +styles/A +styli +styling/A +stylishness/S +stylish/PY +stylistically +stylistic/S +stylist/MS +stylites +stylization/MS +stylize/DSG +stylos +stylus/SM +stymieing +stymie/SD +stymy's +styptic/S +styrene/MS +Styrofoam/S +Styx/M +suable +Suarez/M +suasion/EMS +suaveness/S +suave/PRYT +suavity/SM +subaltern/SM +subarctic/S +subareas +Subaru/M +subassembly/M +subatomic/S +subbasement/SM +subbed +subbing +subbranch/S +subcaste/M +subcategorizing +subcategory/SM +subchain +subclassifications +subclass/MS +subclauses +subcommand/S +subcommittee/SM +subcompact/S +subcomponent/MS +subcomputation/MS +subconcept +subconsciousness/SM +subconscious/PSY +subconstituent +subcontinental +subcontinent/MS +subcontractor/SM +subcontract/SMDG +subcultural +subculture/GMDS +subcutaneous/Y +subdirectory/S +subdistrict/M +subdivide/SRDG +subdivision/SM +subdued/Y +subdue/GRSD +subduer/M +subexpression/MS +subfamily/SM +subfield/MS +subfile/SM +subfreezing +subgoal/SM +subgraph +subgraphs +subgroup/SGM +subharmonic/S +subheading/M +subhead/MGJS +subhuman/S +subindex/M +subinterval/MS +subj +subject/GVDMS +subjection/SM +subjectiveness/M +subjective/PSY +subjectivist/S +subjectivity/SM +subjoin/DSG +subjugate/NGXSD +subjugation/M +subjunctive/S +sublayer +sublease/DSMG +sublet/S +subletting +sublimate/GNSDX +sublimation/M +sublime/GRSDTYP +sublimeness/M +sublimer/M +subliminal/Y +sublimity/SM +sublist/SM +subliterary +sublunary +submachine +submarginal +submarine/MZGSRD +submariner/M +submerge/DSG +submergence/SM +submerse/XNGDS +submersible/S +submersion/M +submicroscopic +submission/SAM +submissiveness/MS +submissive/PY +submit/SA +submittable +submittal +submitted/A +submitter/S +submitting/A +submode/S +submodule/MS +sub/MS +subnational +subnet/SM +subnetwork/SM +subnormal/SY +suboptimal +suborbital +suborder/MS +subordinately/I +subordinates/I +subordinate/YVNGXPSD +subordination/IMS +subordinator +subornation/SM +suborn/GSD +subpage +subparagraph/M +subpart/MS +subplot/MS +subpoena/GSDM +subpopulation/MS +subproblem/SM +subprocess/SM +subprofessional/S +subprogram/SM +subproject +subproof/SM +subquestion/MS +subrange/SM +subregional/Y +subregion/MS +subrogation/M +subroutine/SM +subsample/MS +subschema/MS +subscribe/ASDG +subscriber/SM +subscripted/U +subscription/MS +subscript/SGD +subsection/SM +subsegment/SM +subsentence +subsequence/MS +subsequent/SYP +subservience/SM +subservient/SY +subset/MS +subsidence/MS +subside/SDG +subsidiarity +subsidiary/MS +subsidization/MS +subsidized/U +subsidizer/M +subsidize/ZRSDG +subsidy/MS +subsistence/MS +subsistent +subsist/SGD +subsocietal +subsoil/DRMSG +subsonic +subspace/MS +subspecies/M +substance/MS +substandard +substantially/IU +substantialness/M +substantial/PYS +substantiated/U +substantiate/VGNSDX +substantiation/MFS +substantiveness/M +substantive/PSYM +substantivity +substation/MS +substerilization +substitutability +substituted/U +substitute/NGVBXDRS +substitutionary +substitution/M +substitutive/Y +substrata +substrate/MS +substratum/M +substring/S +substructure/SM +subsume/SDG +subsurface/S +subsystem/MS +subtable/S +subtask/SM +subteen/SM +subtenancy/MS +subtenant/SM +subtend/DS +subterfuge/SM +subterranean/SY +subtest +subtext/SM +subtitle/DSMG +subtleness/M +subtle/RPT +subtlety/MS +subtly/U +subtopic/SM +subtotal/GSDM +subtracter/M +subtraction/MS +subtract/SRDZVG +subtrahend/SM +subtree/SM +subtropical +subtropic/S +subtype/MS +subunit/SM +suburbanite/MS +suburbanization/MS +suburbanized +suburbanizing +suburban/S +suburbia/SM +suburb/MS +subvention/MS +subversion/SM +subversiveness/MS +subversive/SPY +subverter/M +subvert/SGDR +subway/MDGS +subzero +succeeder/M +succeed/GDRS +successfulness/M +successful/UY +succession/SM +successiveness/M +successive/YP +success/MSV +successor/MS +successorship +succinctness/SM +succinct/RYPT +succored/U +succorer/M +succor/SGZRDM +succotash/SM +succubus/M +succulence/SM +succulency/MS +succulent/S +succumb/SDG +such +suchlike +sucker/DMG +suck/GZSDRB +suckle/SDJG +suckling/M +Sucre/M +sucrose/MS +suction/SMGD +Sudanese/M +Sudanic/M +Sudan/M +suddenness/SM +sudden/YPS +Sudetenland/M +sud/S +suds/DSRG +sudsy/TR +sued/DG +suede/SM +Suellen/M +Sue/M +suer/M +suet/MS +Suetonius/M +suety +sue/ZGDRS +Suez/M +sufferance/SM +sufferer/M +suffering/M +suffer/SJRDGZ +suffice/GRSD +sufficiency/SIM +sufficient/IY +suffixation/S +suffixed/U +suffix/GMRSD +suffocate/XSDVGN +suffocating/Y +Suffolk/M +suffragan/S +suffrage/MS +suffragette/MS +suffragist/SM +suffuse/VNGSDX +suffusion/M +Sufi/M +Sufism/M +sugarcane/S +sugarcoat/GDS +sugarless +sugarplum/MS +sugar/SJGMD +sugary/TR +suggest/DRZGVS +suggester/M +suggestibility/SM +suggestible +suggestion/MS +suggestiveness/MS +suggestive/PY +sugillate +Suharto/M +suicidal/Y +suicide/GSDM +Sui/M +suitability/SU +suitableness/S +suitable/P +suitably/U +suitcase/MS +suited/U +suite/SM +suiting/M +suit/MDGZBJS +suitor/SM +Sukarno/M +Sukey/M +Suki/M +sukiyaki/SM +Sukkoth's +Sukkot/S +Sula/M +Sulawesi/M +Suleiman/M +sulfaquinoxaline +sulfa/S +sulfate/MSDG +sulfide/S +sulfite/M +sulfonamide/SM +sulfur/DMSG +sulfuric +sulfurousness/M +sulfurous/YP +sulk/GDS +sulkily +sulkiness/S +sulky/RSPT +Sulla/M +sullenness/MS +sullen/TYRP +sullied/U +Sullivan/M +sully/GSD +Sully/M +sulphate/SM +sulphide/MS +sulphuric +sultana/SM +sultanate/MS +sultan/SM +sultrily +sultriness/SM +sultry/PRT +Sulzberger/M +sumach's +sumac/SM +Sumatra/M +Sumatran/S +sumer/F +Sumeria/M +Sumerian/M +summability/M +summable +summand/MS +summarily +summarization/MS +summarized/U +summarize/GSRDZ +summarizer/M +summary/MS +summation/FMS +summed +Summerdale/M +summerhouse/MS +summer/SGDM +Summer/SM +summertime/MS +summery/TR +summing +summit/GMDS +summitry/MS +summoner/M +summon/JSRDGZ +summons/MSDG +sum/MRS +Sumner/M +sumo/SM +sump/SM +sumptuousness/SM +sumptuous/PY +Sumter/M +Sun +sunbaked +sunbathe +sunbather/M +sunbathing/M +sunbaths +sunbath/ZRSDG +sunbeam/MS +Sunbelt/M +sunblock/S +sunbonnet/MS +sunburn/GSMD +sunburst/MS +suncream +sundae/MS +Sundanese/M +Sundas +Sunday/MS +sunder/SDG +sundial/MS +sundowner/M +sundown/MRDSZG +sundris +sundry/S +sunfish/SM +sunflower/MS +sunglass/MS +Sung/M +sung/U +sunk/SN +sunlamp/S +sunless +sunlight/MS +sunlit +sun/MS +sunned +Sunni/MS +sunniness/SM +sunning +Sunnite/SM +Sunny/M +sunny/RSTP +Sunnyvale/M +sunrise/GMS +sunroof/S +sunscreen/S +sunset/MS +sunsetting +sunshade/MS +Sunshine/M +sunshine/MS +sunshiny +sunspot/SM +sunstroke/MS +suntanned +suntanning +suntan/SM +sunup/MS +superabundance/MS +superabundant +superannuate/GNXSD +superannuation/M +superbness/M +superb/YRPT +supercargoes +supercargo/M +supercharger/M +supercharge/SRDZG +superciliousness/SM +supercilious/PY +supercity/S +superclass/M +supercomputer/MS +supercomputing +superconcept +superconducting +superconductivity/SM +superconductor/SM +supercooled +supercooling +supercritical +superdense +super/DG +superego/SM +supererogation/MS +supererogatory +superficiality/S +superficial/SPY +superfine +superfix/M +superfluity/MS +superfluousness/S +superfluous/YP +superheat/D +superheroes +superhero/SM +superhighway/MS +superhumanness/M +superhuman/YP +superimpose/SDG +superimposition/MS +superintendence/S +superintendency/SM +superintendent/SM +superintend/GSD +superiority/MS +Superior/M +superior/SMY +superlativeness/M +superlative/PYS +superlunary +supermachine +superman/M +Superman/M +supermarket/SM +supermen +supermodel +supermom/S +supernal +supernatant +supernaturalism/M +supernaturalness/M +supernatural/SPY +supernormal/Y +supernovae +supernova/MS +supernumerary/S +superordinate +superpose/BSDG +superposition/MS +superpower/MS +superpredicate +supersaturate/XNGDS +supersaturation/M +superscribe/GSD +superscript/DGS +superscription/SM +superseder/M +supersede/SRDG +supersensitiveness/M +supersensitive/P +superset/MS +supersonically +supersonic/S +supersonics/M +superstar/SM +superstition/SM +superstitious/YP +superstore/S +superstructural +superstructure/SM +supertanker/SM +supertitle/MSDG +superuser/MS +supervene/GSD +supervention/S +supervised/U +supervise/SDGNX +supervision/M +supervisor/SM +supervisory +superwoman/M +superwomen +supineness/M +supine/PSY +supper/DMG +supplanter/M +supplant/SGRD +supplemental/S +supplementary/S +supplementation/S +supplementer/M +supplement/SMDRG +suppleness/SM +supple/SPLY +suppliant/S +supplicant/MS +supplicate/NGXSD +supplication/M +supplier/AM +suppl/RDGT +supply/MAZGSRD +supportability/M +supportable/UI +supported/U +supporter/M +supporting/Y +supportive/Y +support/ZGVSBDR +supposed/Y +suppose/SRDBJG +supposition/MS +suppository/MS +suppressant/S +suppressed/U +suppressible/I +suppression/SM +suppressive/P +suppressor/S +suppress/VGSD +suppurate/NGXSD +suppuration/M +supp/YDRGZ +supra +supranational +supranationalism/M +suprasegmental +supremacist/SM +supremacy/SM +supremal +supremeness/M +supreme/PSRTY +supremo/M +sup/RSZ +supt +Supt/M +Surabaya/M +Surat/M +surcease/DSMG +surcharge/MGSD +surcingle/MGSD +surd/M +sured/I +surefire +surefooted +surely +sureness/MS +sureness's/U +sure/PU +surer/I +surest +surety/SM +surfaced/UA +surface/GSRDPZM +surfacer/AMS +surfaces/A +surfacing/A +surfactant/SM +surfboard/MDSG +surfeit/SDRMG +surfer/M +surfing/M +surf/SJDRGMZ +surged/A +surge/GYMDS +surgeon/MS +surgery/MS +surges/A +surgical/Y +Suriname +Surinamese +Surinam's +surliness/SM +surly/TPR +surmiser/M +surmise/SRDG +surmountable/IU +surmount/DBSG +surname/GSDM +surpassed/U +surpass/GDS +surpassing/Y +surplice/SM +surplus/MS +surplussed +surplussing +surprised/U +surprise/MGDRSJ +surpriser/M +surprising/YU +surrealism/MS +surrealistic +surrealistically +surrealist/S +surreality +surreal/S +surrender/DRSG +surrenderer/M +surreptitiousness/S +surreptitious/PY +surrey/SM +surrogacy/S +surrogate/SDMNG +surrogation/M +surrounding/M +surround/JGSD +surtax/SDGM +surveillance/SM +surveillant +surveyed/A +surveying/M +survey/JDSG +surveyor/MS +surveys/A +survivability/M +survivable/U +survivalist/S +survival/MS +survive/SRDBG +survivor/MS +survivorship/M +Surya/M +Sus +Susana/M +Susanetta/M +Susan/M +Susannah/M +Susanna/M +Susanne/M +Susann/M +susceptibilities +susceptibility/IM +susceptible/I +Susette/M +sushi/SM +Susie/M +Susi/M +suspected/U +suspecter/M +suspect/GSDR +suspecting/U +suspend/DRZGS +suspended/UA +suspender/M +suspenseful +suspense/MXNVS +suspension/AM +suspensive/Y +suspensor/M +suspicion/GSMD +suspiciousness/M +suspicious/YP +Susquehanna/M +Sussex/M +sustainability +sustainable/U +sustain/DRGLBS +sustainer/M +sustainment/M +sustenance/MS +Susy/M +Sutherland/M +Sutherlan/M +sutler/MS +Sutton/M +suture/GMSD +SUV +Suva/M +Suwanee/M +Suzanna/M +Suzanne/M +Suzann/M +suzerain/SM +suzerainty/MS +Suzette/M +Suzhou/M +Suzie/M +Suzi/M +Suzuki/M +Suzy/M +Svalbard/M +svelte/RPTY +Svend/M +Svengali +Sven/M +Sverdlovsk/M +Svetlana/M +SW +swabbed +swabbing +swabby/S +Swabian/SM +swab/MS +swaddle/SDG +swagged +swagger/GSDR +swagging +swag/GMS +Swahili/MS +swain/SM +SWAK +swallower/M +swallow/GDRS +swallowtail/SM +swam +swami/SM +swamper/M +swampland/MS +swamp/SRDMG +swampy/RPT +Swanee/M +swankily +swankiness/MS +swank/RDSGT +swanky/PTRS +swanlike +swan/MS +swanned +swanning +Swansea/M +Swanson/M +swappable/U +swapped +swapper/SM +swapping +swap/S +sward/MSGD +swarmer/M +swarm/GSRDM +swarthiness/M +Swarthmore/M +swarthy/RTP +swart/P +Swartz/M +swashbuckler/SM +swashbuckling/S +swash/GSRD +swastika/SM +SWAT +swatch/MS +swathe +swather/M +swaths +swath/SRDMGJ +swat/S +swatted +swatter/MDSG +swatting +swayback/SD +sway/DRGS +swayer/M +Swaziland/M +Swazi/SM +swearer/M +swear/SGZR +swearword/SM +sweatband/MS +sweater/M +sweatily +sweatiness/M +sweatpants +sweat/SGZRM +sweatshirt/S +sweatshop/MS +sweaty/TRP +Swedenborg/M +Sweden/M +swede/SM +Swede/SM +Swedish +Swed/MN +Sweeney/SM +sweeper/M +sweepingness/M +sweeping/PY +sweep/SBRJGZ +sweeps/M +sweepstakes +sweepstake's +sweetbread/SM +sweetbrier/SM +sweetcorn +sweetened/U +sweetener/M +sweetening/M +sweeten/ZDRGJ +sweetheart/MS +sweetie/MS +sweeting/M +sweetish/Y +Sweet/M +sweetmeat/MS +sweetness/MS +sweetshop +sweet/TXSYRNPG +swellhead/DS +swelling/M +swell/SJRDGT +swelter/DJGS +sweltering/Y +Swen/M +Swenson/M +swept +sweptback +swerve/GSD +swerving/U +swifter/M +swift/GTYRDPS +Swift/M +swiftness/MS +swigged +swigging +swig/SM +swill/SDG +swimmer/MS +swimming/MYS +swim/S +swimsuit/MS +Swinburne/M +swindle/GZRSD +swindler/M +swineherd/MS +swine/SM +swingeing +swinger/M +swinging/Y +swing/SGRZJB +swingy/R +swinishness/M +swinish/PY +Swink/M +swipe/DSG +swirling/Y +swirl/SGRD +swirly/TR +swish/GSRD +swishy/R +swiss +Swiss/S +switchback/GDMS +switchblade/SM +switchboard/MS +switcher/M +switch/GBZMRSDJ +switchgear +switchman/M +switchmen/M +switchover/M +Switzerland/M +Switzer/M +Switz/MR +swivel/GMDS +swizzle/RDGM +swob's +swollen +swoon/GSRD +swooning/Y +swoop/RDSG +swoosh/GSD +swop's +sword/DMSG +swordfish/SM +swordplayer/M +swordplay/RMS +swordsman/M +swordsmanship/SM +swordsmen +swordtail/M +swore +sworn +swot/S +swum +swung +s/XJBG +sybarite/MS +sybaritic +Sybila/M +Sybilla/M +Sybille/M +Sybil/M +Sybyl/M +sycamore/SM +sycophancy/S +sycophantic +sycophantically +sycophant/SYM +Sydelle/M +Sydel/M +Syd/M +Sydney/M +Sykes/M +Sylas/M +syllabicate/GNDSX +syllabication/M +syllabicity +syllabic/S +syllabification/M +syllabify/GSDXN +syllabi's +syllable/SDMG +syllabub/M +syllabus/MS +syllabusss +syllogism/MS +syllogistic +Sylow/M +sylphic +sylphlike +sylph/M +sylphs +Sylvania/M +Sylvan/M +sylvan/S +Sylvester/M +Sylvia/M +Sylvie/M +Syman/M +symbiont/M +symbioses +symbiosis/M +symbiotic +symbol/GMDS +symbolical/Y +symbolics/M +symbolic/SM +symbolism/MS +symbolist/MS +symbolization/MAS +symbolized/U +symbolize/GZRSD +symbolizes/A +Symington/M +symmetric +symmetrically/U +symmetricalness/M +symmetrical/PY +symmetrization/M +symmetrizing +symmetry/MS +Symon/M +sympathetically/U +sympathetic/S +sympathized/U +sympathizer/M +sympathize/SRDJGZ +sympathizing/MYUS +sympathy/MS +symphonic +symphonists +symphony/MS +symposium/MS +symptomatic +symptomatically +symptomatology/M +symptom/MS +syn +synagogal +synagogue/SM +synapse/SDGM +synaptic +synchronism/M +synchronization's +synchronization/SA +synchronize/AGCDS +synchronized/U +synchronizer/MS +synchronousness/M +synchronous/YP +synchrony +synchrotron/M +syncopate/VNGXSD +syncopation/M +syncope/MS +sync/SGD +syndicalist +syndicate/XSDGNM +syndic/SM +syndrome/SM +synergism/SM +synergistic +synergy/MS +synfuel/S +Synge/M +synod/SM +synonymic +synonymous/Y +synonym/SM +synonymy/MS +synopses +synopsis/M +synopsized +synopsizes +synopsizing +synoptic/S +syntactical/Y +syntactics/M +syntactic/SY +syntax/MS +syntheses +synthesis/M +synthesized/U +synthesize/GZSRD +synthesizer/M +synthesizes/A +synthetically +synthetic/S +syphilis/MS +syphilitic/S +syphilized +syphilizing +Syracuse/M +Syriac/M +Syria/M +Syrian/SM +syringe/GMSD +syrup/DMSG +syrupy +sys +systematical/Y +systematics/M +systematic/SP +systematization/SM +systematized/U +systematizer/M +systematize/ZDRSG +systematizing/U +systemically +systemic/S +systemization/SM +system/MS +systole/MS +systolic +Szilard/M +Szymborska/M +TA +Tabasco/MS +Tabatha/M +Tabbatha/M +tabbed +Tabbie/M +Tabbi/M +tabbing +Tabbitha/M +Tabb/M +tabbouleh +tabboulehs +tabby/GSD +Tabby/M +Taber/M +Tabernacle/S +tabernacle/SDGM +Tabina/M +Tabitha/M +tabla/MS +tableau/M +tableaux +tablecloth/M +tablecloths +table/GMSD +tableland/SM +tablespoonful/MS +tablespoon/SM +tablet/MDGS +tabletop/MS +tableware/SM +tabling/M +tabloid/MS +Tab/MR +taboo/GSMD +Tabor/M +tabor/MDGS +Tabriz/SM +tab/SM +tabula +tabular/Y +tabulate/XNGDS +tabulation/M +tabulator/MS +tachometer/SM +tachometry +tachycardia/MS +tachyon/SM +tacitness/MS +taciturnity/MS +taciturn/Y +Tacitus/M +tacit/YP +tacker/M +tack/GZRDMS +tackiness/MS +tackler/M +tackle/RSDMZG +tackling/M +tacky/RSTP +Tacoma/M +taco/MS +tact/FSM +tactfulness/S +tactful/YP +tactical/Y +tactician/MS +tactic/SM +tactile/Y +tactility/S +tactlessness/SM +tactless/PY +tactual/Y +Taddeo/M +Taddeusz/M +Tadd/M +Tadeas/M +Tadeo/M +Tades +Tadio/M +Tad/M +tadpole/MS +tad/SM +Tadzhikistan's +Tadzhikstan/M +Taegu/M +Taejon/M +taffeta/MS +taffrail/SM +Taffy/M +taffy/SM +Taft/M +Tagalog/SM +tagged/U +tagger/S +tagging +Tagore/M +tag/SM +Tagus/M +Tahitian/S +Tahiti/M +Tahoe/M +Taichung/M +taiga/MS +tailback/MS +tail/CMRDGAS +tailcoat/S +tailer/AM +tailgate/MGRSD +tailgater/M +tailing/MS +taillessness/M +tailless/P +taillight/MS +tailor/DMJSGB +Tailor/M +tailpipe/SM +tailspin/MS +tailwind/SM +Tainan/M +Taine/M +taint/DGS +tainted/U +Taipei/M +Taite/M +Tait/M +Taiwanese +Taiwan/M +Taiyuan/M +Tajikistan +takeaway/S +taken/A +takeoff/SM +takeout/S +takeover/SM +taker/M +take/RSHZGJ +takes/IA +taking/IA +Taklamakan/M +Talbert/M +Talbot/M +talcked +talcking +talc/SM +talcum/S +talebearer/SM +talented/M +talentless +talent/SMD +taler/M +tale/RSMN +tali +Talia/M +Taliesin/M +talion/M +talismanic +talisman/SM +talkativeness/MS +talkative/YP +talker/M +talk/GZSRD +talkie/M +talky/RST +Talladega/M +Tallahassee/M +Tallahatchie/M +Tallahoosa/M +tallboy/MS +Tallchief/M +Talley/M +Talleyrand/M +Tallia/M +Tallie/M +Tallinn/M +tallish +tallness/MS +Tallou/M +tallow/DMSG +tallowy +tall/TPR +Tallulah/M +tally/GRSDZ +tallyho/DMSG +Tally/M +Talmudic +Talmudist/MS +Talmud/MS +talon/SMD +talus/MS +Talyah/M +Talya/M +Ta/M +tamable/M +tamale/SM +tamarack/SM +Tamarah/M +Tamara/M +tamarind/MS +Tamar/M +Tamarra/M +Tamas +tambourine/MS +tamed/U +Tameka/M +tameness/S +Tamera/M +Tamerlane/M +tame/SYP +Tamika/M +Tamiko/M +Tamil/MS +Tami/M +Tam/M +Tamma/M +Tammany/M +Tammara/M +tam/MDRSTZGB +Tammie/M +Tammi/M +Tammy/M +Tampa/M +Tampax/M +tampered/U +tamperer/M +tamper/ZGRD +tampon/DMSG +tamp/SGZRD +Tamqrah/M +Tamra/M +tanager/MS +Tanaka/M +Tana/M +Tananarive/M +tanbark/SM +Tancred/M +tandem/SM +Tandie/M +Tandi/M +tandoori/S +Tandy/M +Taney/M +T'ang +Tanganyika/M +tangelo/SM +tangency/M +tangential/Y +tangent/SM +tangerine/MS +tang/GSYDM +tangibility/MIS +tangible/IPS +tangibleness's/I +tangibleness/SM +tangibly/I +Tangier/M +tangle's +tangle/UDSG +tango/MDSG +Tangshan/M +tangy/RST +Tanhya/M +Tania/M +Tani/M +Tanisha/M +Tanitansy/M +tankard/MS +tanker/M +tankful/MS +tank/GZSRDM +Tan/M +tan/MS +tanned/U +Tannenbaum/M +Tanner/M +tanner/SM +tannery/MS +tannest +Tanney/M +Tannhuser/M +Tannie/M +tanning/SM +tannin/SM +Tann/RM +Tanny/M +Tansy/M +tansy/SM +tantalization/SM +tantalized/U +tantalize/GZSRD +tantalizingly/S +tantalizingness/S +tantalizing/YP +tantalum/MS +Tantalus/M +tantamount +tantra/S +tantrum/SM +Tanya/M +Tanzania/M +Tanzanian/S +taoism +Taoism/MS +Taoist/MS +taoist/S +Tao/M +tao/S +Tapdance/M +taped/U +tapeline/S +taperer/M +taper/GRD +tape/SM +tapestry/GMSD +tapeworm/MS +tapioca/MS +tapir/MS +tap/MSDRJZG +tapped/U +tapper/MS +tappet/MS +tapping/M +taproom/MS +taproot/SM +taps/M +Tarah/M +Tara/M +tarantella/MS +tarantula/MS +Tarawa/M +Tarazed/M +Tarbell/M +tardily +tardiness/S +tardy/TPRS +tare/MS +target/GSMD +tar/GSMD +tariff/DMSG +Tarim/M +Tarkington/M +tarmacked +tarmacking +tarmac/S +tarnished/U +tarnish/GDS +tarn/MS +taro/MS +tarot/MS +tarpapered +tarpaulin/MS +tarp/MS +tarpon/MS +tarragon/SM +Tarrah/M +Tarra/M +Tarrance/M +tarred/M +tarring/M +tarry/TGRSD +Tarrytown/M +tarsal/S +tarsi +tarsus/M +tartan/MS +tartaric +Tartar's +tartar/SM +Tartary/M +tartness/MS +tart/PMYRDGTS +Tartuffe/M +Taryn/M +Tarzan/M +Tasha/M +Tashkent/M +Tasia/M +task/GSDM +taskmaster/SM +taskmistress/MS +Tasmania/M +Tasmanian/S +tassellings +tassel/MDGS +Tass/M +tasted/EU +tastefulness/SME +tasteful/PEY +taste/GZMJSRD +tastelessness/SM +tasteless/YP +taster/M +taste's/E +tastes/E +tastily +tastiness/MS +tasting/E +tasty/RTP +tatami/MS +Tatar/SM +Tate/M +tater/M +Tatiana/M +Tatiania/M +tat/SRZ +tatted +tatterdemalion/SM +tattered/M +tatter/GDS +tatting/SM +tattler/M +tattle/RSDZG +tattletale/SM +tattooer/M +tattooist/MS +tattoo/ZRDMGS +tatty/R +Tatum/M +taught/AU +taunter/M +taunting/Y +taunt/ZGRDS +taupe/SM +Taurus/SM +tau/SM +tauten/GD +tautness/S +tautological/Y +tautologous +tautology/SM +taut/PGTXYRDNS +taverner/M +tavern/RMS +tawdrily +tawdriness/SM +tawdry/SRTP +Tawney/M +Tawnya/M +tawny/RSMPT +Tawsha/M +taxable/S +taxably +taxation/MS +taxed/U +taxicab/MS +taxidermist/SM +taxidermy/MS +taxi/MDGS +taximeter/SM +taxing/Y +taxiway/MS +taxonomic +taxonomically +taxonomist/SM +taxonomy/SM +taxpayer/MS +taxpaying/M +tax/ZGJMDRSB +Taylor/SM +Tb +TB +TBA +Tbilisi/M +tbs +tbsp +Tchaikovsky/M +Tc/M +TCP +TD +TDD +Te +teabag/S +teacake/MS +teacart/M +teachable/P +teach/AGS +teacher/MS +teaching/SM +teacloth +teacupful/MS +teacup/MS +Teador/M +teahouse/SM +teakettle/SM +teak/SM +teakwood/M +tealeaves +teal/MS +tea/MDGS +teammate/MS +team/MRDGS +teamster/MS +teamwork/SM +teapot/MS +tearaway +teardrop/MS +tearer/M +tearfulness/M +tearful/YP +teargas/S +teargassed +teargassing +tearjerker/S +tearoom/MS +tear/RDMSG +teary/RT +Teasdale/M +tease/KS +teasel/DGSM +teaser/M +teashop/SM +teasing/Y +teaspoonful/MS +teaspoon/MS +teas/SRDGZ +teatime/MS +teat/MDS +tech/D +technetium/SM +technicality/MS +technicalness/M +technical/YSP +technician/MS +Technicolor/MS +Technion/M +technique/SM +technocracy/MS +technocratic +technocrat/S +technological/Y +technologist/MS +technology/MS +technophobia +technophobic +techs +tectonically +tectonic/S +tectonics/M +Tecumseh/M +Tedda/M +Teddie/M +Teddi/M +Tedd/M +Teddy/M +teddy/SM +Tedie/M +Tedi/M +tediousness/SM +tedious/YP +tedium/MS +Ted/M +Tedman/M +Tedmund/M +Tedra/M +tee/DRSMH +teeing +teem/GSD +teemingness/M +teeming/PY +teenager/M +teenage/RZ +Teena/M +teen/SR +teenybopper/SM +teeny/RT +teepee's +teeshirt/S +teeter/GDS +teethe +teether/M +teething/M +teethmarks +teeth/RSDJMG +teetotaler/M +teetotalism/MS +teetotal/SRDGZ +TEFL +Teflon/MS +Tegucigalpa/M +Teheran's +Tehran +TEirtza/M +tektite/SM +Tektronix/M +telecast/SRGZ +telecommunicate/NX +telecommunication/M +telecommute/SRDZGJ +telecoms +teleconference/GMJSD +Teledyne/M +Telefunken/M +telegenic +telegrammed +telegramming +telegram/MS +telegraphic +telegraphically +telegraphist/MS +telegraph/MRDGZ +telegraphs +telegraphy/MS +telekineses +telekinesis/M +telekinetic +Telemachus/M +Telemann/M +telemarketer/S +telemarketing/S +telemeter/DMSG +telemetric +telemetry/MS +teleological/Y +teleology/M +telepathic +telepathically +telepathy/SM +telephone/SRDGMZ +telephonic +telephonist/SM +telephony/MS +telephotography/MS +telephoto/S +teleprinter/MS +teleprocessing/S +teleprompter +TelePrompter/M +TelePrompTer/S +telescope/GSDM +telescopic +telescopically +teletext/S +telethon/MS +teletype/SM +Teletype/SM +teletypewriter/SM +televangelism/S +televangelist/S +televise/SDXNG +television/M +televisor/MS +televisual +telex/GSDM +Telex/M +tell/AGS +Teller/M +teller/SDMG +telling/YS +Tell/MR +telltale/MS +tellurium/SM +telly/SM +Telnet/M +TELNET/M +telnet/S +telomeric +tel/SY +Telugu/M +temblor/SM +temerity/MS +Tempe/M +temperamental/Y +temperament/SM +temperance/IMS +tempera/SLM +temperately/I +temperateness's/I +temperateness/SM +temperate/SDGPY +temperature/MS +tempered/UE +temper/GRDM +tempering/E +temper's/E +tempers/E +tempest/DMSG +tempestuousness/SM +tempestuous/PY +template/FS +template's +Temple/M +Templeman/M +temple/SDM +Templeton/M +Temp/M +tempoes +tempo/MS +temporal/YS +temporarily +temporarinesses +temporariness/FM +temporary/SFP +temporize/GJZRSD +temporizer/M +temporizings/U +temporizing/YM +temp/SGZTMRD +temptation/MS +tempted +tempter/S +tempt/FS +tempting/YS +temptress/MS +tempura/SM +tenabilities +tenability/UM +tenableness/M +tenable/P +tenably +tenaciousness/S +tenacious/YP +tenacity/S +tenancy/MS +tenanted/U +tenant/MDSG +tenantry/MS +tench/M +tended/UE +tendency/MS +tendentiousness/SM +tendentious/PY +tendered +tenderer +tenderest +tenderfoot/MS +tender/FS +tenderheartedness/MS +tenderhearted/YP +tendering +tenderizer/M +tenderize/SRDGZ +tenderloin/SM +tenderly +tenderness/SM +tending/E +tendinitis/S +tend/ISFRDG +tendon/MS +tendril/SM +tends/E +tenebrous +tenement/MS +tenet/SM +Tenex/M +TENEX/M +tenfold/S +ten/MHB +Tenneco/M +tenner +Tennessean/S +Tennessee/M +Tenney/M +tennis/SM +Tenn/M +Tennyson/M +Tenochtitlan/M +tenon/GSMD +tenor/MS +tenpin/SM +tense/IPYTNVR +tenseness's/I +tenseness/SM +tensile +tensional/I +tension/GMRDS +tensionless +tensions/E +tension's/I +tensity/IMS +tensorial +tensor/MS +tenspot +tens/SRDVGT +tentacle/MSD +tentativeness/S +tentative/SPY +tented/UF +tenterhook/MS +tenter/M +tent/FSIM +tenths +tenth/SY +tenting/F +tenuity/S +tenuousness/SM +tenuous/YP +tenure/SDM +Teodoor/M +Teodora/M +Teodorico/M +Teodor/M +Teodoro/M +tepee/MS +tepidity/S +tepidness/S +tepid/YP +tequila/SM +Tera/M +teratogenic +teratology/MS +terbium/SM +tercel/M +tercentenary/S +tercentennial/S +Terence/M +Terencio/M +Teresa/M +Terese/M +Tereshkova/M +Teresina/M +Teresita/M +Teressa/M +Teriann/M +Teri/M +Terkel/M +termagant/SM +termcap +termer/M +terminable/CPI +terminableness/IMC +terminal/SYM +terminate/CXNV +terminated/U +terminates +terminating +termination/MC +terminative/YC +terminator/SM +termini +terminological/Y +terminology/MS +terminus/M +termite/SM +term/MYRDGS +ternary/S +tern/GIDS +tern's +terpsichorean +Terpsichore/M +terrace/MGSD +terracing/M +terracotta +terrain/MS +Terra/M +terramycin +Terrance/M +Terran/M +terrapin/MS +terrarium/MS +terrazzo/SM +Terrell/M +Terrel/M +Terre/M +Terrence/M +terrestrial/YMS +terribleness/SM +terrible/P +terribly +Terrie/M +terrier/M +terrifically +terrific/Y +terrify/GDS +terrifying/Y +Terrijo/M +Terrill/M +Terri/M +terrine/M +territoriality/M +Territorial/SM +territorial/SY +Territory's +territory/SM +terrorism/MS +terroristic +terrorist/MS +terrorized/U +terrorizer/M +terrorize/RSDZG +terror/MS +terr/S +terrycloth +Terrye/M +Terry/M +terry/ZMRS +terseness/SM +terse/RTYP +Tersina/M +tertian +Tertiary +tertiary/S +Terza/M +TESL +Tesla/M +TESOL +Tessa/M +tessellate/XDSNG +tessellation/M +tesseral +Tessie/M +Tessi/M +Tess/M +Tessy/M +testability/M +testable/U +testamentary +testament/SM +testate/IS +testator/MS +testatrices +testatrix +testbed/S +testcard +tested/AKU +tester/MFCKS +testes/M +testicle/SM +testicular +testifier/M +testify/GZDRS +testily +testimonial/SM +testimony/SM +testiness/S +testing/S +testis/M +testosterone/SM +test/RDBFZGSC +tests/AK +test's/AKF +testy/RTP +tetanus/MS +tetchy/TR +tether/DMSG +tethered/U +Tethys/M +Tetons +tetrachloride/M +tetracycline/SM +tetrafluoride +tetragonal/Y +tetrahalides +tetrahedral/Y +tetrahedron/SM +tetrameron +tetrameter/SM +tetra/MS +tetrasodium +tetravalent +Teutonic +Teuton/SM +Texaco/M +Texan/S +Texas/MS +Tex/M +TeX/M +textbook/SM +text/FSM +textile/SM +Textron/M +textual/FY +textural/Y +textured/U +texture/MGSD +T/G +Thacher/M +Thackeray/M +Thaddeus/M +Thaddus/M +Thadeus/M +Thad/M +Thailand/M +Thaine/M +Thain/M +Thai/S +thalami +thalamus/M +Thales/M +Thalia/M +thalidomide/MS +thallium/SM +thallophyte/M +Thames +than +Thane/M +thane/SM +Thanh/M +thanker/M +thankfuller +thankfullest +thankfulness/SM +thankful/YP +thanklessness/SM +thankless/PY +thanksgiving/MS +Thanksgiving/S +thank/SRDG +Thant/M +Thar/M +Thatcher/M +thatching/M +thatch/JMDRSZG +Thatch/MR +that'd +that'll +that/MS +thaumaturge/M +thaw/DGS +Thaxter/M +Thayer/M +Thayne/M +THC +the +Theadora/M +Thea/M +theatergoer/MS +theatergoing/MS +theater/SM +theatricality/SM +theatrical/YS +theatric/S +theatrics/M +Thebault/M +Thebes +Theda/M +Thedrick/M +Thedric/M +thee/DS +theeing +theft/MS +Theiler/M +their/MS +theism/SM +theistic +theist/SM +Thekla/M +Thelma/M +themas +thematically +thematics +thematic/U +theme/MS +them/GD +Themistocles/M +themselves +thence +thenceforth +thenceforward/S +Theobald/M +theocracy/SM +theocratic +Theocritus/M +theodolite/MS +Theodora/M +Theodore/M +Theodoric/M +Theodor/M +Theodosia/M +Theodosian +Theodosius/M +theologian/SM +theological/Y +theologists +theology/MS +Theo/M +theorem/MS +theoretical/Y +theoretician/MS +theoretic/S +theoretics/M +theorist/SM +theorization/SM +theorize/ZGDRS +theory/MS +theosophic +theosophical +theosophist/MS +Theosophy +theosophy/SM +therapeutically +therapeutic/S +therapeutics/M +therapist/MS +therapy/MS +Theravada/M +thereabout/S +thereafter +thereat +thereby +there'd +therefor +therefore +therefrom +therein +there'll +there/MS +thereof +thereon +Theresa/M +Therese/M +Theresina/M +Theresita/M +Theressa/M +thereto +theretofore +thereunder +thereunto +thereupon +therewith +Therine/M +thermal/YS +thermionic/S +thermionics/M +thermistor/MS +therm/MS +thermocouple/MS +thermodynamical/Y +thermodynamic/S +thermodynamics/M +thermoelastic +thermoelectric +thermoformed +thermoforming +thermogravimetric +thermoluminescence/M +thermometer/MS +thermometric +thermometry/M +thermonuclear +thermopile/M +thermoplastic/S +thermopower +thermo/S +thermosetting +thermos/S +Thermos/SM +thermostable +thermostatically +thermostatic/S +thermostatics/M +thermostat/SM +thermostatted +thermostatting +Theron/M +thesauri +thesaurus/MS +these/S +Theseus/M +thesis/M +thespian/S +Thespian/S +Thespis/M +Thessalonian +Thessalonki/M +Thessaly/M +theta/MS +thew/SM +they +they'd +they'll +they're +they've +th/GNJX +Thia/M +thiamine/MS +Thibaud/M +Thibaut/M +thickener/M +thickening/M +thicken/RDJZG +thicket/SMD +thickheaded/M +thickish +thickness/MS +thickset/S +thick/TXPSRNY +thief/M +Thiensville/M +Thieu/M +thievery/MS +thieve/SDJG +thievishness/M +thievish/P +thighbone/SM +thigh/DM +thighs +thimble/DSMG +thimbleful/MS +Thimbu/M +Thimphu +thine +thingamabob/MS +thingamajig/SM +thing/MP +thinkableness/M +thinkable/U +thinkably/U +think/AGRS +thinker/MS +thinkingly/U +thinking/SMYP +thinned +thinner/MS +thinness/MS +thinnest +thinning +thinnish +thin/STPYR +thiocyanate/M +thiouracil/M +third/DYGS +thirster/M +thirst/GSMDR +thirstily +thirstiness/S +thirsty/TPR +thirteen/MHS +thirteenths +thirtieths +thirty/HMS +this +this'll +thistledown/MS +thistle/SM +thither +Th/M +tho +thole/GMSD +Thomasa/M +Thomasina/M +Thomasine/M +Thomasin/M +Thoma/SM +Thomism/M +Thomistic +Thom/M +Thompson/M +Thomson/M +thong/SMD +thoracic +thorax/MS +Thorazine +Thoreau/M +thoriate/D +Thorin/M +thorium/MS +Thor/M +Thornburg/M +Thorndike/M +Thornie/M +thorniness/S +Thorn/M +thorn/SMDG +Thornton/M +Thorny/M +thorny/PTR +thoroughbred/S +thoroughfare/MS +thoroughgoing +thoroughness/SM +thorough/PTYR +Thorpe/M +Thorstein/M +Thorsten/M +Thorvald/M +those +Thoth/M +thou/DSG +though +thoughtfully +thoughtfulness/S +thoughtful/U +thoughtlessness/MS +thoughtless/YP +thought/MS +thousandfold +thousand/SHM +thousandths +Thrace/M +Thracian/M +thralldom/S +thrall/GSMD +thrash/DSRZGJ +thrasher/M +thrashing/M +threadbare/P +threader/M +threading/A +threadlike +thread/MZDRGS +thready/RT +threatener/M +threaten/GJRD +threatening/Y +threat/MDNSXG +threefold +three/MS +threepence/M +threepenny +threescore/S +threesome/SM +threnody/SM +thresh/DSRZG +thresher/M +threshold/MDGS +threw +thrice +thriftily +thriftiness/S +thriftless +thrift/SM +thrifty/PTR +thriller/M +thrilling/Y +thrill/ZMGDRS +thriver/M +thrive/RSDJG +thriving/Y +throatily +throatiness/MS +throat/MDSG +throaty/PRT +throbbed +throbbing +throb/S +throeing +throe/SDM +thrombi +thromboses +thrombosis/M +thrombotic +thrombus/M +Throneberry/M +throne/CGSD +throne's +throng/GDSM +throttle/DRSZMG +throttler/M +throughout +throughput/SM +throughway's +through/Y +throwaway/SM +throwback/MS +thrower/M +thrown +throwout +throw/SZGR +thrummed +thrumming +thrum/S +thrush/MS +thruster/M +thrust/ZGSR +Thruway/MS +thruway/SM +Thunderbird/M +Thu +Thucydides/M +thudded +thudding +thud/MS +thuggee/M +thuggery/SM +thuggish +thug/MS +Thule/M +thulium/SM +thumbnail/MS +thumbscrew/SM +thumb/SMDG +thumbtack/GMDS +thump/RDMSG +thunderbolt/MS +thunderclap/SM +thundercloud/SM +thunderer/M +thunderhead/SM +thundering/Y +thunderous/Y +thundershower/MS +thunderstorm/MS +thunderstruck +thundery +thunder/ZGJDRMS +thunk +Thurber/M +Thurman/M +Thur/MS +Thursday/SM +Thurstan/M +Thurston/M +thus/Y +thwack/DRSZG +thwacker/M +thwarter/M +thwart/GSDRY +thy +thyme/SM +thymine/MS +thymus/SM +thyratron/M +thyristor/MS +thyroglobulin +thyroidal +thyroid/S +thyronine +thyrotoxic +thyrotrophic +thyrotrophin +thyrotropic +thyrotropin/M +thyroxine/M +thyself +Tia/M +Tianjin +tiara/MS +Tiberius/M +Tiber/M +Tibetan/S +Tibet/M +tibiae +tibial +tibia/M +Tibold/M +Tiburon/M +ticker/M +ticket/SGMD +tick/GZJRDMS +ticking/M +tickler/M +tickle/RSDZG +ticklishness/MS +ticklish/PY +ticktacktoe/S +ticktock/SMDG +tic/MS +Ticonderoga/M +tidal/Y +tidbit/MS +tiddlywinks/M +tide/GJDS +tideland/MS +tidewater/SM +tideway/SM +tidily/U +tidiness/USM +tidying/M +tidy/UGDSRPT +tie/AUDS +tieback/MS +Tiebold/M +Tiebout/M +tiebreaker/SM +Tieck/M +Tiena/M +Tienanmen/M +Tientsin's +tier/DGM +Tierney/M +Tiertza/M +Tiffanie/M +Tiffani/M +tiffany/M +Tiffany/M +tiff/GDMS +Tiffie/M +Tiffi/M +Tiff/M +Tiffy/M +tigerish +tiger/SM +tightener/M +tighten/JZGDR +tightfisted +tightness/MS +tightrope/SM +tight/STXPRNY +tightwad/MS +tigress/SM +Tigris/M +Tijuana/M +tike's +Tilda/M +tilde/MS +Tildie/M +Tildi/M +Tildy/M +tile/DRSJMZG +tiled/UE +Tiler/M +tiles/U +tiling/M +tillable +tillage/SM +till/EGSZDR +tiller/GDM +tiller's/E +Tillich/M +Tillie/M +Tillman/M +Tilly/M +tilth/M +tilt/RDSGZ +Ti/M +timber/DMSG +timbering/M +timberland/SM +timberline/S +timbrel/SM +timbre/MS +Timbuktu/M +ti/MDRZ +timebase +time/DRSJMYZG +timekeeper/MS +timekeeping/SM +timelessness/S +timeless/PY +timeliness/SMU +timely/UTRP +timeout/S +timepiece/MS +timer/M +timescale/S +timeserver/MS +timeserving/S +timeshare/SDG +timespan +timestamped +timestamps +timetable/GMSD +timeworn +Timex/M +timezone/S +timidity/SM +timidness/MS +timid/RYTP +Timi/M +timing/M +Timmie/M +Timmi/M +Tim/MS +Timmy/M +Timofei/M +Timon/M +timorousness/MS +timorous/YP +Timoteo/M +Timothea/M +Timothee/M +Timotheus/M +Timothy/M +timothy/MS +timpani +timpanist/S +Timur/M +Tina/M +tincture/SDMG +tinderbox/MS +tinder/MS +Tine/M +tine/SM +tinfoil/MS +tingeing +tinge/S +ting/GYDM +tingle/SDG +tingling/Y +tingly/TR +Ting/M +tinily +tininess/MS +tinker/SRDMZG +Tinkertoy +tinkle/SDG +tinkling/M +tinkly +tin/MDGS +tinned +tinner/M +tinnily +tinniness/SM +tinning/M +tinnitus/MS +tinny/RSTP +tinplate/S +tinsel/GMDYS +Tinseltown/M +tinsmith/M +tinsmiths +tinter/M +tintinnabulation/MS +Tintoretto/M +tint/SGMRDB +tintype/SM +tinware/MS +tiny/RPT +Tioga/M +Tiphanie/M +Tiphani/M +Tiphany/M +tipi's +tip/MS +tipoff +Tippecanoe/M +tipped +Tipperary/M +tipper/MS +tippet/MS +tipping +tippler/M +tipple/ZGRSD +tippy/R +tipsily +tipsiness/SM +tipster/SM +tipsy/TPR +tiptoeing +tiptoe/SD +tiptop/S +tirade/SM +Tirana's +Tirane +tired/AYP +tireder +tiredest +tiredness/S +tirelessness/SM +tireless/PY +tire/MGDSJ +tires/A +Tiresias/M +tiresomeness/S +tiresome/PY +tiring/AU +Tirolean/S +Tirol/M +tiro's +Tirrell/M +tis +Tisha/M +Tish/M +tissue/MGSD +titanate/M +Titania/M +titanic +titanically +Titanic/M +titanium/SM +titan/SM +Titan/SM +titbit's +titer/M +tither/M +tithe/SRDGZM +tithing/M +Titian/M +titian/S +Titicaca/M +titillate/XSDVNG +titillating/Y +titillation/M +titivate/NGDSX +titivation/M +titled/AU +title/GMSRD +titleholder/SM +titling/A +titmice +titmouse/M +tit/MRZS +Tito/SM +titrate/SDGN +titration/M +titted +titter/GDS +titting +tittle/SDMG +titular/SY +Titus/M +tizzy/SM +TKO +Tlaloc/M +TLC +Tlingit/M +Tl/M +TM +Tm/M +tn +TN +tnpk +TNT +toad/SM +toadstool/SM +toady/GSDM +toadyism/M +toaster/M +toastmaster/MS +toastmistress/S +toast/SZGRDM +toasty/TRS +tobacconist/SM +tobacco/SM +tobaggon/SM +Tobago/M +Tobe/M +Tobey/M +Tobiah/M +Tobias/M +Tobie/M +Tobi/M +Tobin/M +Tobit/M +toboggan/MRDSZG +Tobye/M +Toby/M +Tocantins/M +toccata/M +Tocqueville +tocsin/MS +to/D +today'll +today/SM +Toddie/M +toddler/M +toddle/ZGSRD +Todd/M +Toddy/M +toddy/SM +Tod/M +toecap/SM +toeclip/S +TOEFL +toehold/MS +toeing +toe/MS +toenail/DMGS +toffee/SM +tofu/S +toga/SMD +toge +togetherness/MS +together/P +togged +togging +toggle/SDMG +Togolese/M +Togo/M +tog/SMG +Toiboid/M +toilet/GMDS +toiletry/MS +toilette/SM +toil/SGZMRD +toilsomeness/M +toilsome/PY +Toinette/M +Tojo/M +tokamak +Tokay/M +toke/GDS +tokenism/SM +tokenized +token/SMDG +Tokugawa/M +Tokyoite/MS +Tokyo/M +Toland/M +told/AU +Toledo/SM +tole/MGDS +tolerability/IM +tolerable/I +tolerably/I +tolerance/SIM +tolerant/IY +tolerate/XVNGSD +toleration/M +Tolkien +tollbooth/M +tollbooths +toll/DGS +Tolley/M +tollgate/MS +tollhouse/M +tollway/S +Tolstoy/M +toluene/MS +Tolyatti/M +tomahawk/SGMD +Tomasina/M +Tomasine/M +Toma/SM +Tomaso/M +tomatoes +tomato/M +Tombaugh/M +tomb/GSDM +Tombigbee/M +tomblike +tombola/M +tomboyish +tomboy/MS +tombstone/MS +tomcat/SM +tomcatted +tomcatting +Tome/M +tome/SM +tomfoolery/MS +tomfool/M +Tomi/M +Tomkin/M +Tomlin/M +Tom/M +tommed +Tommie/M +Tommi/M +tomming +tommy/M +Tommy/M +tomographic +tomography/MS +tomorrow/MS +Tompkins/M +Tomsk/M +tom/SM +tomtit/SM +tonality/MS +tonal/Y +tonearm/S +tone/ISRDZG +tonelessness/M +toneless/YP +toner/IM +tone's +Tonga/M +Tongan/SM +tong/GRDS +tongueless +tongue/SDMG +tonguing/M +Tonia/M +tonic/SM +Tonie/M +tonight/MS +Toni/M +Tonio/M +tonk/MS +tonnage/SM +tonne/MS +Tonnie/M +tonsillectomy/MS +tonsillitis/SM +tonsil/SM +ton/SKM +tonsorial +tonsure/SDGM +Tonto/M +Tonya/M +Tonye/M +Tony/M +tony/RT +toodle +too/H +took/A +tool/AGDS +toolbox/SM +tooler/SM +tooling/M +toolkit/SM +toolmaker/M +toolmake/ZRG +toolmaking/M +tool's +toolsmith +Toomey/M +tooter/M +toot/GRDZS +toothache/SM +toothbrush/MSG +tooth/DMG +toothily +toothless +toothmarks +toothpaste/SM +toothpick/MS +tooths +toothsome +toothy/TR +tootle/SRDG +tootsie +Tootsie/M +toots/M +tootsy/MS +topaz/MS +topcoat/MS +topdressing/S +Topeka/M +toper/M +topflight +topgallant/M +topiary/S +topicality/MS +topical/Y +topic/MS +topknot/MS +topless +topmast/MS +topmost +topnotch/R +topocentric +topographer/SM +topographic +topographical/Y +topography/MS +topological/Y +topologist/MS +topology/MS +topped +topper/MS +topping/MS +topple/GSD +topsail/MS +topside/SRM +top/SMDRG +topsoil/GDMS +topspin/MS +Topsy/M +toque/MS +Torah/M +Torahs +torchbearer/SM +torchlight/S +torch/SDMG +toreador/SM +Tore/M +tore/S +Torey/M +Torie/M +tori/M +Tori/M +Torin/M +torment/GSD +tormenting/Y +tormentor/MS +torn +tornadoes +tornado/M +toroidal/Y +toroid/MS +Toronto/M +torpedoes +torpedo/GMD +torpidity/S +torpid/SY +torpor/MS +Torquemada/M +torque/MZGSRD +Torrance/M +Torre/MS +torrence +Torrence/M +Torrens/M +torrential +torrent/MS +Torrey/M +Torricelli/M +torridity/SM +torridness/SM +torrid/RYTP +Torrie/M +Torrin/M +Torr/XM +Torry/M +torsional/Y +torsion/IAM +torsions +torsi's +tor/SLM +torso/SM +tors/S +tort/ASFE +tortellini/MS +torte/MS +torten +tortilla/MS +tortoiseshell/SM +tortoise/SM +Tortola/M +tortoni/MS +tort's +Tortuga/M +tortuousness/MS +tortuous/PY +torture/ZGSRD +torturous +torus/MS +Tory/SM +Tosca/M +Toscanini/M +Toshiba/M +toss/SRDGZ +tossup/MS +totaler/M +totalistic +totalitarianism/SM +totalitarian/S +totality/MS +totalizator/S +totalizing +total/ZGSRDYM +totemic +totem/MS +toter/M +tote/S +toting/M +tot/MDRSG +Toto/M +totted +totterer/M +tottering/Y +totter/ZGRDS +totting +toucan/MS +touchable/U +touch/ASDG +touchdown/SM +touch +touched/U +toucher/M +touchily +touchiness/SM +touching/SY +touchline/M +touchscreen +touchstone/SM +touchy/TPR +toughen/DRZG +toughener/M +toughness/SM +toughs +tough/TXGRDNYP +Toulouse/M +toupee/SM +toured/CF +tourer/M +tour/GZSRDM +touring/F +tourism/SM +touristic +tourist/SM +touristy +tourmaline/SM +tournament/MS +tourney/GDMS +tourniquet/MS +tour's/CF +tours/CF +tousle/GSD +touter/M +tout/SGRD +Tova/M +Tove/M +towardliness/M +towardly/P +towards +toward/YU +towboat/MS +tow/DRSZG +towelette/S +towel/GJDMS +toweling/M +tower/GMD +towering/Y +towhead/MSD +towhee/SM +towline/MS +towner/M +Townes +Towney/M +townhouse/S +Townie/M +townie/S +Townley/M +Town/M +Townsend/M +townsfolk +township/MS +townsman/M +townsmen +townspeople/M +town/SRM +townswoman/M +townswomen +Towny/M +towpath/M +towpaths +towrope/MS +Towsley/M +toxemia/MS +toxicity/MS +toxicological +toxicologist/SM +toxicology/MS +toxic/S +toxin/MS +toyer/M +toymaker +toy/MDRSG +Toynbee/M +Toyoda/M +Toyota/M +toyshop +tr +traceability/M +traceableness/M +traceable/P +trace/ASDG +traceback/MS +traced/U +Tracee/M +traceless/Y +Trace/M +tracepoint/SM +tracer/MS +tracery/MDS +trace's +Tracey/M +tracheae +tracheal/M +trachea/M +tracheotomy/SM +Tracie/M +Traci/M +tracing/SM +trackage +trackball/S +trackbed +tracked/U +tracker/M +trackless +tracksuit/SM +track/SZGMRD +tractability/SI +tractable/I +tractably/I +tract/ABS +Tractarians +traction/KSCEMAF +tractive/KFE +tractor/FKMASC +tract's +tracts/CEFK +Tracy/M +trademark/GSMD +trader/M +tradesman/M +tradesmen +tradespeople +tradespersons +trade/SRDGZM +tradeswoman/M +tradeswomen +traditionalism/MS +traditionalistic +traditionalist/MS +traditionalized +traditionally +traditional/U +tradition/SM +traduce/DRSGZ +Trafalgar/M +trafficked +trafficker/MS +trafficking/S +traffic/SM +tragedian/SM +tragedienne/MS +tragedy/MS +tragically +tragicomedy/SM +tragicomic +tragic/S +trailblazer/MS +trailblazing/S +trailer/GDM +trails/F +trailside +trail/SZGJRD +trainable +train/ASDG +trained/U +trainee/MS +traineeships +trainer/MS +training/SM +trainman/M +trainmen +trainspotter/S +traipse/DSG +trait/MS +traitorous/Y +traitor/SM +Trajan/M +trajectory/MS +trammed +trammeled/U +trammel/GSD +tramming +tram/MS +trample/DGRSZ +trampler/M +trampoline/GMSD +tramp/RDSZG +tramway/M +trance/MGSD +tranche/SM +Tran/M +tranquility/S +tranquilized/U +tranquilize/JGZDSR +tranquilizer/M +tranquilizes/A +tranquilizing/YM +tranquillize/GRSDZ +tranquillizer/M +tranquilness/M +tranquil/PTRY +transact/GSD +transactional +transaction/MS +transactor/SM +transalpine +transaminase +transatlantic +Transcaucasia/M +transceiver/SM +transcendence/MS +transcendentalism/SM +transcendentalist/SM +transcendental/YS +transcendent/Y +transcend/SDG +transconductance +transcontinental +transcribe/DSRGZ +transcriber/M +transcription/SM +transcript/SM +transcultural +transducer/SM +transduction/M +transect/DSG +transept/SM +transferability/M +transferal/MS +transfer/BSMD +transferee/M +transference/SM +transferor/MS +transferral/SM +transferred +transferrer/SM +transferring +transfiguration/SM +transfigure/SDG +transfinite/Y +transfix/SDG +transformational +transformation/MS +transform/DRZBSG +transformed/U +transformer/M +transfuse/XSDGNB +transfusion/M +transgression/SM +transgressor/S +transgress/VGSD +trans/I +transience/SM +transiency/S +transient/YS +transistorize/GDS +transistor/SM +Transite/M +transitional/Y +transition/MDGS +transitivenesses +transitiveness/IM +transitive/PIY +transitivity/MS +transitoriness/M +transitory/P +transit/SGVMD +transl +translatability/M +translatable/U +translated/AU +translate/VGNXSDB +translational +translation/M +translator/SM +transliterate/XNGSD +translucence/SM +translucency/MS +translucent/Y +transmigrate/XNGSD +transmissible +transmission/MSA +transmissive +transmit/AS +transmittable +transmittal/SM +transmittance/MS +transmitted/A +transmitter/SM +transmitting/A +transmogrification/M +transmogrify/GXDSN +transmutation/SM +transmute/GBSD +transnational/S +transoceanic +transom/SM +transonic +transpacific +transparency/MS +transparentness/M +transparent/YP +transpiration/SM +transpire/GSD +transplantation/S +transplant/GRDBS +transpolar +transponder/MS +transportability +transportable/U +transportation/SM +transport/BGZSDR +transpose/BGSD +transposed/U +transposition/SM +Transputer/M +transsexualism/MS +transsexual/SM +transship/LS +transshipment/SM +transshipped +transshipping +transubstantiation/MS +Transvaal/M +transversal/YM +transverse/GYDS +transvestism/SM +transvestite/SM +transvestitism +Transylvania/M +trapdoor/S +trapeze/DSGM +trapezium/MS +trapezoidal +trapezoid/MS +trap/MS +trappable/U +trapped +trapper/SM +trapping/S +Trappist/MS +trapshooting/SM +trashcan/SM +trashiness/SM +trash/SRDMG +trashy/TRP +Trastevere/M +trauma/MS +traumatic +traumatically +traumatize/SDG +travail/SMDG +traveled/U +traveler/M +travelog's +travelogue/S +travel/SDRGZJ +Traver/MS +traversal/SM +traverse/GBDRS +traverser/M +travertine/M +travesty/SDGM +Travis/M +Travus/M +trawler/M +trawl/RDMSZG +tray/SM +treacherousness/SM +treacherous/PY +treachery/SM +treacle/DSGM +treacly +treader/M +treadle/GDSM +treadmill/MS +tread/SAGD +Treadwell/M +treas +treason/BMS +treasonous +treasure/DRSZMG +treasurer/M +treasurership +treasury/SM +Treasury/SM +treatable +treated/U +treater/S +treatise/MS +treatment/MS +treat's +treat/SAGDR +treaty/MS +treble/SDG +Treblinka/M +treeing +treeless +treelike +tree/MDS +treetop/SM +trefoil/SM +Trefor/M +trekked +trekker/MS +Trekkie/M +trekking +trek/MS +trellis/GDSM +Tremaine/M +Tremain/M +trematode/SM +Tremayne/M +tremble/JDRSG +trembler/M +trembles/M +trembly +tremendousness/M +tremendous/YP +tremolo/MS +tremor/MS +tremulousness/SM +tremulous/YP +trenchancy/MS +trenchant/Y +trencherman/M +trenchermen +trencher/SM +trench/GASD +trench's +trendily +trendiness/S +trend/SDMG +trendy/PTRS +Trenna/M +Trent/M +Trenton/M +trepanned +trepidation/MS +Tresa/M +Trescha/M +trespasser/M +trespass/ZRSDG +Tressa/M +tressed/E +tresses/E +tressing/E +tress/MSDG +trestle/MS +Trevar/M +Trevelyan/M +Trever/M +Trevino/M +Trevor/M +Trev/RM +Trey/M +trey/MS +triableness/M +triable/P +triadic +triad/MS +triage/SDMG +trial/ASM +trialization +trialled +trialling +triamcinolone +triangle/SM +triangulable +triangularization/S +triangular/Y +triangulate/YGNXSD +triangulation/M +Triangulum/M +Trianon/M +Triassic +triathlon/S +triatomic +tribalism/MS +tribal/Y +tribe/MS +tribesman/M +tribesmen +tribeswoman +tribeswomen +tribulate/NX +tribulation/M +tribunal/MS +tribune/SM +tributary/MS +tribute/EGSF +tribute's +trice/GSDM +tricentennial/S +triceps/SM +triceratops/M +trichinae +trichina/M +trichinoses +trichinosis/M +trichloroacetic +trichloroethane +trichotomy/M +trichromatic +Tricia/M +trickery/MS +trick/GMSRD +trickily +trickiness/SM +trickle/DSG +trickster/MS +tricky/RPT +tricolor/SMD +tricycle/SDMG +trident/SM +tridiagonal +tried/UA +triennial/SY +trier/AS +trier's +tries/A +Trieste/M +triffid/S +trifle/MZGJSRD +trifler/M +trifluoride/M +trifocals +trigged +trigger/GSDM +triggest +trigging +triglyceride/MS +trigonal/Y +trigonometric +trigonometrical +trigonometry/MS +trigram/S +trig/S +trihedral +trike/GMSD +trilateral/S +trilby/SM +trilingual +trillion/SMH +trillionth/M +trillionths +trillium/SM +trill/RDMGS +trilobite/MS +trilogy/MS +trimaran/MS +Trimble/M +trimer/M +trimester/MS +trimmed/U +trimmer/MS +trimmest +trimming/MS +trimness/S +trimodal +trimonthly +trim/PSYR +Trimurti/M +Trina/M +Trinidad/M +trinitarian/S +trinitrotoluene/SM +trinity/MS +Trinity/MS +trinketer/M +trinket/MRDSG +triode/MS +trio/SM +trioxide/M +tripartite/N +tripartition/M +tripe/MS +triphenylarsine +triphenylphosphine +triphenylstibine +triphosphopyridine +triple/GSD +triplet/SM +triplex/S +triplicate/SDG +triplication/M +triply/GDSN +Trip/M +tripodal +tripod/MS +tripoli/M +Tripoli/M +tripolyphosphate +tripos/SM +tripped +Trippe/M +tripper/MS +tripping/Y +Tripp/M +trip/SMY +triptych/M +triptychs +tripwire/MS +trireme/SM +Tris +trisect/GSD +trisection/S +trisector +Trisha/M +Trish/M +trisodium +Trista/M +Tristam/M +Tristan/M +tristate +trisyllable/M +tritely/F +triteness/SF +trite/SRPTY +tritium/MS +triton/M +Triton/M +triumphal +triumphalism +triumphant/Y +triumph/GMD +triumphs +triumvirate/MS +triumvir/MS +triune +trivalent +trivet/SM +trivia +triviality/MS +trivialization/MS +trivialize/DSG +trivial/Y +trivium/M +Trixie/M +Trixi/M +Trix/M +Trixy/M +Trobriand/M +trochaic/S +trochee/SM +trod/AU +trodden/UA +trodes +troff/MR +troglodyte/MS +troika/SM +Trojan/MS +troll/DMSG +trolled/F +trolleybus/S +trolley/SGMD +trolling/F +trollish +Trollope/M +trollop/GSMD +trolly's +trombone/MS +trombonist/SM +tromp/DSG +Trondheim/M +trooper/M +troopship/SM +troop/SRDMZG +trope/SM +Tropez/M +trophic +trophy/MGDS +tropical/SY +tropic/MS +tropism/SM +tropocollagen +troposphere/MS +tropospheric +troth/GDM +troths +trot/S +Trotsky/M +trotted +trotter/SM +trotting +troubadour/SM +troubled/U +trouble/GDRSM +troublemaker/MS +troubler/M +troubleshooter/M +troubleshoot/SRDZG +troubleshot +troublesomeness/M +troublesome/YP +trough/M +troughs +trounce/GZDRS +trouncer/M +troupe/MZGSRD +trouper/M +trouser/DMGS +trousseau/M +trousseaux +Troutman/M +trout/SM +trove/SM +troweler/M +trowel/SMDRGZ +trow/SGD +Troyes +Troy/M +troy/S +Trstram/M +truancy/MS +truant/SMDG +truce/SDGM +Truckee/M +trucker/M +trucking/M +truckle/GDS +truckload/MS +truck/SZGMRDJ +truculence/SM +truculent/Y +Truda/M +Trudeau/M +Trude/M +Trudey/M +trudge/SRDG +Trudie/M +Trudi/M +Trudy/M +true/DRSPTG +truelove/MS +Trueman/M +trueness/M +truer/U +truest/U +truffle/MS +truism/SM +Trujillo/M +Trula/M +truly/U +Trumaine/M +Truman/M +Trumann/M +Trumbull/M +trump/DMSG +trumpery/SM +trumpeter/M +trumpet/MDRZGS +Trump/M +truncate/NGDSX +truncation/M +truncheon/MDSG +trundle/GZDSR +trundler/M +trunk/GSMD +trunnion/SM +trusser/M +trussing/M +truss/SRDG +trusted/EU +trusteeing +trustee/MDS +trusteeship/SM +truster/M +trustful/EY +trustfulness/SM +trustiness/M +trusting/Y +trust/RDMSG +trusts/E +trustworthier +trustworthiest +trustworthiness/MS +trustworthy/UP +trusty/PTMSR +Truth +truthfulness/US +truthful/UYP +truths/U +truth/UM +TRW +trying/Y +try/JGDRSZ +tryout/MS +trypsin/M +tryst/GDMS +ts +T's +tsarevich +tsarina's +tsarism/M +tsarist +tsetse/S +Tsimshian/M +Tsiolkovsky/M +Tsitsihar/M +tsp +tsunami/MS +Tsunematsu/M +Tswana/M +TTL +tty/M +ttys +Tuamotu/M +Tuareg/M +tubae +tubal +tuba/SM +tubbed +tubbing +tubby/TR +tubeless +tubercle/MS +tubercular/S +tuberculin/MS +tuberculoses +tuberculosis/M +tuberculous +tuber/M +tuberose/SM +tuberous +tube/SM +tubing/M +tub/JMDRSZG +Tubman/M +tubular/Y +tubule/SM +tucker/GDM +Tucker/M +tuck/GZSRD +Tuckie/M +Tuck/RM +Tucky/M +Tucson/M +Tucuman/M +Tudor/MS +Tue/S +Tuesday/SM +tufter/M +tuft/GZSMRD +tufting/M +tugboat/MS +tugged +tugging +tug/S +tuition/ISM +Tulane/M +tularemia/S +tulip/SM +tulle/SM +Tulley/M +Tull/M +Tully/M +Tulsa/M +tum +tumbledown +tumbler/M +tumbleweed/MS +tumble/ZGRSDJ +tumbrel/SM +tumescence/S +tumescent +tumidity/MS +tumid/Y +tummy/SM +tumor/MDS +tumorous +Tums/M +tumult/SGMD +tumultuousness/M +tumultuous/PY +tumulus/M +tunableness/M +tunable/P +tuna/SM +tundra/SM +tun/DRJZGBS +tune/CSDG +tunefulness/MS +tuneful/YP +tuneless/Y +tuner/M +tune's +tuneup/S +tung +tungstate/M +tungsten/SM +Tunguska/M +Tungus/M +tunic/MS +tuning/A +tuning's +Tunisia/M +Tunisian/S +Tunis/M +tunned +tunneler/M +tunnel/MRDSJGZ +tunning +tunny/SM +tupelo/M +Tupi/M +tuple/SM +tuppence/M +Tupperware +Tupungato/M +turban/SDM +turbid +turbidity/SM +turbinate/SD +turbine/SM +turbocharged +turbocharger/SM +turbofan/MS +turbojet/MS +turboprop/MS +turbo/SM +turbot/MS +turbulence/SM +turbulent/Y +turd/MS +tureen/MS +turf/DGSM +turfy/RT +Turgenev/M +turgidity/SM +turgidness/M +turgid/PY +Turing/M +Turin/M +Turkestan/M +Turkey/M +turkey/SM +Turkic/SM +Turkish +Turkmenistan/M +turk/S +Turk/SM +turmeric/MS +turmoil/SDMG +turnabout/SM +turnaround/MS +turn/AZGRDBS +turnbuckle/SM +turncoat/SM +turned/U +turner/M +Turner/M +turning/MS +turnip/SMDG +turnkey/MS +turnoff/MS +turnout/MS +turnover/SM +turnpike/MS +turnround/MS +turnstile/SM +turnstone/M +turntable/SM +turpentine/GMSD +Turpin/M +turpitude/SM +turquoise/SM +turret/SMD +turtleback/MS +turtledove/MS +turtleneck/SDM +turtle/SDMG +turves's +turvy +Tuscaloosa/M +Tuscan +Tuscany/M +Tuscarora/M +Tuscon/M +tush/SDG +Tuskegee/M +tusker/M +tusk/GZRDMS +tussle/GSD +tussock/MS +tussocky +Tussuad/M +Tutankhamen/M +tutelage/MS +tutelary/S +Tut/M +tutored/U +tutorial/MS +tutor/MDGS +tutorship/S +tut/S +Tutsi +tutted +tutting +tutti/S +Tuttle/M +tutu/SM +Tuvalu +tuxedo/SDM +tux/S +TVA +TV/M +TVs +twaddle/GZMRSD +twaddler/M +Twain/M +twain/S +TWA/M +twang/MDSG +twangy/TR +twas +tweak/SGRD +tweediness/M +Tweedledee/M +Tweedledum/M +Tweed/M +twee/DP +tweed/SM +tweedy/PTR +tween +tweeter/M +tweet/ZSGRD +tweezer/M +tweeze/ZGRD +twelfth +twelfths +twelvemonth/M +twelvemonths +twelve/MS +twentieths +twenty/MSH +twerp/MS +twice/R +twiddle/GRSD +twiddler/M +twiddly/RT +twigged +twigging +twiggy/RT +twig/SM +Twila/M +twilight/MS +twilit +twill/SGD +twiner/M +twine/SM +twinge/SDMG +Twinkie +twinkler/M +twinkle/RSDG +twinkling/M +twinkly +twinned +twinning +twin/RDMGZS +twirler/M +twirling/Y +twirl/SZGRD +twirly/TR +twisted/U +twister/M +twists/U +twist/SZGRD +twisty +twitch/GRSD +twitchy/TR +twit/S +twitted +twitterer/M +twitter/SGRD +twittery +twitting +twixt +twofer/MS +twofold/S +two/MS +twopence/SM +twopenny/S +twosome/MS +twp +Twp +TWX +Twyla/M +TX +t/XTJBG +Tybalt/M +Tybie/M +Tybi/M +tycoon/MS +tyeing +Tye/M +tying/UA +tyke/SM +Tylenol/M +Tyler/M +Ty/M +Tymon/M +Tymothy/M +tympani +tympanist/SM +tympanum/SM +Tynan/M +Tyndale/M +Tyndall/M +Tyne/M +typeahead +typecast/SG +typed/AU +typedef/S +typeface/MS +typeless +type/MGDRSJ +types/A +typescript/SM +typeset/S +typesetter/MS +typesetting/SM +typewriter/M +typewrite/SRJZG +typewriting/M +typewritten +typewrote +typhoid/SM +Typhon/M +typhoon/SM +typhus/SM +typicality/MS +typically +typicalness/M +typical/U +typification/M +typify/SDNXG +typing/A +typist/MS +typographer/SM +typographic +typographical/Y +typography/MS +typological/Y +typology/MS +typo/MS +tyrannic +tyrannicalness/M +tyrannical/PY +tyrannicide/M +tyrannizer/M +tyrannize/ZGJRSD +tyrannizing/YM +tyrannosaur/MS +tyrannosaurus/S +tyrannous +tyranny/MS +tyrant/MS +Tyree/M +tyreo +Tyrolean/S +Tyrol's +Tyrone/M +tyrosine/M +tyro/SM +Tyrus/M +Tyson/M +tzarina's +tzar's +Tzeltal/M +u +U +UAR +UART +UAW +Ubangi/M +ubiquitous/YP +ubiquity/S +Ucayali/M +Uccello/M +UCLA/M +Udale/M +Udall/M +udder/SM +Udell/M +Ufa/M +ufologist/S +ufology/MS +UFO/S +Uganda/M +Ugandan/S +ugh +ughs +uglification +ugliness/MS +uglis +ugly/PTGSRD +Ugo/M +uh +UHF +Uighur +Ujungpandang/M +UK +ukase/SM +Ukraine/M +Ukrainian/S +ukulele/SM +UL +Ula/M +Ulberto/M +ulcerate/NGVXDS +ulceration/M +ulcer/MDGS +ulcerous +Ulick/M +Ulises/M +Ulla/M +Ullman/M +ulnae +ulna/M +ulnar +Ulrica/M +Ulrich/M +Ulrick/M +Ulric/M +Ulrika/M +Ulrikaumeko/M +Ulrike/M +Ulster/M +ulster/MS +ult +ulterior/Y +ultimas +ultimate/DSYPG +ultimateness/M +ultimatum/MS +ultimo +ultracentrifugally +ultracentrifugation +ultracentrifuge/M +ultraconservative/S +ultrafast +ultrahigh +ultralight/S +ultramarine/SM +ultramodern +ultramontane +ultra/S +ultrashort +ultrasonically +ultrasonic/S +ultrasonics/M +ultrasound/SM +ultrastructure/M +Ultrasuede +ultraviolet/SM +Ultrix/M +ULTRIX/M +ululate/DSXGN +ululation/M +Ulyanovsk/M +Ulysses/M +um +umbel/MS +umber/GMDS +Umberto/M +umbilical/S +umbilici +umbilicus/M +umbrage/MGSD +umbrageous +umbra/MS +umbrella/GDMS +Umbriel/M +Umeko/M +umiak/MS +umlaut/GMDS +umpire/MGSD +ump/MDSG +umpteen/H +UN +unabated/Y +unabridged/S +unacceptability +unacceptable +unaccepted +unaccommodating +unaccountability +unaccustomed/Y +unadapted +unadulterated/Y +unadventurous +unalienability +unalterableness/M +unalterable/P +unalterably +Una/M +unambiguity +unambiguous +unambitious +unamused +unanimity/SM +unanimous/Y +unanticipated/Y +unapologetic +unapologizing/M +unappeasable +unappeasably +unappreciative +unary +unassailableness/M +unassailable/P +unassertive +unassumingness/M +unassuming/PY +unauthorized/PY +unavailing/PY +unaware/SPY +unbalanced/P +unbar +unbarring +unbecoming/P +unbeknown +unbelieving/Y +unbiased/P +unbid +unbind/G +unblessed +unblinking/Y +unbodied +unbolt/G +unbreakability +unbred +unbroken +unbuckle +unbudging/Y +unburnt +uncap +uncapping +uncatalogued +uncauterized/MS +unceasing/Y +uncelebrated +uncertain/P +unchallengeable +unchangingness/M +unchanging/PY +uncharacteristic +uncharismatic +unchastity +unchristian +uncial/S +uncivilized/Y +unclassified +uncle/MSD +unclouded/Y +uncodable +uncollected +uncoloredness/M +uncolored/PY +uncombable +uncommunicative +uncompetitive +uncomplicated +uncomprehending/Y +uncompromisable +unconcerned/P +unconcern/M +unconfirmed +unconfused +unconscionableness/M +unconscionable/P +unconscionably +unconstitutional +unconsumed +uncontentious +uncontrollability +unconvertible +uncool +uncooperative +uncork/G +uncouple/G +uncouthness/M +uncouth/YP +uncreate/V +uncritical +uncross/GB +uncrowded +unction/IM +unctions +unctuousness/MS +unctuous/PY +uncustomary +uncut +undated/I +undaunted/Y +undeceive +undecided/S +undedicated +undefinability +undefinedness/M +undefined/P +undelete +undeliverability +undeniableness/M +undeniable/P +undeniably +undependable +underachiever/M +underachieve/SRDGZ +underact/GDS +underadjusting +underage/S +underarm/DGS +underbedding +underbelly/MS +underbidding +underbid/S +underbracing +underbrush/MSDG +undercarriage/MS +undercharge/GSD +underclassman +underclassmen +underclass/S +underclothes +underclothing/MS +undercoating/M +undercoat/JMDGS +underconsumption/M +undercooked +undercount/S +undercover +undercurrent/SM +undercut/S +undercutting +underdeveloped +underdevelopment/MS +underdog/MS +underdone +undereducated +underemphasis +underemployed +underemployment/SM +underenumerated +underenumeration +underestimate/NGXSD +underexploited +underexpose/SDG +underexposure/SM +underfed +underfeed/SG +underfloor +underflow/GDMS +underfoot +underfund/DG +underfur/MS +undergarment/SM +undergirding +undergoes +undergo/G +undergone +undergrad/MS +undergraduate/MS +underground/RMS +undergrowth/M +undergrowths +underhand/D +underhandedness/MS +underhanded/YP +underheat +underinvestment +underlaid +underlain/S +underlay/GS +underlie +underline/GSDJ +underling/MS +underlip/SM +underloaded +underly/GS +undermanned +undermentioned +undermine/SDG +undermost +underneath +underneaths +undernourished +undernourishment/SM +underpaid +underpants +underpart/MS +underpass/SM +underpay/GSL +underpayment/SM +underperformed +underpinned +underpinning/MS +underpin/S +underplay/SGD +underpopulated +underpopulation/M +underpowered +underpricing +underprivileged +underproduction/MS +underrate/GSD +underregistration/M +underreported +underreporting +underrepresentation/M +underrepresented +underscore/SDG +undersealed +undersea/S +undersecretary/SM +undersell/SG +undersexed +undershirt/SM +undershoot/SG +undershorts +undershot +underside/SM +undersigned/M +undersign/SGD +undersized +undersizes +undersizing +underskirt/MS +undersold +underspecification +underspecified +underspend/G +understaffed +understandability/M +understandably +understanding/YM +understand/RGSJB +understate/GSDL +understatement/MS +understocked +understood +understrength +understructure/SM +understudy/GMSD +undertaken +undertaker/M +undertake/SRGZJ +undertaking/M +underthings +undertone/SM +undertook +undertow/MS +underused +underusing +underutilization/M +underutilized +undervaluation/S +undervalue/SDG +underwater/S +underway +underwear/M +underweight/S +underwent +underwhelm/DGS +underwood/M +Underwood/M +underworld/MS +underwrite/GZSR +underwriter/M +underwritten +underwrote +under/Y +undeserving +undesigned +undeviating/Y +undialyzed/SM +undiplomatic +undiscerning +undiscriminating +undo/GJ +undoubted/Y +undramatic +undramatized/SM +undress/G +undrinkability +undrinkable +undroppable +undue +undulant +undulate/XDSNG +undulation/M +unearthliness/S +unearthly/P +unearth/YG +unease +uneconomic +uneducated +unemployed/S +unencroachable +unending/Y +unendurable/P +unenergized/MS +unenforced +unenterprising +UNESCO +unethical +uneulogized/SM +unexacting +unexceptionably +unexcited +unexpectedness/MS +unfading/Y +unfailingness/M +unfailing/P +unfamiliar +unfashionable +unfathomably +unfavored +unfeeling +unfeigned/Y +unfelt +unfeminine +unfertile +unfetchable +unflagging +unflappability/S +unflappable +unflappably +unflinching/Y +unfold/LG +unfoldment/M +unforced +unforgeable +unfossilized/MS +unfraternizing/SM +unfrozen +unfulfillable +unfunny +unfussy +ungainliness/MS +ungainly/PRT +Ungava/M +ungenerous +ungentle +unglamorous +ungrammaticality +ungrudging +unguent/MS +ungulate/MS +unharmonious +unharness/G +unhistorical +unholy/TP +unhook/DG +unhydrolyzed/SM +unhygienic +Unibus/M +unicameral +UNICEF +unicellular +Unicode/M +unicorn/SM +unicycle/MGSD +unicyclist/MS +unideal +unidimensional +unidiomatic +unidirectionality +unidirectional/Y +unidolized/MS +unifiable +unification/MA +unifier/MS +unifilar +uniformity/MS +uniformness/M +uniform/TGSRDYMP +unify/AXDSNG +unilateralism/M +unilateralist +unilateral/Y +unimodal +unimpeachably +unimportance +unimportant +unimpressive +unindustrialized/MS +uninhibited/YP +uninominal +uninsured +unintellectual +unintended +uninteresting +uninterruptedness/M +uninterrupted/YP +unintuitive +uninviting +union/AEMS +unionism/SM +unionist/SM +Unionist/SM +unionize +Union/MS +UniPlus/M +unipolar +uniprocessor/SM +uniqueness/S +unique/TYSRP +Uniroyal/M +unisex/S +UniSoft/M +unison/MS +Unisys/M +unitarianism/M +Unitarianism/SM +unitarian/MS +Unitarian/MS +unitary +unite/AEDSG +united/Y +uniter/M +unitize/GDS +unit/VGRD +unity/SEM +univ +Univac/M +univalent/S +univalve/MS +univariate +universalism/M +universalistic +universality/SM +universalize/DSRZG +universalizer/M +universal/YSP +universe/MS +university/MS +Unix/M +UNIX/M +unjam +unkempt +unkind/TP +unkink +unknightly +unknowable/S +unknowing +unlabored +unlace/G +unlearn/G +unlikeable +unlikeliness/S +unlimber/G +unlimited +unlit +unliterary +unloose/G +unlucky/TP +unmagnetized/MS +unmanageably +unmannered/Y +unmask/G +unmeaning +unmeasured +unmeetable +unmelodious +unmemorable +unmemorialized/MS +unmentionable/S +unmerciful +unmeritorious +unmethodical +unmineralized/MS +unmissable +unmistakably +unmitigated/YP +unmnemonic +unmobilized/SM +unmoral +unmount/B +unmovable +unmoving +unnaturalness/M +unnavigable +unnerving/Y +unobliging +unoffensive +unofficial +unorganized/YP +unorthodox +unpack/G +unpaintable +unpalatability +unpalatable +unpartizan +unpatronizing +unpeople +unperceptive +unperson +unperturbed/Y +unphysical +unpick/G +unpicturesque +unpinning +unpleasing +unploughed +unpolarized/SM +unpopular +unpractical +unprecedented/Y +unpredictable/S +unpreemphasized +unpremeditated +unpretentiousness/M +unprincipled/P +unproblematic +unproductive +unpropitious +unprovable +unproven +unprovocative +unpunctual +unquestionable +unraisable +unravellings +unreadability +unread/B +unreal +unrealizable +unreasoning/Y +unreceptive +unrecordable +unreflective +unrelenting/Y +unremitting/Y +unrepeatability +unrepeated +unrepentant +unreported +unrepresentative +unreproducible +unrest/G +unrestrained/P +unrewarding +unriddle +unripe/P +unromantic +unruliness/SM +unruly/PTR +unsaleable +unsanitary +unsavored/YP +unsavoriness/M +unseal/GB +unsearchable +unseasonal +unseeing/Y +unseen/S +unselfconsciousness/M +unselfconscious/P +unselfishness/M +unsellable +unsentimental +unset +unsettledness/M +unsettled/P +unsettling/Y +unshapely +unshaven +unshorn +unsighted +unsightliness/S +unskilful +unsociability +unsociable/P +unsocial +unsound/PT +unspeakably +unspecific +unspectacular +unspoilt +unspoke +unsporting +unstable/P +unstigmatized/SM +unstilted +unstinting/Y +unstopping +unstrapping +unstudied +unstuffy +unsubdued +unsubstantial +unsubtle +unsuitable +unsuspecting/Y +unswerving/Y +unsymmetrical +unsympathetic +unsystematic +unsystematized/Y +untactful +untalented +untaxing +unteach/B +untellable +untenable +unthinking +until/G +untiring/Y +unto +untouchable/MS +untowardness/M +untoward/P +untraceable +untrue +untruthfulness/M +untwist/G +Unukalhai/M +unusualness/M +unutterable +unutterably +unvocalized/MS +unvulcanized/SM +unwaivering +unwarrantable +unwarrantably +unwashed/PS +unwearable +unwearied/Y +unwed +unwedge +unwelcome +unwell/M +unwieldiness/MS +unwieldy/TPR +unwind/B +unwomanly +unworkable/S +unworried +unwrap +unwrapping +unyielding/Y +unyoke +unzip +up +Upanishads +uparrow +upbeat/SM +upbraid/GDRS +upbringing/M +upbring/JG +UPC +upchuck/SDG +upcome/G +upcountry/S +updatability +updater/M +update/RSDG +Updike/M +updraft/SM +upend/SDG +upfield +upfront +upgradeable +upgrade/DSJG +upheaval/MS +upheld +uphill/S +upholder/M +uphold/RSGZ +upholster/ADGS +upholsterer/SM +upholstery/MS +UPI +upkeep/SM +uplander/M +upland/MRS +uplifter/M +uplift/SJDRG +upload/GSD +upmarket +upon +upped +uppercase/GSD +upperclassman/M +upperclassmen +uppercut/S +uppercutting +uppermost +upper/S +upping +uppish +uppity +upraise/GDS +uprated +uprating +uprear/DSG +upright/DYGSP +uprightness/S +uprise/RGJ +uprising/M +upriver/S +uproariousness/M +uproarious/PY +uproar/MS +uproot/DRGS +uprooter/M +ups +UPS +upscale/GDS +upset/S +upsetting/MS +upshot/SM +upside/MS +upsilon/MS +upslope +upstage/DSRG +upstairs +upstandingness/M +upstanding/P +upstart/MDGS +upstate/SR +upstream/DSG +upstroke/MS +upsurge/DSG +upswing/GMS +upswung +uptake/SM +upthrust/GMS +uptight +uptime +Upton/M +uptown/RS +uptrend/M +upturn/GDS +upwardness/M +upward/SYP +upwelling +upwind/S +uracil/MS +Ural/MS +Urania/M +uranium/MS +Uranus/M +uranyl/M +Urbain/M +Urbana/M +urbane/Y +urbanism/M +urbanite/SM +urbanity/SM +urbanization/MS +urbanize/DSG +Urban/M +urbanologist/S +urbanology/S +Urbano/M +urban/RT +Urbanus/M +urchin/SM +Urdu/M +urea/SM +uremia/MS +uremic +ureter/MS +urethane/MS +urethrae +urethral +urethra/M +urethritis/M +Urey/M +urge/GDRSJ +urgency/SM +urgent/Y +urger/M +Uriah/M +uric +Uriel/M +urinal/MS +urinalyses +urinalysis/M +urinary/MS +urinate/XDSNG +urination/M +urine/MS +Uri/SM +URL +Ur/M +urning/M +urn/MDGS +urogenital +urological +urologist/S +urology/MS +Urquhart/M +Ursala/M +Ursa/M +ursine +Ursola/M +Urson/M +Ursula/M +Ursulina/M +Ursuline/M +urticaria/MS +Uruguayan/S +Uruguay/M +Urumqi +US +USA +usability/S +usable/U +usably/U +USAF +usage/SM +USART +USCG +USC/M +USDA +us/DRSBZG +used/U +use/ESDAG +usefulness/SM +useful/YP +uselessness/MS +useless/PY +Usenet/M +Usenix/M +user/M +USG/M +usherette/SM +usher/SGMD +USIA +USMC +USN +USO +USP +USPS +USS +USSR +Ustinov/M +usu +usuals +usual/UPY +usurer/SM +usuriousness/M +usurious/PY +usurpation/MS +usurper/M +usurp/RDZSG +usury/SM +UT +Utahan/SM +Utah/M +Uta/M +Ute/M +utensil/SM +uteri +uterine +uterus/M +Utica/M +utile/I +utilitarianism/MS +utilitarian/S +utility/MS +utilization/MS +utilization's/A +utilize/GZDRS +utilizer/M +utilizes/A +utmost/S +Utopia/MS +utopianism/M +utopian's +Utopian/S +utopia/S +Utrecht/M +Utrillo/M +utterance/MS +uttered/U +utterer/M +uttermost/S +utter/TRDYGS +uucp/M +UV +uvula/MS +uvular/S +uxorious +Uzbekistan +Uzbek/M +Uzi/M +V +VA +vacancy/MS +vacantness/M +vacant/PY +vacate/NGXSD +vacationist/SM +vacationland +vacation/MRDZG +vaccinate/NGSDX +vaccination/M +vaccine/SM +vaccinial +vaccinia/M +Vachel/M +vacillate/XNGSD +vacillating/Y +vacillation/M +vacillator/SM +Vaclav/M +vacua's +vacuity/MS +vacuo +vacuolated/U +vacuolate/SDGN +vacuole/SM +vacuolization/SM +vacuousness/MS +vacuous/PY +vacuum/GSMD +Vader/M +Vaduz/M +vagabondage/MS +vagabond/DMSG +vagarious +vagary/MS +vaginae +vaginal/Y +vagina/M +vagrancy/MS +vagrant/SMY +vagueing +vagueness/MS +vague/TYSRDP +Vail/M +vaingloriousness/M +vainglorious/YP +vainglory/MS +vain/TYRP +val +valance/SDMG +Valaree/M +Valaria/M +Valarie/M +Valdemar/M +Valdez/M +Valeda/M +valediction/MS +valedictorian/MS +valedictory/MS +Vale/M +valence/SM +Valencia/MS +valency/MS +Valene/M +Valenka/M +Valentia/M +Valentijn/M +Valentina/M +Valentine/M +valentine/SM +Valentin/M +Valentino/M +Valenzuela/M +Valera/M +Valeria/M +Valerian/M +Valerie/M +Valerye/M +Valry/M +vale/SM +valet/GDMS +valetudinarianism/MS +valetudinarian/MS +Valhalla/M +valiance/S +valiantness/M +valiant/SPY +Valida/M +validated/AU +validate/INGSDX +validates/A +validation/AMI +validity/IMS +validnesses +validness/MI +valid/PIY +Valina/M +valise/MS +Valium/S +Valkyrie/SM +Vallejo +Valle/M +Valletta/M +valley/SM +Vallie/M +Valli/M +Vally/M +Valma/M +Val/MY +Valois/M +valor/MS +valorous/Y +Valparaiso/M +Valry/M +valuable/IP +valuableness/IM +valuables +valuably/I +valuate/NGXSD +valuation/CSAM +valuator/SM +value/CGASD +valued/U +valuelessness/M +valueless/P +valuer/SM +value's +values/E +valve/GMSD +valveless +valvular +Va/M +vamoose/GSD +vamp/ADSG +vamper +vampire/MGSD +vamp's +vanadium/MS +Vance/M +Vancouver/M +vandalism/MS +vandalize/GSD +vandal/MS +Vandal/MS +Vanda/M +Vandenberg/M +Vanderbilt/M +Vanderburgh/M +Vanderpoel/M +Vandyke/SM +vane/MS +Vanessa/M +Vang/M +vanguard/MS +Vania/M +vanilla/MS +vanisher/M +vanish/GRSDJ +vanishing/Y +vanity/SM +Van/M +Vanna/M +vanned +Vannie/M +Vanni/M +vanning +Vanny/M +vanquisher/M +vanquish/RSDGZ +van/SMD +vantage/MS +Vanuatu +Vanya/M +Vanzetti/M +vapidity/MS +vapidness/SM +vapid/PY +vaporer/M +vaporing/MY +vaporisation +vaporise/DSG +vaporization/AMS +vaporize/DRSZG +vaporizer/M +vapor/MRDJGZS +vaporous +vapory +vaquero/SM +VAR +Varanasi/M +Varese/M +Vargas/M +variability/IMS +variableness/IM +variable/PMS +variables/I +variably/I +variance/I +variances +variance's +Varian/M +variant/ISY +variate/MGNSDX +variational +variation/M +varicolored/MS +varicose/S +variedly +varied/U +variegate/NGXSD +variegation/M +varier/M +varietal/S +variety/MS +various/PY +varistor/M +Varityping/M +varlet/MS +varmint/SM +varnished/U +varnisher/M +varnish/ZGMDRS +var/S +varsity/MS +varying/UY +vary/SRDJG +vascular +vasectomy/SM +Vaseline/DSMG +vase/SM +Vasili/MS +Vasily/M +vasomotor +Vasquez/M +vassalage/MS +vassal/GSMD +Vassar/M +Vassili/M +Vassily/M +vastness/MS +vast/PTSYR +v/ASV +VAT +Vatican/M +vat/SM +vatted +vatting +vaudeville/SM +vaudevillian/SM +Vaudois +Vaughan/M +Vaughn/M +vaulter/M +vaulting/M +vault/ZSRDMGJ +vaunter/M +vaunt/GRDS +VAXes +Vax/M +VAX/M +Vazquez/M +vb +VCR +VD +VDT +VDU +vealed/A +vealer/MA +veal/MRDGS +veals/A +Veblen/M +vectorial +vectorization +vectorized +vectorizing +vector's/F +vector/SGDM +Veda/MS +Vedanta/M +veejay/S +veep/S +veer/DSG +veering/Y +vegan/SM +Vega/SM +Vegemite/M +veges +vegetable/MS +vegetarianism/MS +vegetarian/SM +vegetate/DSNGVX +vegetation/M +vegetative/PY +vegged +veggie/S +vegging +veg/M +vehemence/MS +vehemency/S +vehement/Y +vehicle/SM +vehicular +veiling/MU +veil's +veil/UGSD +vein/GSRDM +veining/M +vela/M +Vela/M +velarize/SDG +velar/S +Velsquez/M +Velzquez +Velcro/SM +veld/SM +veldt's +Velez/M +Vella/M +vellum/MS +Velma/M +velocipede/SM +velocity/SM +velor/S +velour's +velum/M +Velveeta/M +velveteen/MS +velvet/GSMD +Velvet/M +velvety/RT +venality/MS +venal/Y +venation/SM +vend/DSG +vender's/K +vendetta/MS +vendible/S +vendor/MS +veneerer/M +veneer/GSRDM +veneering/M +venerability/S +venerable/P +venerate/XNGSD +veneration/M +venereal +venetian +Venetian/SM +Venezuela/M +Venezuelan/S +vengeance/MS +vengeful/APY +vengefulness/AM +venialness/M +venial/YP +Venice/M +venireman/M +veniremen +venison/SM +Venita/M +Venn/M +venomousness/M +venomous/YP +venom/SGDM +venous/Y +venter/M +ventilated/U +ventilate/XSDVGN +ventilation/M +ventilator/MS +vent/ISGFD +ventral/YS +ventricle/MS +ventricular +ventriloquies +ventriloquism/MS +ventriloquist/MS +ventriloquy +vent's/F +Ventura/M +venture/RSDJZG +venturesomeness/SM +venturesome/YP +venturi/S +venturousness/MS +venturous/YP +venue/MAS +Venusian/S +Venus/S +veraciousness/M +veracious/YP +veracities +veracity/IM +Veracruz/M +Veradis +Vera/M +verandahed +veranda/SDM +verbalization/MS +verbalized/U +verbalizer/M +verbalize/ZGRSD +verballed +verballing +verbal/SY +verbatim +verbena/MS +verbiage/SM +verb/KSM +verbose/YP +verbosity/SM +verboten +verdant/Y +Verde/M +Verderer/M +verdict/SM +verdigris/GSDM +Verdi/M +verdure/SDM +Vere/M +Verena/M +Verene/M +verge/FGSD +Verge/M +verger/SM +verge's +Vergil's +veridical/Y +Veriee/M +verifiability/M +verifiableness/M +verifiable/U +verification/S +verified/U +verifier/MS +verify/GASD +Verile/M +verily +Verina/M +Verine/M +verisimilitude/SM +veritableness/M +veritable/P +veritably +verity/MS +Verlag/M +Verlaine/M +Verla/M +Vermeer/M +vermicelli/MS +vermiculite/MS +vermiform +vermilion/MS +vermin/M +verminous +Vermonter/M +Vermont/ZRM +vermouth/M +vermouths +vernacular/YS +vernal/Y +Verna/M +Verne/M +Vernen/M +Verney/M +Vernice/M +vernier/SM +Vern/NM +Vernon/M +Vernor/M +Verona/M +Veronese/M +Veronica/M +veronica/SM +Veronika/M +Veronike/M +Veronique/M +verrucae +verruca/MS +versa +Versailles/M +Versatec/M +versatileness/M +versatile/YP +versatility/SM +versed/UI +verse's +verses/I +verse/XSRDAGNF +versicle/M +versification/M +versifier/M +versify/GDRSZXN +versing/I +version/MFISA +verso/SM +versus +vertebrae +vertebral/Y +vertebra/M +vertebrate/IMS +vertebration/M +vertex/SM +vertical/YPS +vertices's +vertiginous +vertigoes +vertigo/M +verve/SM +very/RT +Vesalius/M +vesicle/SM +vesicular/Y +vesiculate/GSD +Vespasian/M +vesper/SM +Vespucci/M +vessel/MS +vestal/YS +Vesta/M +vest/DIGSL +vestibular +vestibule/SDM +vestige/SM +vestigial/Y +vesting/SM +vestment/ISM +vestryman/M +vestrymen +vestry/MS +vest's +vesture/SDMG +Vesuvius/M +vetch/SM +veteran/SM +veterinarian/MS +veterinary/S +veter/M +veto/DMG +vetoes +vet/SMR +vetted +vetting/A +Vevay/M +vexation/SM +vexatiousness/M +vexatious/PY +vexed/Y +vex/GFSD +VF +VFW +VG +VGA +vhf +VHF +VHS +VI +via +viability/SM +viable/I +viably +viaduct/MS +Viagra/M +vial/MDGS +viand/SM +vibe/S +vibraharp/MS +vibrancy/MS +vibrant/YS +vibraphone/MS +vibraphonist/SM +vibrate/XNGSD +vibrational/Y +vibration/M +vibrato/MS +vibrator/SM +vibratory +vibrio/M +vibrionic +viburnum/SM +vicarage/SM +vicariousness/MS +vicarious/YP +vicar/SM +vice/CMS +viced +vicegerent/MS +vicennial +Vicente/M +viceregal +viceroy/SM +Vichy/M +vichyssoise/MS +vicing +vicinity/MS +viciousness/S +vicious/YP +vicissitude/MS +Vickers/M +Vickie/M +Vicki/M +Vicksburg/M +Vicky/M +Vick/ZM +Vic/M +victimization/SM +victimized/U +victimizer/M +victimize/SRDZG +victim/SM +Victoir/M +Victoria/M +Victorianism/S +Victorian/S +victoriousness/M +victorious/YP +Victor/M +victor/SM +victory/MS +Victrola/SM +victualer/M +victual/ZGSDR +vicua/S +Vidal/M +Vida/M +videlicet +videocassette/S +videoconferencing +videodisc/S +videodisk/SM +video/GSMD +videophone/SM +videotape/SDGM +Vidovic/M +Vidovik/M +Vienna/M +Viennese/M +Vientiane/M +vier/M +vie/S +Vietcong/M +Viet/M +Vietminh/M +Vietnamese/M +Vietnam/M +viewed/A +viewer/AS +viewer's +viewfinder/MS +viewgraph/SM +viewing/M +viewless/Y +view/MBGZJSRD +viewpoint/SM +views/A +vigesimal +vigilance/MS +vigilante/SM +vigilantism/MS +vigilantist +vigilant/Y +vigil/SM +vignette/MGDRS +vignetter/M +vignetting/M +vignettist/MS +vigor/MS +vigorousness/M +vigorous/YP +vii +viii +Vijayawada/M +Viki/M +Viking/MS +viking/S +Vikki/M +Vikky/M +Vikram/M +Vila +vile/AR +vilely +vileness/MS +vilest +Vilhelmina/M +vilification/M +vilifier/M +vilify/GNXRSD +villager/M +village/RSMZ +villainousness/M +villainous/YP +villain/SM +villainy/MS +Villa/M +villa/MS +Villarreal/M +ville +villeinage/SM +villein/MS +villi +Villon/M +villus/M +Vilma/M +Vilnius/M +Vilyui/M +Vi/M +vi/MDR +vim/MS +vinaigrette/MS +Vina/M +Vince/M +Vincent/MS +Vincenty/M +Vincenz/M +vincible/I +Vinci/M +Vindemiatrix/M +vindicate/XSDVGN +vindication/M +vindicator/SM +vindictiveness/MS +vindictive/PY +vinegar/DMSG +vinegary +vine/MGDS +vineyard/SM +Vinita/M +Vin/M +Vinnie/M +Vinni/M +Vinny/M +vino/MS +vinous +Vinson/M +vintage/MRSDG +vintager/M +vintner/MS +vinyl/SM +violable/I +Viola/M +Violante/M +viola/SM +violate/VNGXSD +violator/MS +Viole/M +violence/SM +violent/Y +Violet/M +violet/SM +Violetta/M +Violette/M +violinist/SM +violin/MS +violist/MS +viol/MSB +violoncellist/S +violoncello/MS +viper/MS +viperous +VIP/S +viragoes +virago/M +viral/Y +vireo/SM +Virge/M +Virgie/M +Virgilio/M +Virgil/M +virginal/YS +Virgina/M +Virginia/M +Virginian/S +Virginie/M +virginity/SM +virgin/SM +Virgo/MS +virgule/MS +virile +virility/MS +virologist/S +virology/SM +virtual/Y +virtue/SM +virtuosity/MS +virtuosoes +virtuoso/MS +virtuousness/SM +virtuous/PY +virulence/SM +virulent/Y +virus/MS +visage/MSD +Visakhapatnam's +Visa/M +visa/SGMD +Visayans +viscera +visceral/Y +viscid/Y +viscoelastic +viscoelasticity +viscometer/SM +viscose/MS +viscosity/MS +viscountcy/MS +viscountess/SM +viscount/MS +viscousness/M +viscous/PY +viscus/M +vise/CAXNGSD +viselike +vise's +Vishnu/M +visibility/ISM +visible/PI +visibly/I +Visigoth/M +Visigoths +visionariness/M +visionary/PS +vision/KMDGS +vision's/A +visitable/U +visitant/SM +visitation/SM +visited/U +visit/GASD +visitor/MS +vis/MDSGV +visor/SMDG +VISTA +vista/GSDM +Vistula/M +visualization/AMS +visualized/U +visualizer/M +visualizes/A +visualize/SRDZG +visual/SY +vitae +vitality/MS +vitalization/AMS +vitalize/ASDGC +vital/SY +vita/M +Vita/M +vitamin/SM +Vite/M +Vitia/M +vitiate/XGNSD +vitiation/M +viticulture/SM +viticulturist/S +Vitim/M +Vito/M +Vitoria/M +vitreous/YSP +vitrifaction/S +vitrification/M +vitrify/XDSNG +vitrine/SM +vitriolic +vitriol/MDSG +vitro +vittles +Vittoria/M +Vittorio/M +vituperate/SDXVGN +vituperation/M +vituperative/Y +Vitus/M +vivace/S +vivaciousness/MS +vivacious/YP +vivacity/SM +viva/DGS +Vivaldi +Viva/M +vivaria +vivarium/MS +vivaxes +Vivekananda/M +vive/Z +Vivia/M +Viviana/M +Vivian/M +Vivianna/M +Vivianne/M +vividness/SM +vivid/PTYR +Vivie/M +Viviene/M +Vivien/M +Vivienne/M +vivifier +vivify/NGASD +Vivi/MN +viviparous +vivisect/DGS +vivisectional +vivisectionist/SM +vivisection/MS +Viviyan/M +Viv/M +vivo +Vivyan/M +Vivyanne/M +vixenish/Y +vixen/SM +viz +vizier/MS +vizor's +VJ +Vladamir/M +Vladimir/M +Vladivostok/M +Vlad/M +VLF +VLSI +VMS/M +VOA +vocable/SM +vocab/S +vocabularian +vocabularianism +vocabulary/MS +vocalic/S +vocalise's +vocalism/M +vocalist/MS +vocalization/SM +vocalized/U +vocalizer/M +vocalize/ZGDRS +vocal/SY +vocation/AKMISF +vocational/Y +vocative/KYS +vociferate/NGXSD +vociferation/M +vociferousness/MS +vociferous/YP +vocoded +vocoder +vodka/MS +voe/S +Vogel/M +vogue/GMSRD +vogueing +voguish +voiceband +voiced/CU +voice/IMGDS +voicelessness/SM +voiceless/YP +voicer/S +voices/C +voicing/C +voidable +void/C +voided +voider/M +voiding +voidness/M +voids +voil +voile/MS +volar +volatileness/M +volatile/PS +volatility/MS +volatilization/MS +volatilize/SDG +volcanically +volcanic/S +volcanism/M +volcanoes +volcano/M +vole/MS +Volga/M +Volgograd/M +vol/GSD +volitionality +volitional/Y +volition/MS +Volkswagen/SM +volleyball/MS +volleyer/M +volley/SMRDG +Vol/M +Volstead/M +voltage/SM +voltaic +Voltaire/M +Volta/M +volt/AMS +Volterra/M +voltmeter/MS +volubility/S +voluble/P +volubly +volume/SDGM +volumetric +volumetrically +voluminousness/MS +voluminous/PY +voluntarily/I +voluntariness/MI +voluntarism/MS +voluntary/PS +volunteer/DMSG +voluptuary/SM +voluptuousness/S +voluptuous/YP +volute/S +Volvo/M +vomit/GRDS +Vonda/M +Von/M +Vonnegut/M +Vonnie/M +Vonni/M +Vonny/M +voodoo/GDMS +voodooism/S +voraciousness/MS +voracious/YP +voracity/MS +Voronezh/M +Vorster/M +vortex/SM +vortices's +vorticity/M +votary/MS +vote/CSDG +voter/SM +vote's +votive/YP +voucher/GMD +vouchsafe/SDG +vouch/SRDGZ +vowelled +vowelling +vowel/MS +vower/M +vow/SMDRG +voyage/GMZJSRD +voyager/M +voyageur/SM +voyeurism/MS +voyeuristic +voyeur/MS +VP +vs +V's +VT +Vt/M +VTOL +vulcanization/SM +vulcanized/U +vulcanize/SDG +Vulcan/M +vulgarian/MS +vulgarism/MS +vulgarity/MS +vulgarization/S +vulgarize/GZSRD +vulgar/TSYR +Vulgate/SM +Vulg/M +vulnerability/SI +vulnerable/IP +vulnerably/I +vulpine +vulturelike +vulture/SM +vulturous +vulvae +vulva/M +vying +Vyky/M +WA +Waals +Wabash/M +WAC +Wacke/M +wackes +wackiness/MS +wacko/MS +wacky/RTP +Waco/M +Wac/S +wadded +wadding/SM +waddle/GRSD +Wade/M +wader/M +wade/S +wadi/SM +wad/MDRZGS +Wadsworth/M +wafer/GSMD +waffle/GMZRSD +Wafs +wafter/M +waft/SGRD +wag/DRZGS +waged/U +wager/GZMRD +wage/SM +wagged +waggery/MS +wagging +waggishness/SM +waggish/YP +waggle/SDG +waggly +Wagnerian +Wagner/M +wagoner/M +wagon/SGZMRD +wagtail/SM +Wahl/M +waif/SGDM +Waikiki/M +wailer/M +wail/SGZRD +wain/GSDM +Wain/M +wainscot/SGJD +Wainwright/M +wainwright/SM +waistband/MS +waistcoat/GDMS +waister/M +waist/GSRDM +waistline/MS +Waite/M +waiter/DMG +Waiter/M +wait/GSZJRD +Wait/MR +waitpeople +waitperson/S +waitress/GMSD +waiver/MB +waive/SRDGZ +Wakefield/M +wakefulness/MS +wakeful/PY +Wake/M +wake/MGDRSJ +waken/SMRDG +waker/M +wakeup +Waksman/M +Walbridge/M +Walcott/M +Waldemar/M +Walden/M +Waldensian +Waldheim/M +Wald/MN +Waldo/M +Waldon/M +Waldorf/M +wale/DRSMG +Wales +Walesa/M +Walford/M +Walgreen/M +waling/M +walkabout/M +walkaway/SM +walker/M +Walker/M +walk/GZSBJRD +walkie +Walkman/S +walkout/SM +walkover/SM +walkway/MS +wallaby/MS +Wallace/M +Wallache/M +wallah/M +Wallas/M +wallboard/MS +Wallenstein/M +Waller/M +wallet/SM +walleye/MSD +wallflower/MS +Wallie/M +Wallis +Walliw/M +Walloon/SM +walloper/M +walloping/M +wallop/RDSJG +wallower/M +wallow/RDSG +wallpaper/DMGS +wall/SGMRD +Wall/SMR +Wally/M +wally/S +walnut/SM +Walpole/M +Walpurgisnacht +walrus/SM +Walsh/M +Walter/M +Walther/M +Walton/M +waltzer/M +Walt/ZMR +waltz/MRSDGZ +Walworth/M +Waly/M +wampum/SM +Wanamaker/M +Wanda/M +wanderer/M +wander/JZGRD +wanderlust/SM +Wandie/M +Wandis/M +wand/MRSZ +wane/S +Waneta/M +wangler/M +wangle/RSDGZ +Wang/M +Wanids/M +Wankel/M +wanna +wannabe/S +wanned +wanner +wanness/S +wannest +wanning +wan/PGSDY +Wansee/M +Wansley/M +wanted/U +wanter/M +want/GRDSJ +wantonness/S +wanton/PGSRDY +wapiti/MS +warble/GZRSD +warbler/M +warbonnet/S +ward/AGMRDS +Warde/M +warden/DMGS +Warden/M +warder/DMGS +Ward/MN +wardrobe/MDSG +wardroom/MS +wardship/M +wards/I +warehouseman/M +warehouse/MGSRD +Ware/MG +ware/MS +warfare/SM +Warfield/M +war/GSMD +warhead/MS +Warhol/M +warhorse/SM +warily/U +warinesses/U +wariness/MS +Waring/M +warless +warlike +warlock/SM +warlord/MS +warmblooded +warmed/A +warmer/M +warmheartedness/SM +warmhearted/PY +warmish +warmness/MS +warmongering/M +warmonger/JGSM +warms/A +warmth/M +warmths +warm/YRDHPGZTS +warned/U +warner/M +Warner/M +warn/GRDJS +warning/YM +Warnock/M +warpaint +warpath/M +warpaths +warper/M +warplane/MS +warp/MRDGS +warranted/U +warranter/M +warrant/GSMDR +warranty/SDGM +warred/M +warrener/M +Warren/M +warren/SZRM +warring/M +warrior/MS +Warsaw/M +wars/C +warship/MS +warthog/S +wartime/SM +wart/MDS +warty/RT +Warwick/M +wary/URPT +Wasatch/M +washable/S +wash/AGSD +washbasin/SM +washboard/SM +washbowl/SM +Washburn/M +washcloth/M +washcloths +washday/M +washed/U +washer/GDMS +washerwoman/M +washerwomen +washing/SM +Washingtonian/S +Washington/M +Wash/M +Washoe/M +washout/SM +washrag/SM +washroom/MS +washstand/SM +washtub/MS +washy/RT +wasn't +WASP +waspishness/SM +waspish/PY +Wasp's +wasp/SM +was/S +wassail/GMDS +Wasserman/M +Wassermann/M +wastage/SM +wastebasket/SM +wastefulness/S +wasteful/YP +wasteland/MS +wastepaper/MS +waster/DG +waste/S +wastewater +wast/GZSRD +wasting/Y +wastrel/MS +Watanabe/M +watchable/U +watchband/SM +watchdogged +watchdogging +watchdog/SM +watched/U +watcher/M +watchfulness/MS +watchful/PY +watch/JRSDGZB +watchmake/JRGZ +watchmaker/M +watchman/M +watchmen +watchpoints +watchtower/MS +watchword/MS +waterbird/S +waterborne +Waterbury/M +watercolor/DMGS +watercolorist/SM +watercourse/SM +watercraft/M +watercress/SM +waterer/M +waterfall/SM +waterfowl/M +waterfront/SM +Watergate/M +waterhole/S +Waterhouse/M +wateriness/SM +watering/M +water/JGSMRD +waterless +waterlily/S +waterline/S +waterlogged +waterloo +Waterloo/SM +waterman/M +watermark/GSDM +watermelon/SM +watermill/S +waterproof/PGRDSJ +watershed/SM +waterside/MSR +watersider/M +Waters/M +waterspout/MS +watertightness/M +watertight/P +Watertown/M +waterway/MS +waterwheel/S +waterworks/M +watery/PRT +Watkins +WATS +Watson/M +wattage/SM +Watteau/M +Wattenberg/M +Watterson/M +wattle/SDGM +Watt/MS +watt/TMRS +Watusi/M +Wat/ZM +Waugh/M +Waukesha/M +Waunona/M +Waupaca/M +Waupun/M +Wausau/M +Wauwatosa/M +waveband/MS +waveform/SM +wavefront/MS +waveguide/MS +Waveland/M +wavelength/M +wavelengths +wavelet/SM +wavelike +wavenumber +waver/GZRD +wavering/YU +Waverley/M +Waverly/M +Wave/S +wave/ZGDRS +wavily +waviness/MS +wavy/SRTP +waxer/M +waxiness/MS +wax/MNDRSZG +waxwing/MS +waxwork/MS +waxy/PRT +wayfarer/MS +wayfaring/S +waylaid +Wayland/M +Waylan/M +waylayer/M +waylay/GRSZ +wayleave/MS +Waylen/M +Waylin/M +Waylon/M +Way/M +waymarked +way/MS +Wayne/M +Waynesboro/M +wayside/MS +waywardness/S +wayward/YP +WC +we +weakener/M +weaken/ZGRD +weakfish/SM +weakish +weakliness/M +weakling/SM +weakly/RTP +weakness/MS +weak/TXPYRN +weal/MHS +wealthiness/MS +wealth/M +wealths +wealthy/PTR +weaner/M +weanling/M +wean/RDGS +weapon/GDMS +weaponless +weaponry/MS +wearable/S +wearer/M +wearied/U +wearily +weariness/MS +wearing/Y +wearisomeness/M +wearisome/YP +wear/RBSJGZ +wearying/Y +weary/TGPRSD +weasel/SGMDY +weatherbeaten +weathercock/SDMG +weatherer/M +Weatherford/M +weathering/M +weatherize/GSD +weatherman/M +weather/MDRYJGS +weathermen +weatherperson/S +weatherproof/SGPD +weatherstripped +weatherstripping/S +weatherstrip/S +weaver/M +Weaver/M +weaves/A +weave/SRDGZ +weaving/A +webbed +Webber/M +webbing/MS +Webb/RM +weber/M +Weber/M +Webern/M +webfeet +webfoot/M +Web/MR +website/S +web/SMR +Webster/MS +Websterville/M +we'd +wedded/A +Weddell/M +wedder +wedding/SM +wedge/SDGM +wedgie/RST +Wedgwood/M +wedlock/SM +Wed/M +Wednesday/SM +wed/SA +weeder/M +weediness/M +weedkiller/M +weedless +wee/DRST +weed/SGMRDZ +weedy/TRP +weeing +weekday/MS +weekender/M +weekend/SDRMG +weekly/S +weeknight/SM +Weeks/M +week/SYM +weenie/M +ween/SGD +weeny/RSMT +weeper/M +weep/SGZJRD +weepy/RST +weevil/MS +weft/SGMD +Wehr/M +Weibull/M +Weidar/M +Weider/M +Weidman/M +Weierstrass/M +weighed/UA +weigher/M +weigh/RDJG +weighs/A +weighted/U +weighter/M +weightily +weightiness/SM +weighting/M +weight/JMSRDG +weightlessness/SM +weightless/YP +weightlifter/S +weightlifting/MS +weighty/TPR +Weill/M +Wei/M +Weinberg/M +Weiner/M +Weinstein/M +weirdie/SM +weirdness/MS +weirdo/SM +weird/YRDPGTS +weir/SDMG +Weisenheimer/M +Weiss/M +Weissman/M +Weissmuller/M +Weizmann/M +Welbie/M +Welby/M +Welcher/M +Welches +welcomeness/M +welcome/PRSDYG +welcoming/U +welder/M +Weldon/M +weld/SBJGZRD +Weldwood/M +welfare/SM +welkin/SM +we'll +Welland/M +wellbeing/M +Weller/M +Wellesley/M +Welles/M +wellhead/SM +Wellington/MS +wellington/S +Wellman/M +wellness/MS +well/SGPD +Wells/M +wellspring/SM +Wellsville/M +Welmers/M +Welsh +welsher/M +Welshman/M +Welshmen +welsh/RSDGZ +Welshwoman/M +Welshwomen +welter/GD +welterweight/MS +welt/GZSMRD +wencher/M +wench/GRSDM +Wendall/M +Wenda/M +wend/DSG +Wendeline/M +Wendell/M +Wendel/M +Wendie/M +Wendi/M +Wendye/M +Wendy/M +wen/M +Wenonah/M +Wenona/M +went +Wentworth/M +wept/U +were +we're +weren't +werewolf/M +werewolves +Werner/M +Wernher/M +Werther/M +werwolf's +Wes +Wesleyan +Wesley/M +Wessex/M +Wesson/M +westbound +Westbrooke/M +Westbrook/M +Westchester/M +wester/DYG +westerly/S +westerner/M +westernization/MS +westernize/GSD +westernmost +Western/ZRS +western/ZSR +Westfield/M +Westhampton/M +Westinghouse/M +westing/M +Westleigh/M +Westley/M +Westminster/M +Westmore/M +West/MS +Weston/M +Westphalia/M +Westport/M +west/RDGSM +westward/S +Westwood/M +wetback/MS +wetland/S +wetness/MS +wet/SPY +wettable +wetter/S +wettest +wetting +we've +Weyden/M +Weyerhauser/M +Weylin/M +Wezen/M +WFF +whacker/M +whack/GZRDS +whaleboat/MS +whalebone/SM +whale/GSRDZM +Whalen/M +whaler/M +whaling/M +whammed +whamming/M +wham/MS +whammy/S +wharf/SGMD +Wharton/M +wharves +whatchamacallit/MS +what'd +whatever +what/MS +whatnot/MS +what're +whatsoever +wheal/MS +wheatgerm +Wheaties/M +Wheatland/M +wheat/NMXS +Wheaton/M +Wheatstone/M +wheedle/ZDRSG +wheelbarrow/GSDM +wheelbase/MS +wheelchair/MS +wheeler/M +Wheeler/M +wheelhouse/SM +wheelie/MS +wheeling/M +Wheeling/M +Wheelock/M +wheel/RDMJSGZ +wheelwright/MS +whee/S +wheeze/SDG +wheezily +wheeziness/SM +wheezy/PRT +Whelan/M +whelk/MDS +Wheller/M +whelm/DGS +whelp/DMGS +whence/S +whenever +when/S +whensoever +whereabout/S +whereas/S +whereat +whereby +where'd +wherefore/MS +wherein +where/MS +whereof +whereon +where're +wheresoever +whereto +whereupon +wherever +wherewith +wherewithal/SM +wherry/DSGM +whether +whet/S +whetstone/MS +whetted +whetting +whew/GSD +whey/MS +which +whichever +whiff/GSMD +whiffle/DRSG +whiffler/M +whiffletree/SM +whig/S +Whig/SM +while/GSD +whilom +whilst +whimmed +whimming +whimper/DSG +whimsey's +whimsicality/MS +whimsical/YP +whim/SM +whimsy/TMDRS +whine/GZMSRD +whining/Y +whinny/GTDRS +whiny/RT +whipcord/SM +whiplash/SDMG +Whippany/M +whipped +whipper/MS +whippersnapper/MS +whippet/MS +whipping/SM +Whipple/M +whippletree/SM +whippoorwill/SM +whipsaw/GDMS +whips/M +whip/SM +whirligig/MS +whirlpool/MS +whirl/RDGS +whirlwind/MS +whirlybird/MS +whirly/MS +whirred +whirring +whir/SY +whisker/DM +whiskery +whiskey/SM +whisk/GZRDS +whisperer/M +whisper/GRDJZS +whispering/YM +whist/GDMS +whistleable +whistle/DRSZG +whistler/M +Whistler/M +whistling/M +Whitaker/M +Whitby/M +Whitcomb/M +whitebait/M +whitecap/MS +whiteface/M +Whitefield/M +whitefish/SM +Whitehall/M +Whitehead/M +whitehead/S +Whitehorse/M +Whiteleaf/M +Whiteley/M +White/MS +whitener/M +whiteness/MS +whitening/M +whiten/JZDRG +whiteout/S +white/PYS +whitespace +whitetail/S +whitewall/SM +whitewash/GRSDM +whitewater +Whitewater/M +whitey/MS +Whitfield/M +whither/DGS +whitier +whitiest +whiting/M +whitish +Whitley/M +Whitlock/M +Whit/M +Whitman/M +Whitney/M +whit/SJGTXMRND +Whitsunday/MS +Whittaker/M +whitter +Whittier +whittle/JDRSZG +whittler/M +whiz +whizkid +whizzbang/S +whizzed +whizzes +whizzing +WHO +whoa/S +who'd +whodunit/SM +whoever +wholegrain +wholeheartedness/MS +wholehearted/PY +wholemeal +wholeness/S +wholesale/GZMSRD +wholesaler/M +wholesomeness/USM +wholesome/UYP +whole/SP +wholewheat +who'll +wholly +whom +who/M +whomever +whomsoever +whoopee/S +whooper/M +whoop/SRDGZ +whoosh/DSGM +whop +whopper/MS +whopping/S +who're +whorehouse/SM +whoreish +whore/SDGM +whorish +whorl/SDM +whose +whoso +whosoever +who've +why +whys +WI +Wiatt/M +Wichita/M +wickedness/MS +wicked/RYPT +wicker/M +wickerwork/MS +wicketkeeper/SM +wicket/SM +wick/GZRDMS +wicking/M +widemouthed +widener/M +wideness/S +widen/SGZRD +wide/RSYTP +widespread +widgeon's +widget/SM +widower/M +widowhood/S +widow/MRDSGZ +width/M +widths +widthwise +Wieland/M +wielder/M +wield/GZRDS +Wiemar/M +wiener/SM +wienie/SM +Wier/M +Wiesel/M +wife/DSMYG +wifeless +wifely/RPT +wigeon/MS +wigged +wigging/M +Wiggins +wiggler/M +wiggle/RSDGZ +wiggly/RT +wight/SGDM +wiglet/S +wigmaker +wig/MS +Wigner/M +wigwagged +wigwagging +wigwag/S +wigwam/MS +Wilberforce/M +Wilbert/M +Wilbur/M +Wilburn/M +Wilburt/M +Wilcox/M +Wilda/M +wildcat/SM +wildcatted +wildcatter/MS +wildcatting +wildebeest/SM +Wilde/MR +Wilden/M +Wilder/M +wilderness/SM +wilder/P +wildfire/MS +wildflower/S +wildfowl/M +wilding/M +wildlife/M +wildness/MS +Wildon/M +wild/SPGTYRD +wile/DSMG +Wileen/M +Wilek/M +Wiley/M +Wilford/M +Wilfred/M +Wilfredo/M +Wilfrid/M +wilfulness's +Wilhelmina/M +Wilhelmine/M +Wilhelm/M +Wilie/M +wilily +wiliness/MS +Wilkerson/M +Wilkes/M +Wilkins/M +Wilkinson/M +Willabella/M +Willa/M +Willamette/M +Willamina/M +Willard/M +Willcox/M +Willdon/M +willed/U +Willem/M +Willemstad/M +willer/M +Willetta/M +Willette/M +Willey/M +willfulness/S +willful/YP +Williamsburg/M +William/SM +Williamson/M +Willied/M +Willie/M +willies +Willi/MS +willinger +willingest +willingness's +willingness/US +willing/UYP +Willisson/M +williwaw/MS +Will/M +Willoughby/M +willower/M +Willow/M +willow/RDMSG +willowy/TR +willpower/MS +will/SGJRD +Willy/SDM +Willyt/M +Wilma/M +Wilmar/M +Wilmer/M +Wilmette/M +Wilmington/M +Wilona/M +Wilone/M +Wilow/M +Wilshire/M +Wilsonian +Wilson/M +wilt/DGS +Wilt/M +Wilton/M +wily/PTR +Wimbledon/M +wimp/GSMD +wimpish +wimple/SDGM +wimpy/RT +wince/SDG +Winchell/M +wincher/M +winchester/M +Winchester/MS +winch/GRSDM +windbag/SM +windblown +windbreak/MZSR +windburn/GSMD +winded +winder/UM +windfall/SM +windflower/MS +Windham/M +Windhoek/M +windily +windiness/SM +winding/MS +windjammer/SM +windlass/GMSD +windless/YP +windmill/GDMS +window/DMGS +windowless +windowpane/SM +Windows +windowsill/SM +windpipe/SM +windproof +windrow/GDMS +wind's +winds/A +windscreen/MS +windshield/SM +windsock/MS +Windsor/MS +windstorm/MS +windsurf/GZJSRD +windswept +windup/MS +wind/USRZG +Windward/M +windward/SY +Windy/M +windy/TPR +wineglass/SM +winegrower/SM +Winehead/M +winemake +winemaster +wine/MS +winery/MS +Winesap/M +wineskin/M +Winfield/M +Winfred/M +Winfrey/M +wingback/M +wingding/MS +wingeing +winger/M +wing/GZRDM +wingless +winglike +wingman +wingmen +wingspan/SM +wingspread/MS +wingtip/S +Winifield/M +Winifred/M +Wini/M +winker/M +wink/GZRDS +winking/U +Winkle/M +winkle/SDGM +winless +Win/M +winnable +Winnah/M +Winna/M +Winnebago/M +Winne/M +winner/MS +Winnetka/M +Winnie/M +Winnifred/M +Winni/M +winning/SY +Winnipeg/M +Winn/M +winnow/SZGRD +Winny/M +Winograd/M +wino/MS +Winonah/M +Winona/M +Winooski/M +Winsborough/M +Winsett/M +Winslow/M +winsomeness/SM +winsome/PRTY +Winston/M +winterer/M +wintergreen/SM +winterize/GSD +Winters +winter/SGRDYM +wintertime/MS +Winthrop/M +wintriness/M +wintry/TPR +winy/RT +win/ZGDRS +wipe/DRSZG +wiper/M +wirehair/MS +wireless/MSDG +wireman/M +wiremen +wirer/M +wire's +wires/A +wiretap/MS +wiretapped +wiretapper/SM +wiretapping +wire/UDA +wiriness/S +wiring/SM +wiry/RTP +Wisc +Wisconsinite/SM +Wisconsin/M +wisdoms +wisdom/UM +wiseacre/MS +wisecrack/GMRDS +wised +wisely/TR +Wise/M +wiseness +wisenheimer/M +Wisenheimer/M +wises +wise/URTY +wishbone/MS +wishfulness/M +wishful/PY +wish/GZSRD +wishy +wising +Wis/M +wisp/MDGS +wispy/RT +wist/DGS +wisteria/SM +wistfulness/MS +wistful/PY +witchcraft/SM +witchdoctor/S +witchery/MS +witch/SDMG +withal +withdrawal/MS +withdrawer/M +withdrawnness/M +withdrawn/P +withdraw/RGS +withdrew +withe/M +wither/GDJ +withering/Y +Witherspoon/M +with/GSRDZ +withheld +withholder/M +withhold/SJGZR +within/S +without/S +withs +withstand/SG +withstood +witlessness/MS +witless/PY +Wit/M +witness/DSMG +witnessed/U +wit/PSM +witted +witter/G +Wittgenstein/M +witticism/MS +Wittie/M +wittily +wittiness/SM +wittings +witting/UY +Witt/M +Witty/M +witty/RTP +Witwatersrand/M +wive/GDS +wives/M +wizard/MYS +wizardry/MS +wizen/D +wiz's +wk/Y +Wm/M +WNW +woad/MS +wobble/GSRD +wobbler/M +wobbliness/S +wobbly/PRST +Wodehouse/M +woebegone/P +woefuller +woefullest +woefulness/SM +woeful/PY +woe/PSM +woke +wok/SMN +Wolcott/M +wold/MS +Wolfe/M +wolfer/M +Wolff/M +Wolfgang/M +wolfhound/MS +Wolfie/M +wolfishness/M +wolfish/YP +Wolf/M +wolfram/MS +wolf/RDMGS +Wolfy/M +Wollongong/M +Wollstonecraft/M +Wolsey/M +Wolverhampton/M +wolverine/SM +Wolverton/M +wolves/M +woman/GSMYD +womanhood/MS +womanish +womanized/U +womanizer/M +womanize/RSDZG +womanizes/U +womankind/M +womanlike +womanliness/SM +womanly/PRT +wombat/MS +womb/SDM +womenfolk/MS +women/MS +wonderer/M +wonderfulness/SM +wonderful/PY +wonder/GLRDMS +wondering/Y +wonderland/SM +wonderment/SM +wondrousness/M +wondrous/YP +Wong/M +wonk/S +wonky/RT +wonned +wonning +won/SG +won't +wontedness/MU +wonted/PUY +wont/SGMD +Woodard/M +Woodberry/M +woodbine/SM +woodblock/S +Woodbury/M +woodcarver/S +woodcarving/MS +woodchopper/SM +woodchuck/MS +woodcock/MS +woodcraft/MS +woodcut/SM +woodcutter/MS +woodcutting/MS +woodenness/SM +wooden/TPRY +woodgrain/G +woodhen +Woodhull/M +Woodie/M +woodiness/MS +woodland/SRM +Woodlawn/M +woodlice +woodlot/S +woodlouse/M +woodman/M +Woodman/M +woodmen +woodpecker/SM +woodpile/SM +Woodrow/M +woodruff/M +woo/DRZGS +woodshedded +woodshedding +woodshed/SM +woodside +Wood/SM +woodsman/M +woodsmen +wood/SMNDG +woodsmoke +woods/R +Woodstock/M +woodsy/TRP +Woodward/MS +woodwind/S +woodworker/M +woodworking/M +woodwork/SMRGZJ +woodworm/M +woodyard +Woody/M +woody/TPSR +woofer/M +woof/SRDMGZ +Woolf/M +woolgatherer/M +woolgathering/M +woolgather/RGJ +woolliness/MS +woolly/RSPT +Woolongong/M +wool/SMYNDX +Woolworth/M +Woonsocket/M +Wooster/M +Wooten/M +woozily +wooziness/MS +woozy/RTP +wop/MS! +Worcestershire/M +Worcester/SM +wordage/SM +word/AGSJD +wordbook/MS +Worden/M +wordily +wordiness/SM +wording/AM +wordless/Y +wordplay/SM +word's +Wordsworth/M +wordy/TPR +wore +workability's +workability/U +workableness/M +workable/U +workably +workaday +workaholic/S +workaround/SM +workbench/MS +workbook/SM +workday/SM +worked/A +worker/M +workfare/S +workforce/S +work/GZJSRDMB +workhorse/MS +workhouse/SM +working/M +workingman/M +workingmen +workingwoman/M +workingwomen +workload/SM +workmanlike +Workman/M +workman/MY +workmanship/MS +workmate/S +workmen/M +workout/SM +workpiece/SM +workplace/SM +workroom/MS +works/A +worksheet/S +workshop/MS +workspace/S +workstation/MS +worktable/SM +worktop/S +workup/S +workweek/SM +worldlier +worldliest +worldliness/USM +worldly/UP +worldwide +world/ZSYM +wormer/M +wormhole/SM +worm/SGMRD +Worms/M +wormwood/SM +wormy/RT +worn/U +worried/Y +worrier/M +worriment/MS +worrisome/YP +worrying/Y +worrywart/SM +worry/ZGSRD +worsen/GSD +worse/SR +worshiper/M +worshipfulness/M +worshipful/YP +worship/ZDRGS +worsted/MS +worst/SGD +worth/DG +worthily/U +worthinesses/U +worthiness/SM +Worthington/M +worthlessness/SM +worthless/PY +Worth/M +worths +worthwhile/P +Worthy/M +worthy/UTSRP +wort/SM +wost +wot +Wotan/M +wouldn't +would/S +wouldst +would've +wound/AU +wounded/U +wounder +wounding +wounds +wound's +wove/A +woven/AU +wovens +wow/SDG +Wozniak/M +WP +wpm +wrack/SGMD +wraith/M +wraiths +Wrangell/M +wrangle/GZDRS +wrangler/M +wraparound/S +wrap/MS +wrapped/U +wrapper/MS +wrapping/SM +wraps/U +wrasse/SM +wrathful/YP +wrath/GDM +wraths +wreak/SDG +wreathe +wreath/GMDS +wreaths +wreckage/MS +wrecker/M +wreck/GZRDS +wrenching/Y +wrench/MDSG +wren/MS +Wren/MS +Wrennie/M +wrester/M +wrestle/JGZDRS +wrestler/M +wrestling/M +wrest/SRDG +wretchedness/SM +wretched/TPYR +wretch/MDS +wriggle/DRSGZ +wriggler/M +wriggly/RT +Wright/M +wright/MS +Wrigley/M +wringer/M +wring/GZRS +wrinkled/U +wrinkle/GMDS +wrinkly/RST +wristband/SM +wrist/MS +wristwatch/MS +writable/U +write/ASBRJG +writer/MA +writeup +writhe/SDG +writing/M +writ/MRSBJGZ +written/UA +Wroclaw +wrongdoer/MS +wrongdoing/MS +wronger/M +wrongfulness/MS +wrongful/PY +wrongheadedness/MS +wrongheaded/PY +wrongness/MS +wrong/PSGTYRD +Wronskian/M +wrote/A +wroth +wrought/I +wrung +wry/DSGY +wryer +wryest +wryness/SM +W's +WSW +wt +W/T +Wuhan/M +Wu/M +Wurlitzer/M +wurst/SM +wuss/S +wussy/TRS +WV +WW +WWI +WWII +WWW +w/XTJGV +WY +Wyatan/M +Wyatt/M +Wycherley/M +Wycliffe/M +Wye/MH +Wyeth/M +Wylie/M +Wylma/M +Wyman/M +Wyndham/M +Wyn/M +Wynne/M +Wynnie/M +Wynn/M +Wynny/M +Wyo/M +Wyomingite/SM +Wyoming/M +WYSIWYG +x +X +Xanadu +Xanthippe/M +Xanthus/M +Xaviera/M +Xavier/M +Xebec/M +Xe/M +XEmacs/M +Xenakis/M +Xena/M +Xenia/M +Xenix/M +xenon/SM +xenophobe/MS +xenophobia/SM +xenophobic +Xenophon/M +Xenos +xerographic +xerography/MS +xerox/GSD +Xerox/MGSD +Xerxes/M +Xever/M +Xhosa/M +Xi'an +Xian/S +Xiaoping/M +xii +xiii +xi/M +Ximenes/M +Ximenez/M +Ximian/SM +Xingu/M +xis +xiv +xix +XL +Xmas/SM +XML +Xochipilli/M +XOR +X's +XS +xterm/M +Xuzhou/M +xv +xvi +xvii +xviii +xx +XXL +xylem/SM +xylene/M +Xylia/M +Xylina/M +xylophone/MS +xylophonist/S +Xymenes/M +Y +ya +yacc/M +Yacc/M +yachting/M +yachtsman +yachtsmen +yachtswoman/M +yachtswomen +yacht/ZGJSDM +yack's +Yagi/M +yahoo/MS +Yahweh/M +Yakima/M +yakked +yakking +yak/SM +Yakut/M +Yakutsk/M +Yale/M +Yalies/M +y'all +Yalonda/M +Yalow/M +Yalta/M +Yalu/M +Yamaha/M +yammer/RDZGS +Yamoussoukro +yam/SM +Yanaton/M +Yance/M +Yancey/M +Yancy/M +Yang/M +Yangon +yang/S +Yangtze/M +Yankee/SM +yank/GDS +Yank/MS +Yaounde/M +yapped +yapping +yap/S +Yaqui/M +yardage/SM +yardarm/SM +Yardley/M +Yard/M +yardman/M +yardmaster/S +yardmen +yard/SMDG +yardstick/SM +yarmulke/SM +yarn/SGDM +Yaroslavl/M +yarrow/MS +Yasmeen/M +Yasmin/M +Yates +yaw/DSG +yawl/SGMD +yawner/M +yawn/GZSDR +yawning/Y +Yb/M +yd +Yeager/M +yeah +yeahs +yearbook/SM +yearling/M +yearlong +yearly/S +yearner/M +yearning/MY +yearn/JSGRD +year/YMS +yea/S +yeastiness/M +yeast/SGDM +yeasty/PTR +Yeats/M +yecch +yegg/MS +Yehudi/M +Yehudit/M +Yekaterinburg/M +Yelena/M +yell/GSDR +yellowhammers +yellowish +Yellowknife/M +yellowness/MS +Yellowstone/M +yellow/TGPSRDM +yellowy +yelper/M +yelp/GSDR +Yeltsin +Yemeni/S +Yemenite/SM +Yemen/M +Yenisei/M +yenned +yenning +yen/SM +Yentl/M +yeomanry/MS +yeoman/YM +yeomen +yep/S +Yerevan/M +Yerkes/M +Yesenia/M +yeshiva/SM +yes/S +yessed +yessing +yesterday/MS +yesteryear/SM +yet +ye/T +yeti/SM +Yetta/M +Yettie/M +Yetty/M +Yevette/M +Yevtushenko/M +yew/SM +y/F +Yggdrasil/M +Yiddish/M +yielded/U +yielding/U +yield/JGRDS +yikes +yin/S +yipe/S +yipped +yippee/S +yipping +yip/S +YMCA +YMHA +Ymir/M +YMMV +Ynes/M +Ynez/M +yo +Yoda/M +yodeler/M +yodel/SZRDG +Yoder/M +yoga/MS +yoghurt's +yogi/MS +yogurt/SM +yoke/DSMG +yoked/U +yokel/SM +yokes/U +yoking/U +Yoknapatawpha/M +Yokohama/M +Yoko/M +Yolanda/M +Yolande/M +Yolane/M +Yolanthe/M +yolk/DMS +yon +yonder +Yong/M +Yonkers/M +yore/MS +Yorgo/MS +Yorick/M +Yorke/M +Yorker/M +yorker/SM +Yorkshire/MS +Yorktown/M +York/ZRMS +Yoruba/M +Yosemite/M +Yoshiko/M +Yoshi/M +Yost/M +you'd +you'll +youngish +Young/M +youngster/MS +Youngstown/M +young/TRYP +you're +your/MS +yourself +yourselves +you/SH +youthfulness/SM +youthful/YP +youths +youth/SM +you've +Yovonnda/M +yow +yowl/GSD +Ypres/M +Ypsilanti/M +yr +yrs +Y's +Ysabel/M +YT +ytterbium/MS +yttrium/SM +yuan/M +Yuba/M +Yucatan +yucca/MS +yuck/GSD +yucky/RT +Yugo/M +Yugoslavia/M +Yugoslavian/S +Yugoslav/M +Yuh/M +Yuki/M +yukked +yukking +Yukon/M +yuk/S +yule/MS +Yule/MS +yuletide/MS +Yuletide/S +Yul/M +Yulma/M +yum +Yuma/M +yummy/TRS +Yunnan/M +yuppie/SM +yup/S +Yurik/M +Yuri/M +yurt/SM +Yves/M +Yvette/M +Yvon/M +Yvonne/M +Yvor/M +YWCA +YWHA +Zabrina/M +Zaccaria/M +Zachariah/M +Zacharia/SM +Zacharie/M +Zachary/M +Zacherie/M +Zachery/M +Zach/M +Zackariah/M +Zack/M +zagging +Zagreb/M +zag/S +Zahara/M +Zaire/M +Zairian/S +Zak/M +Zambezi/M +Zambia/M +Zambian/S +Zamboni +Zamenhof/M +Zamora/M +Zandra/M +Zane/M +Zaneta/M +zaniness/MS +Zan/M +Zanuck/M +zany/PDSRTG +Zanzibar/M +Zapata/M +Zaporozhye/M +Zappa/M +zapped +zapper/S +zapping +zap/S +Zarah/M +Zara/M +Zared/M +Zaria/M +Zarla/M +Zealand/M +zeal/MS +zealot/MS +zealotry/MS +zealousness/SM +zealous/YP +Zea/M +Zebadiah/M +Zebedee/M +Zeb/M +zebra/MS +Zebulen/M +Zebulon/M +zebu/SM +Zechariah/M +Zedekiah/M +Zed/M +Zedong/M +zed/SM +Zeffirelli/M +Zeiss/M +zeitgeist/S +Zeke/M +Zelda/M +Zelig/M +Zellerbach/M +Zelma/M +Zena/M +Zenger/M +Zenia/M +zenith/M +zeniths +Zen/M +Zennist/M +Zeno/M +Zephaniah/M +zephyr/MS +Zephyrus/M +Zeppelin's +zeppelin/SM +Zerk/M +zeroed/M +zeroing/M +zero/SDHMG +zestfulness/MS +zestful/YP +zest/MDSG +zesty/RT +zeta/SM +zeugma/M +Zeus/M +Zhdanov/M +Zhengzhou +Zhivago/M +Zhukov/M +Zia/M +Zibo/M +Ziegfeld/MS +Ziegler/M +zig +zigged +zigging +Ziggy/M +zigzagged +zigzagger +zigzagging +zigzag/MS +zilch/S +zillion/MS +Zilvia/M +Zimbabwean/S +Zimbabwe/M +Zimmerman/M +zincked +zincking +zinc/MS +zing/GZDRM +zingy/RT +zinnia/SM +Zionism/MS +Zionist/MS +Zion/SM +zip/MS +zipped/U +zipper/GSDM +zipping/U +zippy/RT +zips/U +zirconium/MS +zircon/SM +Zita/M +Zitella/M +zither/SM +zit/S +zloty/SM +Zn/M +zodiacal +zodiac/SM +Zoe/M +Zola/M +Zollie/M +Zolly/M +Zomba/M +zombie/SM +zombi's +zonal/Y +Zonda/M +Zondra/M +zoned/A +zone/MYDSRJG +zones/A +zoning/A +zonked +Zonnya/M +zookeepers +zoological/Y +zoologist/SM +zoology/MS +zoom/DGS +zoophyte/SM +zoophytic +zoo/SM +Zorah/M +Zora/M +Zorana/M +Zorina/M +Zorine/M +Zorn/M +Zoroaster/M +Zoroastrianism/MS +Zoroastrian/S +Zorro/M +Zosma/M +zounds/S +Zr/M +Zs +Zsazsa/M +Zsigmondy/M +z/TGJ +Zubenelgenubi/M +Zubeneschamali/M +zucchini/SM +Zukor/M +Zulema/M +Zululand/M +Zulu/MS +Zuni/S +Zrich/M +Zuzana/M +zwieback/MS +Zwingli/M +Zworykin/M +Z/X +zydeco/S +zygote/SM +zygotic +zymurgy/S diff --git a/modules/install.js b/modules/install.js index 3e467ca1..e5e22c76 100644 --- a/modules/install.js +++ b/modules/install.js @@ -3,22 +3,76 @@ Beware: resource / view / big / resource-http / etc */ var npm = require("npm"); +var m = require('../lib/resources/modules'); var modules = require('./modules'); +var fs = require("fs"); +var colors = require('colors'); -var arr = Object.keys(modules); + var arr = Object.keys(modules); -npm.load({exit: false}, function (err) { - if(err) throw err; - iterate(); + function _install () { + + if(arr.length === 0) { + console.log('DONE'.blue); + process.exit(); + } + + var p = arr.pop(); + m.install(p, function(err, r){ + if (err) { + console.log('ERROR'.red, err.message, p); + return; + } + console.log('INSTALLED'.green, p); + _install(); + }); + }; + +_install(); + +return; +// legacy module installer code +npm.load({ exit: false }, function (err) { + if (err) throw err; + checkPackage(); }); -function iterate () { + +function checkPackage () { if (arr.length === 0) { process.exit(); } var m = arr.pop(); - npm.commands.install([m + "@" + modules[m]], function (err, data) { - if (err) throw err; - iterate(); - }); + if (isVersionInstalled(m, modules[m])) { + // do not install if module@version is already installed + console.log(('found ' + m + '@' + modules[m]).green); + checkPackage(); + } else { + // install version + console.log(('missing ' + m + '@' + modules[m]).yellow); + npm.commands.install([m + "@" + modules[m]], function (err, data) { + if (err) throw err; + console.log(('installed ' + m + '@' + modules[m]).blue); + checkPackage(); + }); + } +}; + +// +// Checks local node_modules for package +// Returns boolean +function isVersionInstalled (m, v) { + // console.log('checking version for', m, '@', v); + var pkg, result = false; + try { + pkg = fs.readFileSync('./node_modules/' + m + '/package.json').toString(); + pkg = JSON.parse(pkg); + // console.log(pkg.version) + if (pkg.version === v) { + result = true; + } + } catch (err) { + result = false; + } + return result; }; \ No newline at end of file diff --git a/modules/modules.js b/modules/modules.js index 541a9565..5ac4741b 100644 --- a/modules/modules.js +++ b/modules/modules.js @@ -3,6 +3,7 @@ module['exports'] = { "aws-sdk": "2.1.26", "bluebird": "2.3.11", "busboy": "0.2.9", + "byline": "4.2.1", "body-parser": "1.9.2", "cheerio": "0.17.0", "collect-stream": "1.0.0", @@ -12,25 +13,28 @@ module['exports'] = { "contentful-management": "0.1.1", "cron-parser": "0.4.5", "datauri": "0.5.5", + "esri-proj-codes": "1.0.0", "fraktur": "1.0.0", "dateformat": "1.0.8", "debug": "2.1.0", "gengo": "0.1.8", + "geoip-lite" : "1.1.6", "geocodio": "0.0.1", + "got": "9.6.0", "github": "0.2.3", "github-url-from-git": "1.4.0", "gm": "1.17.0", "hyperquest": "1.0.1", "irc": "0.3.7", - "jugglingdb-nano": "0.0.3-pre", "map-async": "0.1.1", "map-sync": "0.1.1", + "marked": "0.3.3", "mime": "1.2.11", "mkdirp": "0.5.0", "moment": "2.8.4", "mschema": "0.5.5", "mschema-forms": "0.5.0", - "mustache": "0.8.2", + "mustache": "2.3.0", "object-path": "0.6.0", "npm": "2.1.8", "once": "1.3.1", @@ -43,15 +47,17 @@ module['exports'] = { "resource-user": "0.5.2", "slack-notify": "0.1.2", "slug": "0.8.0", + "spellcheck": "0.0.5", "stream-body-parser": "0.0.1", "stream-buffers": "1.1.0", "stripe": "2.9.0", "stream-transcoder": "0.0.5", "through2": "0.6.3", + "then-sleep": "*", "trycatch": "1.5.11", "twilio": "1.8.0", "twit": "1.1.18", "view": "0.6.0", - "xml2js": "2.6.2", + "xml2js": "0.4.8", "xtend": "4.0.0" -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index e0c3f7e3..499cc2f2 100644 --- a/package.json +++ b/package.json @@ -2,51 +2,121 @@ "name": "hook.io", "version": "1.0.0", "description": "microservice and webhook hosting platform", - "main": "server.js", + "main": "index.js", "directories": { "example": "examples" }, + "bin": {}, "dependencies": { + "async": "^1.3.0", "big": "^0.5.0", "body-parser": "^1.9.2", "busboy": "^0.2.9", "cheerio": "^0.17.0", - "colors": "^1.0.3", - "cron-parser": "^0.4.5", + "chroot": "^1.0.10", + "coffee-script": "^1.10.0", + "colors": "^1.3.3", + "cron-parser": "^2.11.0", "datauri": "^0.5.5", - "dateformat": "^1.0.8", + "date-fns-timezone": "^0.1.4", + "dateformat": "^1.0.12", "debug": "^2.1.0", "github": "^0.2.2", + "hook.io-datastore": "https://github.com/bigcompany/hook.io-datastore/tarball/master", + "hook.io-i18n": "https://github.com/bigcompany/hook.io-i18n/tarball/master", + "hook.io-logs": "https://github.com/bigcompany/hook.io-logs/tarball/master", + "hook.io-sdk": "https://github.com/bigcompany/hook.io-sdk/tarball/master", + "hook.io-vfs": "https://github.com/bigcompany/hook.io-vfs/tarball/master", + "hpm": "https://github.com/bigcompany/hpm/tarball/master", + "hyperdirect": "0.0.0", "hyperquest": "^1.0.1", + "i18n-2": "^0.4.6", + "is-docker": "^1.1.0", "jugglingdb-nano": "https://github.com/Marak/nano-adapter/tarball/master", + "merge-params": "https://github.com/bigcompany/merge-params/tarball/master", + "merge-stream": "^0.1.8", + "microcule": "https://github.com/stackvana/microcule/tarball/master", + "microcule-examples": "https://github.com/stackvana/microcule-examples/tarball/master", + "microtail": "https://github.com/marak/microtail/tarball/master", "mime": "^1.2.11", + "minimist": "^1.1.1", "mkdirp": "^0.5.0", - "mschema": "^0.5.4", - "mschema-forms": "*", - "mustache": "^0.8.2", + "ms": "2.0.0", + "mschema": "https://github.com/mschema/mschema/tarball/master", + "mschema-forms": "https://github.com/mschema/mschema-forms/tarball/master", + "mschema-rpc": "https://github.com/mschema/mschema-rpc/tarball/master", + "nano": "^6.2.0", + "node-uuid": "^1.4.3", + "npm": "^6.9.0", + "octonode": "^0.7.6", + "parse-service-request": "https://github.com/bigcompany/parse-service-request/tarball/master", "passport": "^0.2.1", "passport-github": "^0.1.5", + "posix": "^4.1.1", + "redis": "2.8.0", "request": "^2.48.0", - "resource": "^0.5.3", + "resource": "https://github.com/bigcompany/resource/tarball/master", "resource-email": "0.5.x", - "resource-http": "^0.6.0", - "resource-user": "0.5.x", - "slug": "^0.8.0", - "stream-body-parser": "0.0.1", + "resource-forms": "https://github.com/bigcompany/forms/tarball/master", + "resource-http": "https://github.com/bigcompany/http/tarball/master", + "resource-user": "https://github.com/bigcompany/user/tarball/master", + "rimraf": "^2.6.3", + "route-parser": "0.0.4", + "rss": "^1.1.1", + "run-remote-service": "https://github.com/bigcompany/run-remote-service/tarball/master", + "run-service": "https://github.com/bigcompany/run-service/tarball/master", + "slug": "1.1.0", "stream-buffers": "^1.1.0", - "stripe": "^3.3.4", + "stripe": "^6.34.0", + "then-sleep": "^1.0.1", "through2": "^0.6.3", - "trycatch": "^1.5.11", - "view": "^0.6.0" + "timezone-support": "^1.8.1", + "tree-kill": "^0.1.1", + "trycatch": "^1.5.20", + "view": "https://github.com/bigcompany/view/tarball/master", + "websocket-stream": "^3.1.0", + "ws": "^6.2.1" }, "devDependencies": { + "eslint-plugin-html": "^5.0.3", + "gulp-mustache": "^4.1.1", "gulp-rename": "*", - "gulp-mustache": "*" + "nyc": "11.1.x", + "tap": "^12.6.2", + "tape": "^4.10.1" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "tap --no-timeout tests/**/*-test.js", "start": "sh scripts/start.sh" }, + "engines": { + "node": ">=10.15.3" + }, + "nyc": { + "all": false, + "include": [ + "lib/**/*.js", + "view/**/*.js" + ], + "exclude": [ + "coverage", + "locales", + "modules", + "white", + "docs", + "docker", + "public", + "reports", + "tests", + "node_modules" + ], + "reporter": [ + "html", + "lcov", + "clover" + ], + "report-dir": "./reports/coverage" + }, "author": "Marak", "license": "AGPL" } diff --git a/public/agents/Merlin/agent.js b/public/agents/Merlin/agent.js new file mode 100644 index 00000000..1d5ae0c2 --- /dev/null +++ b/public/agents/Merlin/agent.js @@ -0,0 +1,2 @@ +clippy.ready("Merlin",{overlayCount:3,sounds:["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32"],framesize:[128,128],animations:{MoveLeft:{frames:[{duration:100,images:[[0,0]],exitBranch:19},{duration:100,images:[[128,0]],exitBranch:0},{duration:100,images:[[256,0]],exitBranch:1},{duration:100,images:[[384,0]],exitBranch:2},{duration:100,images:[[512,0]],exitBranch:3},{duration:100,images:[[640,0]],exitBranch:4},{duration:400,images:[[768,0]],exitBranch:4},{duration:100,images:[[896,0]],exitBranch:6,sound:"17",branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[896,0]],exitBranch:6},{duration:100,images:[[1024,0]],exitBranch:8},{duration:100,images:[[1152,0]],exitBranch:9},{duration:100,images:[[1280,0]],exitBranch:9},{duration:100,images:[[1408,0]],exitBranch:9},{duration:100,images:[[1536,0]],exitBranch:18},{duration:100,images:[[1664,0]],exitBranch:17},{duration:100,images:[[1792,0]],exitBranch:16,branching:{branches:[{frameIndex:19,weight:100}]}},{duration:200,images:[[1920,0]],exitBranch:17,sound:"12"},{duration:100,images:[[2048,0]],exitBranch:18},{duration:100,images:[[2176,0]],exitBranch:6},{duration:0}],useExitBranching:!0},Congratulate:{frames:[{duration:100,images:[[0,0]],exitBranch:24},{duration:100,images:[[2304,0]],exitBranch:0},{duration:100,images:[[2432,0]],exitBranch:1},{duration:100,images:[[2560,0]],exitBranch:2},{duration:100,images:[[0,128]],exitBranch:3},{duration:100,images:[[128,128]],exitBranch:4,sound:"9"},{duration:100,images:[[256,128]],exitBranch:5},{duration:100,images:[[384,128]],exitBranch:6},{duration:100,images:[[512,128]],exitBranch:7},{duration:100,images:[[640,128]],exitBranch:8},{duration:500,images:[[768,128]],exitBranch:9},{duration:100,images:[[896,128]],exitBranch:10},{duration:100,images:[[1024,128]],exitBranch:11},{duration:400,images:[[1152,128]],exitBranch:17,branching:{branches:[{frameIndex:16,weight:80}]}},{duration:100,images:[[1024,128]],exitBranch:13},{duration:100,images:[[1280,128]],exitBranch:14,branching:{branches:[{frameIndex:12,weight:100}]}},{duration:100,images:[[1152,128]],exitBranch:17,branching:{branches:[{frameIndex:24,weight:100}]}},{duration:100,images:[[1408,128]],exitBranch:18},{duration:100,images:[[1536,128]],exitBranch:19,sound:"30"},{duration:100,images:[[1664,128]],exitBranch:20},{duration:100,images:[[1792,128]],exitBranch:21},{duration:100,images:[[1920,128]],exitBranch:22},{duration:100,images:[[2048,128]],exitBranch:23},{duration:100,images:[[0,0]],exitBranch:24},{duration:0}],useExitBranching:!0},Hide:{frames:[{duration:100,images:[[0,0]],exitBranch:15},{duration:100,images:[[2176,128]],exitBranch:0,sound:"10",branching:{branches:[{frameIndex:3,weight:100}]}},{duration:100,images:[[2176,128]],exitBranch:0},{duration:100,images:[[2304,128]],exitBranch:2},{duration:50,images:[[2432,128]],exitBranch:3},{duration:50,images:[[2560,128]],exitBranch:4},{duration:50,images:[[0,256]],exitBranch:5},{duration:100,images:[[128,256]],exitBranch:6},{duration:100,images:[[256,256]],exitBranch:7},{duration:100,images:[[384,256]],exitBranch:8},{duration:100,images:[[512,256]],exitBranch:9},{duration:50,images:[[640,256]],exitBranch:10},{duration:50,images:[[768,256]],exitBranch:11},{duration:100,images:[[896,256]],exitBranch:12},{duration:100,images:[[1024,256]],exitBranch:13},{duration:0}]},Pleased:{frames:[{duration:100,images:[[0,0]],exitBranch:5},{duration:100,images:[[768,768]],exitBranch:0},{duration:100,images:[[1024,768]],exitBranch:1},{duration:100,images:[[1152,768]],exitBranch:2},{duration:100,images:[[1408,768]],exitBranch:3},{duration:0}],useExitBranching:!0},Acknowledge:{frames:[{duration:100,images:[[0,0]],exitBranch:8},{duration:100,images:[[1152,256]],exitBranch:0},{duration:50,images:[[1280,256]],exitBranch:1},{duration:100,images:[[1408,256]],exitBranch:2},{duration:100,images:[[1536,256]],exitBranch:3},{duration:100,images:[[1408,256]],exitBranch:4},{duration:50,images:[[1280,256]],exitBranch:7},{duration:100,images:[[1152,256]],exitBranch:8},{duration:50,images:[[0,0]]}]},Thinking:{frames:[{duration:100,images:[[0,0]],exitBranch:13},{duration:100,images:[[1664,256]],exitBranch:0},{duration:100,images:[[1792,256]],exitBranch:1},{duration:200,images:[[1920,256]],exitBranch:2},{duration:200,images:[[2048,256]],exitBranch:3},{duration:1100,images:[[2176,256]],exitBranch:4,branching:{branches:[{frameIndex:9,weight:35}]}},{duration:1200,images:[[2048,256]],exitBranch:3},{duration:1400,images:[[2176,256]],exitBranch:4},{duration:1200,images:[[2048,256]],exitBranch:3,branching:{branches:[{frameIndex:5,weight:100}]}},{duration:1e3,images:[[2176,256]],exitBranch:3},{duration:200,images:[[2176,256],[2304,256]],exitBranch:4},{duration:400,images:[[2176,256],[2432,256]],exitBranch:10},{duration:200,images:[[2176,256],[2304,256]],exitBranch:4,branching:{branches:[{frameIndex:5,weight:100}]}},{duration:0}],useExitBranching:!0},Suggest:{frames:[{duration:100,images:[[0,0]],exitBranch:13},{duration:100,images:[[2560,256]],exitBranch:0,sound:"15"},{duration:100,images:[[0,384]],exitBranch:1},{duration:100,images:[[128,384]],exitBranch:1},{duration:100,images:[[256,384]],exitBranch:3},{duration:100,images:[[384,384]],exitBranch:4},{duration:100,images:[[512,384]],exitBranch:5},{duration:100,images:[[640,384]],exitBranch:9},{duration:100,images:[[768,384]],exitBranch:9,branching:{branches:[{frameIndex:13,weight:100}]}},{duration:100,images:[[896,384]],exitBranch:10},{duration:100,images:[[1024,384]],exitBranch:11},{duration:100,images:[[1152,384]],exitBranch:12},{duration:100,images:[[1280,384]],exitBranch:0},{duration:0}],useExitBranching:!0},Explain:{frames:[{duration:100,images:[[0,0]],exitBranch:6},{duration:100,images:[[1408,384]],exitBranch:0},{duration:100,images:[[1536,384]],exitBranch:1},{duration:100,images:[[1664,384]],exitBranch:2},{duration:100,images:[[1792,384]],exitBranch:3},{duration:100,images:[[1920,384]],exitBranch:4},{duration:0}],useExitBranching:!0},Decline:{frames:[{duration:100,images:[[0,0]],exitBranch:12},{duration:100,images:[[2048,384]],exitBranch:0},{duration:100,images:[[2176,384]],exitBranch:1},{duration:100,images:[[2304,384]],exitBranch:2},{duration:100,images:[[2432,384]],exitBranch:3},{duration:500,images:[[2560,384]],exitBranch:4},{duration:200,images:[[0,512]],exitBranch:5},{duration:100,images:[[2560,384]],exitBranch:4},{duration:200,images:[[128,512]],exitBranch:7},{duration:100,images:[[2560,384]],exitBranch:4},{duration:200,images:[[0,512]],exitBranch:9},{duration:100,images:[[2560,384]],exitBranch:4,branching:{branches:[{frameIndex:12,weight:100}]}},{duration:0}],useExitBranching:!0},DontRecognize:{frames:[{duration:100,images:[[0,0]],exitBranch:6},{duration:100,images:[[1536,768]],exitBranch:0},{duration:100,images:[[1664,768]],exitBranch:1},{duration:100,images:[[1792,768]],exitBranch:2},{duration:100,images:[[1920,768]],exitBranch:3},{duration:100,images:[[2048,768]],exitBranch:4},{duration:0}],useExitBranching:!0},Writing:{frames:[{duration:100,images:[[0,0]],exitBranch:25,sound:"11",branching:{branches:[{frameIndex:2,weight:100}]}},{duration:50,images:[[0,0]],exitBranch:25},{duration:200,images:[[1792,1920]],exitBranch:1},{duration:200,images:[[1920,1920]],exitBranch:2},{duration:200,images:[[2048,1920]],exitBranch:3},{duration:300,images:[[2176,1920]],exitBranch:4},{duration:400,images:[[2304,1920]],exitBranch:17},{duration:300,images:[[1024,1920]],exitBranch:17},{duration:100,images:[[384,1920]],exitBranch:17,sound:"29",branching:{branches:[{frameIndex:11,weight:100}]}},{duration:200,images:[[256,1920]],exitBranch:17},{duration:100,images:[[512,1920]],exitBranch:17,sound:"32"},{duration:200,images:[[640,1920]],exitBranch:17},{duration:100,images:[[768,1920]],exitBranch:17},{duration:300,images:[[896,1920]],exitBranch:17},{duration:100,images:[[640,1920]],exitBranch:17,sound:"32",branching:{branches:[{frameIndex:10,weight:50}]}},{duration:300,images:[[896,1920]],exitBranch:17},{duration:200,images:[[1024,1920]],exitBranch:17,branching:{branches:[{frameIndex:18,weight:50}]}},{duration:1e3,images:[[128,1920]],exitBranch:20,branching:{branches:[{frameIndex:8,weight:40},{frameIndex:9,weight:40}]}},{duration:500,images:[[256,1920]],exitBranch:17},{duration:1e3,images:[[128,1920]],exitBranch:20,branching:{branches:[{frameIndex:8,weight:80},{frameIndex:18,weight:20}]}},{duration:100,images:[[1280,1920]],exitBranch:21,sound:"30"},{duration:100,images:[[1408,1920]],exitBranch:22},{duration:100,images:[[2432,1920]],exitBranch:23},{duration:100,images:[[1536,1920]],exitBranch:24},{duration:100,images:[[1664,1920]],exitBranch:1},{duration:0}],useExitBranching:!0},Write:{frames:[{duration:100,images:[[0,0]],exitBranch:21,sound:"11",branching:{branches:[{frameIndex:2,weight:100}]}},{duration:50,images:[[0,0]],exitBranch:21},{duration:200,images:[[1792,1920]],exitBranch:19},{duration:200,images:[[1920,1920]],exitBranch:2},{duration:200,images:[[2048,1920]],exitBranch:18},{duration:300,images:[[2176,1920]],exitBranch:17},{duration:300,images:[[2304,1920]],exitBranch:7},{duration:300,images:[[1024,1920]],exitBranch:8},{duration:200,images:[[256,1920]],exitBranch:16},{duration:100,images:[[384,1920]],exitBranch:8},{duration:100,images:[[512,1920]],exitBranch:8,sound:"29"},{duration:100,images:[[640,1920]],exitBranch:8},{duration:150,images:[[896,1920]],exitBranch:8},{duration:200,images:[[384,1920]],exitBranch:8},{duration:100,images:[[1024,1920]],exitBranch:8},{duration:100,images:[[128,1920]],exitBranch:16},{duration:100,images:[[1152,1920]],exitBranch:17,branching:{branches:[{frameIndex:21,weight:100}]}},{duration:100,images:[[1280,1920]],exitBranch:18,sound:"30"},{duration:100,images:[[1408,1920]],exitBranch:19},{duration:100,images:[[1536,1920]],exitBranch:20},{duration:100,images:[[1664,1920]],exitBranch:1},{duration:0}]},Idle3_2:{frames:[{duration:100,images:[[384,640]],exitBranch:23},{duration:100,images:[[512,640]],exitBranch:0},{duration:100,images:[[640,640]],exitBranch:1},{duration:750,images:[[768,640]],exitBranch:2},{duration:100,images:[[640,640]],exitBranch:1},{duration:100,images:[[512,640]],exitBranch:0},{duration:1e3,images:[[384,640]]},{duration:100,images:[[512,640]]},{duration:100,images:[[640,640]],exitBranch:1},{duration:1e3,images:[[768,640]],exitBranch:8},{duration:100,images:[[640,640]],exitBranch:1},{duration:830,images:[[512,640]],exitBranch:0},{duration:100,images:[[768,640]],exitBranch:10,branching:{branches:[{frameIndex:9,weight:20}]}},{duration:100,images:[[896,640]],exitBranch:12},{duration:100,images:[[1024,640]],exitBranch:13},{duration:1250,images:[[1152,640]],exitBranch:14},{duration:300,images:[[1280,640]],exitBranch:14},{duration:1400,images:[[1408,640]],exitBranch:13},{duration:200,images:[[1536,640]],exitBranch:13},{duration:200,images:[[1536,640]],exitBranch:13,branching:{branches:[{frameIndex:21,weight:80}]}},{duration:200,images:[[1280,640]],exitBranch:14,sound:"13",branching:{branches:[{frameIndex:22,weight:100}]}},{duration:200,images:[[1280,640]],exitBranch:14},{duration:1400,images:[[1152,640]],exitBranch:16,branching:{branches:[{frameIndex:16,weight:100}]}},{duration:50,images:[[0,0]]}],useExitBranching:!0},Idle3_1:{frames:[{duration:100,images:[[1664,640]],exitBranch:27,branching:{branches:[{frameIndex:24,weight:50},{frameIndex:2,weight:25}]}},{duration:100,images:[[1792,640]],exitBranch:0,branching:{branches:[{frameIndex:3,weight:100}]}},{duration:100,images:[[1792,640]],exitBranch:0,sound:"16"},{duration:100,images:[[1920,640]],exitBranch:24},{duration:100,images:[[2048,640]],exitBranch:3},{duration:100,images:[[2176,640]],exitBranch:4},{duration:100,images:[[2304,640]],exitBranch:4},{duration:100,images:[[2560,640],[2432,640]],exitBranch:3},{duration:100,images:[[2560,640],[0,768]],exitBranch:3},{duration:100,images:[[2560,640],[128,768]],exitBranch:8},{duration:100,images:[[2560,640],[0,768]],exitBranch:3},{duration:100,images:[[2560,640],[2432,640]],exitBranch:3},{duration:100,images:[[2560,640],[0,768]],exitBranch:3},{duration:100,images:[[2560,640],[128,768]],exitBranch:12},{duration:100,images:[[2560,640],[0,768]],exitBranch:3},{duration:100,images:[[2560,640],[2432,640]],exitBranch:3},{duration:100,images:[[2560,640],[0,768]],exitBranch:3},{duration:100,images:[[2560,640],[128,768]],exitBranch:16},{duration:100,images:[[2560,640],[0,768]],exitBranch:3},{duration:200,images:[[2560,640],[2432,640]],exitBranch:3},{duration:100,images:[[256,768]],exitBranch:21},{duration:100,images:[[384,768]],exitBranch:22},{duration:200,images:[[512,768]]},{duration:300,images:[[640,768]],exitBranch:27,branching:{branches:[{frameIndex:27,weight:100}]}},{duration:1e3,images:[[384,768]],exitBranch:25,sound:"20"},{duration:600,images:[[512,768]],exitBranch:26},{duration:400,images:[[640,768]],exitBranch:27},{duration:50,images:[[0,0]]}]},Congratulate_2:{frames:[{duration:100,images:[[0,0]],exitBranch:19},{duration:100,images:[[768,768]],exitBranch:0},{duration:100,images:[[896,768]],exitBranch:1},{duration:100,images:[[1024,768]],exitBranch:2},{duration:100,images:[[1152,768]],exitBranch:3},{duration:90,images:[[1280,768]],exitBranch:4},{duration:80,images:[[1408,768]],exitBranch:3,sound:"4"},{duration:90,images:[[1280,768]],exitBranch:3},{duration:80,images:[[1408,768]],exitBranch:3,sound:"4"},{duration:80,images:[[1280,768]],exitBranch:3},{duration:80,images:[[1408,768]],exitBranch:3,sound:"28"},{duration:80,images:[[1280,768]],exitBranch:3},{duration:80,images:[[1408,768]],exitBranch:3,sound:"22"},{duration:90,images:[[1280,768]],exitBranch:3},{duration:80,images:[[1408,768]],exitBranch:3,sound:"2"},{duration:80,images:[[1280,768]],exitBranch:3},{duration:80,images:[[1408,768]],exitBranch:3,sound:"28"},{duration:90,images:[[1280,768]],exitBranch:3},{duration:100,images:[[1408,768]],exitBranch:3,sound:"28",branching:{branches:[{frameIndex:19,weight:100}]}},{duration:0}],useExitBranching:!0},StartListening:{frames:[{duration:100,images:[[0,0]],exitBranch:6},{duration:100,images:[[1536,768]],exitBranch:0},{duration:100,images:[[1664,768]],exitBranch:1},{duration:100,images:[[1792,768]],exitBranch:2},{duration:100,images:[[1920,768]],exitBranch:3},{duration:100,images:[[2048,768]],exitBranch:4},{duration:0}],useExitBranching:!0},Idle2_2:{frames:[{duration:100,images:[[2176,768]],exitBranch:15},{duration:100,images:[[2304,768]],exitBranch:0},{duration:100,images:[[2432,768]],exitBranch:1},{duration:100,images:[[2560,768]],exitBranch:2,branching:{branches:[{frameIndex:4,weight:50},{frameIndex:5,weight:25},{frameIndex:6,weight:25}]}},{duration:1e3,images:[[0,896]],exitBranch:3,branching:{branches:[{frameIndex:7,weight:100}]}},{duration:2e3,images:[[0,896]],exitBranch:3,branching:{branches:[{frameIndex:7,weight:100}]}},{duration:3e3,images:[[0,896]],exitBranch:3,branching:{branches:[{frameIndex:7,weight:100}]}},{duration:100,images:[[0,896],[128,896]],exitBranch:3},{duration:100,images:[[0,896],[256,896]],exitBranch:7},{duration:100,images:[[0,896],[128,896]],exitBranch:3},{duration:500,images:[[0,896]],exitBranch:3,branching:{branches:[{frameIndex:6,weight:30}]}},{duration:100,images:[[2560,768]],exitBranch:12},{duration:100,images:[[2432,768]],exitBranch:13},{duration:100,images:[[2304,768]],exitBranch:14},{duration:100,images:[[2176,768]],exitBranch:15},{duration:50,images:[[0,0]]}]},Announce:{frames:[{duration:50,images:[[0,0]],exitBranch:37},{duration:100,images:[[384,896]],exitBranch:36},{duration:100,images:[[512,896]],exitBranch:1},{duration:100,images:[[640,896]],exitBranch:2},{duration:100,images:[[768,896]],exitBranch:3,sound:"5"},{duration:100,images:[[896,896]],exitBranch:4},{duration:100,images:[[1024,896]],exitBranch:5},{duration:100,images:[[1152,896]],exitBranch:6,sound:"5"},{duration:100,images:[[1280,896]],exitBranch:7},{duration:100,images:[[1408,896]],exitBranch:8},{duration:150,images:[[1664,896],[1536,896]],exitBranch:25,sound:"19"},{duration:100,images:[[1664,896],[1792,896]],exitBranch:25},{duration:100,images:[[1664,896],[1920,896]],exitBranch:25},{duration:100,images:[[1664,896],[2048,896]],exitBranch:25},{duration:100,images:[[1664,896],[1792,896]],exitBranch:25},{duration:50,images:[[1664,896],[1920,896]],exitBranch:25},{duration:100,images:[[1664,896],[1536,896]],exitBranch:25},{duration:100,images:[[1664,896],[1792,896]],exitBranch:25},{duration:100,images:[[1664,896],[1920,896]],exitBranch:25},{duration:100,images:[[1664,896],[2048,896]],exitBranch:25},{duration:50,images:[[1664,896],[1920,896]],exitBranch:25},{duration:100,images:[[1664,896],[1536,896]],exitBranch:25},{duration:200,images:[[1664,896],[2048,896]],exitBranch:25},{duration:100,images:[[1664,896],[1792,896]],exitBranch:25},{duration:100,images:[[1664,896],[1920,896]],exitBranch:25},{duration:100,images:[[2176,896]],exitBranch:26},{duration:100,images:[[2304,896]],exitBranch:29},{duration:100,images:[[2432,896]],exitBranch:26},{duration:100,images:[[2560,896]],exitBranch:27,branching:{branches:[{frameIndex:37,weight:100}]}},{duration:100,images:[[0,1024]],exitBranch:30},{duration:100,images:[[128,1024]],exitBranch:31},{duration:100,images:[[256,1024]],exitBranch:32},{duration:100,images:[[768,896]],exitBranch:33,sound:"25"},{duration:100,images:[[640,896]],exitBranch:34},{duration:100,images:[[512,896]],exitBranch:35},{duration:100,images:[[384,896]],exitBranch:36},{duration:100,images:[[0,0]],exitBranch:37,sound:"5"},{duration:0}],useExitBranching:!0},GetAttention:{frames:[{duration:100,images:[[0,0]],exitBranch:10},{duration:100,images:[[384,1024]],exitBranch:0},{duration:50,images:[[512,1024]],exitBranch:1},{duration:50,images:[[640,1024]],exitBranch:2},{duration:100,images:[[768,1024]],exitBranch:3},{duration:400,images:[[896,1024]],exitBranch:4},{duration:100,images:[[1024,1024]],exitBranch:5},{duration:100,images:[[1152,1024]],exitBranch:6,sound:"14"},{duration:100,images:[[1024,1024]],exitBranch:5},{duration:100,images:[[896,1024]],exitBranch:4},{duration:0}]},Idle2_1:{frames:[{duration:100,images:[[1280,1024]],exitBranch:25},{duration:100,images:[[1408,1024]],exitBranch:0},{duration:100,images:[[1536,1024]],exitBranch:1},{duration:100,images:[[1664,1024]],exitBranch:2},{duration:750,images:[[1792,1024]],exitBranch:3},{duration:100,images:[[1792,1024],[1920,1024]],exitBranch:4},{duration:200,images:[[1792,1024],[2048,1024]],exitBranch:5},{duration:100,images:[[1792,1024],[1920,1024]],exitBranch:4},{duration:850,images:[[1792,1024]],exitBranch:3,branching:{branches:[{frameIndex:9,weight:40},{frameIndex:21,weight:35},{frameIndex:4,weight:25}]}},{duration:100,images:[[2176,1024]],exitBranch:8},{duration:100,images:[[2304,1024]],exitBranch:9},{duration:100,images:[[2432,1024]],exitBranch:10},{duration:830,images:[[2560,1024]],exitBranch:17},{duration:100,images:[[2560,1024],[0,1152]],exitBranch:16},{duration:200,images:[[2560,1024],[128,1152]],exitBranch:15},{duration:100,images:[[2560,1024],[0,1152]],exitBranch:16},{duration:750,images:[[2560,1024]],exitBranch:17,branching:{branches:[{frameIndex:12,weight:30}]}},{duration:100,images:[[256,1152]],exitBranch:18},{duration:100,images:[[384,1152]],exitBranch:19},{duration:100,images:[[512,1152]],exitBranch:20},{duration:100,images:[[640,1152]],exitBranch:25,branching:{branches:[{frameIndex:25,weight:100}]}},{duration:200,images:[[1664,1024]]},{duration:100,images:[[1536,1024]]},{duration:100,images:[[1408,1024]]},{duration:100,images:[[1280,1024]]},{duration:50,images:[[0,0]]}]},GestureLeft:{frames:[{duration:100,images:[[0,0]],exitBranch:5},{duration:100,images:[[768,1152]],exitBranch:0},{duration:100,images:[[896,1152]],exitBranch:1},{duration:100,images:[[1024,1152]],exitBranch:2},{duration:100,images:[[1152,1152]],exitBranch:3},{duration:0}],useExitBranching:!0},Surprised:{frames:[{duration:100,images:[[0,0]],exitBranch:7},{duration:100,images:[[1280,1152]],exitBranch:3,sound:"21"},{duration:100,images:[[1408,1152]],exitBranch:3},{duration:100,images:[[1536,1152]],exitBranch:4},{duration:100,images:[[1664,1152]],exitBranch:5,branching:{branches:[{frameIndex:7,weight:100}]}},{duration:100,images:[[1792,1152]],exitBranch:6},{duration:100,images:[[1920,1152]],exitBranch:0},{duration:0}],useExitBranching:!0},GestureRight:{frames:[{duration:100,images:[[0,0]],exitBranch:5},{duration:100,images:[[384,2560]],exitBranch:0},{duration:100,images:[[512,2560]],exitBranch:1},{duration:100,images:[[640,2560]],exitBranch:2},{duration:100,images:[[768,2560]],exitBranch:3},{duration:0}],useExitBranching:!0},Idle1_4:{frames:[{duration:100,images:[[2048,1152]],exitBranch:11},{duration:100,images:[[2176,1152]],exitBranch:0},{duration:100,images:[[2304,1152]],exitBranch:1},{duration:1500,images:[[2432,1152]],exitBranch:2},{duration:200,images:[[2432,1152],[2560,1152]],exitBranch:3},{duration:300,images:[[2432,1152],[0,1280]],exitBranch:4},{duration:200,images:[[2432,1152],[2560,1152]],exitBranch:3},{duration:300,images:[[2432,1152]],exitBranch:8,branching:{branches:[{frameIndex:12,weight:100}]}},{duration:100,images:[[2304,1152]],exitBranch:9},{duration:100,images:[[2176,1152]],exitBranch:10},{duration:100,images:[[2048,1152]],exitBranch:11},{duration:100,images:[[0,0]]},{duration:0}],useExitBranching:!0},LookLeftReturn:{frames:[{duration:100,images:[[128,1280]],exitBranch:1},{duration:100,images:[[256,1280]],exitBranch:2},{duration:100,images:[[384,1280]],exitBranch:3},{duration:100,images:[[0,0]]}]},GestureUp:{frames:[{duration:100,images:[[0,0]],exitBranch:6},{duration:100,images:[[512,1280]],exitBranch:0},{duration:100,images:[[640,1280]],exitBranch:1},{duration:100,images:[[768,1280]],exitBranch:2},{duration:100,images:[[896,1280]],exitBranch:3},{duration:100,images:[[1024,1280]],exitBranch:4},{duration:0}],useExitBranching:!0},Idle1_1:{frames:[{duration:100,images:[[1152,1280]],exitBranch:8},{duration:300,images:[[1280,1280]],exitBranch:0},{duration:1200,images:[[1408,1280]],exitBranch:1},{duration:200,images:[[1280,1280]],exitBranch:2},{duration:1e3,images:[[1152,1280]],exitBranch:5,branching:{branches:[{frameIndex:9,weight:100}]}},{duration:200,images:[[0,0],[1536,1280]],exitBranch:8},{duration:300,images:[[0,0],[1664,1280]],exitBranch:7},{duration:200,images:[[0,0],[1536,1280]],exitBranch:8},{duration:100,images:[[0,0]]},{duration:0}],useExitBranching:!0},Idle1_3:{frames:[{duration:100,images:[[1792,1280]],exitBranch:8},{duration:100,images:[[1920,1280]],exitBranch:0},{duration:1500,images:[[2048,1280]],exitBranch:3,branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[1920,1280]],exitBranch:0},{duration:100,images:[[1792,1280]],exitBranch:3},{duration:200,images:[[0,0],[1536,1280]],exitBranch:8},{duration:300,images:[[0,0],[1664,1280]],exitBranch:7},{duration:200,images:[[0,0],[1536,1280]],exitBranch:8},{duration:100,images:[[0,0]]},{duration:0}],useExitBranching:!0},Idle1_2:{frames:[{duration:100,images:[[2176,1280]],exitBranch:9},{duration:100,images:[[2304,1280]],exitBranch:0},{duration:1200,images:[[2432,1280]],exitBranch:1},{duration:300,images:[[2432,1280],[2560,1280]],exitBranch:2},{duration:300,images:[[2432,1280],[0,1408]],exitBranch:3},{duration:200,images:[[2432,1280],[2560,1280]],exitBranch:6},{duration:200,images:[[2432,1280]],exitBranch:7,branching:{branches:[{frameIndex:10,weight:100}]}},{duration:100,images:[[2304,1280]],exitBranch:0},{duration:100,images:[[2176,1280]],exitBranch:9},{duration:100,images:[[0,0]],exitBranch:10},{duration:0}],useExitBranching:!0},Read:{frames:[{duration:100,images:[[0,0]],exitBranch:19},{duration:100,images:[[128,1408]],exitBranch:18,sound:"11"},{duration:100,images:[[256,1408]],exitBranch:15},{duration:100,images:[[384,1408]],exitBranch:15},{duration:100,images:[[512,1408]],exitBranch:13},{duration:100,images:[[640,1408]],exitBranch:13},{duration:100,images:[[768,1408]],exitBranch:11},{duration:200,images:[[896,1408]],exitBranch:11},{duration:200,images:[[1024,1408]],exitBranch:11},{duration:300,images:[[1152,1408]],exitBranch:11},{duration:300,images:[[1280,1408]],exitBranch:11},{duration:100,images:[[1408,1408]],exitBranch:12},{duration:100,images:[[1536,1408]],exitBranch:13,branching:{branches:[{frameIndex:19,weight:100}]}},{duration:100,images:[[1664,1408]],exitBranch:14,sound:"30"},{duration:100,images:[[1792,1408]],exitBranch:15},{duration:100,images:[[1920,1408]],exitBranch:16},{duration:100,images:[[2048,1408]],exitBranch:17},{duration:100,images:[[2176,1408]],exitBranch:18},{duration:100,images:[[2304,1408]],exitBranch:0},{duration:0}]},Processing:{frames:[{duration:100,images:[[0,0]],exitBranch:27},{duration:100,images:[[2432,1408]],exitBranch:0},{duration:100,images:[[2560,1408]],exitBranch:1},{duration:100,images:[[0,1536]],exitBranch:2},{duration:100,images:[[128,1536]],exitBranch:3,branching:{branches:[{frameIndex:6,weight:100}]}},{duration:100,images:[[128,1536]],exitBranch:3,sound:"30"},{duration:220,images:[[256,1536]],exitBranch:5,sound:"31",branching:{branches:[{frameIndex:8,weight:100}]}},{duration:260,images:[[256,1536]],exitBranch:5,sound:"1"},{duration:220,images:[[384,1536]],exitBranch:5},{duration:210,images:[[512,1536]],exitBranch:5},{duration:220,images:[[640,1536]],exitBranch:5},{duration:210,images:[[768,1536]],exitBranch:5},{duration:210,images:[[896,1536]],exitBranch:5},{duration:210,images:[[1024,1536]],exitBranch:5},{duration:220,images:[[1152,1536]],exitBranch:5},{duration:220,images:[[1280,1536]],exitBranch:5},{duration:230,images:[[1408,1536]],exitBranch:5,branching:{branches:[{frameIndex:6,weight:25},{frameIndex:17,weight:50}]}},{duration:210,images:[[256,1536]],exitBranch:5,sound:"6"},{duration:210,images:[[384,1536]],exitBranch:5},{duration:210,images:[[512,1536]],exitBranch:5},{duration:210,images:[[640,1536],[1536,1536]],exitBranch:19},{duration:220,images:[[768,1536],[1664,1536]],exitBranch:22},{duration:220,images:[[896,1536],[1792,1536]],exitBranch:23},{duration:210,images:[[1024,1536],[1920,1536]],exitBranch:24},{duration:210,images:[[1152,1536]],exitBranch:5},{duration:230,images:[[1280,1536]],exitBranch:5},{duration:230,images:[[1408,1536]],exitBranch:5,branching:{branches:[{frameIndex:6,weight:40},{frameIndex:7,weight:60}]}},{duration:0}],useExitBranching:!0},Wave:{frames:[{duration:100,images:[[0,0]],exitBranch:16},{duration:100,images:[[2176,512]],exitBranch:0},{duration:100,images:[[2304,512]],exitBranch:1},{duration:100,images:[[2432,512]],exitBranch:2},{duration:100,images:[[2560,512]],exitBranch:3},{duration:100,images:[[0,640]],exitBranch:4},{duration:100,images:[[2560,512]],exitBranch:3},{duration:100,images:[[2432,512]],exitBranch:2},{duration:100,images:[[128,640]],exitBranch:2},{duration:100,images:[[256,640]],exitBranch:8},{duration:100,images:[[128,640]],exitBranch:2},{duration:100,images:[[2432,512]],exitBranch:2},{duration:100,images:[[2560,512]],exitBranch:3},{duration:100,images:[[0,640]],exitBranch:4},{duration:100,images:[[2560,512]],exitBranch:3},{duration:100,images:[[2432,512]],exitBranch:2},{duration:0}],useExitBranching:!0},DoMagic1:{frames:[{duration:100,images:[[0,0]],exitBranch:19},{duration:100,images:[[0,1664]],exitBranch:0},{duration:100,images:[[128,1664]],exitBranch:17},{duration:100,images:[[256,1664]],exitBranch:16},{duration:100,images:[[384,1664]],exitBranch:15},{duration:100,images:[[512,1664]],exitBranch:15},{duration:100,images:[[640,1664]],exitBranch:16},{duration:100,images:[[768,1664]],exitBranch:14},{duration:100,images:[[896,1664]],exitBranch:14},{duration:100,images:[[1024,1664]],exitBranch:14},{duration:100,images:[[1152,1664]],exitBranch:12},{duration:100,images:[[1280,1664]],exitBranch:12},{duration:100,images:[[1408,1664]],exitBranch:13,branching:{branches:[{frameIndex:19,weight:100}]}},{duration:100,images:[[1536,1664]],exitBranch:14},{duration:100,images:[[1664,1664]],exitBranch:15},{duration:100,images:[[1792,1664]],exitBranch:16},{duration:100,images:[[1920,1664]],exitBranch:17},{duration:100,images:[[2048,1664]],exitBranch:18},{duration:100,images:[[2176,1664]],exitBranch:0},{duration:0}]},DoMagic2:{frames:[{duration:100,images:[[2304,1664]],exitBranch:11},{duration:80,images:[[2432,1664]],exitBranch:0},{duration:80,images:[[2560,1664]],exitBranch:11},{duration:100,images:[[0,1792]]},{duration:100,images:[[128,1792]],exitBranch:5,branching:{branches:[{frameIndex:18,weight:100}]}},{duration:100,images:[[256,1792]],exitBranch:6,sound:"26"},{duration:100,images:[[384,1792]],exitBranch:7},{duration:100,images:[[512,1792]],exitBranch:8},{duration:100,images:[[640,1792]],exitBranch:9},{duration:100,images:[[768,1792]],exitBranch:10},{duration:100,images:[[896,1792]],exitBranch:15},{duration:100,images:[[1536,1664]],exitBranch:12},{duration:100,images:[[1664,1664]],exitBranch:13},{duration:100,images:[[1792,1664]],exitBranch:14},{duration:100,images:[[1920,1664]],exitBranch:15},{duration:100,images:[[2048,1664]],exitBranch:16},{duration:100,images:[[2176,1664]],exitBranch:17},{duration:100,images:[[0,0]],exitBranch:18},{duration:0}],useExitBranching:!0},LookRight:{frames:[{duration:100,images:[[0,0]],exitBranch:4},{duration:100,images:[[1024,1792]],exitBranch:0},{duration:100,images:[[1152,1792]],exitBranch:1},{duration:50,images:[[1280,1792]],exitBranch:2},{duration:0}]},Alert:{frames:[{duration:30,images:[[0,0]],exitBranch:2},{duration:30,images:[[2560,1536]],exitBranch:0},{duration:0}],useExitBranching:!0},MoveRight:{frames:[{duration:100,images:[[0,0]],exitBranch:19},{duration:100,images:[[128,0]],exitBranch:0},{duration:100,images:[[256,0]],exitBranch:1},{duration:100,images:[[384,0]],exitBranch:2},{duration:100,images:[[512,0]],exitBranch:3},{duration:100,images:[[640,0]],exitBranch:4},{duration:400,images:[[768,0]],exitBranch:4},{duration:100,images:[[1408,1792]],exitBranch:6,sound:"17",branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[1408,1792]],exitBranch:6},{duration:100,images:[[1536,1792]],exitBranch:8},{duration:100,images:[[1664,1792]],exitBranch:9},{duration:100,images:[[1792,1792]],exitBranch:9},{duration:100,images:[[1920,1792]],exitBranch:9},{duration:100,images:[[2048,1792]],exitBranch:18},{duration:100,images:[[2176,1792]],exitBranch:17},{duration:100,images:[[2304,1792]],exitBranch:16,branching:{branches:[{frameIndex:19,weight:100}]}},{duration:200,images:[[2432,1792]],exitBranch:17,sound:"12"},{duration:100,images:[[2560,1792]],exitBranch:18},{duration:100,images:[[0,1920]],exitBranch:6},{duration:0}],useExitBranching:!0},Reading:{frames:[{duration:100,images:[[0,0]],exitBranch:34},{duration:100,images:[[128,1408]],exitBranch:33,sound:"11"},{duration:100,images:[[256,1408]],exitBranch:30},{duration:100,images:[[384,1408]],exitBranch:29},{duration:100,images:[[512,1408]],exitBranch:28},{duration:100,images:[[640,1408]],exitBranch:28},{duration:100,images:[[512,2048]],exitBranch:13,branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[512,2048]],exitBranch:13,branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[512,2048]],exitBranch:13},{duration:200,images:[[640,2048]],exitBranch:13},{duration:200,images:[[768,2048]],exitBranch:27},{duration:100,images:[[896,2048]],exitBranch:13},{duration:300,images:[[1024,2048]],exitBranch:13},{duration:100,images:[[1152,2048]],exitBranch:27},{duration:100,images:[[768,1408]],exitBranch:13},{duration:200,images:[[896,1408]],exitBranch:13,branching:{branches:[{frameIndex:17,weight:100}]}},{duration:900,images:[[1408,1408]],exitBranch:28,sound:"7"},{duration:100,images:[[1024,1408]],exitBranch:27},{duration:200,images:[[1152,1408]],exitBranch:27,branching:{branches:[{frameIndex:19,weight:60},{frameIndex:21,weight:40}]}},{duration:200,images:[[1280,1408]],exitBranch:27,branching:{branches:[{frameIndex:22,weight:70},{frameIndex:20,weight:30}]}},{duration:800,images:[[1408,1408]],exitBranch:27,sound:"23",branching:{branches:[{frameIndex:21,weight:30},{frameIndex:22,weight:70}]}},{duration:1e3,images:[[1280,1408]],exitBranch:27 +},{duration:200,images:[[1280,2048]],exitBranch:27,branching:{branches:[{frameIndex:6,weight:50}]}},{duration:1500,images:[[1280,2048]],exitBranch:27,branching:{branches:[{frameIndex:6,weight:20},{frameIndex:7,weight:20},{frameIndex:8,weight:20}]}},{duration:900,images:[[1408,1408]],exitBranch:28},{duration:50,images:[[1280,2048]],exitBranch:27,sound:"7"},{duration:1e3,images:[[1408,1408]],exitBranch:28,branching:{branches:[{frameIndex:8,weight:100}]}},{duration:100,images:[[1408,1408]],exitBranch:28},{duration:100,images:[[1664,1408]],exitBranch:29,sound:"30"},{duration:100,images:[[1792,1408]],exitBranch:30},{duration:100,images:[[1920,1408]],exitBranch:31},{duration:100,images:[[2048,1408]],exitBranch:32},{duration:100,images:[[2176,1408]],exitBranch:33},{duration:100,images:[[2304,1408]],exitBranch:0},{duration:0}],useExitBranching:!0},GetAttentionContinued:{frames:[{duration:100,images:[[1024,1024]],exitBranch:3},{duration:100,images:[[1152,1024]],exitBranch:0,sound:"14"},{duration:100,images:[[1024,1024]],exitBranch:3},{duration:100,images:[[896,1024]],exitBranch:4,branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[768,1024]],exitBranch:5},{duration:50,images:[[640,1024]],exitBranch:6},{duration:50,images:[[512,1024]],exitBranch:7},{duration:100,images:[[384,1024]],exitBranch:8},{duration:50,images:[[0,0]],exitBranch:9},{duration:0}]},WriteContinued:{frames:[{duration:100,images:[[128,1920]],exitBranch:14},{duration:100,images:[[256,1920]],exitBranch:0},{duration:100,images:[[384,1920]],exitBranch:1},{duration:150,images:[[512,1920]],exitBranch:1,sound:"29"},{duration:200,images:[[640,1920]],exitBranch:1},{duration:100,images:[[768,1920]],exitBranch:1},{duration:150,images:[[896,1920]],exitBranch:1},{duration:200,images:[[384,1920]],exitBranch:1},{duration:100,images:[[640,1920]],exitBranch:1},{duration:100,images:[[512,1920]],exitBranch:1,sound:"32"},{duration:100,images:[[768,1920]],exitBranch:1},{duration:100,images:[[896,1920]],exitBranch:1},{duration:100,images:[[1024,1920]],exitBranch:0},{duration:100,images:[[128,1920]],exitBranch:14},{duration:100,images:[[1152,1920]],exitBranch:15,branching:{branches:[{frameIndex:20,weight:100}]}},{duration:100,images:[[1280,1920]],exitBranch:16,sound:"30"},{duration:100,images:[[1408,1920]],exitBranch:17},{duration:100,images:[[1536,1920]],exitBranch:18},{duration:100,images:[[1664,1920]],exitBranch:19},{duration:50,images:[[0,0]],exitBranch:20},{duration:0}]},Confused:{frames:[{duration:100,images:[[0,0]],exitBranch:14},{duration:100,images:[[1152,2432]],exitBranch:0},{duration:100,images:[[1280,2432]],exitBranch:1},{duration:100,images:[[1408,2432]],exitBranch:2},{duration:100,images:[[1536,2432]],exitBranch:3},{duration:100,images:[[1792,2432],[1664,2432]],exitBranch:4},{duration:100,images:[[1792,2432],[2048,2432],[1920,2432]],exitBranch:5,sound:"24"},{duration:100,images:[[1792,2432],[2304,2432],[2176,2432]],exitBranch:6},{duration:100,images:[[1792,2432],[1664,2432],[2176,2432]],exitBranch:6},{duration:100,images:[[1792,2432],[2048,2432],[2176,2432]],exitBranch:6,sound:"24"},{duration:100,images:[[1792,2432],[2304,2432],[1920,2432]],exitBranch:5},{duration:100,images:[[1792,2432],[1664,2432]],exitBranch:4},{duration:100,images:[[1792,2432],[2048,2432]],exitBranch:4,sound:"24"},{duration:100,images:[[1792,2432],[2304,2432]],exitBranch:4},{duration:0}],useExitBranching:!0},LookRightBlink:{frames:[{duration:100,images:[[1280,1792],[256,2304]],exitBranch:4},{duration:100,images:[[1280,1792],[384,2304]],exitBranch:2},{duration:100,images:[[1280,1792],[256,2304]],exitBranch:4},{duration:4e3,images:[[1280,1792]],exitBranch:5},{duration:100,images:[[1280,1792]],exitBranch:5,branching:{branches:[{frameIndex:8,weight:100}]}},{duration:100,images:[[1152,1792]],exitBranch:6},{duration:100,images:[[1024,1792]],exitBranch:7},{duration:100,images:[[0,0]],exitBranch:8},{duration:0}]},Search:{frames:[{duration:100,images:[[0,0]]},{duration:150,images:[[256,512]],sound:"11",branching:{branches:[{frameIndex:3,weight:100}]}},{duration:100,images:[[256,512]]},{duration:100,images:[[384,512]]},{duration:100,images:[[512,512]]},{duration:100,images:[[640,512]]},{duration:100,images:[[768,512]]},{duration:100,images:[[896,512]]},{duration:300,images:[[1024,512]],branching:{branches:[{frameIndex:10,weight:90}]}},{duration:100,images:[[1024,512]]},{duration:150,images:[[1024,512]]},{duration:150,images:[[1152,512]]},{duration:150,images:[[1280,512]]},{duration:150,images:[[1408,512]]},{duration:150,images:[[1536,512]]},{duration:100,images:[[1664,512]],branching:{branches:[{frameIndex:9,weight:33},{frameIndex:17,weight:33}]}},{duration:1800,images:[[896,512]],sound:"8",branching:{branches:[{frameIndex:18,weight:100}]}},{duration:2300,images:[[896,512]],sound:"27"},{duration:100,images:[[768,512]]},{duration:100,images:[[640,512]]},{duration:100,images:[[512,512]]},{duration:100,images:[[384,512]]},{duration:150,images:[[256,512]]},{duration:100,images:[[0,0]],exitBranch:23}]},Uncertain:{frames:[{duration:100,images:[[0,0]],exitBranch:5},{duration:100,images:[[0,2048]],exitBranch:0},{duration:100,images:[[128,2048]],exitBranch:1},{duration:100,images:[[256,2048]],exitBranch:2},{duration:100,images:[[384,2048]],exitBranch:3},{duration:0}],useExitBranching:!0},LookLeft:{frames:[{duration:100,images:[[0,0]],exitBranch:4},{duration:100,images:[[384,1280]],exitBranch:0},{duration:100,images:[[256,1280]],exitBranch:1},{duration:100,images:[[128,1280]],exitBranch:2},{duration:0}]},LookDownReturn:{frames:[{duration:100,images:[[2048,512]],exitBranch:1},{duration:100,images:[[1920,512]],exitBranch:2},{duration:100,images:[[1792,512]],exitBranch:3},{duration:100,images:[[0,0]]}]},Hearing_4:{frames:[{duration:30,images:[[1408,2048]],exitBranch:5},{duration:3250,images:[[1536,2048]],exitBranch:0},{duration:100,images:[[1536,2048],[1664,2048]],exitBranch:0},{duration:200,images:[[1536,2048],[1792,2048]],exitBranch:2},{duration:100,images:[[1536,2048],[1664,2048]],exitBranch:0,branching:{branches:[{frameIndex:1,weight:100}]}},{duration:50,images:[[0,0]]}]},LookUpReturn:{frames:[{duration:100,images:[[1920,2048]],exitBranch:1},{duration:100,images:[[2048,2048]],exitBranch:2},{duration:100,images:[[2176,2048]],exitBranch:3},{duration:100,images:[[0,0]]}]},Hearing_1:{frames:[{duration:30,images:[[2560,2048]],exitBranch:5},{duration:3500,images:[[0,2176]],exitBranch:0,branching:{branches:[{frameIndex:1,weight:10}]}},{duration:100,images:[[0,2176],[128,2176]],exitBranch:0},{duration:200,images:[[0,2176],[256,2176]],exitBranch:2},{duration:100,images:[[0,2176],[128,2176]],exitBranch:0,branching:{branches:[{frameIndex:1,weight:100}]}},{duration:100,images:[[0,0]]}]},Greet:{frames:[{duration:100,images:[[0,0]],exitBranch:14},{duration:50,images:[[384,2176]],exitBranch:0,sound:"3"},{duration:50,images:[[512,2176]],exitBranch:13},{duration:100,images:[[640,2176]],exitBranch:2},{duration:100,images:[[768,2176]],exitBranch:3},{duration:100,images:[[896,2176]],exitBranch:4},{duration:100,images:[[1024,2176]],exitBranch:4},{duration:50,images:[[1152,2176]],exitBranch:3},{duration:400,images:[[1280,2176]],exitBranch:3,branching:{branches:[{frameIndex:12,weight:50}]}},{duration:50,images:[[1280,2176],[1408,2176]],exitBranch:8},{duration:100,images:[[1280,2176],[1536,2176]],exitBranch:9},{duration:50,images:[[1280,2176],[1408,2176]],exitBranch:8},{duration:100,images:[[1280,2176]],exitBranch:3,branching:{branches:[{frameIndex:14,weight:100}]}},{duration:100,images:[[384,2176]],exitBranch:0},{duration:0}],useExitBranching:!0},Hearing_3:{frames:[{duration:30,images:[[1664,2176]],exitBranch:5},{duration:3400,images:[[1792,2176]],exitBranch:0},{duration:100,images:[[1792,2176],[1920,2176]],exitBranch:0},{duration:200,images:[[1792,2176],[2048,2176]],exitBranch:2},{duration:100,images:[[1792,2176],[1920,2176]],exitBranch:0,branching:{branches:[{frameIndex:1,weight:100}]}},{duration:100,images:[[0,0]]}]},WriteReturn:{frames:[{duration:100,images:[[1408,1920]],exitBranch:1,sound:"30"},{duration:100,images:[[2432,1920]],exitBranch:2},{duration:100,images:[[1536,1920]],exitBranch:3},{duration:100,images:[[1664,1920]],exitBranch:4},{duration:50,images:[[0,0]]}]},Hearing_2:{frames:[{duration:30,images:[[2176,2176]],exitBranch:5},{duration:3500,images:[[2304,2176]],exitBranch:0,branching:{branches:[{frameIndex:1,weight:10}]}},{duration:100,images:[[2304,2176],[2432,2176]],exitBranch:0},{duration:200,images:[[2304,2176],[2560,2176]],exitBranch:2},{duration:100,images:[[2304,2176],[2432,2176]],exitBranch:0,branching:{branches:[{frameIndex:1,weight:100}]}},{duration:100,images:[[0,0]]}]},GetAttentionReturn:{frames:[{duration:100,images:[[768,1024]]},{duration:100,images:[[640,1024]]},{duration:100,images:[[512,1024]]},{duration:100,images:[[384,1024]]},{duration:100,images:[[0,0]]}]},RestPose:{frames:[{duration:100,images:[[0,0]]}]},LookDownBlink:{frames:[{duration:100,images:[[2048,512],[0,2304]]},{duration:100,images:[[2048,512],[128,2304]]},{duration:100,images:[[2048,512],[0,2304]]},{duration:4e3,images:[[2048,512]],exitBranch:5},{duration:100,images:[[2048,512]],branching:{branches:[{frameIndex:8,weight:100}]}},{duration:100,images:[[1920,512]],exitBranch:6},{duration:100,images:[[1792,512]],exitBranch:7},{duration:100,images:[[0,0]],exitBranch:8},{duration:0}]},LookUpBlink:{frames:[{duration:100,images:[[1920,2048],[2304,2048]],exitBranch:4},{duration:100,images:[[1920,2048],[2432,2048]],exitBranch:2},{duration:100,images:[[1920,2048],[2304,2048]],exitBranch:4},{duration:4e3,images:[[1920,2048]],exitBranch:5},{duration:100,images:[[1920,2048]],exitBranch:5,branching:{branches:[{frameIndex:8,weight:100}]}},{duration:100,images:[[2048,2048]],exitBranch:6},{duration:100,images:[[2176,2048]],exitBranch:7},{duration:100,images:[[0,0]],exitBranch:8},{duration:0}]},Think:{frames:[{duration:100,images:[[0,0]],exitBranch:6},{duration:100,images:[[1664,256]],exitBranch:0},{duration:100,images:[[1792,256]],exitBranch:1},{duration:200,images:[[1920,256]],exitBranch:2},{duration:200,images:[[2048,256]],exitBranch:3},{duration:100,images:[[2176,256]],exitBranch:4},{duration:0}],useExitBranching:!0},Blink:{frames:[{duration:100,images:[[0,0],[1536,1280]]},{duration:300,images:[[0,0],[1664,1280]]},{duration:100,images:[[0,0],[1536,1280]]},{duration:50,images:[[0,0]]}]},Show:{frames:[{duration:100,images:[[512,2304]]},{duration:100,images:[[640,2304]],sound:"18"},{duration:100,images:[[768,2304]]},{duration:100,images:[[896,2304]]},{duration:100,images:[[1024,2304]]},{duration:100,images:[[1152,2304]]},{duration:100,images:[[1280,2304]]},{duration:100,images:[[1408,2304]]},{duration:100,images:[[1536,2304]],exitBranch:9},{duration:100,images:[[0,0]]}]},LookRightReturn:{frames:[{duration:100,images:[[1280,1792]],exitBranch:1},{duration:100,images:[[1152,1792]],exitBranch:2},{duration:100,images:[[1024,1792]],exitBranch:3},{duration:100,images:[[0,0]]}]},StopListening:{frames:[{duration:100,images:[[0,0]],exitBranch:6},{duration:100,images:[[1664,2304]],exitBranch:0},{duration:100,images:[[1792,2304]],exitBranch:1},{duration:100,images:[[1920,2304]],exitBranch:2},{duration:100,images:[[2048,2304]],exitBranch:3},{duration:100,images:[[2176,2304]],exitBranch:4},{duration:0}],useExitBranching:!0},MoveDown:{frames:[{duration:100,images:[[0,0]],exitBranch:20},{duration:100,images:[[128,0]],exitBranch:0},{duration:100,images:[[256,0]],exitBranch:1},{duration:100,images:[[384,0]],exitBranch:2},{duration:100,images:[[512,0]],exitBranch:3},{duration:100,images:[[640,0]],exitBranch:4},{duration:400,images:[[768,0]],exitBranch:4},{duration:100,images:[[2304,2304]],exitBranch:5,sound:"17",branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[2304,2304]],exitBranch:6},{duration:100,images:[[2432,2304]],exitBranch:8},{duration:100,images:[[2560,2304]],exitBranch:9},{duration:100,images:[[0,2432]],exitBranch:9},{duration:100,images:[[128,2432]],exitBranch:9},{duration:100,images:[[256,2432]],exitBranch:9},{duration:100,images:[[384,2432]],exitBranch:15,branching:{branches:[{frameIndex:20,weight:100}]}},{duration:100,images:[[512,2432]],exitBranch:16,sound:"12"},{duration:100,images:[[640,2432]],exitBranch:17},{duration:100,images:[[768,2432]],exitBranch:18},{duration:100,images:[[896,2432]],exitBranch:19},{duration:100,images:[[1024,2432]],exitBranch:5},{duration:0}],useExitBranching:!0},ReadContinued:{frames:[{duration:100,images:[[1408,1408]],exitBranch:14},{duration:200,images:[[1280,1408]],exitBranch:0},{duration:100,images:[[1152,1408]],exitBranch:0},{duration:100,images:[[640,2048]],exitBranch:0},{duration:200,images:[[768,1408]],exitBranch:0},{duration:300,images:[[640,2048]],exitBranch:0},{duration:300,images:[[768,2048]],exitBranch:0},{duration:300,images:[[896,2048]],exitBranch:0},{duration:400,images:[[1024,2048]],exitBranch:0},{duration:100,images:[[1152,2048]],exitBranch:0},{duration:100,images:[[768,1408]],exitBranch:0},{duration:250,images:[[896,1408]],exitBranch:14},{duration:300,images:[[1024,1408]],exitBranch:0},{duration:100,images:[[1408,1408]],exitBranch:14},{duration:100,images:[[1536,1408]],exitBranch:15,branching:{branches:[{frameIndex:22,weight:100}]}},{duration:100,images:[[1664,1408]],exitBranch:16,sound:"30"},{duration:100,images:[[1792,1408]],exitBranch:17},{duration:100,images:[[1920,1408]],exitBranch:18},{duration:100,images:[[2048,1408]],exitBranch:19},{duration:100,images:[[2176,1408]],exitBranch:20},{duration:100,images:[[2304,1408]],exitBranch:21},{duration:50,images:[[0,0]],exitBranch:22},{duration:0}]},LookDown:{frames:[{duration:100,images:[[0,0]],exitBranch:4},{duration:100,images:[[1792,512]],exitBranch:0},{duration:100,images:[[1920,512]],exitBranch:1},{duration:100,images:[[2048,512]],exitBranch:2},{duration:0}]},Sad:{frames:[{duration:100,images:[[0,0]],exitBranch:5},{duration:100,images:[[2048,1536]],exitBranch:0},{duration:100,images:[[2176,1536]],exitBranch:1},{duration:100,images:[[2304,1536]],exitBranch:2},{duration:100,images:[[2432,1536]],exitBranch:3},{duration:0}],useExitBranching:!0},Process:{frames:[{duration:100,images:[[0,0]]},{duration:100,images:[[2432,1408]]},{duration:100,images:[[2560,1408]]},{duration:100,images:[[0,1536]]},{duration:100,images:[[128,1536]],branching:{branches:[{frameIndex:6,weight:100}]}},{duration:100,images:[[128,1536]],sound:"30"},{duration:220,images:[[256,1536]],sound:"31",branching:{branches:[{frameIndex:8,weight:100}]}},{duration:260,images:[[256,1536]],sound:"1"},{duration:220,images:[[384,1536]]},{duration:210,images:[[512,1536]]},{duration:220,images:[[640,1536]]},{duration:210,images:[[768,1536]]},{duration:210,images:[[896,1536]]},{duration:210,images:[[1024,1536]]},{duration:220,images:[[1152,1536]]},{duration:220,images:[[1280,1536]]},{duration:230,images:[[1408,1536]],branching:{branches:[{frameIndex:6,weight:33},{frameIndex:17,weight:33}]}},{duration:210,images:[[256,1536]],sound:"6"},{duration:210,images:[[384,1536]]},{duration:210,images:[[512,1536]]},{duration:210,images:[[640,1536],[1536,1536]]},{duration:220,images:[[768,1536],[1664,1536]]},{duration:220,images:[[896,1536],[1792,1536]]},{duration:210,images:[[1024,1536],[1920,1536]]},{duration:210,images:[[1152,1536]]},{duration:230,images:[[1280,1536]]},{duration:230,images:[[1408,1536]],branching:{branches:[{frameIndex:6,weight:30}]}},{duration:100,images:[[128,1536]],exitBranch:28,sound:"30"},{duration:100,images:[[128,1536]],exitBranch:29},{duration:100,images:[[0,1536]],exitBranch:30},{duration:100,images:[[2560,1408]],exitBranch:31},{duration:100,images:[[2432,1408]],exitBranch:32},{duration:100,images:[[0,0]]}]},LookUp:{frames:[{duration:100,images:[[0,0]],exitBranch:4},{duration:100,images:[[2176,2048]],exitBranch:0},{duration:100,images:[[2048,2048]],exitBranch:1},{duration:50,images:[[1920,2048]],exitBranch:2},{duration:0}]},GestureDown:{frames:[{duration:100,images:[[0,0]],exitBranch:6},{duration:100,images:[[2432,2432]],exitBranch:0},{duration:100,images:[[2560,2432]],exitBranch:1},{duration:100,images:[[0,2560]],exitBranch:2},{duration:100,images:[[128,2560]],exitBranch:3},{duration:100,images:[[256,2560]],exitBranch:4},{duration:0}],useExitBranching:!0},ReadReturn:{frames:[{duration:100,images:[[2560,1920]],exitBranch:1},{duration:100,images:[[1664,1408]],exitBranch:2,sound:"30"},{duration:100,images:[[1792,1408]],exitBranch:3},{duration:100,images:[[1920,1408]],exitBranch:4},{duration:100,images:[[2048,1408]],exitBranch:5},{duration:100,images:[[2176,1408]],exitBranch:6},{duration:100,images:[[2304,1408]],exitBranch:7},{duration:100,images:[[0,0]]}]},Searching:{frames:[{duration:100,images:[[0,0]],exitBranch:19},{duration:150,images:[[256,512]],exitBranch:0,sound:"11",branching:{branches:[{frameIndex:3,weight:100}]}},{duration:100,images:[[256,512]],exitBranch:0},{duration:100,images:[[384,512]],exitBranch:2},{duration:100,images:[[512,512]],exitBranch:3},{duration:100,images:[[640,512]],exitBranch:4},{duration:100,images:[[768,512]],exitBranch:5},{duration:100,images:[[896,512]],exitBranch:6},{duration:300,images:[[1024,512]],exitBranch:7,branching:{branches:[{frameIndex:10,weight:90}]}},{duration:100,images:[[1024,512]],exitBranch:7},{duration:150,images:[[1024,512]],exitBranch:7},{duration:150,images:[[1152,512]],exitBranch:7},{duration:150,images:[[1280,512]],exitBranch:7},{duration:150,images:[[1408,512]],exitBranch:7},{duration:150,images:[[1536,512]],exitBranch:7},{duration:100,images:[[1664,512]],exitBranch:7,branching:{branches:[{frameIndex:9,weight:85},{frameIndex:17,weight:5},{frameIndex:18,weight:5}]}},{duration:100,images:[[1664,512]],exitBranch:7,branching:{branches:[{frameIndex:9,weight:100}]}},{duration:1800,images:[[896,512]],exitBranch:7,sound:"8",branching:{branches:[{frameIndex:9,weight:100}]}},{duration:2300,images:[[896,512]],exitBranch:6,sound:"27",branching:{branches:[{frameIndex:9,weight:100}]}},{duration:0}],useExitBranching:!0},MoveUp:{frames:[{duration:100,images:[[0,0]],exitBranch:18},{duration:100,images:[[128,0]],exitBranch:0},{duration:100,images:[[256,0]],exitBranch:1},{duration:100,images:[[384,0]],exitBranch:2},{duration:100,images:[[512,0]],exitBranch:3},{duration:100,images:[[640,0]],exitBranch:4},{duration:400,images:[[768,0]],exitBranch:5},{duration:100,images:[[896,2560]],exitBranch:5,sound:"17",branching:{branches:[{frameIndex:9,weight:100}]}},{duration:100,images:[[896,2560]],exitBranch:6},{duration:100,images:[[1024,2560]],exitBranch:8},{duration:100,images:[[1152,2560]],exitBranch:9},{duration:100,images:[[1280,2560]],exitBranch:9},{duration:100,images:[[1408,2560]],exitBranch:9},{duration:100,images:[[1536,2560]],exitBranch:9},{duration:100,images:[[1664,2560]],exitBranch:15,branching:{branches:[{frameIndex:18,weight:100}]}},{duration:100,images:[[1792,2560]],exitBranch:16,sound:"12"},{duration:100,images:[[1920,2560]],exitBranch:17},{duration:100,images:[[2048,2560]],exitBranch:5},{duration:0}],useExitBranching:!0},LookLeftBlink:{frames:[{duration:100,images:[[128,1280],[2176,2560]],exitBranch:4},{duration:100,images:[[128,1280],[2304,2560]],exitBranch:2},{duration:100,images:[[128,1280],[2176,2560]],exitBranch:4},{duration:4e3,images:[[128,1280]],exitBranch:5},{duration:100,images:[[128,1280]],exitBranch:5,branching:{branches:[{frameIndex:8,weight:100}]}},{duration:100,images:[[256,1280]],exitBranch:6},{duration:100,images:[[384,1280]],exitBranch:7},{duration:100,images:[[0,0]],exitBranch:8},{duration:0}]}}}); \ No newline at end of file diff --git a/public/agents/Merlin/map.png b/public/agents/Merlin/map.png new file mode 100644 index 00000000..7b6bd68c Binary files /dev/null and b/public/agents/Merlin/map.png differ diff --git a/public/agents/Merlin/sounds-mp3.js b/public/agents/Merlin/sounds-mp3.js new file mode 100644 index 00000000..cb90367c --- /dev/null +++ b/public/agents/Merlin/sounds-mp3.js @@ -0,0 +1,3 @@ +clippy.soundsReady("Merlin",{1:"data:audio/mpeg;base64,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",2:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAQAAAI+AHNzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc6KioqKioqKioqKioqKioqKioqKioqKioqLR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR/////////////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQADggG2v9BGACpl4jREoAW3WgicPqD8oCDooGNQISgIOwQ5R3Lh/LvKAhBAEP5eIHf/+IHXf/g//n///g+D5+XD6pJVL4j8yNib+O4tE9JZWrx2jxJEep5kq21Y7RMRBhPQtwn/+MixCIbK1a8AYFoAGoyqSRUtBBMuApwWQGSUgR0J4E9MTJZ0vJJJHUkmeJsS5OPl2tV/V1f6nUksySMXX////vpOkktJ3Uksy9fV///3RspGZIqMpFMjYIABBBBAAqe5vMnmKBLfpOr/+MgxBEYM2qENYVoAf6R0kiWL3/qLw4icSw+//hyhuCarRJpe//y+eRJIpJE00Lv//6KCJkZpJGaRkSxl///+ma6jFAvIqSU5Ir/////NXSMWMjYxUs0JE0TcxbJTEFNRTMuOTkuNVX/4yLECwAAA0gBwAAAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=",3:"data:audio/mpeg;base64,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",4:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAQAAAI+AHNzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc6KioqKioqKioqKioqKioqKioqKioqKioqLR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR/////////////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQAErmW0kVBGADWJuMgErmMbwDfIGQALx/+/+7u/6O56InoiIie6Im7uccW78RPiIiFuITd3c0AxZ8QHCjl1A/BA4UBA5gh/g///8H/8EDmj/WD/D4AQCAQCASCSOSSQCAQD9l7/+MixBAYse8eX4E4Aj/b9R0oNf8TDIjDIpT/jYqJIihKESKympZDrmDYfEYqWEVJs0y6P4SAHAuF4tFI4SEY1h01Psv5IdOBUFRCWZ/8t1gIRCb//gUqAQkGjwdBoTCWXx/S92f9SZgY/+MgxAkWkf6UAYFoANFH5qPUqMySUkp/0jYli4mDp0q0fzImDxIpmTH0ll01/xKVDUS6BqmXlJOZF5LR/9aaKKR5ixwP7BYO/yIsOgI6J//+cAoYMnGpKHRXYWUe5apMQU1FMy45OS7/4yLECQAAA0gBwAAANaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=",5:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/zUMAAAAAAAAAAAABJbmZvAAAABwAAAAgAAAPGAD4+Pj4+Pj4+Pj4+PllZWVlZWVlZWVlZWXV1dXV1dXV1dXV1dXWRkZGRkZGRkZGRkZGsrKysrKysrKysrKysyMjIyMjIyMjIyMjI5OTk5OTk5OTk5OTk5P///////////////wD/80DEABE4dmQBSRgBAGCZPRAKBQKBQKxWTo0aNGjRoECBiEIQgLcPDw8MAAAAAA8PDw8MAAAAAA8PDw8MAAAAAA8PDw9IAAAAIDx/////v//0adFgE7ZAdLMakra3AjLwUAL7Y8gx+P/zQsQWGWJOlAGaaABCNM28lC+4F6Thxc+XGHsZEv5LmBooS4YIdpl9DTmZcMTAz/oNpumkiYIspv+9NTILdzI+Xj5dRNTL//0G1W06SNbJLRMUBx7//oMpCRZpKgpJLrdt//9gQI8ozP/zQMQMF0pqyl/PGAJK6MzQ3rE+ko/g1m3CjM08ma1hFHK6qFMl+wMyqTqvlVVVJVzVfL2KGfabalI06ZEZxoyEs5IUm/5xNG4S3+eWlLuUzXt2ZhSWFTBxSv+Lf/XVAJp57+oORXAg//NCxAkW6JZtlNpGTAFXQJugnbMAmhgkSRhhyAyfVZvhKFiNIKDCRJJFwRlWmB9ZkOhsOiAaYnGgZo9RkobU8MrfGEmnguAQMAk64AeKuMC79M4UU3a7JK6kluokB/yaCKb6fGbxYGTF//NAxAkV8Opk8tmGcHURt4wm5At+tQviABIJ8L6415bxU6uPXzImSGSgqQMrnwWbQScSS6HibTYlcNB56AOGlCq+8Nb3CGtOZrqlQALpoIH77XKYLgC52lpZnlIklqlsdmAOg67zw9P/80LEDBSoumTzWhgAk7LbeFSrhnOVu2v5yzS/exyKtHc2Wv53oLULhRLws4oKPeKwEsgROl3v4TmVF5ONWA2TKELLElU/12FNGFz+Yywr3o8WpKUT3ix8gJiJopq3p5akD3JnPUARiW7/80DEFRahInABmUAAr8IxQPCTY/EcgxTeUjE6GD5Xmf9DD4ReV+f/PgEygXOiVazmoExA8MnSs7/UfBNB8BkJ0rOo9gvUZKHy6klzX7aUf61B9UG6ISIaWGAcyihobOwqFl1a3rmC9v/zQsQVEOBZ3APPGACurBU7lQV4Kuyoa4Kuyp3iXrO8S6Fnc6Jcqs7nRLiUqdzoK4lWd3Ev50llVuzpJUxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==",6:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jKMAAAAAAAAAAAABJbmZvAAAABwAAACAAAAmQABYWFh4eHiUlJS0tLTQ0NDw8PENDQ0tLS1JSUlJaWlphYWFpaWlwcHB4eHiAgICHh4ePj4+PlpaWnp6epaWlra2ttLS0vLy8w8PDy8vLy9LS0tra2uHh4enp6fDw8Pj4+P///wAAAAAAAAAAAAAAAAAAAP/jGMQADGiiiflGEACAAAOAf9s/eyBAgQIEIw8AECERERF3d3d3ZAghBzKAgCAIB+D4P////8u/hj//Lv1Agv6rf3bZ/taYJv/jGMQJDukmqAGMYACHh4ekoSjd4TxLCGV9uesJTLc7lVtbR6nj6FuOoqIh+U0uVt3dMu//d3y+1/06oIG1K0W0h1B2igB/hP/jGMQIDTj2wlnIGACpsgYKgHEAVAXD8sXRZ1mmOZ0HmnK1V1MbM0qtc7MrAQpQqA0DAPOX/v1f/9C1SWS20AAfzZmcLUvGOP/jGMQODUCi7lgSWCZCCJQTCEgP1Cs/PCuNQsBuZA0NDgwc4PlAAGBfBUsDIxlzrlW6Eei3Av/AH9DshWAkcIIDmHAFAxQ87f/jGMQUDRjCrZAQ0igNdQEa5I0eyoLIiLRCTJoQ0DUwFCSUr1cl9UvPvbRu/TUf/srDXD0ByvHSDGCJCRljyz7YEMdMe3Cbxv/jGMQaC6jOnABDzChFqNR12OHEWHESBA1bK5uXH4QKpGLw+/x/9E0YOOUCgTgJ8qMlV0owAkPJmpXJzgllRKfiwvc97yvvgv/jGMQmDPD6uYAbDEgZ2yO7vMogLFF03rf0orG5ZbbQAB5zk9mKHWMYHBkEEGNXRxbQIGfUTXadCBhQo1Q8ndkSgb4sWbdNtP/jGMQtDSkK8lgYxkoqWZN1BIe7VXAYiCxBGRBAjDchQQg+48lNvMsriukUkyVEuKTxwNE4pcqS0aC5Q3DQNAUOiUNRbZ9rNf/jGMQzDQjGmAAKUij0Kr/kznzsNnffKgjAkI5zj4OeOwODzbXibGMWtnQWPGILFCtSYKGDhEHEDjCzig2QetqKuqszkaUkkv/jGMQ5DTjyoABjxkhHMz9PrA6dGZJFQLhItEcZwMDDixzjmgUWlYZLIyk6DGCrxHNx8kmqgIgNRlf99SoX9gATOVoVmy0MiP/jGMQ/DMkO3kBgxk7n7CwnBUIxkExfBqKi2HoNhBEElIDx4OaCYGCtimNhZA8Xjt9afVVX9a7l8H03vYVb0em22SRsgBuDmP/jGMRGDTDKnAALGChEk0qk9ClJW8aQli5ANnLwlaTWiCbSSBGQe+y03xbaIP11oIGqAB/qx6C0cqgIwEmGFgh9VThKO5F2x//jGMRMDJjSsMgSUigShzCBFQYxal+1jiWCgg4cQLBN1wrhi65Yb7wKkSaTkkgAFeZFDnDhMTggLJhGoA5RCTyZAgYiJLSUw//jGMRUDTkKsZARhkglqMTRwSsHhFZcIi1Ei8MV//+zVfB///C2nUhGqphTwrXafWBVE5HYnl4KBaOrY+k1gRi+IodE8dRBHv/jGMRaDKjO1lgRhioH8Wh8IodDSNRCHk6MVShew3weSMNNF2Mz15oq8Wuvt2oSPUhoVMuAzQKwyFQyAhYTmXJ6FUB4+aX9ov/jGMRiFtECsZATGEjGzBZpW5Up4WxCVeCYrQENRc4ttNDJlREKiblkTLOF1z3ZCEEeTIYenziSgYUpi1PsrE6xfLOUz+Cqhf/jGMRBEMjulMh6TEgqDqqBQv/Ab/zNvKulpJoXCIATQK8faaaVlXvG15PHfsavZ2eO5n1LfUqa1ZEmKUOEIVZLCpNkgNBhz//jGMQ4EtD2pZBL0kmD0acdz8TQnIauup/sbvz/dVb8qvAUJJIAG/3ZLUca700A4EcSehk3SWxBJYoKKQDEEzyYDAdwQjO73v/jGMQnD/jurbhCDEi9pwgJw8PCbxoeVb2KUeioXZfW3yJIiq7X8kX/AFv/nbtNLdqNRBi8HKEYG4lqS5xPZLbJbcbLh4FDBP/jGMQiD4DutZAzEkjtKdtt684sqxjS0Y01w0TAhk0ZUuYqq9w1xerrFtadtttA/2ihxdAEFQ+cJRMAgFS732btr8u9bmjwBv/jGMQfD/Di9lgo2EpAkQSUSSUYvRReEUUGu+6uglZ2+wJPFtjkNHkQkLgIWFmXoO2rdr8/6xUkbHZFgTBBxB04BjhgiCJXjP/jGMQaDMjuraAwzEl9qUdA03pmtbxSelQgD8FIDnfq5n6XFMtqqnJBfH+UiNVk545RLiINyKbAcHs3JA+JBEHQIGBiJs3JHP/jGMQhDKjCpYATDCklpGEjtNhqm16oGt39ST9//dWhAOSSAA3OdcgTA8IDDCEOEgICukRCz+0r3Gh4wjUsCBiD8EAUBF73w//jGMQpDNimqbgo0iQNahzfNvq/45WlavXXrXPrJfTCoV45h7h0AigwD1FsNBWKxOGg4Q49961untVq01qSkFUkgW2PMtrRjv/jGMQwDSj6rMB7zEp6YOUJXR/ZUYxgsS3jY1JBOdJwJCUTi0CIUjkpMvOUhmWCecLiwdn9233iccg2JFihuFJVqNBf+u37Qf/jGMQ2DDiuoAArGCR4XpEIIiSUA48DsPEcZiXDwtGJ0huKlsCR9CLY+CGIAdpF2oS+BnNt/buUjn4zIZXt6gt/XoqWWpO22//jGMRADqECoAAzGEhAvPzm7sQkKolBi16RSpVqT39/HSu8Qjr2Gsvwwe6sahUtNseLU5K5P9tCXy47/01e+5+afNqMWTATB//jGMRADSkC5lgZhkpnBS0uJ3lwTe+ozrq4igksFAKDcs46iRKt1IieW+icwPD35h7Qqq8Af//n/uMYxVISVRwWbLLpJiYkQP/jGMRGC7jmoABaTElkVyIEC4rFaA4YcKQRDRsTRLSKBQeCoLB3tG66f10KDbQIPN3YVqEkzmHFz6ovEAdVU5kkhOSEjtbJpv/jGMRSDSiWsZFJGABmQT4ANwbYb4n58xYyElBbRy/g5RNhZokEpEkiXiRNVonUv+aI1mZqZGCRjqMl1I2/+YmA6HQmKWcFf//jGMRYGEG6iMmMaAAXEIMvcKnv/+kTAQowoCrASAQEYUBEwCIMKNQEJBwGlhpX2qPCVbhFO54qTEFNRTMuOTkuNaqqqqqqqv/jGMQyCHhhuCHDGACqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==",7:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAA8AAAa7ACYmJiYmJjY2NjY2NjZFRUVFRUVVVVVVVVVVZGRkZGRkZHR0dHR0dIODg4ODg4OTk5OTk5OToqKioqKisrKysrKyssHBwcHBwcHR0dHR0dHg4ODg4ODg8PDw8PDw8P///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQAEAAugH9DGAACklNyXfgYuAAAAAAAIA3B8HwfBwEAQ9UuficHwQBAEDiZf8ocUCAIA+D4Pn//6QQBB3//4PgQEKP/9Kwff//oDFpTADMRiISTMZkNLQyg0AeDCM5YNNKTJRNM/+MixBsZCPKAAZt4AB4gYGFEwS3tbWlazEDhtrBJCzEe7rqTPg5pSsX/+uv8btbU2uMfNvFEhQNmToOBIeJBQX0HxwELPO/9jlLRIs/6CT/TkP////SqW/278DkZlz+tbLciHNHieJSq/+MgxBIYma6IVdsoAFqexiKUUKFLejstEQEx/dnHNedH+7iweCZGGsLVJTNnslaPUYVnqtGqd94gyo6sh0edLpJVHMN2RQOcPmlvawSNSwhUGy0LmV3/7f+mpTvAFSv/6/yhldoZK3z/4yLEChcpToxU1gZwajSLWoTlDaW5mXYmmexsQw5b9cysQa+EjlP5yu9ya4Tpkn3du9pPln+yev93+ysFK8EmdmgqsQjFBsaVPB1YBYoaq832J3wr///6bnKa5NKf/0r/x6qeVDFMUwL/4yDECRa5KnQK28RwCjj34aZkRX9GAUVEjFwoIajEdpQq2lLW3Bd0g6p47bV6EVlR9PmcqM7ZZ0r07Cg4RQeMgB1VrCdAsHGFWWvS9y60HIlFO17k+7/o8z/f/q//05t//5PuICU11//jIsQJFrEmfDTJinBDZ1N3/pKWhkDgoYBHI6bZh3hyCHqVHPQzO+j+t/VdmLIrUrAcOxdwqWWOPhR3BYWIlK1vFzlTnG1kUMckC7CKK1267NbatiV2+1qNLOi37tGlCAPv//wDR0eIIf/jIMQKE/EahZTLxnBuVyEc/XTNVCF3beDhgBqAPhiak3aJVXqeG6trwbMNj6thJpCm5N33IsX8PRYgjWhzgwoVFWbEJrTosrd/atiLf+5vr+2hDEmzVgUgvBQJmQD+cGPJtU/mGguH/+MixBUUoOZ0AOYEcAPhEeBKjiIB1N6y/dDJ7qlLOGVuvN0+uWpyVaIimR2q+u+zY0pw+2gNpFOcFtvkXO2/l/FHkflDX+/1//6VT////Jkc1GHWpK7gGBKGfvHWLhjANAtSljTcnWbE/+MgxB4T8JKMVNbSTKRXXa5WxQ8hWYkIbyLg+NPxm1KAo9tt7MyhTW2NsVczpYx9fq7NH/+9+z9z/1epH/+lgEOn3////5WPufl28JXEhwQDtDSuL6hNRAGwBb6I00lPJmCKyC7jz3f/4yLEKRShBqW+yk5wW2d6I26m8PuQtj1oTjvVsGHZsDSpjtvyO/ImJHJL5b+jdd9X3XVe39ClBLeu222wyiOqtNHQuE0lBEjugKBVomBQBgbCgm66sLkhltXGOqxuZhGi0Rmu/ZLrvgz/4yDEMhRhBqEewkRwfa+oVsQ9raZBA1l4/tNqU65rHZVmqzFan//pf////oVdVUANUmtocwuHM2pF4BhApflcQGICycxhMFFWmSynqS2zLcNbq44ZkNSDK90rLKnL97CqD0uUsQOSVv/jIsQ7FFEKfDVaGADVKApYMGGmm26M2x6a1WSTErSuhDtq1Q7OOkYx7bNxHjbREzwQMCDR4lGgZsQQDNjTHXgrfIGSUoeZXNEMSBC8wqdREaLWyiMUmW0MLjUNuoMQn9pcehrMch8u1v/jIMRFIZGGbAGbSAB2bsK2Ne2cqW4+Le+O7VPWGrKQGQUKoSSSBQP2hiGS4ANioGOjk0LHqWwmxlJp4dZ3Bj9WsXHQcKPQp0214u9dRlWQHdxo3EZz/AAHstVv9v3U7r/EIBEIQ6nA/+MixBkaiq50A5hQAKIXQkiBSnxIFcjFgW/X5MWHR8PSLX/j4qQFCMeGf/5csaTGkJEVN///ICM8kJyYlJh8aREX///5UgMJyRiZiUiU0oRr//wTNA8CwVFAcMgiDKpMQU1FMy45OS41/+MgxAoAAANIAcAAAKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=",8:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jKMAAAAAAAAAAAABJbmZvAAAABwAAAAwAAAPwADY2NjY2NjY2SUlJSUlJSUlbW1tbW1tbW21tbW1tbW1tbYCAgICAgICAkpKSkpKSkpKkpKSkpKSkpKS2tra2tra2tsnJycnJycnJ29vb29vb29vb7e3t7e3t7e3//////////wAAAAAAAAAAAAAAAAAAAP/jGMQACxheOAgIhgBQAKAGEAwMDHCACIlDgYs4PggA5+s/B8MVAgCf/lz5+c7/zn3fE7///l3//QoJUBh0M2M2FmQUBLtiEP/jGMQOCQhSVDBJjAB+GbXC6YUDztu/6vKez2f/8w1v9//1VQlBGqgGf8PCUB1wFOmxuAJABwBANUGADcVEDVKUxIrIigxKO//jGMQkEBh6gZAT3gSz/9RwQFw8ILi5w4UCIOKd/yhwhUCEMKdyn//y6n/+7///5wUIhANUajUaZU40LUoLeFI1q75rZ44DwP/jGMQeEzGaoMjBxnIkKdFHzzxoT+p96//+3+RaOR5JMg+fa5Oan18G4QJb+LFxU2Xz9JpNGK9Ef/+zpr9+fnvyEzBwZay0W//jGMQMD0iOpMhjzCQL6AngTgiQ9KPh2iRnE4GFWQPZM1ijQMmyrUt//ZPA0+yq0Auef/4stNta/7P//66kVYnJaBbbaB49sP/jGMQJDVCG5lgqBiaJeNAYBYFzjYbWhR1ULmpLBKxYsVt//61nHKtfpHoB07+6T2V6vuo//9ctQhVMesQt1o5Xi0RYdUN5k//jGMQODUCOkMh7zCRMaur1kUMXeRan3sRn885//+okVFjosMe061zzh1rvqlUW6P///+phl1ZOnjwluAKHMSHvLyhjV5r8d//jGMQUDOCGcCBmEiSaS3tul1THR0VWhQxWkFDYa6P7etAsFYaYvnkRif/hpagJcgKWppeWogTkCEg+4Hx7SaUrexlX+12gwf/jGMQbCmhOdFgoTAAyaf+e/9f//6fZ1v9nt0oLVgr2arAxWus0NhmrrGnEmDUp2/R+riIGm7Ozu0fRTX+rv//1f//qQLsutv/jGMQsCgBabDAo0gDv4rxV0KPyPttV6o/+vV1fiv//12M////qVnq6AYhAiMmmGjRRR5lxedpSNWWAwaGgKDIoHjRoVb1f///jGMQ/B+AGgbgARgD6Rmz//7P//X7P//XVTEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/jGMRaCsh2EHgJhgRVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==",9:"data:audio/mpeg;base64,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",10:"data:audio/mpeg;base64,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",11:"data:audio/mpeg;base64,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",12:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAwAAAWCAC8vLy8vLy8vQkJCQkJCQkJVVVVVVVVVVWhoaGhoaGhoaHt7e3t7e3t7jo6Ojo6Ojo6hoaGhoaGhoaG0tLS0tLS0tMbGxsbGxsbG2tra2tra2tra7Ozs7Ozs7Oz//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQADgAGlP1BEAAQgokz+A1AgCGJwfeXP6wxy4f4II/8H/ggAP/lAx7///DH/5R3//6wfD7lg+D4OBguD7wfB9+oEB3HMOjFOYmj2V816vU//zUn//0v/99f//vY88n//SZmEBLE/+MixCMcS4LAAYY4AHJg8B4LAGf/oulzx85huFQ2MiwXg+EUTMD4biN//0YSxuNyB43EgfJmkxoPA+Gxo1GpQbHHElva7+hpqDpqKcecpg6cSBZgTGfdW2fjiCr//tKpn6AXFQ2BuSQr/+MgxA0XaZbIy8wwADBSZtOZWJv878vZJA8ouiKUmpKSk6Wu0FmnOpI25Kt65tTVZNHH2HIApMCBMfEoRLiKSbEjiSSXk2lg0HQoHBqiIUQ9QFc5ClzA7/X/////RYUx8gw3+xJMA+L/4yLEChaBQsTKYZBwrGtWJqWEki9y5OuLyWf7jLHNNMe0f1/N/fF1ZQ0w4SiQXCEFQYC4AEDgJCqmnBYCiMBazLS4S01AVjQZIpYAkEQCRCRVlvHkQES+j//+hg+AAIf8I+UnDsbDMsr/4yDEDBXxHsFsYlBwAMzQXJCNAuotHXThOrzcnC4mIq6tHR5if+J+WqDQ/RA7DgHwUATCg8IJMhEValyyNy2NMhkiKNOpMoNIh0JX1ioqBQ6d+/dd//WoWcAa/Av/ynll8MwS6vV5hf/jIsQPFtEWuZR5mHCROWLYhZ59kIJ7EW1s3Tid+Kb0pN4uo5t5Y6Hjts4DgkHKWhWd6axYDYbDQd9h0VFIQJhN4qkSt3xdhemIt3o/9X3/1/Ql//6jyZb6FYgXbbaC7aAAM2c6FakKzf/jIMQPFUFmzl5iTJUfGyGIqYnAqBI0SiI0KTAqPkMGVEUWSzSsVWVnqxlqaJdD615pRwCSBQmKenytn1tP/vxv3lqdFihUkVJEhs2R3///w4UMwCttCltttg3cnucNFKGcMNppshjm/+MixBUUoQbFvmGGcCS6IBiy3pokv9yBvl04HAwMdAgJhRe5tIYhROUYgDnktUKMTewToer+n6fkPjP27Orcj+u+l7RBe3/eGL6FNaHA1hsE1FhLyxkFJKQAGApcU5FyyLxdCt279fXt/+MgxB4UGNasAHpYcK41Za7YtGpSCYGxO910Ii4mDoKlQkHRKVcyzxWPkUvb/////////Zf/5sRLDQiU8NFh6hvFk6SwrHctlA9bD4jC4XR8nFZO3qiBAxKCBAx9Rk4YZAcAY8w2KwL/4yLEKBQRCqwAYlJwwwg9trrrzqWvbfKkl150DTETg+bNh/f0dsTi9NAQ6///////S/0/xer0/8L/w8Gtq+HEiREiAQMilxEKhUhcsRCpFKKFEilaFDjUaRIgEAkZ/eZIkZn1KM41Egb/4yDEMxQ49smUMkxwiwNB1bioKxECqgaDvXw1WdkcGv///////rBX+3xLFcIaRot50K9gY1Ip2OJtljy1GVlYKCBB0Mv9nKGCggRyO3+7GChgQkFhYWEboqz5lQsLkTMW//i/Fmf////jIsQ9EGkCIAR4xHD9fs//WkxBTUUzLjk5LjWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==", +13:"data:audio/mpeg;base64,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",14:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAgAAAPgAENDQ0NDQ0NDQ0NDQ15eXl5eXl5eXl5eXnl5eXl5eXl5eXl5eXmUlJSUlJSUlJSUlJSurq6urq6urq6urq6uysrKysrKysrKysrK5OTk5OTk5OTk5OTk5P///////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQADtAexZdGEAACAF6ALlqGHgMBgMmH58Tg+DgIAg7ggCHwfB8H36wfP+DhwuD8H4IHP/B8Hz+//5c///6f/+GJc/+D5pU+FVqGlMGZFUa9GaP/+W7/u/TQJV0D04i5eJ9dkMxP/+MixB8aAdrEAZhoABspGZa02+54wM0yCyamRIlTLMRcAxTMkh6BVzEqE5SNSIMpy6eSUg+kiovGSJKEJm6lE4tvtnHlHOSKN9/8JNaxNX//qqNnSWlbPCY0knpDB0Vj1rGm1f5jvHn3/+MgxBMYskqcw9hYAC1qtTQ01KT1OOWkw2pEiMS6praUNpplud8sZCLC5VJz1jo8iFIex0kOTaJR9ih4mjcPQEyxOUSPbWT7an//4/9SWt/r///9v/u+aOtFP/5JyH5NrjO4nIb1yv3/4yLECxcpzowqwhCUq2bt7WZKS5JtYSgIWas6sGxPA0TOMt5qPNWoiZ86rWrNU5LNsHQbC6igqqmssD5oeDoaFzeKhp//bmvn/U02nLq/LIIrkmf/i23xv/8JUfR/Uir2mqgbbbaRTCv/4yDEChbSvp2+es64xxQnc+t2rLi1c/HTY2BqznveSIiRAuduNHdMu+fmvUXn/6f21RgdD83rXOC40JkUOc447+676aTb//973sui3/+n7+t9SIW77tO710sX+qv6agpQqqhqTdX+jv/jIsQJFpkqeFVYWABjlS8rTM7e7vWH91hzfJI6/9Vm4NFCaVkCAYXWzjq/u+uoeiqod/if2wMTpKBNaHgMysSDTtZlTbGUkA0i3d4qzLdtvyyFTfxEnf/q//1e1SVVAEcMAAABAMBAQv/jIMQKErEucb2NOAAIALoY6LY5CikrGGOvxSKAdLXjxI47/Uyd/80w8eJCn/8bDpQaAW/iqAMS/8AiscLf/pXaJhZIB//4qgTKIuFYGUxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVVVV/+MixBoAAANIAcAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",15:"data:audio/mpeg;base64,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",16:"data:audio/mpeg;base64,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",17:"data:audio/mpeg;base64,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",18:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAABUAAAkuABwcHBwnJycnJzMzMzMzPj4+Pj5JSUlJVVVVVVVgYGBgYGxsbGxsd3d3d4KCgoKCjo6Ojo6ZmZmZmaSkpKSwsLCwsLu7u7u7x8fHx8fS0tLS3d3d3d3p6enp6fT09PT0/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQADsDmiZ9BEAAEpCABC7bB/wDHHzGAEiABSEb9Cfz85zuQDAxZ+oHz4gBAoc5BH85/nLdb6H/iD5d/B8/l///P//ieJ3cwbk88s+Wqty7ilFe8QRmVCioiLx/RRdOFA/5LbjBR/+MixCAbo+7MAYEoAZ10ExBLm1yTtXsl52OYUI1X/81ijBcQIIFOKFKkLKiEM7IR//xATrId1dznnMHijiDWMJDUEg6A6///53JOx3/tfFxITESB1hYPLQLPvDWNH/bM/KYnEkeiSH44/+MgxA0WobLEAcxAAAiOmAkEw80zM7RMOGlG2jnoiU9pO//1IwNnccd9TFTx//ytCIza00o7o8U/1EdtfAt340uKk0segTo1MT1MChiM6KKWfFx1bTv/+xFI1R/mddetCpTCEJJJEkH/4yLEDRbZJsQAExhwqDUspgbEsRi+UBKFI8E8sDuWywTywQxUcvlIlGpbJByW0h6grL3XmZ+vX2Xr/pSt4OcJwmMkpmW0//ce2S2wqurgqzKiJGkSiITCURNDqv+HwDlZfXKmtErrrb//4yDEDRdZkrTLRWAAsnetXc3PdXbzLV3D5t1p9cdGR8uXHQlAkJJwDYtHI6kIPgEjmDEAwKigC4gA3EAqEskEMTB1D8GhfMR3Kh6Dc2EAsHcQWCIfHFGMKOUcX/11CTwufEzvq2P////jIsQKFpnGlAGLWAD/+/9//97nISSYccQXl0tuilcdh0blJJhWuQvbn0kIoBoiHjUpKzxqugsCaoSlzxuWTQtPJJG0zWNKV1/49InHpLHgKoObfZ+HBn2f07Pt/WogICABshIfJUx8kf/jIMQLFMNeYAeNOAEu/HRr/mj3+41Q4U/+cVBaOjU3/9DwEhCMA+N//zAFDwlHTjv//5x2lTVNHV////Go1NNWrTtDv////5w8aw8axymjppE3EnwACgACgACAQBgQCgYADxybZ/Ru/+MixBMRoXY+X4JQQs5jO3+Yt/7s93NJ/3Y92PG5h7/92d2PnEItgrJBbFv0P4JhYxd9P6M3/+ZNP037/////oq5oP//+NxLf/Zf+tNt9T3RJykXVUZWmOuYRWebPMMRP/o393Tf///f/+MgxCgTk/KcAYI4AUGhAy6KjnmCINgI//+3/+xI42+lr2////9v/pWeeOgFg5LnoM85Kg//7/+6EKHFhQ5WQMldldCCAcA4fD53d6oUVGCwiKmfRGpT/7293VQ+LuIBwPujbIRFvuv/4yLENBebRrjLwigAnMOMFiRwIOHtnIfp+n00ylK1+nz/////9X6TmDiiimv8sgtat+6A9QnD9f/aaYGFFdj6FGAwQx0ZaqUpVT/t/1IoGLCADHfSffdlY44bKccd/+v/+yf////8yuf/4yDEMRELSsQACI69v9///b0t6ohh5wnhY0WjpY52iqr/+gAAlYMUgoLfzWyIp5KvkOQWAHCIvrpcrUN//+61ciun/X2yILoNA4gOD6E6u3//mQ3DwsJB4WFjGbd3df//1+vxEpg8Ff/jIsRHFAsyzAAIirwN3oNAm63/1fRVD/+f/6ToMFBGfI3U7mIIIWUSgFK30//+t/rZCEOd1ToQWDowPjjDXWi/bzUct7I7f/////////k3r/T39FM05BMaFCIeFR5kuPEDv1atSv/7PP/jIMRSE5t64MoQir5anV2MJKKKpVaa1v5FPIx/VyA3FhCuRfuYxhRjf///7e3f/aR1I5U+y9lOgcHCYuh37b//tXM2YzgKKoHSGFi0OWv3/3W30Y4mJn7EGNMf/6//OALogwVUjCm0/+MixF4UO5LQAAiKvL/8QwiVzicZRMFOsX9LVt/7da+lmoIChCM2659XZkVg8ERgqBhFDU3o+WNho8CpV3//r/+CDAQBAwCo9YKuyLK9KNY1//FNEJn8kSXQNSZUbKMalP7//MkSJP5p/+MgxGkUMebQygjKmFHRHRyP88/MJCrMrGMZ9////+rozKQk7///qnoxxg4XiAiLig12uzp/9NP7N0Q1REBhYSAo07Z//sJq//wEiEEBjPMYu+0iqsrschDorlUpTGMY3//++6E/627/4yLEcxSbbsQACMq8roVwjHO5L/9aewYxgYDUBYxvR///pVjPQyzL/beu9v////7f/6y6nciKAFDnQMFYNQb/9//2ISWbMaAMoiEUQhCF86x5eCMmA2A3/UhRCUpUN9/1LVjZdloVqo7/4yDEfBQsFrwACITcz0Sv0pXTWQk7kIYjO/////9HKwEDEhhYoke0trNLE4P//iUsHA6CqgCqqqmov/7sqQqiSlLu1n3ItyFOQsyoYylab+rLMtP9P53QikKd0bp/6OpQylVgxy3/bf/jIsSGFJseuMoIRLz/5eYygIlHB3u1/7tr+sH3FzdQNHliXS8aswhn8sLVZ/b4iEvMh3B318HCDoGHLhieMkvHuUS+PA2dFbJfPG7m5cPKdFbJfnjMkieSo9DRTorZJX86ZJsYGSzq3f/jIMSPFFpmsZVCEAB//+qxkmis1MTyKv///06SjJlpOXSVRNiVEG//qV//xziBA6hOh2pF4yLqBu7GJOUDSkxBTUUzLjk5LjWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MixJgbu15sAYVoAaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/+MgxIUAAANIAcAAAKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=",19:"data:audio/mpeg;base64,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f6n7NvUdQSWttC6/pLRmkRu2eGfUhQSQzAotHYAFUjq2repI3Ws9TENWzLJAnqUqoqNLLMzABVBxN++Huxv+ofB6nGjm//NCxOUUkpZw9JNguA7/jm4zgAYVurorUlLogcSpQaDacu1bzkGDjdqfEEOih5YsMlQrIXqZEl/oDEUCRskgAC8+qi3zR3ouoVremwtRFlqVYpEHz6zIIAwf9kFLZJdlymV9qU1R/XUo//NAxO4YopaeXlNauhNL0IDDjCdNqHER7PJHfeucnams1vtWy9WRZjmo37WZqkMbYII5WXvzCDAJJLXbdbaAH5n+cPWUukFWNvQCxRRMaCJeOHWcfAPwtH/25epRls1I8jNsRW1GPb3/80LE5hbBjnG+i1CUW65TTLQnToWURXZtq3PVpksOpIudGPTWORTS5LyRunmdRz/aSz1Ca/jkMrtsgAS+uYbLmVDdNVawlEaGbC+igg6zpdvWgXh0f91oIMZWn3AXMCSdS7RXv///kHj/80DE5xaCknEebBS4up2zlGqbT4dV1JIL7rk+jXM7JfUxcMNmhB4DYgGA4GAwH9+KsRPrqoZfbn11Jjr/ABlRO0rU9bqpdIL25rZEuGjKddzR1sbGIdQKruh/ZbnqzL+SBCzKi5Trnv/zQsToFemepl47VpbqJu2pWGRyXM++b+HPjZC7zj70mImKAckLzyzoq+bzaRyXjccqMt3QAuTrSPrSSMWTWkykak0RQi9VIi7pKWpNFFaaSwhxMUmVrdBCuqt7sQAuMhEWJtIfmYhGQP/zQMTsGFn2lb5bVpoeFXkuIWHfiaiqlqXjjaE+7nuGRRtCYOpINEJEUMlkPC6KRbSivvQ8fWxCovUhCjjTAAyVc7G79P2mpbK1K4AomuvaLj41WxOANCy6OrXZ2Oo6Hc5VbnKiqpqb//NCxOUUiZpkVItWlCsPWgs0TA4JHlgZQ8keK6QZCp57yISPRgVWR/2W/Sv/+tULhXTYOdQM7x+2L4fIEbmmURCcUXhNHI/+X9VjkasFBBkqFRUUDz6xY0aCop/WKNoFVCwt6BXiwuzU//NAxO4Y2fJcNJQQmMywuykJCrEjBfqrZFCRpYq3+KiyTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr/80LE5RPRTmA+YhRwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr/80DE8RNotggKekZOqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==", +20:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jKMAAAAAAAAAAAABJbmZvAAAABwAAAAsAAAOoADs7Ozs7Ozs7O05OTk5OTk5OTmJiYmJiYmJiYnZ2dnZ2dnZ2domJiYmJiYmJiZ2dnZ2dnZ2dnbGxsbGxsbGxscTExMTExMTExNjY2NjY2NjY2Ozs7Ozs7Ozs7P///////////wAAAAAAAAAAAAAAAAAAAP/jGMQACehaaPlFAABEKkILyEIT/kDgBh8XfT1B/p/y4Ph//+oEATP91X//9H///zijmXD6FvqKUhTUrAW+f/z/7dQkFKSByP/jGMQTD1jOnbmJEAAuAMGXAaAhWcv/k6Yozmxb9RTk1FH1YQQQT0J/R6P17//d//Kfp/+r9KVV3MapfKDqJ728/7jM2xH69f/jGMQQEBmisAGGKACfJc7t3VG1Oo6f9GyO7systnYnI7V3KYcIPVZh4qKBShVP1n/nzl23//7P/6P1IfLK/9/5kHyiQb5Eff/jGMQKD5mKuMnIGAA//FcNaZ7xIsOLbe0vqv/n4/4nL5dznLzhsmh7EbUyV3rHiBGBjofUTyXvs/1tf//2f2P9NZ6uyrFErP/jGMQGDjFmrAAYxEykvY0NmM4hssDGz9LyPNQ36rWn5fdHqUqoXuKMBQotgNB1CwExlFLdHb5aUuRo//6ePlkR9eHwyK8/5//jGMQIDBmCnAAIxExcGGFOrE65EWV1po/99/9+nRnuq7qikDKHR4fapCG9P/ehSxb////DqhzhnOLlkSTnT9VBXooGDDsdVf/jGMQSDLl2jAAYykzHn/VW9f9fTeY1jK195WqkGPBWVeasHI//FFgq/ez//9UH/A6Tld5vTy+jlKkwDA4YaVzOtHcuis/la//jGMQaDTGehMgRRHCxn91Z6/Vqu2arG0LNszgKSoIsO548LJ/ATakvW22y780uzd095jcrftRuarTT+zLLs/6//Q//+xl/9f/jGMQgCFgChbgIRgD9KlPckgbBEvgMgRDzLOaR6U20//d7O/TsT0+e/9D/+3+ns99+2moIAojBgkEKY6W6cBQCDCxIYHQ6dP/jGMQ5CRASZFhYRgBiGVbeoCkn0MH//+36NSP//9Sf///+MRVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/jGMRPCtg2KPB4xABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==",21:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAgAAAPgAENDQ0NDQ0NDQ0NDQ15eXl5eXl5eXl5eXnl5eXl5eXl5eXl5eXmUlJSUlJSUlJSUlJSurq6urq6urq6urq6uysrKysrKysrKysrK5OTk5OTk5OTk5OTk5P///////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQADDgyfF9GGAAr3Zbrv77QQIEMPAARAnPlA/OS4Pn1Owffz/Lg+BHd3ygY/9/p//9H//y5//xO//ggbafGjYIx4rLCr6FBnuIOWrGaKgTjSStkPIIxIDNNC4egUGwgKbJWgMjF/+MixCoe8W6IAZjYAOcibMABQgrMUAqaHwYHgYWfj6LPTFXBazKpw0AkBQwwSrrn///bx/WOeqfljf/////hh39f+Vf7/q/5AEHW//Sp4bTb/ez6ska//+74fMD2iy5oVzuX//P1/e/q/+MgxAoWmeakAdloALWqaxag0vaaDo5cKKhDfUxxlNT9SKzUvGxkXwGaB4gnoUgHIBuDWOc//9Too0jEqMTUvoN/9Gkp1oGZgdPof/XSsp0mPmgfOf1ipEIv/yiidU3JCDIbZbbaF9r/4yLEChbbItJeQ0S+/pantvSyanPmZMCACmd1t2rsktE2NiebrLwtDISY2S/Rq26KjNi8TjMQEof//qQxWqn//mVBwrGe//66lIa1E//+6uwwsvL//9YZ7v9alf9lKpPTiy2yWh13f6X/4yDEChZ5MsF+U9JyaVx742wIYaB2i3Icqs3rv//+GTm2jRo6RjBGhj////BAogQCttIkTaMIkKJE1nwfKLNFg6pVQYevov8xWv/xy50DBX6qk+tCdF3SvU37E9UBhv/zG3vxoEBsUP/jIsQLF2DujVR+GHBBZEml0iF9o6MLT0TjApAU6RpvBymyt0i8Ympmajs1NnhaMTYOR1cJRaXiSDVtZ65mbbWvTaNAlaLAUViZhLv7F6EfuX/2VO0f0cy+z13f///+pQg+clu221kt+P/jIMQJFFDCjP4xkkzlBTWATEjqo6ZYkmKQyhcqhZ95UpZ/GMUOXJZqCp3W7zxbWduyqj1in4ifiZ/DSj1hHSIlncqGvz1vW75YFflXdX9XrcIlywGVCAq0bbjcFUHQl2yQSeSCrjr7/+MixBIK4ApM/jBGAO1/Op/fZ/LVedOv/9f9mlH////s///1liyFTEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",22:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAQAAAI+AHNzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc6KioqKioqKioqKioqKioqKioqKioqKioqLR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR/////////////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQADqgC0jVBGACppukIAZU40TvLg+flD8QHMQHJQEHSgYOYIOygIYnBB0P5cH4Pg/plAxBD/8oc+t//+CH1h///KBjgggFmtAvhj+REwx/FYuKo+/yoiTR6WX/qVHsG0ctVjtaH/+MixCAcM27IAYJQAYXIXowC5E0Jw9RWMc1DUPnLCUIsFoWxEkIskUmRDnTov8484hJSEekBCSlDf///0c44hOKkpCPSAVSUZEP////6D5DlOPHqj0sQiyUHpYfMNgMZgwAqIQ5hXP5i/+MgxAsWUX59lcEYAMQhCEIvP/rMzMzM359Vf//vVEqWzBgEBAQESWwZVVV9mCgICJUFQVPFgaBoGQVO/waBoO/9QlBUFTvWCoNA0+LA0DUr8FXfyoKu/9QNB2pMQU1FMy45OS41qqr/4yLEDAAAA0gAAAAAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=",23:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jKMAAAAAAAAAAAABJbmZvAAAABwAAAAgAAALQAExMTExMTExMTExMTGZmZmZmZmZmZmZmZoCAgICAgICAgICAgICZmZmZmZmZmZmZmZmzs7Ozs7Ozs7Ozs7OzzMzMzMzMzMzMzMzM5ubm5ubm5ubm5ubm5v///////////////wAAAAAAAAAAAAAAAAAAAP/jGMQADEh+cAlJGABE1AoAQEDCaOSPVBQKGLXRoEEIQhDzQIGIBhYOAgch8EAxg4CHBB2jwQ5R3Of4Y5QMKkS5YrhXyWdIMv/jGMQJDzCapAGZEACQ0slnNgmeTqyoD20qS0GAUdZiAMDHiY3Rbz428sqXo1J73sDyhpgiQlH9n////u+7/X+qgKctoAtABf/jGMQHDeDq4lnQEAIieMj7kzZEyLyZVHsgjH8xLprsfLrHVqMjbdJbJV2Xs1VbY8D/////6gIPO+Q9ZgH6SDttottoEjrrTv/jGMQKD6p22lhqDnLvVRJUHY+7Oy8m2BswAYKHKyz1Pak4unc1UUhcWfPN8X//7//37f8x3/+wv//nz//4QN9YtWQZJIAJIP/jGMQGDjCy2lh52CYROOxyTS3+dK+MIgwAeDn7mGCR1w7HYeSyts5SE8c6jkMdQZKc+wb/////lgx9bPl2flVkp22gW2gO3//jGMQIDSCW7llJAAK5rzXmRigmRItzLi0hWvoWWaToQnkTSYpNAN4dYIq73I///1f/4t+qmebTqrUamXqf0pIKXuLxI/P9j//jGMQOD5DmkAGZEADv2Dqrwc1yhmIZqf//H88r8u7Vpf//CIEOsMBASgLfsQYP4NEhb/QokLK/9X//3VLVEwBZ5WVBUsDRUP/jGMQKCzABaDHDGABo8JXCF3ETxK4qCpYOrBo8JXCFyj3//8SuETxMDSw0oS04dUxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVQ==",24:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/zUMAAAAAAAAAAAABJbmZvAAAABwAAAAgAAAPGAD4+Pj4+Pj4+Pj4+PllZWVlZWVlZWVlZWXV1dXV1dXV1dXV1dXWRkZGRkZGRkZGRkZGsrKysrKysrKysrKysyMjIyMjIyMjIyMjI5OTk5OTk5OTk5OTk5P///////////////wD/80DEABFQBl4pQBAAASMzCAgBAxicPvrP4Ph/icHwfP/WfwfB/L/iAEHCcHwffE4P/5QEHcuCAIAg7Lh9QPh/rB8+UB8HwfD/BAEHwQBC7ggGFQdQaSzb7yWknqjdDsTlo0x2Jb0q7P/zQsQVFssSjAGFKACsrlZ9EnJvWmVSRqbOW+yNvu8chUt3vrrVKNpR71KtkdVdSpqVEHXaJo6vTuhlqLsyESQPoOCA5hcrigLhFIS/l5p/8QpJqq/flbP////d7b//fyt39n9/9s91m//zQMQVGWtmlAHBMAC6+uzZ7bPDP9Mj7G9m/+b/fb+/3/w9Pe/t839+7Q2a820OpkPbc/bhBo3fC5qDzGht2MspzED6Y28OmqJ74RXDobSF/p25WOYpbahuqPruFn222222RtulA8C6//NCxAoTeO7WXCjMcqGUzU2oQ1OmxPDJYjmOYMHGYvYRWjqN0quidi/z+WAAkdJCIAjjiHNWdEhho8HA4bacKnCaGs39DPcPrxplWJHJ/+rQq6uqyZdrI51F2sPY1OKxw2ZkkuY0vAHI//NAxBgXudLLHEsGlNRGZquZgl7ueajm8xuPfDSN1bBYswQsiPth9Oa+s/pGX4ZSdSn/0ya8nLsbzPJkBPF0pLIdgYUNEfewd5b6SsWHNnrFXw1R391+ttlkjbB1lXKTSZ9lNBNmggj/80LEFBKQ1t5fRhgC53bjkFePCLVqFTCal5Y+KjTwuGrousUQFw6Jg00s1pawRDFBQDClrRE2pvr78S3dXv6tfZ6KlQPCoFe8hNhp2c5kGM6ojqOK9DORmGLd7VUXdyOjESWVnO6OZDv/80DEJRuDgogBhSgAxE5k0R0KgxzrM5yHU7HIRilNqR7mLQpNEOziTFZ1WWQzpeXuiKISKxTkuFKV1R5Gor3Dg9DiBmpo6s9qOyyJT//mEBE4z/FVjBIIKBUC4PrJFVrmtmuVvVVqCv/zQsQSEihyOKvICAAWCIg6VBUeGr+sFeWPcGlB2o8In/KnRKdUDWCpYOCI98RBwRPu87/KnQVdEs7LHgauBpQNP+DSTEFNRTMuOTkuNaqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==",25:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/zUMAAAAAAAAAAAABJbmZvAAAABwAAAAgAAAPGAD4+Pj4+Pj4+Pj4+PllZWVlZWVlZWVlZWXV1dXV1dXV1dXV1dXWRkZGRkZGRkZGRkZGsrKysrKysrKysrKysyMjIyMjIyMjIyMjI5OTk5OTk5OTk5OTk5P///////////////wD/80DEABE4dmQBSRgBAGCZPRAKBQKBQKxWTo0aNGjRoECBiEIQgLcPDw8MAAAAAA8PDw8MAAAAAA8PDw8MAAAAAA8PDw9IAAAAIDx/////v//0adFgE7ZAdLMakra3AjLwUAL7Y8gx+P/zQsQWGWJOlAGaaABCNM28lC+4F6Thxc+XGHsZEv5LmBooS4YIdpl9DTmZcMTAz/oNpumkiYIspv+9NTILdzI+Xj5dRNTL//0G1W06SNbJLRMUBx7//oMpCRZpKgpJLrdt//9gQI8ozP/zQMQMF0pqyl/PGAJK6MzQ3rE+ko/g1m3CjM08ma1hFHK6qFMl+wMyqTqvlVVVJVzVfL2KGfabalI06ZEZxoyEs5IUm/5xNG4S3+eWlLuUzXt2ZhSWFTBxSv+Lf/XVAJp57+oORXAg//NCxAkW6JZtlNpGTAFXQJugnbMAmhgkSRhhyAyfVZvhKFiNIKDCRJJFwRlWmB9ZkOhsOiAaYnGgZo9RkobU8MrfGEmnguAQMAk64AeKuMC79M4UU3a7JK6kluokB/yaCKb6fGbxYGTF//NAxAkV8Opk8tmGcHURt4wm5At+tQviABIJ8L6415bxU6uPXzImSGSgqQMrnwWbQScSS6HibTYlcNB56AOGlCq+8Nb3CGtOZrqlQALpoIH77XKYLgC52lpZnlIklqlsdmAOg67zw9P/80LEDBSoumTzWhgAk7LbeFSrhnOVu2v5yzS/exyKtHc2Wv53oLULhRLws4oKPeKwEsgROl3v4TmVF5ONWA2TKELLElU/12FNGFz+Yywr3o8WpKUT3ix8gJiJopq3p5akD3JnPUARiW7/80DEFRahInABmUAAr8IxQPCTY/EcgxTeUjE6GD5Xmf9DD4ReV+f/PgEygXOiVazmoExA8MnSs7/UfBNB8BkJ0rOo9gvUZKHy6klzX7aUf61B9UG6ISIaWGAcyihobOwqFl1a3rmC9v/zQsQVEOBZ3APPGACurBU7lQV4Kuyoa4Kuyp3iXrO8S6Fnc6Jcqs7nRLiUqdzoK4lWd3Ev50llVuzpJUxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==",26:"data:audio/mpeg;base64,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rbVZlM7kBRU8pc+d//NCxLYT6h8XHkIKmiYMhl51zMrgsSDM25/1d7j/3G0qrVZF+3739PUva+1TilyleDSLfSxGt5Op3lWNzUQNuLOMRFXf/7//7auyUAispKiuqiybLoCIwups7PJc7AafHJThalYRFJCe//NAxMIT0w7GXGiEuPZbiAaKiZg2kqvCYqnVHlM436WUWWMhd1YSqUh333KpyLe3+LxCsQu1OWpXh3bb/a2SSUEV/XVGKufLTO4N90d+gj0pGivFDrwlzMiOqRBwyvcv/+Gxnj/yv/v/80LEzRMaLw5eaMSaXXtXqz1eqyIeqF5f7F7rp6tSunaykoNrZz2WYRtM1GhDWoy8AQaHZfvrCwVcG8zF14lJ79rrXkN1cWRxyB5wwCoYI4kUcpAw6kKZRxVmMBGiREn/hWYSwrlTDv//80DE3BPZdwJeaMqWYtC18wrBjCAtVU1ETRDc3roCWu7Al1qr6y6O8KSobXkZR11VaZkHLNQ0z861bvZHwRJ13/23/22tklAXczpMujZAyd4oQCUGqlHBkSPDoQDj1b5w1TDpjjBXtf/zQsTnFTqO+v54xLrR+isKi5v8pnfU49TEZH3UVxoKKhAfInBUjF5Xw3OyMW2d21bg1OUag4JWr54u+camaZtddttbY3JgHfbMW/4tl3KDgpmvQafqyALjalPIdzwRVO3v16CJC++tNv/zQMTuGmNesvxghL1bCDqCb2ZtWROjZkzl//v0fvonvpsysjUZ9bPqmxCtXHFemT2BBRCo6HIQdxisEVZniHaImH29ttwCO1aS1ZuksweYrpHxsg1oVJRFLudZR+HY3m8b+nl/9z////NCxN8WcaryXoDKlvb+X6moTVYAfupnlf7epW9db8I/T+rF0K8G3+v67aBO6qXN0X9QHr7nflplfLbB9KqW22222WNtwA/LfUttNrtQWla6LGqlTV1Opj9HI5KKocEwRGKq/xI10XdH//NAxOEWg2LiXliEv6GH7IhQpCF+Z+rFW7M9Sdzl//7KaqjuvX39KIhm2LfHdH/QPdjdTHzYxxYDfwgHExSJEML0BfIm0sPzEKPx9DiD4lOviEw1xlPydLom4fKn/AMAg2MmReNv/Fr/80LE4hbK7vMeaYS7RVCjCCwpEmBlqjFdf+fFlBa8K3D9CQC34LIhlXXpJP/8RiBhipJB8pDRCYgQBoQLcgDARoh+wpPRUv6//+LCcKQGDRA2DQvkI+D9RBpAiHDuE2jLCOg5alMjJNX/80DE4hWC7spfTSgC+uv//xAYaRRSFdFQ/8tVTEFNRUxBTUUzLjk5LjVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/zQsTnJos2QAGUoABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/zQMSkAAADSAHAAABVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",27:"data:audio/mpeg;base64,SUQzBAAAAAAALlREUkwAAAAMAAADMTk5NS0wOS0wNwBUU1NFAAAADgAAA0xhdmY1NC41LjEwMAD/4yjAAAAAAAAAAAAASW5mbwAAAAcAAAAFAAAB+ABtbW1tbW1tbW1tbW1tbW1tbW1tkpKSkpKSkpKSkpKSkpKSkpKSkpK2tra2tra2tra2tra2tra2tra2ttvb29vb29vb29vb29vb29vb29vb//////////////////////////8AAAAAAAAAAAAAAAAAAAD/4xjEAAx4VnR5SQAAeobdm/85o25EAJgmTo5znOkaOekEOXP85gg6XB8//wQ/wf//Lg//KHP//+GPLh/wQDDnZQyfntUFlY7/4xjECQ64lrQBmAAAE+l1WlkD4LriSos27g1rsY87/2L9/k/lXtfT/z7qyDvsvckvF0/5pn1/+v9zej//+pFAoCqobzMvEiP/4xjECQ9RKsWRzRAAlLRzjuESFkCch5WWnTya7JmhukZsi7KX/////9kBbU9GdzDP9wss4VxXqrTmG0P/++eSqtVAsD/8L0D/4xjEBg3AZq2QEwQEK9WjkEQpKwlCUTnjkUGAnmFB0OyQNf//OhxkNZ7lnyyS366w1HB27f5WWIyX//iVygDB8gRsN5Oqunb/4xjECgr4thgASYYo7ScKPMTVsnO+c1ZQQOl9lgMSH6hdn/rFeLN//+3/F2YqLExBTUUzLjk5LjWqqqqqqqqqqqqqqqqqqqo=",28:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAQAAAI+AHNzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc6KioqKioqKioqKioqKioqKioqKioqKioqLR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR/////////////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQAEBgG1u1BGACHeGbRgQuoGD585E78Lg+D4IVygY4IFMoCBzBAEHSgIHMEAQwcBAMS4IHMp/lz//4AOZQEAQOf+H///u5QEPUCCvsMPLP33z/LAAhVSD6LyVGR/5INRwcz7f4C/+MixBoasprVlYFAAKEKioKduf+eBocsHINemVfaeHxUGshyDWYu5hp6v/DkPVD0wVNGKs37aVNV/xkB0hQtMFKtfx//////+orFklHSwsFAa1qp/9jRKNBVYLFQagKzjt2+S9nGv8qU/+MgxAsVi2aEAYc4AT+vjjFD2Y7/KOKWEIOf/xqXGp8SSlWVk/w0WEoiUUHpgirX//lSJJB5VHJ023///moznMuNxxFJDv/9f//NHyrUHXHiZE1TGHYMTtVMQU1FMy45OS41VVVVVVX/4yLEDwAAA0gBwAAAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=",29:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAsAAAUZADMzMzMzMzMzM0dHR0dHR0dHR1xcXFxcXFxcXHBwcHBwcHBwcIWFhYWFhYWFhZmZmZmZmZmZma6urq6urq6ursLCwsLCwsLCwtfX19fX19fX1+vr6+vr6+vr6////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQAEOBSaClJAABCANhsnkgIBQKAwBsEwTFYrRo0bbEAQOeD4PggCGXP5cEAQBAEOXggCAIHE/wx/wTB/93B8HwQDH//h+CDvgh////h9T58JPPwl34anXXtCv2shB/ka1E7d1zM/+MixBcZhAawAYkoAFPb/LvGqqcnbu5zkJob/yI858UUpDTm//ncrnZndXjomIqzIHmO///F53ZUkV3W7qx5hBimR3Q3//W++S6Kr7IZHRSjhwkdQOdlI0ji6RWNOROSSNuVAaJ8nrVt/+MgxA0W6T6+XcsYAAhsWikvezdCVwbdf1DDBYLGTMNWgEJ4DG2d1LzCksYGvgMNjq1BTaREfoDvHG5hpZ601PYWBolOvI8O6n1HjygrpKu+Vu0+3VkhEj+W/0L7//9q1EGgIFYuYYP/4yLEDBcp1rmUSYaUzGZu42o95Ng5z3bt+0rfWU58fDMt8jlFld3cDNz3I/hIRu/XNOl17vvxIX+UTfsz/7BGuOLvrH4IOOfW+gps9Liax0oTL3RPuXeSs8pl8Py9D7cgIFJI1tRbTuP/4yDECxUbbrQAMES9Ji0uRy957R4t2ka4BgTKXLNRP/unf/+n0/mR2y69XvqjpUvVvcvZOqe/Sn7VbeZ28rTFNorUKynYMdC0KpZZBu77K3jaXm0SoGr/v///oQOQhifbHl02ZQKjI//jIsQRFOjOyZQwRHCwIACR1d5PpJoQQUOPIXn0uh6IFC4Pg+BCcTvkJRx8Mn7qDiTgeW+sMcIUrA5NTlgd//Z4IZ+GIDWxFaidZ/L1v69NqqoqqujC4lOqcYjAoPiwM+6RJCsRBR52tP/jIMQZFHgGyZIIRAABQ0JSqwRXT7SbGNuWtwpQJAKRK0ukTqUkTyoylRXCQUeLHpUqRkJ1RZ2u07q6zu7pqJC1RV3YZAT//4MFYXan8EBz0vkI1/fvwXxBF97IXwRRMZUCJHIGgwTZ/+MixCIUsNrEyhiGcHg+9G/JX4nfPh5eikBnwdHKGgRnP4D1V6gxOQ/LvvxoP4gStQ5wgiii70n2ZHfBAH7////v5BeZAKjjtNfPzkkjrI4tJAqdDodBo8tYieDSUtEQcirmZYeG57hq/+MgxCsS0FLRlUYQAKCriMjOniKnryx5Kg5rqH9T+Srln+V/T1udXLP+vS3laktgqtWsbCz82l1mTZth4hUslC4Vms7IQWIYx/vNUQOJOLB0ilL2U6pIFrMlZS/8l3eiqlSfmNSdmZn/4yLEOh/EFqwBjCgAr3QgtuRWFyC/10yoRRM9GYhCIpBChTkix3EVcTFyshjZctSrYalyKKncciHHiDGWKDCmMJB9SCRiCY0HDg5Csn/8rIYSSmZgLnBCV0GnWz1kS9R4t4a/Qt35XLf/4yDEFww4Bi4zwBAA/6INKBr//+s7dBpQNVnUfrOiU7/+//1neDVMQU1FMy45OS41VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==",30:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAUAAAKmAGJiYmJiYmJiYmJiYmJiYmJiYmKJiYmJiYmJiYmJiYmJiYmJiYmJibGxsbGxsbGxsbGxsbGxsbGxsbGx2NjY2NjY2NjY2NjY2NjY2NjY2Nj//////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQADIACnn9BEADyyw4QRqcCkzdSfWD4Pn1B+GOD5+H+Uc78u/gh1Agc/+f//KO/l/8o7/8QHP+J+XB+H2kqf0B+/9m/oq+68m+JFQ9t1qWyFMzLV2fZnYhVb3dct0diqb8xSEOR/+MixCkdQ/bAAYUoARbpItUtyupzCAkURHDxaqypV2c23/vFRcTGChhAgnODiJg8ruKjzjhsPCSB4PgN///jg+NFyAcPi4oRnIQizowRECCYrKVHERJLqtmTcXdvAOzDd7KjRM3NtLa7/+MgxBAVe9rAAY9oAcci4I6EjHn/ur/6v/rRUyf6//////61f6tv/RWgmyf1N1KrXXr//pWtSXfbetJ///+pm/dW92rMHM00ETE4ddNzZJn4UswaQ26V3ATnlSHaDcW5O4WgNsLmMAP/4yLEFReaorgBgmgAiEuDaCarZJRPN3eHgFmJ2MItImmJ4yV8sTdNTKdf+p1N/+r/6///+up137///oTE46zInuamxk091yor/XAqgaCpUTFhLYKiopUAkmZGAgJjoCAmFASjGoCaqUb/4yDEEhKg7ggXwzAAPX+ZnPVTjHEiVgoBWaRIz6JEsOJJaJQVWCp0RHhEe/Pes7nf/xKs7t4i89xLO/iK/8Szv//Ev+JVTEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==",31:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jKMAAAAAAAAAAAABJbmZvAAAABwAAACAAAAmQABYWFh4eHiUlJS0tLTQ0NDw8PENDQ0tLS1JSUlJaWlphYWFpaWlwcHB4eHiAgICHh4ePj4+PlpaWnp6epaWlra2ttLS0vLy8w8PDy8vLy9LS0tra2uHh4enp6fDw8Pj4+P///wAAAAAAAAAAAAAAAAAAAP/jGMQADKDWXAFMAAC95mkzNL3lgkCAIh4nBoDQmOQnZnGcATA+Tz/5mZmcpSlFhgJYliWZr/YcADpQ55Q5wfWf9wn9YU/9jf/jGMQID0DmmAGPYACDViwVKpTgykiTC6nYfhyX3OxHMhSUOivN8ty5eAoUh+ozmj5LAfay04OckRLGFXMdT0tZ6Za01btsMP/jGMQGDSCK4lnCGAIf7EIdGd8SAmfCgJSiTgLAQGAQiCHe/FAVOm0SRZAiGNNlrFgK/8PwupFv///+pWHvrqYm+aTDQfRxf//jGMQMDPDClMBL0ihTiZryow8gx6Q4+vNA80blCIDWEQWA1ZEF6TZaTNvFCZERoOKOdP+pd/P+d0e0c1w2xi4mEDaGmXED+P/jGMQTEcD6kAB70kiaFsTkVWYWGtyq/eUni2LFZUTUk2sicuy0SE5IXD5YybIE3wKBsCOvKGEc0aEIMvMjbqPqtclu22wAf//jGMQHDOEK9lgSBkp2aQIpzkbgaO2ZmjaTAbA6GCzBhbp/y0/RzI67ufoBizBDQ2q9Oh5lxv/rQTXjz9UcpYiAKtO4dQ9JpP/jGMQOCwCWrMArzCWBQxrUcJWKwIIIEyZNMohl0nBhHElVRNI1zUrWQ2u2JWy20AAfziFTDizuIRQ0OrxmZVgTAaOwASoFQf/jGMQdDTi+6lgY2CrQ5lhcPJOKAl2X4AwxLsaEBalVr6/bapZ//20lEYfOBkuQuAAvFGgZTQOBnjtkOm60hwJ5ubGL0JMZ0f/jGMQjDJDSmABL0ilBOGCrCCDEGeEvvJEV2qjqqEslto/r5CTIAgICeStOE5lOjB1kIL+pXJ1HbHBjUb8AQAEcSMqqsKl/bf/jGMQrDMj6xkATxkhmBjVT7xgy5VU/fKlCCM37VVOCcFdxLJDqNEZrS2kMJkySpeh4mFA4uAMA6NDFZgEQfCo+CYPn7kvE/v/jGMQyDPjGpAAbEiif0tZLrbtgAB5lyc5PpMphFGAaTUMJwhg3ZHpCGFDqpqpQUftMj4DAgm/buTS7+KpLCiu//c/tyHAf+P/jGMQ5DSjTAlgYxivFsDgJxQAM4ZVhERAkTLbUHqxjCSJ+okSYpJSImDhhQNZK/TkNbGKMGZ9ndTij9ouqf/ZzB9olhMKQ8P/jGMQ/DQkSoMgaRkyevXn5+dr3222osbOENckgTOQPTCEDA1pj4QIR2KkQPCu/2sjYfU+g7WrLqYxcPjnDFeYIw9Ox2oICAv/jGMRFCyDmpAA7DEkyEGw8JJMZNOigJhtpRVcUBnI17K//3f1F/nFxPVm4////v1dQjBHSirajL/m1O3Y9pZq4oUJCKWUgsf/jGMRTDSjOrMgLEigqQqXFKTTMDo+//9v/1fzcrln4vJ90x8+HK4i4gS3grou0eHEYF1qFc00ZXLUb6lONcjwH+LAW0usiuP/jGMRZC/DauMlJEABvCyi4kgLwf5x6esjtdTy5zTBVE/Wp9bt4U7D9//Nq/eIWqxcwMbT4Yl1EW1KfygYf6bP7///0KseRlf/jGMRkGAlmlAGPeADN/I5P9ZgyYlduJOzfsxH0G0f8KuRvRqVYCxmQ8oCVBkE7C5BZCBOmxAi0oY501JFAoFxCgyS1kBLprf/jGMQ+FxnGkAGPiAAk6iIonUzd31uzvmpjdbfS3dNNNtSR0ySO8ez7/+KVkEE/8HvnrfM9q2Jc8lE4RwhMBS6QEx2J5ONthf/jGMQcE5kupZHMQAC/xhhzWKXvSbWvlpz++rLWqp3F7MWHoqUC4sFx4qCRJLBhhAqWCpllhFAqpiBRr0q//WpCcKfkc8imOf/jGMQIDljmmMgSUkjFIEbLkRFaEUioVCoVCoVPKhloBRwBicCSI7iFEhbQJtQTTSwy4LJFlAUsVRr2qTZ9VbttHJttgH+2cf/jGMQJD2h6+lgL3gbzEfJ1gKEoF8pGA1jPcEPTjW3Lacih8EDhQEAQDAJgQMCcCBYB+suXPpOoTrf1//75Ot8pQkG8/0oup//jGMQGDODurMgRkkggZkjqqqSL10A6AKOEaz1pNIpdUiFQqmKRTALExETELlgdIiv/1d1P/VS//3aav/jV6X1FYl2oCbh0CP/jGMQNDMkOoAB6Rkx7ScgAwNgbBMNvQElnpyzZJ5KkUqWnkwU60Ku+9N6Zg6IFCWU/9FW1O2O20AAf506aCJ81VrwMBC4OQv/jGMQUDUCy6lgY2CaQ5KAHgVFAjgMBMJCktV0hM46AmD7cKLJ2oK1FnuZVLej8YfL/8/8dY1pqVw415UqCw8RYdQcMEiC0dP/jGMQaDND+sYDCTEmDb2tZj78mpzNnkRpzJ3mY3rdb3a7MyJBvFZChqH9ZFUwB5EIqEx+bhCGUADFWZB5oJUsTWulK0oZEgP/jGMQhDLCerYASXiXyIx6YAbm+/YoK6L+Oz7t99/WgBqoAPyNO241MEwYOKHwCA+pytNBMx6loYeRME5QIBcTzghNqM7TNLf/jGMQpDTDetZAZkkgCQu0IvT+LGcufo63WO6S7YACGyIpzBmGwo1SuxAm1FA3w7uP5TIEyq7Q31SKSMQRhH3LJot59Jn7iqf/jGMQvDMiy/lgIxifv97PtqoBBqgB+7j47l/XbAbAGJ9RAY4sFRD3eH91YjKNHEkhxJe4zUvWiLy+ZKgWUBXLJVM41rnPq+f/jGMQ2DUDupZBJjEicnsntxdCt/Q5D0OQaBEaGZw6rTJ9uvO178cvPX2fm223q4u1bQBGAnEw1NI2qr96zr00TYbQFtZYIqf/jGMQ8DOjyoAFMGACyMpeM4HoiuEgTxqXSHeRYNRC00DliXl4sdQWQ8zpaYGgKaOQeS1L/NjY2dJNnWyNKpL/RPubFhdUamv/jGMRDF4H2iMmSaAAilMTVSSWl/6lTAuFwvrQW6XMnAIK3fnjaHiJQgwCU6IjwLHip0ROWCqjzBKGhEMgqMDkqdESngqMPJ//jGMQgC2gBoNHDGAD///8is6WLHhK48RhoskxBTUUzLjk5LjWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==",32:"data:audio/mpeg;base64,SUQzBAAAAAAAGFRTU0UAAAAOAAADTGF2ZjU0LjUuMTAwAP/jMMAAAAAAAAAAAABJbmZvAAAABwAAAAoAAASxADc3Nzc3Nzc3N01NTU1NTU1NTU1jY2NjY2NjY2Njenp6enp6enp6epCQkJCQkJCQkJCmpqampqampqamvb29vb29vb29vdPT09PT09PT09Pp6enp6enp6enp/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/jIMQAEiAeaAlDGAILwIAAAAgIB9QIAgGZcH3g/E4fEYfEAIBh/EZ/KA+8QAgCYPg/DBcHz8oc8T/rBD/xAcKAh/+GP6gffP8Tg+CAIf/EgY/iMP61SzNHv/VNVY5lRoQkp2fSr3sf/+MixBIYTBa0AYYQAPZLrX96o/nVXNT8rnMtXr9X/lXW19Uq36M/qz1chnZ1lVU0VO21/6mrZZ7vO5p0dJU3k2e5LZ29nxVSkDO5VYzOoYQWR2V2OdDTEMjKQ0KqEMTWOGydrIH+HZMn/+MgxAwXDBKoAYYoAPh92vX0uLs5p/VnPOZyyr9V0Y5qmlN/nO6V9+n/f5ztshmL7//npRM/sWkyrVWT//7lMzIeSc5xQwk8olQspjUVB6G//9bSMp7OQH0nZnDjVXcegVT+sh5GsOv/4yLEChYjSrgBwxABH/+qGa9WHY17st5HT3Rbeyu6poXd5u/39mp9y/TI//fVDFTvVNjJ9lXV2677XVmO0hD9Woj133s5Ha4IOh0TKjKZd3Nq8tEmdo/wbtUbJW47bbS3IpkWgSH1KsX/4yDEDRepIrpcMMZwbeEDJESYQpATExjwlKhVVEIvs5uZY5Mxm7Ao9ruZgwoIHktF2m3uKYYSulmoQKYJ3m4fB+8vekIFC5+lQu0mUOHYXrm/UUof1I3tZQZ/93QYUhbXCVC+K3OLn//jIsQJFsPGsAAYRtzwh/L5aPzTChEp67CRrDv8wNeV38PtbPmV//155Lewz/jxl/P7P6VQ87sy+bZQyI1pWMaxkyNbMmVmyay6+RqblDya5WW5qfz1mzAyyISNUuaRCyJToCAAhm5iFP/jIMQKE9A+oAAxhgQaAIXFFvSfXo6WI0rHAgD7KEKGh9juan/6rWVnwQW3jWpD599L4IVtrD8jVr9MXvFFGHtdKBgmFxQLpA77o5LktTIOD4gqTWyIKRW+A3jRpC6pNIhfAzSxibio/+MixBUR+BqsAUYQAGVPZDl61PVLOtV93vdwD2k3Hq7Cuzqznex7pYspr8iSCckZVOkh5EVsSUcWeVdlWtUHURJAggJQBSm4cNk5gWnY4JAF+McIjP84RYAgN/6Czqb/+YWGKwSFf/9i/+MgxCkVa3Z4DYcoABUQjiLf//iwsUWMaIiIwS////oLGSlWMtxFf////2ESiQkhw7EjqhYqNUOgCkxBTUUzLjk5LjWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr/4yLELgAAA0gBwAAAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="}); \ No newline at end of file diff --git a/public/agents/Merlin/sounds-ogg.js b/public/agents/Merlin/sounds-ogg.js new file mode 100644 index 00000000..a2db2bf3 --- /dev/null +++ b/public/agents/Merlin/sounds-ogg.js @@ -0,0 +1,8 @@ +clippy.soundsReady("Merlin",{1:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAAALQAAAAAAAGo3oVICAAAAq1hFyC5XWVZZW1xdX1dbW1lWXF5cXF1SXVhZXlhaWGFa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",2:"data:audio/ogg;base64,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",3:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAARMHQAAAAAAABQPoCACAAAAj9X/dx9cX2BWW1xeYGFiY15iYWJgYGBfYFtbW1tVWFpV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",4:"data:audio/ogg;base64,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",5:"data:audio/ogg;base64,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", +6:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAAALQAAAAAAAEeaqGcCAAAAPn6XiS5XWVZZW1xdX1dbW1lWXF5cXF1SXVhZXlhaWGFa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",7:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAARYGwAAAAAAAGs4PkMCAAAAQzqprx1bVlhWWFpZW1haU1hVU1RTUFZTV1VXVVhSV1dX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",8:"data:audio/ogg;base64,T2dnUwACAAAAAAAAAADR5swoAAAAAB1D/nEBHgF2b3JiaXMAAAAAAUAfAAAAAAAAgFcAAAAAAACZAU9nZ1MAAAAAAAAAAAAA0ebMKAEAAACer43ZC0n///////////+1A3ZvcmJpcx0AAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDA5MDcwOQEAAAAYAAAAQ29tbWVudD1Qcm9jZXNzZWQgYnkgU29YAQV2b3JiaXMSQkNWAQAAAQAMUhQhJRlTSmMIlVJSKQUdY1BbRx1j1DlGIWQQU4hJGaV7TyqVWErIEVJYKUUdU0xTSZVSlilFHWMUU0ghU9YxZaFzFEuGSQklbE2udBZL6JljljFGHWPOWkqdY9YxRR1jUlJJoXMYOmYlZBQ6RsXoYnwwOpWiQii+x95S6S2FiluKvdcaU+sthBhLacEIYXPttdXcSmrFGGOMMcbF4lMogtCQVQAAAQAAQAQBQkNWAQAKAADCUAxFUYDQkFUAQAYAgAAURXEUx3EcR5IkywJCQ1YBAEAAAAIAACiO4SiSI0mSZFmWZVmWpnmWqLmqL/uuLuuu7eq6DoSGrAQAyAAAGIYhh95JzJBTkEkmKVXMOQih9Q455RRk0lLGmGKMUc6QUwwxBTGG0CmFENROOaUMIghDSJ1kziBLPejgYuc4EBqyIgCIAgAAjEGMIcaQcwxKBiFyjknIIETOOSmdlExKKK20lkkJLZXWIueclE5KJqW0FlLLpJTWQisFAAAEOAAABFgIhYasCACiAAAQg5BSSCnElGJOMYeUUo4px5BSzDnFmHKMMeggVMwxyByESCnFGHNOOeYgZAwq5hyEDDIBAAABDgAAARZCoSErAoA4AQCDJGmapWmiaGmaKHqmqKqiKKqq5Xmm6ZmmqnqiqaqmqrquqaqubHmeaXqmqKqeKaqqqaqua6qq64qqasumq9q26aq27MqybruyrNueqsq2qbqybqqubbuybOuuLNu65Hmq6pmm63qm6bqq69qy6rqy7Zmm64qqK9um68qy68q2rcqyrmum6bqiq9quqbqy7cqubbuyrPum6+q26sq6rsqy7tu2rvuyrQu76Lq2rsqurquyrOuyLeu2bNtCyfNU1TNN1/VM03VV17Vt1XVtWzNN1zVdV5ZF1XVl1ZV1XXVlW/dM03VNV5Vl01VlWZVl3XZlV5dF17VtVZZ9XXVlX5dt3fdlWdd903V1W5Vl21dlWfdlXfeFWbd93VNVWzddV9dN19V9W9d9YbZt3xddV9dV2daFVZZ139Z9ZZh1nTC6rq6rtuzrqizrvq7rxjDrujCsum38rq0Lw6vrxrHrvq7cvo9q277w6rYxvLpuHLuwG7/t+8axqaptm66r66Yr67ps675v67pxjK6r66os+7rqyr5v67rw674vDKPr6roqy7qw2rKvy7ouDLuuG8Nq28Lu2rpwzLIuDLfvK8evC0PVtoXh1XWjq9vGbwvD0jd2vgAAgAEHAIAAE8pAoSErAoA4AQAGIQgVYxAqxiCEEFIKIaRUMQYhYw5KxhyUEEpJIZTSKsYgZI5JyByTEEpoqZTQSiilpVBKS6GU1lJqLabUWgyhtBRKaa2U0lpqKbbUUmwVYxAy56RkjkkopbRWSmkpc0xKxqCkDkIqpaTSSkmtZc5JyaCj0jlIqaTSUkmptVBKa6GU1kpKsaXSSm2txRpKaS2k0lpJqbXUUm2ttVojxiBkjEHJnJNSSkmplNJa5pyUDjoqmYOSSimplZJSrJiT0kEoJYOMSkmltZJKK6GU1kpKsYVSWmut1ZhSSzWUklpJqcVQSmuttRpTKzWFUFILpbQWSmmttVZrai22UEJroaQWSyoxtRZjba3FGEppraQSWympxRZbja21WFNLNZaSYmyt1dhKLTnWWmtKLdbSUoyttZhbTLnFWGsNJbQWSmmtlNJaSq3F1lqtoZTWSiqxlZJabK3V2FqMNZTSYikptZBKbK21WFtsNaaWYmyx1VhSizHGWHNLtdWUWouttVhLKzXGGGtuNeVSAADAgAMAQIAJZaDQkJUAQBQAAGAMY4xBaBRyzDkpjVLOOSclcw5CCCllzkEIIaXOOQiltNQ5B6GUlEIpKaUUWyglpdZaLAAAoMABACDABk2JxQEKDVkJAEQBACDGKMUYhMYgpRiD0BijFGMQKqUYcw5CpRRjzkHIGHPOQSkZY85BJyWEEEIppYQQQiillAIAAAocAAACbNCUWByg0JAVAUAUAABgDGIMMYYgdFI6KRGETEonpZESWgspZZZKiiXGzFqJrcTYSAmthdYyayXG0mJGrcRYYioAAOzAAQDswEIoNGQlAJAHAEAYoxRjzjlnEGLMOQghNAgx5hyEECrGnHMOQggVY845ByGEzjnnIIQQQueccxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAR8FQAAAAAAANHmzCgCAAAA/Vof1hdWUFdVYWRbXmNeYV9aYmFcWVhUVVpYS3oN9zkV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",9:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAARAHwAAAAAAAE1tBi4CAAAAmOLfiCFZVVhaVlpdWVdYXWJgWltaXF1fXVxfXV5cWVle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", +10:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAAALgAAAAAAAJ78jxYCAAAAsTzqei9WWFhcWlxjXVhXWFRYVlZZVVVQVVRUUlpfWVpZ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",11:"data:audio/ogg;base64,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",12:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAR8FQAAAAAAALnu8lkCAAAAphlsRBdZWWNZXl1ZVF1WXFRcV11XYFtdV1tdVo5qaHut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",13:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAATsLAAAAAAAAIWk5AsCAAAAfNEKri5OWF1bY19iX19fXF9ZXV9bXlxcXl1fYVxZWVhc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", +14:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAASQCwAAAAAAACZjJiECAAAAPVHaTg1dVlldVlxXWVdYVFVNumTN1P/UAABAAvCV9tlXpa57rLnbuDNIKX/v+sSEJ9qQVZqXnvxvJn7bSgsrxQU8IfXancanFltqutoddXRetCth52QL2LMOIVvm+AHbXjzMNFtB5Dxt359bDUEEqt5ajj9x2oAjAPBjAgWQ+Xf6hkfa2X6nNEhFBWVlbV6UDhqHyoa3YMmEvWNaQQEAv0gqjigiSMtN92tNUjeAu4vefsxR53I9Igedu6yqpFnoRXI3AHmWWiT7q7bAPrABDKs8MnT9f9WpbX4Myu5OpdAs7b4Zsqw6XnlMUvXt1T58F3yzClKQ+J5z3titc6djHB+HaAIRFHXoOkSKJbvN7BVBG9vznhhcPzW2KV5FA45ZJfPsiljAjDYeAN5X31/nmrZtDrsHT+PI/0p1Is++6MZqoED0xGRU0Krosd7eNSDMHSdHQfDXlvipvQYVikq57kou/vuF4vYsr+weMl44KNs9B1IPaD1LoqthBIpYJfef4AI0AFgAABRlUumOdBXl/np8+9uUa6fZDi6KqFeFg1evVg3j23toUHBA0yuAFJJV8+NVpK1HSnC5i9PKokzv/ZDSV/WOotl+7Zsp7fO9tcEqiluJ+08MgAsAvgBQgmJC615aUdO8RpeNIX593S8LCrXMY0yaJ/fdW6N/oBmUKwzsqSSf/Lr17M+EwjcpPEvmxF7kRMEwMUVLfgBXFFwROmizvDjLh5eHEbuOiwSGWZj/T7AGyMACWJyS/j+0XHqzQxMaJWHf5Mm+MYKGbOYcxre3Txm5eZ8qExaPgKYxw1BQbGfBCNGxfxF7qvpyhBhGB2woDuvl00fF98VIMM96d0+8YVmKV822fzEC+IYFdHkBdOqOi+r+smo144uS33T+j/RvLhTFfqBq/bpIaBKDFkTDfj2sKUXpSADo16TgMa4vRsInHMop4/1fyBcJmVZ9QgbIqBSsuPpzDbE9AYZXzdy/YgaYEwtYqluD/yPutUFSE0J7YTp1cTAh6gLuOTOa31mPpH8vIiDHc3ZHt+qq1Ky5PtlpU0+xLEWh50JbjqLq1mhHLydu3E7L1xZbrH8fS56pB3pWmftPEAHOyW4ALU9AAaDoorOyQiI3WrfhiBn6/e2MADwpUUM7tOrxDA1VgAIVD22I0OYd/2Xk09ws8Y97r1sSIh5aoT1T8XMJxM3Cl+7S2QUu4Riiczp6VYg9++4B3AKbAaBS3p6vC+/K4rbd3VdlGAnHTVRR1BV1iNsgBygeDLi7DPtIz9zpSoVH1SwmeNaso3CfZWnbUKxe4l1N+JUqNzH9V2+TFrhmY3Z2VPPHf2IDZGABRVcpVStOQxkf+iv9xyMz2r47JAI4NamgVzem2BhmCanVpXZdV/lw8WQg9yZoN/sSoz6e1rJylrFRWyFveXUPNa74N7VVhbWH1WkAcs9JOK4GQwFgUZSZWzo555zzs4f/3VhbO3+lAwAANV/7c6vpGQBGZibQMG4vuddtmU+SmZn56CfJhHE0qEkTAGTqADIzn5hxZiZlfBw=",15:"data:audio/ogg;base64,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",16:"data:audio/ogg;base64,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", +17:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAARwFwAAAAAAAC2WVSMCAAAAU2RgMxlVW1pZVlddWVpZWlpcWFtWW1pZWlpdYVxbglJQ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",18:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAQEKQAAAAAAAJ6wuRgCAAAAGt6nmStXXV1fVmJaWlxYVllVAQEBUVZcY2JnYl1lYWprXGFiZ2NfX2RiWVpbXFgoeh2W8CNAswcA1ApDc5GNs3Xf+KdF9XNnO1Hr7WK9O5y8HoWwvNw0CEOx2HL8D28fH4fWmdns9vZ2ZrSpjRMIlm3P8YwYMUqEDUNHeNVXve/hhKrg+GoAhqMdthpceg4CC9OPVz/N2jjf3tUX34/Xkon88+3f09wm12avolHZ2HbSb3PpwErFSjs5xiWie8+6ch/z/Fo4/isfxwGIsxcfdo9del3/NvY1WNSHS0Natpj6iHmVomeIMV/nwEGg9r/Yl+lPm7mndz35Y9OsQPT7vekuZ28tSKnSffxhT7gmR9FYrxELzEcp594hL7dPow/eRyLTqnzkb19y8Si9crQ80nkHpyZXU2u6KfUXfh1H/PYlpuVslWMbp0s4CPx41iPnB7+fuOHmj4d+9k9KGVHFV/09TAQBYDgL1/e/3jUjAOt3ZY97ZNd1exsyjKFbxw74LM8WD2uqN4pBZNE3yKgYtB+OP6LMBig798PhcHhrEQCapbnCLNio0dnjwJf5n5PI+PNwZP81Myj+wFYzShB/3FSo7E2RceVEpRW6YijGkz+Ivslidteuzb+DS2hHd0Ndvw3KbipxajpRoPyzVMcf/ddB6TQDO4riQFXrwG1UilED+Fft9vrE+Z7m5ycPpPviSMK1BjPooAzTGHfvRP14cqTzMil3WRQesmr354ImrYCmus0FRcfmljTfQ2KMq+rmgozL26fHghebZVbS4DRpy3xPVYMkEpAAjqB+PwsAcwQoUamNeV2dnvV8pyfpTww3zy/dvEx3zN4mXmi5qLh82RMtEbepMtOK6u2vD5TOa7OtaCo4lxWmsltzg/NzaSq9v1FI7HZfoRk6P1p3YHr2vZsBht0yOV8AYBvG28RjsJey++Xmn2SzHDnVoxpdKuROpx77bnkt/b2H+o5ylAA4kBUiOPU5rDAMwymwHPzlzVMqfxPNW9O3eCG4BDurRxFhyzr2pOfKiHrjZZgBfuBytXzZFQEcAM/oN3sbdt2gIJ5TjsEsjgr6K6qIVRNZim25PBql4TWMexpv1N2wchSxa7XM2PXpKnF2u4fp7REa7c7VnBKi0Kt1J6n/dP2Ycu3DAMyTmWR9zj963foRsysAAAXcqCgMNy/VMn4q7VXGtVOKKajRFWNIen56fsysPeUt2DOBxNJIoRBWryfR1ktr76G37Y8ku/bdxtBBQ7Lv16STfvQogGVPPj9tBvz2hzsBetijLphdEQBADgLF0rn3UZd3qB8aH+JgcTRKNowdcyl7kwy9nQfy/mTq9q1p5Bj0hXzmiwgXs+1pj1/Hp41rYIow1Oummq3ekRNCbG5z4UfXw9NpWQV+VSL3WgOEAL6lN9q9nNjy8sN86wzSGp6azF7jNrSc/6SUiw7rN7cwL1FYwz9hiMmgr6sLEgJfcfdVim0Ja+FXsNCpWgdUtdXHtBWP00/1juZzFuOPyyDVDHJJ4pc0MIbiy7x1DlaG5r9fHDbXvAVQzt7vyT7KREbrf0WV7bP4PY/1cSY8msZvWOMnLq+7MH+avxyYHJfGb3h1ivjl4gQ1rLlTqpPB7xyWz18D5wAAAABy1FqLFcCDr9r96rRXNNG993hwDBjRRZ7oIfwY4vdDNMy8fBOth3gyAgAUve6fw72bMcavbMnYo8yvR0uh5OXgl21UD1F0XmVduen8xh5dBwBuWoLWGu0AYDJsnyADQFd9JHny0dx97RBqvejfe2O13lzq7B0k+vzIm+tXWL584DWTRggAhH3j73WfCAFaW0GhlLGXDHlPEmc7/Aq0gX+QDZ1ZG//ZCG5ffNAalhGwADxmABSGq2CIpoZsPl5J23fr2WaLiV3L8uVrl81q61FClInLx0eRqAolQNt5O922dVTGKjcicXsM/TAl1tY0qO1vZ8/bMvjek5IODzT68ER8q8MDdifad/wFWI8K9twItQIyG6vARnJNTz4f/Hd1SWu/wigZx3zTr47Ns3bnQnf908WdL9uvXFOZRI4JlfIEt15N/7yvl0Ioijo3+ojKYlbQP+M2901DLmT5F6rnrVCvl3fl3LQccmhD2IdRg2uAAShAZNzNS+WNY7++xxuTy7j9eZ8+xUsbHqqUgLbmo/0vHjiWV70hdOrxyqtpQzJHcclVslgBSO/mYEli8coFWpk0z96HMxZL3zxLkkNOh5fs+Gh+9x1fFWB+JYNJM8SCC80tX0IigH5GZWAZme7bj3+8FozxK1+mKdOTee3Qz/eXj13MxcqC1i6Rd4rjB2MvRVc4e6h+3QtVc3QiNkZ1Z7/MhoPcbcOcfex0tT+XJ6X5qreW8/0iw48c3RVmlx4FfmIy0MB6B1g6Qjb2dYAJNuC/uKWjuw9+mXZvevjv915KtxPL1YiFGuO766/3U5/If4/cu64jaa0ABXVn8qGNLW1V5U1XbXCnhSEPqXeuUTNu6e2hyKcQ4uTbfMashKvRnnZ65AVBg2UnmCBLcAPxd332Ts8w1dbq1yu63q1urajVv/9sXZW5WlJdSip66zzOzy/MQauQU4nBClq7fP4f3Y69k52Sd/Sb4RxTqbpHH0TNIiVUmSm1tIafnXjfhwB6q6qVgSrsagkJQj/XDODYwLLWh93UjR+rMOvBfx5MjkqzLkbNfv/+gQrJ7G3uN1N+Y+Xva9Wli9qiGKAI0pRqwz//HzNa2YNWWMJjReZLpclRr5jvKdA7RApbu/3hTlzV6r7GDoJqQ0rDiBpKAokAvgbg5HbaOP/++vBBqqRKN9SfjddiuQR3tFqpJveern+sceX5GA9RPOKSO2j/9xOp4yRUzfNoPbjbeGrsQtlucMea4VXMaNZLUYExsV3TRYrDQ7rZDwGGaaSt8dumAyhgg0QDWGc2j2cAEM6GWF/rLdtw1j2U3li12dbOXISIu0oj52QnTipXE/f+X5/UJWuyQ7LWkrKWnLyy6vPvzW7CM6+HSijY4Ji1tZJOcQZalwhSTGu8Y1fUUCaDAd7C3nY1gqY0r7VAdVzLMAIY4G+ZFZDBIgnPfHRkfLD17PDOP6lX+5h2y0H/Sw+0isFNdUE95IpBWgvSmufPW5/dOvbTMvp0LHSjqxlG6LNXf0+d3lpyzGHVWciB1ZG2oris+VD7OSHg/TahJDJwHlGCZRLcwJIWgH0dYw0JGCY/2j6cOmlOlOXQQ7NkanfptiBZw8T830OftkbGy/23Qw+Pq7IIcUxsldXMcAQUAOV2E/fIoG1LYXonNJhQ1j0PRe8Pmdl6+kZujXS7N3ZoUtXWsO06QI0S3qYBGebmhLemw37K2dWHHviid1JyjYz5qv/dQ1tOlvkrX/+3MeqzzHg4oSGimdJyR/7rd2eWZbTESmiZJdsYAo96wjl4mGdDyRc5+IH3a5rokuaNuAN6q6qV4RtYD0O6pSOABw3ApQevpO0ZLu2O/rtmszNOKoucTnXuX4znY/XunJ5fXfvv+YzdMyRBxYBX00ANn76aUc2FrI2pS1eOjNeFafHNjWsYQVWZ4bi4sWTnP1mWeRjmKnprQ5jX3NZjAAw/esdwAbECfAp4/T8+0t8mTJ5rpbz89HTsoiJFyIqo+tKNxsfElXfHc3JgFT9t/7//RrIbW2mDL6akKJpGVaWyJxI5HvPs3oVkDhM7dK7o7o5avFAC0Am+t/jbnAF+JDPT5paywKMEAYC+ZZavAAAa8/h3H+tj4uTAePDXrYdk7WcuRMMcbWQPGvHyR/V12YViRqYMJrGVhfqaYOXucTz+eEsgwXR473lUSeXQqa+GETqk91lDw0YMOTl693E9cTx2IjdwnbglICyACkZZKq71ka00wocX1689sOW/jb9fp2aiZolkhxyrgoZ5sq3/Tdno+wgYTkU1bkfY3nl6thl3JAFv1XShozT26eZLLHyi1HjEYOYlvTQdTR4QiaEoNHJlFcagPLDERAN68qixGgL0zGXh5/ObcPvyh/8ueXKMdCTubT36JM0l5uMq1dAkGvHpx+3nXxXo7vXnx6t5QnARWk1VKmqW3mHLZEXCppvAMK0TvCRmqYICBtB7de8HbqU2WIb1pgFgwC+pAgeDRjvgvUOrnCbNxqSdNbsp0bSk35beKDXURakYWpQKuSbm3Yn3n56+NMxab0vTV0b/d3tGq1pu9nl6OHpRq6qyBz5paN3VmsLOHXRNuAFPQM/amNuEAHJlleXmhhMNoAEYYFO3ARBCy0+eHo1H1lpKNK5+Eai4/9WxBDH4MP2rn3QMqZevJ+/su4tTWZprhUmlJ1Tqq9d33veguUNUhEj+KhdskVgdRlfH5MjdOTaibpJTnvcoHmLvbl+VoABLqohM+A2AkSXLixcTk71+8sXP1bPXiXlt9GfW8930L4y2JtUxBD+NwaCs7oGF795bOseUqKIC5XgYYyUycSaIuj3NORpfET0bZ1zyIdpq6p4cdc9uYKRu+HoH/FCouQkUE6CGp8ZQX3/qe2x86MFxQnIiIW2z16wXDz34/5XMtdt2L4K6QkUou3++eK7RojLKkdAYnXzCe4ZGiH5kLxdhEy9NT7oLRvCPOnId1hxqpBIz/NYBW4NkuYFAqoGCjpttrqub/elXe3InsJTnT1+m3J8uxpo9efal9fW8Pu2rETJ3UFqQ4aT/P2u9gkCWukqXBJd0jpaPagKSBD4oFj54XSpZxARsWyAAaqGylsamBio2F2hA1LFmjTV/8f/xf7d0xpMxeUpYDzvf5+OT77HPXUpZVy/fr5uvv0P6e3L6NrkdkWTYSBzdL0N2A8MalHYQxxWJRqC8nXya7dNEQ8uy9e1OQUlqWxMNBc7STSsFHpiADVBAKH/ybZoP/bo188dmwvQmT2bsJ3LMXlEV8rKRjclny+av/67xPosx6KDWizGR0cYdcCi9qWh7kUtl36bz8cUghuHWWMFcFj9ZctL6W8ZDjAEZMgDk6ocnFgUATb3chru4cYLlCQkA9P1nb5MLkB+nAQ==",19:"data:audio/ogg;base64,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", +20:"data:audio/ogg;base64,T2dnUwACAAAAAAAAAABezkUTAAAAAI5cKgEBHgF2b3JiaXMAAAAAAUAfAAAAAAAAgFcAAAAAAACZAU9nZ1MAAAAAAAAAAAAAXs5FEwEAAADGCyS3C0n///////////+1A3ZvcmJpcx0AAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDA5MDcwOQEAAAAYAAAAQ29tbWVudD1Qcm9jZXNzZWQgYnkgU29YAQV2b3JiaXMSQkNWAQAAAQAMUhQhJRlTSmMIlVJSKQUdY1BbRx1j1DlGIWQQU4hJGaV7TyqVWErIEVJYKUUdU0xTSZVSlilFHWMUU0ghU9YxZaFzFEuGSQklbE2udBZL6JljljFGHWPOWkqdY9YxRR1jUlJJoXMYOmYlZBQ6RsXoYnwwOpWiQii+x95S6S2FiluKvdcaU+sthBhLacEIYXPttdXcSmrFGGOMMcbF4lMogtCQVQAAAQAAQAQBQkNWAQAKAADCUAxFUYDQkFUAQAYAgAAURXEUx3EcR5IkywJCQ1YBAEAAAAIAACiO4SiSI0mSZFmWZVmWpnmWqLmqL/uuLuuu7eq6DoSGrAQAyAAAGIYhh95JzJBTkEkmKVXMOQih9Q455RRk0lLGmGKMUc6QUwwxBTGG0CmFENROOaUMIghDSJ1kziBLPejgYuc4EBqyIgCIAgAAjEGMIcaQcwxKBiFyjknIIETOOSmdlExKKK20lkkJLZXWIueclE5KJqW0FlLLpJTWQisFAAAEOAAABFgIhYasCACiAAAQg5BSSCnElGJOMYeUUo4px5BSzDnFmHKMMeggVMwxyByESCnFGHNOOeYgZAwq5hyEDDIBAAABDgAAARZCoSErAoA4AQCDJGmapWmiaGmaKHqmqKqiKKqq5Xmm6ZmmqnqiqaqmqrquqaqubHmeaXqmqKqeKaqqqaqua6qq64qqasumq9q26aq27MqybruyrNueqsq2qbqybqqubbuybOuuLNu65Hmq6pmm63qm6bqq69qy6rqy7Zmm64qqK9um68qy68q2rcqyrmum6bqiq9quqbqy7cqubbuyrPum6+q26sq6rsqy7tu2rvuyrQu76Lq2rsqurquyrOuyLeu2bNtCyfNU1TNN1/VM03VV17Vt1XVtWzNN1zVdV5ZF1XVl1ZV1XXVlW/dM03VNV5Vl01VlWZVl3XZlV5dF17VtVZZ9XXVlX5dt3fdlWdd903V1W5Vl21dlWfdlXfeFWbd93VNVWzddV9dN19V9W9d9YbZt3xddV9dV2daFVZZ139Z9ZZh1nTC6rq6rtuzrqizrvq7rxjDrujCsum38rq0Lw6vrxrHrvq7cvo9q277w6rYxvLpuHLuwG7/t+8axqaptm66r66Yr67ps675v67pxjK6r66os+7rqyr5v67rw674vDKPr6roqy7qw2rKvy7ouDLuuG8Nq28Lu2rpwzLIuDLfvK8evC0PVtoXh1XWjq9vGbwvD0jd2vgAAgAEHAIAAE8pAoSErAoA4AQAGIQgVYxAqxiCEEFIKIaRUMQYhYw5KxhyUEEpJIZTSKsYgZI5JyByTEEpoqZTQSiilpVBKS6GU1lJqLabUWgyhtBRKaa2U0lpqKbbUUmwVYxAy56RkjkkopbRWSmkpc0xKxqCkDkIqpaTSSkmtZc5JyaCj0jlIqaTSUkmptVBKa6GU1kpKsaXSSm2txRpKaS2k0lpJqbXUUm2ttVojxiBkjEHJnJNSSkmplNJa5pyUDjoqmYOSSimplZJSrJiT0kEoJYOMSkmltZJKK6GU1kpKsYVSWmut1ZhSSzWUklpJqcVQSmuttRpTKzWFUFILpbQWSmmttVZrai22UEJroaQWSyoxtRZjba3FGEppraQSWympxRZbja21WFNLNZaSYmyt1dhKLTnWWmtKLdbSUoyttZhbTLnFWGsNJbQWSmmtlNJaSq3F1lqtoZTWSiqxlZJabK3V2FqMNZTSYikptZBKbK21WFtsNaaWYmyx1VhSizHGWHNLtdWUWouttVhLKzXGGGtuNeVSAADAgAMAQIAJZaDQkJUAQBQAAGAMY4xBaBRyzDkpjVLOOSclcw5CCCllzkEIIaXOOQiltNQ5B6GUlEIpKaUUWyglpdZaLAAAoMABACDABk2JxQEKDVkJAEQBACDGKMUYhMYgpRiD0BijFGMQKqUYcw5CpRRjzkHIGHPOQSkZY85BJyWEEEIppYQQQiillAIAAAocAAACbNCUWByg0JAVAUAUAABgDGIMMYYgdFI6KRGETEonpZESWgspZZZKiiXGzFqJrcTYSAmthdYyayXG0mJGrcRYYioAAOzAAQDswEIoNGQlAJAHAEAYoxRjzjlnEGLMOQghNAgx5hyEECrGnHMOQggVY845ByGEzjnnIIQQQueccxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAASIEwAAAAAAAF7ORRMCAAAAOVAyhxVaWFtiYWBfXl9fXltfXVtYVFVXVEB2EXrycAtY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",21:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAT6DAAAAAAAAF8uF08CAAAAo+ktXw5ZVlNZWEhMUFNTXFZZVY4Ws4vKtqnjq5U063F+7E4vq38fuh6Xyy/f5zx0WzK1rY6iyyYSe2+TwDMay8WbOu7ckvkD52YizQVQVPUifvNSKdJkgqXWsYBPXyRErOvwc09c1bnm3RwGgpfGM5lVQA+jAFhUyo3Lhigx857WiFE1uZm1xge6PRGWtqsfe4+OWmmzY0UpQEmAuIIScyWeZ4EPD7b8neZ5pN56qx2P9HWzEdm5HRySr6/fwH69ZwOC2QCSvgK4Vd5NgANI1b5HZ4nJP6LyQgKi1q//zjZdAEBBul+5SM6nj4beyvEn9PqXvylALZu9FaagzxKMKY0294tD0aeZrpzqJSqaX0kMD0aCBIqcZljLAACs68Cq9gPAQQMYJlP69SoAHs+6g+eHKKhd+kbH7QVwJGzne3VNQDg9omZkWpPzCODO6zZFBDGHrMTMuby+xnrLLuX78z7MIBUTWsZnSTvOM0IIjlx7QGUAAOwSrJ4uAPqcBgWgfhycKADk8x1GocaIV5yZ71GLwGkRcPX+SQBA3v83NgAA7ubupwIAcrs/cU3WWV/X1KJYy5cn9dIZG4/1oTarXi+aG7lPAYpWp6UyAAAk4AEHwA8A508NAHm5sQIAgCsORfn4aUaktETK7IeguaKRKEQRKa182wsGXAHYgOyfEkfMTajHY1BVXo7VxfOzAIZWh+EyAAA+gQ8AwA+A/vNIAM11cUwBAEAdbxTiH54ERSO4D9ekwUhzOzxrTELqT4JcsgKDQAF0MtPL8CGqDkWPoU7U1t6ry7kuyweCV3vgZQAAdMAbQF0A8PYAIL57EQAAwmjsCJrDrs3D4yT+b38xpSjdf/yb4QGtBCnPVtd/PiL3/nqf3h6be6Hs2BlndQFHgChBmZ18RHYAOX5XHUxlAABUAY5KAdCzTwWgXr62IQC0VbLTlBqhQL1f+jc1CUCNN2ihPfZ7n5lAZvN4fzcb7YT9+LwvqTUchAQJxFTk9Ksq9TA3ske7iv8qo5kOfpZZUH8i0L96CmAYYGRRdgrCRDIVNdY9AOHOh+0yFyAsAu6be7IwyPJI1CJ0+s45mFnHGNPnVzcd5xEebA6R8yJFu4KzezmUlSXl66G108OnqwGGFsdiwToBYFFUNilDGD1wae2r9NE6Or178o8l1NleZ60zL19NpBr3/arr7J2exW6i4/ru5WWiJmRyRt7QGaH5/cF020ecs7EdLo+nann8rR2kelWcTtHHHgpHDYYXY2MxUVAAi1rpjIxDN6udzumfKycjj0N3+E7+lUW1iXsgF/r4knns4+g6XUYtotLbHIOhBJenqXDuqfg8Kpf5O4iJu1mEg1F1iF5nz9fhG7G8SFdUchQ1VBgvxlfUs+4fPvt4kcf/PrD+b5Lh/t2stRZtITQrHOZzxnTnDm+hzrEthckEkkc3/Iwf0C7f/uh3qZkTeyakh/10f7B3ZvRoil/JB4gzJOccq/pcpDduDrZhYAeAoihz4/7kwWTy7uwX488HNeheav51ba2TpTJrzEXM30chR9a3VpNRSPET3yplYXgY4IPDriou1U2Ek3cGXJYebYTUgPH1i6OlStJ+rhMB",22:"data:audio/ogg;base64,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",23:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAASBDAAAAAAAAJGSl2YCAAAACPRSNg5aXVdaSkFEQU5bWltdYa4ZbshVAN9Xz5Dh0NS9r6akDVMyMjb18T72QYjGct7uKhpX5725imarSG+uolYB58mdagHnyTmjIT61orHYWr1u8QNHSucH5u2IT9UWCYbK0nk7r8eabDl4DKqc9LEP3ijoAACrzzTTNjMbxce6Ic8z6WcTMp9ywzs1vI+mKWpszPlw7TJ3hMluZyViGo629beCyx/PLxGRBCrmyENkZHB78FuclUFfVVOMV9U/ErVIlTEatvY4CbJcTMOrdvUHDQg8MNiwfPn11rt7g7mzej/+Iihh8fmtxqh/kizuwnCQci18PsIhP4NteC4t/uZq1oPZ9eOFpYhs9kRqPbzEggXQPLe4OEqmPHIJcra5xJqc9uZnF2yUAGgAMhxARibyUf+/mLjYCIcLdmlXtSbyRmLY77+WS86i+PJoxzyLYW2aiY/JBeYsPr5UQBXpiygyb3Q39qzxfvd9YPJr84vtjhLO6Rg657TeCJ6d9spnB2w1ABpIhCQg+RXHqbveehFgJqWUHZEoOArucyXjW1KAKhNEkRtPRe7Ll2cK0YwDJc7GoZvMdh3vOEiy74zNzqVNgu8Enl5jZUoBfgLQYAWG5DclIe4HCdC3aAPw/9M6EJKw4xjbl7N2GcoBZX3eoRYHKGb79w3Y87BugIoxf85J2gmCCQCen7bjSwFeCaDBhJAOSEkRpJylUgAsR2IxxcGnggOeKoCLAlQ3KwT7JvfqqB/GkRlK6ZXU74CIHywyeZ1i2bATgX4SAJ6ftuVLASYngAYPIQWQOiIhGbwelwAVkkUEtGY0AO+L4jy5BCAJQImuoMcWpjMvg4lmpDdkkhx6BwQE+IItBfwCnl7jSmQHqBIkbEhkUACpQ4ogxm/hIONbSRvVY5dIhdISZdeh/K+A+9DAHf3p3QYlWduiblC5yjHPTxFjV9wiWUhIygVun30kfG10xFEAsl1MkxrLJgQg5gBk9KEm1l68Sxr5Xb8961y/PLmk3ZGE1GXV0ZMxHT9JXssai6Fq0dwy7gin5ZwsRiqRX0671er0eBdXaTgry+jTuM9w4OXeT5sBxSGK2FWwCapd9aU+cAp6xgKQ4OCRjEwRLR/LdCU+X4L5eN+1si1HyYJLwgxR9txUb6uDhOuTpSSANZfp4Hzzfz69WgaiMJjC6ZLNlUMJcjzMZoRc+sw72dtymdppmUT41LKaVVs3Oz0A4HsysuHDHfvFfP4qZZ1M3d07bM+umCcn+Y65uGUyO7WVhW1dO+M8P/4Be9aTobOD2S990NuJFIrRwRFZwL9Y3ZylJUyJVsZwtV1sGzmXYo6jHwGyGPOpqjHg+/5ae0xz92nKD+PcSI0qpiBTljRcLOMQ5qlMeUwYNrGGpx9O9/P1XAxqSVFTic9HvTIdbeQEZmNPdBDaVr1RiJhU/BANNewGL6aOGPoM64xuZfop889+D7Ou6CvKtEruPMNp4Y+M78V7Ye7yfrx7f++8eA3VuVwOC+u7YWE9sjkvhbm4hcUO8NivPuq7dz7b7b7naZcqMS7b+Hh6X3o4Zdjv2bwrNdHWSw7j48VPbv3VR333pvcE",24:"data:audio/ogg;base64,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",25:"data:audio/ogg;base64,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", +26:"data:audio/ogg;base64,T2dnUwACAAAAAAAAAABaAiomAAAAADqahncBHgF2b3JiaXMAAAAAASJWAAAAAAAAHp0AAAAAAACpAU9nZ1MAAAAAAAAAAAAAWgIqJgEAAAAw8ExrDkn////////////////FA3ZvcmJpcx0AAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDA5MDcwOQEAAAAYAAAAQ29tbWVudD1Qcm9jZXNzZWQgYnkgU29YAQV2b3JiaXMiQkNWAQBAAAAYQhAqBa1jjjrIFSGMGaKgQsopxx1C0CGjJEOIOsY1xxhjR7lkikLJgdCQVQAAQAAApBxXUHJJLeecc6MYV8xx6CDnnHPlIGfMcQkl55xzjjnnknKOMeecc6MYVw5yKS3nnHOBFEeKcacY55xzpBxHinGoGOecc20xt5JyzjnnnHPmIIdScq4155xzpBhnDnILJeecc8YgZ8xx6yDnnHOMNbfUcs4555xzzjnnnHPOOeecc4wx55xzzjnnnHNuMecWc64555xzzjnnHHPOOeeccyA0ZBUAkAAAoKEoiuIoDhAasgoAyAAAEEBxFEeRFEuxHMvRJA0IDVkFAAABAAgAAKBIhqRIiqVYjmZpniZ6oiiaoiqrsmnKsizLsuu6LhAasgoASAAAUFEUxXAUBwgNWQUAZAAACGAoiqM4juRYkqVZngeEhqwCAIAAAAQAAFAMR7EUTfEkz/I8z/M8z/M8z/M8z/M8z/M8z/M8DQgNWQUAIAAAAIIoZBgDQkNWAQBAAAAIIRoZQ51SElwKFkIcEUMdQs5DqaWD4CmFJWPSU6xBCCF87z333nvvgdCQVQAAEAAAYRQ4iIHHJAghhGIUJ0RxpiAIIYTlJFjKeegkCN2DEEK4nHvLuffeeyA0ZBUAAAgAwCCEEEIIIYQQQggppJRSSCmmmGKKKcccc8wxxyCDDDLooJNOOsmkkk46yiSjjlJrKbUUU0yx5RZjrbXWnHOvQSljjDHGGGOMMcYYY4wxxhgjCA1ZBQCAAAAQBhlkkEEIIYQUUkgppphyzDHHHANCQ1YBAIAAAAIAAAAcRVIkR3IkR5IkyZIsSZM8y7M8y7M8TdRETRVV1VVt1/ZtX/Zt39Vl3/Zl29VlXZZl3bVtXdZdXdd1Xdd1Xdd1Xdd1Xdd1XdeB0JBVAIAEAICO5DiO5DiO5EiOpEgKEBqyCgCQAQAQAICjOIrjSI7kWI4lWZImaZZneZaneZqoiR4QGrIKAAAEABAAAAAAAICiKIqjOI4kWZamaZ6neqIomqqqiqapqqpqmqZpmqZpmqZpmqZpmqZpmqZpmqZpmqZpmqZpmqZpmqZpAqEhqwAACQAAHcdxHEdxHMdxJEeSJCA0ZBUAIAMAIAAAQ1EcRXIsx5I0S7M8y9NEz/RcUTZ1U1dtIDRkFQAACAAgAAAAAAAAx3M8x3M8yZM8y3M8x5M8SdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TdM0TQNCQ1YCAGQAABCTkEpOsVdGKcYktF4qpBST1HuomGJMOu2pQgYpB7mHSiGloNPeMqWQUgx7p5hCyBjqoYOQMYWw19pzz733HggNWREARAEAAMYgxhBjyDEmJYMSMcckZFIi55yUTkompaRWWsykhJhKi5FzTkonJZNSWgupZZJKayWmAgAAAhwAAAIshEJDVgQAUQAAiDFIKaQUUkoxp5hDSinHlGNIKeWcck45x5h0ECrnGHQOSqSUco45p5xzEjIHlXMOQiadAACAAAcAgAALodCQFQFAnAAAgJBzijEIEWMQQgkphVBSqpyT0kFJqYOSUkmpxZJSjJVzUjoJKXUSUiopxVhSii2kVGNpLdfSUo0txpxbjL2GlGItqdVaWqu5xVhzizX3yDlKnZTWOimtpdZqTa3V2klpLaTWYmktxtZizSnGnDMprYWWYiupxdhiyzW1mHNpLdcUY88pxp5rrLnHnIMwrdWcWss5xZh7zLHnmHMPknOUOimtdVJaS63VmlqrNZPSWmmtxpBaiy3GnFuLMWdSWiypxVhaijHFmHOLLdfQWq4pxpxTiznHWoOSsfZeWqs5xZh7iq3nmHMwNseeO0q5ltZ6Lq31XnMuQtbci2gt59RqDyrGnnPOwdjcgxCt5Zxq7D3F2HvuORjbc/Ct1uBbzUXInIPQufimezBG1dqDzLUImXMQOugidPDJeJRqLq3lXFrrPdYafM05CNFa7inG3lOLvdeem7C9ByFayz3F2IOKMfiaczA652JUrcHHnIOQtRahey9K5yCUqrUHmWtQMtcidPDF6KCLLwAAYMABACDAhDJQaMiKACBOAIBByDmlGIRKKQihhJRCKClVjEnImIOSMSellFJaCCW1ijEImWNSMsekhBJaKiW0EkppqZTSWiiltZZajCm1FkMpqYVSWiultJZaqjG1VmPEmJTMOSmZY1JKKa2VUlqrHJOSMSipg5BKKSnFUlKLlXNSMuiodBBKKqnEVFJpraTSUimlxZJSbCnFVFuLtYZSWiypxFZSajG1VFuLMdeIMSkZc1Iy56SUUlIrpbSWOSelg45K5qCkklJrpaQUM+akdA5KyiCjUlKKLaUSUyiltZJSbKWk1lqMtabUWi0ltVZSarGUEluLMdcWS02dlNZKKjGGUlprMeaaWosxlBJbKSnGkkpsrcWaW2w5hlJaLKnEVkpqsdWWY2ux5tRSjSm1mltsucaUU4+19pxaqzW1VGNrseZYW2+11pw7Ka2FUlorJcWYWouxxVhzKCW2klJspaQYW2y5thZjD6G0WEpqsaQSY2sx5hhbjqm1WltsuabUYq219hxbbj2lFmuLsebSUo01195jTTkVAAAw4AAAEGBCGSg0ZCUAEAUAABjDGGMQGqWcc05Kg5RzzknJnIMQQkqZcxBCSClzTkJKLWXOQUiptVBKSq3FFkpJqbUWCwAAKHAAAAiwQVNicYBCQ1YCAFEAAIgxSjEGoTFGKecgNMYoxRiESinGnJNQKcWYc1Ayx5yDUErmnHMQSgkhlFJKSiGEUkpJqQAAgAIHAIAAGzQlFgcoNGRFABAFAAAYY5wzziEKnaXOUiSpo9ZRayilGkuMncZWe+u50xp7bbk3lEqNqdaOa8u51d5pTT23HAsAADtwAAA7sBAKDVkJAOQBABDGKMWYc84ZhRhzzjnnDFKMOeecc4ox55yDEELFmHPOQQghc845CKGEkjnnHIQQSuicg1BKKaV0zkEIoZRSOucghFJKKZ1zEEoppZQCAIAKHAAAAmwU2ZxgJKjQkJUAQB4AAGAMQs5Jaa1hzDkILdXYMMYclJRii5yDkFKLuUbMQUgpxqA7KCm1GGzwnYSUWos5B5NSizXn3oNIqbWag8491VZzz733nGKsNefecy8AAHfBAQDswEaRzQlGggoNWQkA5AEAEAgpxZhzzhmlGHPMOeeMUowx5pxzijHGnHPOQcUYY845ByFjzDnnIISQMeaccxBC6JxzDkIIIXTOOQchhBA656CDEEIInXMQQgghhAIAgAocAAACbBTZnGAkqNCQlQBAOAAAACGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBC6JxzzjnnnHPOOeecc84555xzzjknAMi3wgHA/8HGGVaSzgpHgwsNWQkAhAMAAApBKKViEEopJZJOOimdk1BKKZGDUkrppJRSSgmllFJKCKWUUkoIHZRSQimllFJKKaWUUkoppZRSOimllFJKKaWUyjkppZNSSimlRM5JKSGUUkoppYRSSimllFJKKaWUUkoppZRSSimlhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEAgC4GxwAIBJsnGEl6axwNLjQkJUAQEgAAKAUc45KCCmUkFKomKKOQikppFJKChFjzknqHIVQUiipg8o5CKWklEIqIXXOQQclhZBSCSGVjjroKJRQUiollNI5KKWEFEpKKZWQQkipdJRSKCWVlEIqIZVSSkgllRBKCp2kVEoKqaRUUgiddJBCJyWkkkoKqZOUUiolpZRKSiV0UkIqKaUQQkqplBBKSCmlTlJJqaQUQighhZRSSiWlkkpKIZVUQgmlpJRSKKGkVFJKKaWSUikAAODAAQAgwAg6yaiyCBtNuPAAFBqyEgAgAwBAlHTWaadJIggxRZknDSnGILWkLMMQU5KJ8RRjjDkoRkMOMeSUGBdKCKGDYjwmlUPKUFG5t9Q5BcUWY3zvsRcBAAAIAgAEhAQAGCAomAEABgcIIwcCHQEEDm0AgIEImQkMCqHBQSYAPEBESAUAiQmK0oUuCCGCdBFk8cCFEzeeuOGEDm0QAAAAAAAQAPABAJBQABER0cxVWFxgZGhscHR4fICEBAAAAAAACAB8AAAkIkBERDRzFRYXGBkaGxwdHh8gIQEAAAAAAAAAAEBAQAAAAAAAIAAAAEBAT2dnUwAAgDoAAAAAAABaAiomAgAAAHySNeIfYWuEhIKLgo2NiZSPiIqJhoeNhYeFgYqPin+BhYmBgxzHDmW/pyIgAZoB10AdRqv2hdp9GI1Fbjg2Kxf6PaFSv/zf/wdC1HLGavR2GavZ9nHJ89VR6j6f2QitmMcB7OEXo9TNc0D6MP7/OYf/H/Y3pwjZzTobkB+XDUhFPzVqAAA8T02Sf4F5iRYAZAOwAAuw9YnCgfLhzVVV+3/f0yIYlMs0CgH56f7D0e5jjXuQPfeJi+BVRrjd3tqMXer9HGmLNZ8EodkcDGI4z6P/lGtca51o+8mAmuoPrL3ZMZD6YUYrym75OYUAitjJGxpJLSrgIADbd9MDYEp0UwuAaZNM7fqsFUUsHPTD2tOlsXv6w8+XVt3YUjNhyWN5+mMwjDBvdEQH+XghotKK8rv5o4n3EOpPAgATyf7u8enOEFN3QVZscZCFaNCtaJU+SFIxc5nofsmDZmZXzsLEJC/cdwzhzqSGgftNQwulbeBQWJrWAJ44XYkAEIH1fXgA5Ka2ApRpam0doOl0Zq7j9L3D88o3Gfu5eMnsVBSJ4hXydAJVvR2EFHwBjcFsRPy/h9HZY9BKazKLTzS17GFFlfN+KrfkQs/5bnexwNPWYTqOaNQZcqi/qlF1WDmc+nSZ4BwuXoPo5zjWAjehgdsesiNCLXIouQmne/4oXa05DURge78BgCDbbdICgHUMBZYHjKp61lWvW3SffxwE3hQRHIC8JlNyqp1BqUDPA4GTmMMgQYNJFCwqZbZx22ac96LcTeUGwfnIa+EIx8WudbJVHaM057imWz8WP8jN/RxQ5GGpUTaXCkVb8/+i27C7cq4VdKuhLEUJW7NsNgE+Se1nSTLVqXA3CFhyIQEHICWZR9eusmLKTvWsy+SAHd3YstGpYU8HkUgjQWCqN4oXmvH93mwdwcH9ydWnOrOVIqu+MVcOSYbu+/BfnbuVUn07y3gbZ6FG+GZd21/7kkwJw8j5GMm8Oh8S23V/pXck2jw1xLZrvq8YD3XlppVdzHY/4RX1ri2VSt0CHont1woOAOw1ARgCrVrCmjwAXbrjlR2aXOn/qaLihc5YjxIMV+1MNGJRdGQQfRz1ocdaVlHh9Qof5ziEAZvpXj2uclM8qd+3uhcBcF2iMpiLlky++JTIm3UZf6v71cxYV2Spak+oVM+qPRxy7yJx1BfnysPWoaxuzEO8YAk+FP1bHv5YnXiQYAbsNwEDguAVFLXhGT2ayxOq+iCKXftn96ps23gBAagfT6fVI5lGbDYUcjcHCay96icgO2U1sm8GWTCLChy2Tf+emLF5Pv5w375r8j9hAy5dwlm6tOGAAKwhd3f4myGE5PEOmZWXQ1nqNbf7FJl5IXZqJeN/bauuD1vWLa+chOTKx1S3cTitlv5o7WXgUDNgv+kABiygFZLdnGsA9/j7u7gyOmTb/AqLVixUMcCkSr4vEWdEXfLxVxVpGkXpZYd0ejWU4fszqx0ihiEUi4UKtfhSC2ZOVgdmCO/+yeMkcpljEURiVO+69frOMbCFaYeTQe8OqcB+so4jMV6YM887erG2Les23kU/3uZln8IcW6AeWz5OAR5prTIAzMC83xAAJLwCNJuJZk/cflzFkleHYdWyq+fG/8YCJ0KtriNZdeO2NZe4AQR+6A61YuPwXuQExw0ikeoJcFWjPgbK7mbD7+IY1OBOB7Quu2h1TqOqXZyT5THIezN0XN3nTnTM+/2fxrRA6mHMiHPMfY8ydeUKc81+S0+0V/qWD3hwTmktvogtJsABgLspuiTQYqID1R4M9TlttwYIn1X1fwWnindT/T9395PE8LItoSZoNCD4bryiuELlq4QbHDxIa9zfxWkkBNuUhu0t69Dg6ywqxBytsMzUfSMiDKvVOI2P1pm6hbe6VHzVmzWNnWRdfcwdzpvdWXdfB6kCZK/XBaWWhOuer79bZg9ua3WWxfnaSpQq6MiaOt447VXACRMwuZsAAAC0B8NMc7tu7630nzn73tq6EB4MZw3Po53Pm8AsqBAVA6oUyNT7uGnMAcICrJkpsQ2uWtNZrAC6G+gCh5NsQhp+1aV1jE1lxSN2AoNnzOZc0hdabB2I9a8UZCKW5JozsK6QlnFxdUMtSns0huOivS6EzqijtMlrnScrZCLFuLvrCIAC/jiNEwCYgOV+0wGAmaOS1nYwwKo/VlzP/GWLN7OqbtNdII2/Uyjl1KRwHgflN9YlO8J2DOkwXL38b3tNnWIrr98NDlDqSPq2hXjVigqlj1BFt4wBEDLdBw0L6Ywbd+SvhDFLTUyLeBPiGzc5BC+jW1e+zMKy7hRti8kKukiSiysvCZCZ8jqhAL5YLSoA6ID9pjAA4AAsYJHIrVVc/P7vLD5Hw4bS9+rfzTztMcDDEaLWwF68IuQJZPLtvXtdrptqE2SjVHhqMKYJze+R2yOmNaNLTW5NXdG9GVI9iZjTF4ocuvZManz3BlVn2+suTbKaX1g3QCkLXsqRKosFMWRxC1VKkS1pPxk1jw49BJlwMr1oHb5IjSsAEID1flMAEHAADAWAtDbizxBvV/p4CnKjVW5XuFqiXkQEu4qJ6kI1kVfABZBbeI7WWpbRqqqUuQvyjywUI8haxi5dGlZpw0PyYcK2XHZ7Knm737RhboIHUrGYfeDJBnmpxVr4CKk41dVFxnIHtURH5Cv1riO31KE+29VpWqsZQt4qtv8BnkjNUwDowHJXDQCgnuQC0OvrIwkAAIraXzkUUKFk7b6+WG1Dw4Fy3hrOVt13UfX6EBXR5ZgAAmKhzk7H9uq7odw0ywHLpSkWAX1f82zMamKDow+meNyS3eU5kqmXCPvs+OuLfagsGtvtHtVqPqXEJpXZ+mF9O7Ymk/BwiY4t7tZiYdBgIA7eOK1qDQcVcDdFAAsMwAbIxKhUa9o9lBcidxa+nfeuV/l+tBtj4yNZXJEY/l4ETQ4QrzLZO2xsdCEzm06rQ+Qk6ruzRSFn8aJWFUP4owPZ+Jt/T2v1qePIbY98t8rZod8TH3WzspD3HbcF1UXGgT6OHEqrsg87oxw7rI5jKEWdvAldkUvJ1gC+OK0KBQgTYFcFAOAAciZHIkcCAJAmqKIJBoGkx44pnPpileFHxaCyNW3u7EYqnH0Hut8XaTzntO61q1cSIX5cnEDTivB61A+KaGPUOX5DtpEX7rcB8EK+WPp0JPaJc7hdDXaw9ZoK4sfOHDTYTy6rB4N1sYewzeq3EptCvaeb+X9tfhq5hVFMmZuukzW+SC2ixGjdAfuHBwBQmwXwJIZXAE3+sJHysiYWHcW0YnD5DWjWwnu4AlDEy2eB0GSHWuWJQ2kylFSErVKhPcyoQXKd0wCt/z4tjAN2gPnvqC8aEu4Po1FXtY2LKqGzuhAH3eiOiwQ123XLNDgH0aUOl90pXVNMSoZgrURyIvadUKFxuqMDvggtKomBCOba9eEBREqYB2ABR+oq8UYMqten9K4K3P/Dt1qkgMYBCHVLhKoAVA19ANHTR+mqiUhV/qSd2xxxmc6R16JZGBx/Gi905ERb9utHJTswFobX0CGDNx9Vy4nP2nqgXPR1RsZiORlazUh769RQtKCEOFcHB9KU7bV6JRLTEsXdcHoc/vi0lQEggtR+EwCMpTanNWpx7YSu0A6OPGMll+00urgJx/T1rUVlVbCiBWQbX+3MkHWIAxzxf/NYiN5aaOeRkUsYBBFPG8jqHpLtsSC74Rhjv9aDzFwvCAHttp5hJNd243d9YaH/bAaWde5vGgrZPMxFV+8i137/+WNt6YYgJqV63+3EHb74jLUESACp/aYAAJHtqsNOTP0qFzV3EM9WUd7bQkAjcOeJFClp9dIpRqqDFK72/VI+WoOI/Bqvi6l6FwKo1lswIFD++w7c9MDk+ByweIw1l25o6WyZGm8YNCkbMyuC1bhIL7OsRizUD11vzaU6opQ5sBxIn7ch8b08eZJ1/L/OA94o1YWBCFYBd1MEgJC7AO0WgIqheAD0SJ/Qq1k/FsWqeKJZ9hbGoXV6JDAbnUrsiipQAPXdq0diE4g0QclValGalgRWr13l51+HjMXQ+0tV/xgaZLT6YvcrUh8BJ2JgwPEYYhrT86Jd2UJIw9mn5gr+XvqR8BLohXuhnDnLZJOwN9rAqliP9JxjAN4I1YUGIAL2mgIAdqLdAwAMw/AKFrrn26dcFa9366EbO4q/F6BUFXVuwrwFCMRAQF5j44jgW5HCo6XEebsteFCnGT1maw0IbEo07dP9rwTi3KEWhgrFq7VR6lma+ndosJzm6kF8Duk7Svfd7sb26E8J7mDDg7cCtoSQdhu5sIM1S492HLIDtc3bzVpHPCsA3gjVhQEgAvN+UwCWYLQHWACGYbQHiAe+CO82m4+1OotcRVpoBIA6fI0HRaUERNDizdbpAXUBpzmgwNULEl5iLBJQBupWVBmcIxrsvzWbtpnIYZQjv0LVIT8BjacrdNtSSl9peUqg6jI5jGDSZxiKhNW626lPi13E/jEGzUdcAmiHHm09hCIWMnsVvsjUfQkAkNs7PAGApTLStC8A/Vi/2xzE5LYtS5TZa8c7wdFSiPk7BzBKFIeIqjkUMbZUF5BZn55GnG4Lf99QHdgAx6x/1rZafTLbJvElvCjAPEK8cW7TuGxumdkcPVSYnPTWDe0rZ2ADhJWG9XEvcNknw2WYBN5yQsMI1zMcA77opBMNgALWd1MAABGAWv0kmWinH3oi3W6oLE7Snju6/hPvDowh4RDlBDCHGFdRkZ7PQEBr6JNzveMNubG0T3HWOe6trodELcrM1yVAd2GTuT7TopFWOcBbWXdv93nZJZ1jeKmgHbStlAMZdosNqakmpQ4MFtLvgemMteO87vWnA974lNnCQADsNwUAEAegNpL19fG/3QpyUN4SG4sKufpwUFBKo4Xhwz/HgUUWoXGMDYuSAm8SOdhBUz6e2hEAiLiVzNLiWLJt8XpY95vb6KoKSQ2L6sxHC1qUuTh/eJGdwbcP90irj/FcSTV7B2LTAJf8GlEVR9siY7cJJKV+hCqUKohUUwGe6NSRA6iAXVUAgDk3AEOekQCAIHAwkfq2BXBloxr3h9WyqKyGqkk26Zvyw8LReZf1YNlPHH3PQ7TufvI2hSN6knVp5r0Ogp7oYZj1t4ntXEe8xxCXpY4a/96LxFr6pLReSpGQMFHr9lGTc7ycPWF96/ky5j/G9oh4bIeow2ownCJCwxLYQJfSAr7Y5LUBoALzrg8PAKC1dCastXgOdkXD627qnrMbne6lAw+jhc6zWtEkZHakujtvby3LjlonVfpMabeY2uSHXoMI9BbCDFuo9eOLtLCyDhyrnHjMsMyL1Ldu92GaIqyRMSr925Ns6AbCnzbb3aMkeTo/BREcn0Id0aVkb3BqWqhak97YpLUGIALL/aYAAJvtAeLgII9SjwIzUsZ0gkPfxRYrfw0EdQEheA+CBDCkWqDfBUXBiLEo0Et1AwsK7MxQlKfb/mN4kJrv3BuOJ0h360awv69gMkqawTUXj/QXj4czMtkSbwtomWzsiKMOHeUqXfomtbt2tO1ACOH7Ks0SeKLIvhkAT2dnUwAE4F4AAAAAAABaAiomAwAAAL6j9qATiHqEfYOAhoaFg3t6eXp2f3RyVP7Y5NEAEAG7bgIApAVgHoBZydrxdSN30C96VZ87NX543DWjZC9fMgJ7BSrJqUuqQTlgzt6752Q4WWHYI3UzKzx6sNh5btr3iBwTYW3KzyORwRYLekWPLicYDLnIMm/sw6gXgtwcwIidsX8HOxuGXNQZpXt9y/CZltSqW+fTn6Gn7n1seqfNRAO+2KR9DUAA5v3DAwB2FoDE5AmnF98ky0VY5uRhv/9xDBwnQJruo4+ZjJZHJnfCBJCyoE/a3O7mfK88C96L3zvWuzYYcSuJ9B4rvioRYB9m6xXX0vpSU6aq2EmvaXXNkqOodhLmyDRhtiDMPOSa8wFHL2JWaYsrxWTbBp7Y5LUBAMD+4QEgiGgPMIcXARTr9Tgwu2HX/PVQe2hULeoCSClUilifQlbpvatXJZTd0f81pWMmsyTHdQ3TCOTQQn9/KjNaGIpMnzyJ/0rQuGRtpq0kfxeBdlFOwqhpB9u6njB55AP671QnE/sMEwTc7JZa1WA2TVAXxhwFURRBh7vyAJ7IlLWFAwC7PjwAgHYJM6236kXj2ObO8QwbqZ1t9v9G9l8XHsw67X4XAooGE9dTP5hIjTBFx3CvS/ZJdLX8ny0AyyffsUzjsTl0Iu1bMMxe0/YubHf/enS3zK2M3I4k8yxyOz1Jrc4E/DG8rsSFiXxLOKmRc0bmy3wY2HsdnsjEPQsBAPabIgAMoZbTtFIff8u8bkxa3Wev9sOmDzY75tIRip0WJv6jccAdyazbn0bs9kwc763dZ6gletHGuKDO1VdVLsZTv7haQF6gnkf9A8NcOSxc3u1w3jM6ne9FoYdDtmZlqL1aOJRN4ZuZZ/Xtqb6KdEBGb4ahRcx6heCghgVeqISpAQDA/uEBIE3XAwBYeXIMffAdVTl3RT3y/hNXHWjMCFR5E0S7R4tRjaAaIVv64KOJ+Dfd8SmRKXAqY7GM48rahC5dv4L1ctZbQyq+mtxZerrmoXu8PNDVAnYsiFo6mMfKuFfDHXF2+yro45r6diFmyLnfWV4a4jGjPw3yAB6opD1iAGzCrgYAANRM0zmGEgBQ806yQrkHWrg87V8YNwxKJUxKlAdl3ciqR3KX29D1cHWW1Nkrcf6XcVJUnES7GvEOpQ32zm191jblY0ZqGXvgpBjmslaLgWP7XIrN2r2L1WnIpOd21ztWOKvFaEi/jLoLGrg8CS0NMxOcM/rUoKVUlNcAPliUtUUBEVjuagAAgAcARZ7Wsl4JAMBCU8ZokymX6O+BHyKqoCLyZ16uhqaQjNbj8twK66DJeytRRvT3L6inL9KxQZCy3S9dHiX9i7hfYLbPcYGChF8WuRrUHQJB9qbwfvcYRZd2XWgBpa87F7Sa/hYOfePN3kFEJwhvC00ZTwqI93w+QAI+OISpBkAD864aAEAtV62jKwEA2Z+TN+qrCvLxrR7+mL6qXyeU+0R8GMDoKpVSKvf5/JFPMxWgbX3tL30lTotN+3ZXY2l4f6QTG83vaqEAAOl0TdxPRXEJEOL22xMW6j7VNfZ+r4dIhifsZbenU/OYiThGRN2h0xFarFS9P1kdel/SEasf/kfEfQ4gAPOuBgAAaMlNa9SrHACI+181zSHHiiIOx/7LrqSqgjAs3asb1+zZ/jlBLhHZ7n1UPVUCMBxMrP+3tVAW/GSxOmj5kAiPLmGvuOp09A2hgXPHPmgSSiIvKlFJMT5s36JDKalT8Ud1q1nUuZfa9upWo0aDgNq42ny8cGRifQEeSMQ9GwCA/cMTABIzcyUdXef1Y8lVM6q61UWrcSO3CeNgDwycjUpHdHbmHj3vpRgwrYzKLFY+ljrZdJmvdYGz3fyVs4jK0IIO45uEE4n3kd41R3cqdQ82MvNC47xUklsdhFFj5+xfDz3aWT3UOk2aptre660JTyFsmAn+N6R9gwQA9g9PAIBplxnJEXE4yFynUqPi23jNvz/xS96WS+qGRlviSFbSGMwTJ2Q/j8gAPRBVP4unnJNA9vUu92xah8SGKMOSAg5eQ0y0tldJHZizLhD9QVIVWhxHboG6T7D1rnZnMZH/ZsuvjxSl8IJrndiiPMF1Cv4HpIWGgwosd90UACRtk2RqR6m/vj2mar2y2wy+dh9ePt16ZgykfnHJ7hFdmBE7S6IQA7RW/9lr7KhKaB3D098F9ou35y0uFuqTTYLQ42eO5R8mbEks44O+lm5gLGm/9984MaNwI3xdNLPZKgMUa2li+FrDuT+5WgAe6MPRSQGVDvuHAADtkpznohY0ocZw3JhmOs13W931N19xwaGtyNUMQJWdmpYRd5WJiDzqXd07PINNYtQ9VWeETavvp4vN1j5Sjd9xNc5429LoMrd6Cy88G+rH6zxdvp4LK4fZiYPCk9LVisUMSaYBGC53NrCDf598AB4IVD0bABzcBQBIgHJyQ/3RAV32mKv4u3V3HpZE8wMXPxqgcaANEFED92WchlIkAJqNT+Izsq6HOH6/ADBAvwtbteziY2e4LeNyMcDv/KbC59AyKct43CkB0xN6Ir5exw5lDQfgFpbZx6q0gDjppJPkKy2RvwL+51O1BwAs2CsABGkM7mkdoKZOnZz6JFIbzVaJ2puaEvirVYXyCoPfZsf3q97YZAuzlu2gh327Dzm0h9CdUVRVnbAJ+3SXddAX6wqxi1rsUh7dmzKq/C9MwzKW9NyU0NdQ6hC8MC0wrSGYhJMyQ6C7FiBSChjKmyFKAf6tdfcAHngLlQ+AymOwXwAA1ZFk9TU98mTXZv3wuHJ3zC5M6wTa/JIpgUwPdzAZky9wERNVDx0LyWbBDMifETO4yL2nC6E09s1sAhBKJhanMPZi6Zc8f8yjFiWG35mY28CTTBJXlT+TOMA8JOAMFtxLYHsLytNZLgMeeGuVPwAk2GkCjDGMANnpjqZfsgY6b5rZWIqnS19qI1NXmRoFJZAPrzYtNNlytE70cO/N/5+PdTqz3y90IrDY+Ko8ChHd7+o5GJKhHgFgydxn5xI6OdTSd/w63LQH6y3E+zTD+83elCriObkcPKcK2hj+l/mIEQeVY23mYaWYTWeA3JxGwApNthj1cB/ZsoFs2b6Vo3Dzwi7pWuTeaaQAbethWwcAwHcWgNkun88C9AKb5jQCmq5/jnqno63Zk9wckQKOpQM=",27:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAASqBQAAAAAAAG4u6AcCAAAAXOb1uAdZXFhcW1pRhpfBYypABRNFWR9Pne09s1//lGYwLu882X7RJejEatfg6WuD6GoV6QO7I6VuzSHeeQwmXt8f1phaujVerOP6HNmQEdgZR/TC0EOhi7bVcv8zFmCDb24gHgWenLFz2Xj34RyA6KsloVFX7TT1ed3M1w9/yxR4hlRSYoW3i+Hx4I37QCpEeRCLwSSeLhc2bsx2EFPr78l0pl5rOeDfFXM7H5PCOfb6AX+7HEIsReosoSwM2/AaAKKdsWdKeDhOAIFageDFqWe7B9sljPOjPMYcR2az23+HnarQ7pCocXAeI0VM7Fmqbta18Xsm/olmDNOe3i2Khl/h7CCDOjvT0ary9lK5wW709b3KjT7L8BSqoLFxmQFFAiSAqMD5a7X7+fAgis5fhvcNvDX95BOn3bNdQjXmFIXmqSMF6l8mAbryKuBTGPXwupKnepjbiDr45iW/+fezbRRePAiuMqAiT+lxkZmqIolbdf2pK55cW/Nlg2kkEIoEqFWStKb+z0Lj1o3urF5k+s4aL9XRcc0t8YDmupAVomCsBfVVoOy741d6PyXbVh/dKkWtPEyT77b7W6kYwgegDks0I4EpBKbdTjEcLeIrjhOimqFflVoVig7wffXu/OF0Zc3z8y3LM75+xX5HGU+awdsrKXRqwdSxryoCjtetYLx1x+7NlsnFqVvdDa9hUakmFZ98388+64gokqZUfyN+CvMsjZNxXOVy1wJ6kl46DVABIgNRRf9suzuzzdfC54fNf17byBlAa20tw6xH6jHA/BGAupsfF1AEdQ7bu2VYBADq2DlGkt4sdEjqOKuK0I/Fx+NBzdRNxv0ypgA=",28:"data:audio/ogg;base64,T2dnUwACAAAAAAAAAAD/0yYKAAAAAP0h4PkBHgF2b3JiaXMAAAAAARErAAAAAAAASHEAAAAAAACZAU9nZ1MAAAAAAAAAAAAA/9MmCgEAAABkjZouC0n///////////+1A3ZvcmJpcx0AAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDA5MDcwOQEAAAAYAAAAQ29tbWVudD1Qcm9jZXNzZWQgYnkgU29YAQV2b3JiaXMSQkNWAQAAAQAMUhQhJRlTSmMIlVJSKQUdY1BbRx1j1DlGIWQQU4hJGaV7TyqVWErIEVJYKUUdU0xTSZVSlilFHWMUU0ghU9YxZaFzFEuGSQklbE2udBZL6JljljFGHWPOWkqdY9YxRR1jUlJJoXMYOmYlZBQ6RsXoYnwwOpWiQii+x95S6S2FiluKvdcaU+sthBhLacEIYXPttdXcSmrFGGOMMcbF4lMogtCQVQAAAQAAQAQBQkNWAQAKAADCUAxFUYDQkFUAQAYAgAAURXEUx3EcR5IkywJCQ1YBAEAAAAIAACiO4SiSI0mSZFmWZVmWpnmWqLmqL/uuLuuu7eq6DoSGrAQAyAAAGIYhh95JzJBTkEkmKVXMOQih9Q455RRk0lLGmGKMUc6QUwwxBTGG0CmFENROOaUMIghDSJ1kziBLPejgYuc4EBqyIgCIAgAAjEGMIcaQcwxKBiFyjknIIETOOSmdlExKKK20lkkJLZXWIueclE5KJqW0FlLLpJTWQisFAAAEOAAABFgIhYasCACiAAAQg5BSSCnElGJOMYeUUo4px5BSzDnFmHKMMeggVMwxyByESCnFGHNOOeYgZAwq5hyEDDIBAAABDgAAARZCoSErAoA4AQCDJGmapWmiaGmaKHqmqKqiKKqq5Xmm6ZmmqnqiqaqmqrquqaqubHmeaXqmqKqeKaqqqaqua6qq64qqasumq9q26aq27MqybruyrNueqsq2qbqybqqubbuybOuuLNu65Hmq6pmm63qm6bqq69qy6rqy7Zmm64qqK9um68qy68q2rcqyrmum6bqiq9quqbqy7cqubbuyrPum6+q26sq6rsqy7tu2rvuyrQu76Lq2rsqurquyrOuyLeu2bNtCyfNU1TNN1/VM03VV17Vt1XVtWzNN1zVdV5ZF1XVl1ZV1XXVlW/dM03VNV5Vl01VlWZVl3XZlV5dF17VtVZZ9XXVlX5dt3fdlWdd903V1W5Vl21dlWfdlXfeFWbd93VNVWzddV9dN19V9W9d9YbZt3xddV9dV2daFVZZ139Z9ZZh1nTC6rq6rtuzrqizrvq7rxjDrujCsum38rq0Lw6vrxrHrvq7cvo9q277w6rYxvLpuHLuwG7/t+8axqaptm66r66Yr67ps675v67pxjK6r66os+7rqyr5v67rw674vDKPr6roqy7qw2rKvy7ouDLuuG8Nq28Lu2rpwzLIuDLfvK8evC0PVtoXh1XWjq9vGbwvD0jd2vgAAgAEHAIAAE8pAoSErAoA4AQAGIQgVYxAqxiCEEFIKIaRUMQYhYw5KxhyUEEpJIZTSKsYgZI5JyByTEEpoqZTQSiilpVBKS6GU1lJqLabUWgyhtBRKaa2U0lpqKbbUUmwVYxAy56RkjkkopbRWSmkpc0xKxqCkDkIqpaTSSkmtZc5JyaCj0jlIqaTSUkmptVBKa6GU1kpKsaXSSm2txRpKaS2k0lpJqbXUUm2ttVojxiBkjEHJnJNSSkmplNJa5pyUDjoqmYOSSimplZJSrJiT0kEoJYOMSkmltZJKK6GU1kpKsYVSWmut1ZhSSzWUklpJqcVQSmuttRpTKzWFUFILpbQWSmmttVZrai22UEJroaQWSyoxtRZjba3FGEppraQSWympxRZbja21WFNLNZaSYmyt1dhKLTnWWmtKLdbSUoyttZhbTLnFWGsNJbQWSmmtlNJaSq3F1lqtoZTWSiqxlZJabK3V2FqMNZTSYikptZBKbK21WFtsNaaWYmyx1VhSizHGWHNLtdWUWouttVhLKzXGGGtuNeVSAADAgAMAQIAJZaDQkJUAQBQAAGAMY4xBaBRyzDkpjVLOOSclcw5CCCllzkEIIaXOOQiltNQ5B6GUlEIpKaUUWyglpdZaLAAAoMABACDABk2JxQEKDVkJAEQBACDGKMUYhMYgpRiD0BijFGMQKqUYcw5CpRRjzkHIGHPOQSkZY85BJyWEEEIppYQQQiillAIAAAocAAACbNCUWByg0JAVAUAUAABgDGIMMYYgdFI6KRGETEonpZESWgspZZZKiiXGzFqJrcTYSAmthdYyayXG0mJGrcRYYioAAOzAAQDswEIoNGQlAJAHAEAYoxRjzjlnEGLMOQghNAgx5hyEECrGnHMOQggVY845ByGEzjnnIIQQQueccxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAATnAgAAAAAAAP/TJgoCAAAAJCM6ggRkYVpWhur1H7AmwPeVPpZX3/VcvfXEfsblW6epaS6fl6K3a7HtSj0WWzrqocTZ6uSHQTjsz/U9zDTx9PMWE1OPqxOi2esajHkXYjtrjPDjZc3fYN41czvMG8z/gd2djX7zE/LomrkdBnJgcZqyS8BiUQEUtah0XOHcfWQcvTo9+Ng4B6ihdYbeS576WqL9ExnnckhaZfiPI7HeTQeP9tBtpgkicTxDX5s1Xj6RzbIus+IkSsW3td+MB338DDG52dazdChV0tRMvAByXjXHX0I2LADIFJXjkbli8D76XyhvLmJvuy5tbS3+ymOdd5Jr2CbaaHRcLfknK013t3aT599K1uvpZwlPGted/3DjvFV/Rhl4eWRe0KjXnmwjtq58DHOMUQZyXT1ORYEkMlX+7xrjpNw+qp+/+vL4Eg7/anEsw+mSg/HlsMDKW6jTTYeOodDpDofmsgBU4Wxl/CdbrfPdPjb1NyGXg5+UHh4fF986PC46mTkpS36/pA==",29:"data:audio/ogg;base64,T2dnUwACAAAAAAAAAADhCO90AAAAANK5BqEBHgF2b3JiaXMAAAAAARErAAAAAAAASHEAAAAAAACZAU9nZ1MAAAAAAAAAAAAA4QjvdAEAAAA4Hq/AC0n///////////+1A3ZvcmJpcx0AAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDA5MDcwOQEAAAAYAAAAQ29tbWVudD1Qcm9jZXNzZWQgYnkgU29YAQV2b3JiaXMSQkNWAQAAAQAMUhQhJRlTSmMIlVJSKQUdY1BbRx1j1DlGIWQQU4hJGaV7TyqVWErIEVJYKUUdU0xTSZVSlilFHWMUU0ghU9YxZaFzFEuGSQklbE2udBZL6JljljFGHWPOWkqdY9YxRR1jUlJJoXMYOmYlZBQ6RsXoYnwwOpWiQii+x95S6S2FiluKvdcaU+sthBhLacEIYXPttdXcSmrFGGOMMcbF4lMogtCQVQAAAQAAQAQBQkNWAQAKAADCUAxFUYDQkFUAQAYAgAAURXEUx3EcR5IkywJCQ1YBAEAAAAIAACiO4SiSI0mSZFmWZVmWpnmWqLmqL/uuLuuu7eq6DoSGrAQAyAAAGIYhh95JzJBTkEkmKVXMOQih9Q455RRk0lLGmGKMUc6QUwwxBTGG0CmFENROOaUMIghDSJ1kziBLPejgYuc4EBqyIgCIAgAAjEGMIcaQcwxKBiFyjknIIETOOSmdlExKKK20lkkJLZXWIueclE5KJqW0FlLLpJTWQisFAAAEOAAABFgIhYasCACiAAAQg5BSSCnElGJOMYeUUo4px5BSzDnFmHKMMeggVMwxyByESCnFGHNOOeYgZAwq5hyEDDIBAAABDgAAARZCoSErAoA4AQCDJGmapWmiaGmaKHqmqKqiKKqq5Xmm6ZmmqnqiqaqmqrquqaqubHmeaXqmqKqeKaqqqaqua6qq64qqasumq9q26aq27MqybruyrNueqsq2qbqybqqubbuybOuuLNu65Hmq6pmm63qm6bqq69qy6rqy7Zmm64qqK9um68qy68q2rcqyrmum6bqiq9quqbqy7cqubbuyrPum6+q26sq6rsqy7tu2rvuyrQu76Lq2rsqurquyrOuyLeu2bNtCyfNU1TNN1/VM03VV17Vt1XVtWzNN1zVdV5ZF1XVl1ZV1XXVlW/dM03VNV5Vl01VlWZVl3XZlV5dF17VtVZZ9XXVlX5dt3fdlWdd903V1W5Vl21dlWfdlXfeFWbd93VNVWzddV9dN19V9W9d9YbZt3xddV9dV2daFVZZ139Z9ZZh1nTC6rq6rtuzrqizrvq7rxjDrujCsum38rq0Lw6vrxrHrvq7cvo9q277w6rYxvLpuHLuwG7/t+8axqaptm66r66Yr67ps675v67pxjK6r66os+7rqyr5v67rw674vDKPr6roqy7qw2rKvy7ouDLuuG8Nq28Lu2rpwzLIuDLfvK8evC0PVtoXh1XWjq9vGbwvD0jd2vgAAgAEHAIAAE8pAoSErAoA4AQAGIQgVYxAqxiCEEFIKIaRUMQYhYw5KxhyUEEpJIZTSKsYgZI5JyByTEEpoqZTQSiilpVBKS6GU1lJqLabUWgyhtBRKaa2U0lpqKbbUUmwVYxAy56RkjkkopbRWSmkpc0xKxqCkDkIqpaTSSkmtZc5JyaCj0jlIqaTSUkmptVBKa6GU1kpKsaXSSm2txRpKaS2k0lpJqbXUUm2ttVojxiBkjEHJnJNSSkmplNJa5pyUDjoqmYOSSimplZJSrJiT0kEoJYOMSkmltZJKK6GU1kpKsYVSWmut1ZhSSzWUklpJqcVQSmuttRpTKzWFUFILpbQWSmmttVZrai22UEJroaQWSyoxtRZjba3FGEppraQSWympxRZbja21WFNLNZaSYmyt1dhKLTnWWmtKLdbSUoyttZhbTLnFWGsNJbQWSmmtlNJaSq3F1lqtoZTWSiqxlZJabK3V2FqMNZTSYikptZBKbK21WFtsNaaWYmyx1VhSizHGWHNLtdWUWouttVhLKzXGGGtuNeVSAADAgAMAQIAJZaDQkJUAQBQAAGAMY4xBaBRyzDkpjVLOOSclcw5CCCllzkEIIaXOOQiltNQ5B6GUlEIpKaUUWyglpdZaLAAAoMABACDABk2JxQEKDVkJAEQBACDGKMUYhMYgpRiD0BijFGMQKqUYcw5CpRRjzkHIGHPOQSkZY85BJyWEEEIppYQQQiillAIAAAocAAACbNCUWByg0JAVAUAUAABgDGIMMYYgdFI6KRGETEonpZESWgspZZZKiiXGzFqJrcTYSAmthdYyayXG0mJGrcRYYioAAOzAAQDswEIoNGQlAJAHAEAYoxRjzjlnEGLMOQghNAgx5hyEECrGnHMOQggVY845ByGEzjnnIIQQQueccxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAASIEwAAAAAAAOEI73QCAAAAQU+othVgX1pcXVpfX1xaXFtdW1RZXVlbXVeGW/injbCx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",30:"data:audio/ogg;base64,T2dnUwACAAAAAAAAAADC3eUjAAAAAETz3psBHgF2b3JiaXMAAAAAARErAAAAAAAASHEAAAAAAACZAU9nZ1MAAAAAAAAAAAAAwt3lIwEAAAAlJrjtC0n///////////+1A3ZvcmJpcx0AAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDA5MDcwOQEAAAAYAAAAQ29tbWVudD1Qcm9jZXNzZWQgYnkgU29YAQV2b3JiaXMSQkNWAQAAAQAMUhQhJRlTSmMIlVJSKQUdY1BbRx1j1DlGIWQQU4hJGaV7TyqVWErIEVJYKUUdU0xTSZVSlilFHWMUU0ghU9YxZaFzFEuGSQklbE2udBZL6JljljFGHWPOWkqdY9YxRR1jUlJJoXMYOmYlZBQ6RsXoYnwwOpWiQii+x95S6S2FiluKvdcaU+sthBhLacEIYXPttdXcSmrFGGOMMcbF4lMogtCQVQAAAQAAQAQBQkNWAQAKAADCUAxFUYDQkFUAQAYAgAAURXEUx3EcR5IkywJCQ1YBAEAAAAIAACiO4SiSI0mSZFmWZVmWpnmWqLmqL/uuLuuu7eq6DoSGrAQAyAAAGIYhh95JzJBTkEkmKVXMOQih9Q455RRk0lLGmGKMUc6QUwwxBTGG0CmFENROOaUMIghDSJ1kziBLPejgYuc4EBqyIgCIAgAAjEGMIcaQcwxKBiFyjknIIETOOSmdlExKKK20lkkJLZXWIueclE5KJqW0FlLLpJTWQisFAAAEOAAABFgIhYasCACiAAAQg5BSSCnElGJOMYeUUo4px5BSzDnFmHKMMeggVMwxyByESCnFGHNOOeYgZAwq5hyEDDIBAAABDgAAARZCoSErAoA4AQCDJGmapWmiaGmaKHqmqKqiKKqq5Xmm6ZmmqnqiqaqmqrquqaqubHmeaXqmqKqeKaqqqaqua6qq64qqasumq9q26aq27MqybruyrNueqsq2qbqybqqubbuybOuuLNu65Hmq6pmm63qm6bqq69qy6rqy7Zmm64qqK9um68qy68q2rcqyrmum6bqiq9quqbqy7cqubbuyrPum6+q26sq6rsqy7tu2rvuyrQu76Lq2rsqurquyrOuyLeu2bNtCyfNU1TNN1/VM03VV17Vt1XVtWzNN1zVdV5ZF1XVl1ZV1XXVlW/dM03VNV5Vl01VlWZVl3XZlV5dF17VtVZZ9XXVlX5dt3fdlWdd903V1W5Vl21dlWfdlXfeFWbd93VNVWzddV9dN19V9W9d9YbZt3xddV9dV2daFVZZ139Z9ZZh1nTC6rq6rtuzrqizrvq7rxjDrujCsum38rq0Lw6vrxrHrvq7cvo9q277w6rYxvLpuHLuwG7/t+8axqaptm66r66Yr67ps675v67pxjK6r66os+7rqyr5v67rw674vDKPr6roqy7qw2rKvy7ouDLuuG8Nq28Lu2rpwzLIuDLfvK8evC0PVtoXh1XWjq9vGbwvD0jd2vgAAgAEHAIAAE8pAoSErAoA4AQAGIQgVYxAqxiCEEFIKIaRUMQYhYw5KxhyUEEpJIZTSKsYgZI5JyByTEEpoqZTQSiilpVBKS6GU1lJqLabUWgyhtBRKaa2U0lpqKbbUUmwVYxAy56RkjkkopbRWSmkpc0xKxqCkDkIqpaTSSkmtZc5JyaCj0jlIqaTSUkmptVBKa6GU1kpKsaXSSm2txRpKaS2k0lpJqbXUUm2ttVojxiBkjEHJnJNSSkmplNJa5pyUDjoqmYOSSimplZJSrJiT0kEoJYOMSkmltZJKK6GU1kpKsYVSWmut1ZhSSzWUklpJqcVQSmuttRpTKzWFUFILpbQWSmmttVZrai22UEJroaQWSyoxtRZjba3FGEppraQSWympxRZbja21WFNLNZaSYmyt1dhKLTnWWmtKLdbSUoyttZhbTLnFWGsNJbQWSmmtlNJaSq3F1lqtoZTWSiqxlZJabK3V2FqMNZTSYikptZBKbK21WFtsNaaWYmyx1VhSizHGWHNLtdWUWouttVhLKzXGGGtuNeVSAADAgAMAQIAJZaDQkJUAQBQAAGAMY4xBaBRyzDkpjVLOOSclcw5CCCllzkEIIaXOOQiltNQ5B6GUlEIpKaUUWyglpdZaLAAAoMABACDABk2JxQEKDVkJAEQBACDGKMUYhMYgpRiD0BijFGMQKqUYcw5CpRRjzkHIGHPOQSkZY85BJyWEEEIppYQQQiillAIAAAocAAACbNCUWByg0JAVAUAUAABgDGIMMYYgdFI6KRGETEonpZESWgspZZZKiiXGzFqJrcTYSAmthdYyayXG0mJGrcRYYioAAOzAAQDswEIoNGQlAJAHAEAYoxRjzjlnEGLMOQghNAgx5hyEECrGnHMOQggVY845ByGEzjnnIIQQQueccxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAQGBgAAAAAAAMLd5SMCAAAA7Vsh5AhaXWJgXV5dInYgpugSCwBIAHLlj672HsqD8eA/W1XaYPlre2/+am+1RulIVnrWe2fGyb9bdbkcE6tuELWRWqnZbCYchviH7DIaQm2Tl+jx996/CkIHfeFvicL3CFrwed9kB4bleIUlNP4AX+0/S/UHwsGruuJ2Spx5JdXu8MfXZuWcR5NLMIO54Yc7SVZplAwHBwVVS6p90IoR1rDGSHDzy96AqodxFamVdh5Phf1wOI66lUBoNjqG4j0SussNEo7lrqeveRo1gN0M+Grl/2dh8voUzv4bVbbrROzN6Ln05Z1t/WdvHM5DVrhkuMxQp4X7USq854oWKiqZTBV3suPYDRsr5uaM5VK4kf4zW3gKvivZzMwnKfWAOuAGqiSG324CmqdeZT7x+vCoBgAHY6UC+EhaR6nCucbl8ksnl+MnFznb7G1Mbtn6a0bSM5q1nIklKf9aQ/nX7WdqAAGp6c9e/q2A6AAybmRkz8ElOt02z1R0M7vl0Gn8ehBHehPHigwMhmdTRWqwUUMgqpXXs8Dnzurj7f17M/ISoln/Pqw1pFYFFu5EoXsMKyQNq+RW9cxgyLWdG24WUI96PQ8kdUf66+DQzqihJuTq5iXPaSKpH+/Xk+n7tokwbKY/dMkCguOpUiiqWI+6RH60/uJiQ6KZd66tzXj4b79Xjf5glGS0cDO8kWssyT7uENaV3J2bM3G6+OKdY/BGWVexcG+LH8GbDPKeTONQgiEu7zpi5M42xgaVAlXCktTNF76/A3qe8XhW/AyoAYra/5uq8uCdT32uD2k7Mk7W/MXX224/3L0UJlvHk9Ux7reaOshQVZK0YwK/d5no0EA2dFBmZhY5LuT9kUJvbhZuflzg53s2n7vJdwAGQH/A5IV3FXIIWzgaNgBgDADM19efWmpIXet9O/nZ5QgAaHyXAMCsAQA=", +31:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAAAALgAAAAAAAPuagHwCAAAAjfqGNy9TXFxZWVZXVV1WW1ZZXFlZV1pZWlpZWV1cVlxd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",32:"data:audio/ogg;base64,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cxBCCKGDEEIIpZTSQQghhFJK6SCEEEIppXQQQgihlFIKAAAqcAAACLBRZHOCkaBCQ1YCAHkAAIAxSjknJaVGKcYgpBRboxRjEFJqrWIMQkqtxVgxBiGl1mLsIKTUWoy1dhBSai3GWkNKrcVYa84hpdZirDXX1FqMtebce2otxlpzzrkAANwFBwCwAxtFNicYCSo0ZCUAkAcAQCCkFGOMOYeUYowx55xDSjHGmHPOKcYYc8455xRjjDnnnHOMMeecc845xphzzjnnnHPOOeegg5A555xz0EHonHPOOQghdM455xyEEAoAACpwAAAIsFFkc4KRoEJDVgIA4QAAgDGUUkoppZRSSqijlFJKKaWUUgIhpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoplVJKKaWUUkoppZRSSimlACDfCgcA/wcbZ1hJOiscDS40ZCUAEA4AABjDGISMOSclpYYxCKV0TkpJJTWMQSilcxJSSimD0FpqpaTSUkoZhJRiCyGVlFoKpbRWaymptZRSKCnFGktKqaXWMuckpJJaS622mDkHpaTWWmqtxRBCSrG11lJrsXVSUkmttdZabS2klFprLcbWYmwlpZZaa6nF1lpMqbUWW0stxtZiS63F2GKLMcYaCwDgbnAAgEiwcYaVpLPC0eBCQ1YCACEBAAQySjnnnIMQQgghUoox56CDEEIIIURKMeacgxBCCCGEjDHnIIQQQgihlJAx5hyEEEIIIYRSOucghFBKCaWUUkrnHIQQQgillFJKCSGEEEIopZRSSikhhBBKKaWUUkopJYQQQiillFJKKaWEEEIopZRSSimllBBCKKWUUkoppZQSQgihlFJKKaWUUkIIpZRSSimllFJKKCGEUkoppZRSSgkllFJKKaWUUkopIZRSSimllFJKKaUAAIADBwCAACPoJKPKImw04cIDEAAAAAIAAkwAgQGCglEIAoQRCAAAAAAACAD4AABICoCIiGjmDA4QEhQWGBocHiAiJAAAAAAAAAAAAAAAAARPZ2dTAARYEAAAAAAAAJVZfDcCAAAA6idRKhJbW1taU1ZfV1laYltbWF5dVFh+obvRKowOAEH0X5yMU3Z7jr1yOeZW3VHi+fZ9cYG23prn9caXeKPW0SNQHtggdV3H0fX+3xmas7u7QXvEQzwhh9sZtaaLN4MRoLPZeBpPMKPed+6x3XmEeEUFgibMFdWEH/293YOtN5f/OTlbXf5eu+juUOxVp6j5Oa8vbj4chvjuMWQMilV9Op3KT+EOZdrvGIkm7LyEkjEGStZWqmFdOpBwrl2G4wNpr+YBl+Wxxom3Q6yqqYImjJ+yYAEAlRIf0t/mZ+WZP+LOmNoSuJ/Yh2ThU75yI9VyMbbxtwO/mfjDSKjLI++FVE9vjvVSHZB/exHndBab+S5bXz0S32Z1IYOs2e220JwZhIpEbuCqXAKGIcllRQ8AUJSYOnmn7+ns653DvXWF8y3+9MVraxtBD4geazy+bxiX8AkDR0kr+9724p1YAB/VA0oeoYQs4heR+uAd727njt/OuSTnKF3VuHirkDhHX304KAGOJKlWI1wFiuJvPelv/f36+snpDCHU+i3vEE9szSMpXeKL7/GU6UKE7jEBGf+LI/a10eBRgqEv+JGr1zYijrD+f/BCPZwSM7KbqXE/D2+2JhsSBI4pjNoD8J0S0h+6dD78PPrz+lrpaGpyk4kglsMPGGwG3Qw87muAGhFbOqpUAbFKG9UJ5suhc2Y758tj3w/RlWb3fdy6lPPMQnEPU5USNGLMEwwQrXlshicaFm3DYfgALIF6dn1x5e3ZTX8zT+g4Tn5Z7/pv9HYSv7syKUic+Nj3hzm6s3MsiTwbQ+NEfR8uW9mNEYc8wR2t3dXe7d6u7ndhbouz3k7zAer1pk1iLlejwuS0XAGGpExThz3wFUsnzfeoe6j1zF/srQ7M8WE/Q/22WHkZDgsJbEfgLUwCspRrGaa9ils8oVP7t1etJyD8kehiYvB462S+Oz7bj9F9zFcjd9aZSUr6NhO0UgCGYChrAQCw+sq//9y3LtfTznVW275arS9ahrlcfr2vhAfSrKq7v93Ue2yxMruwP4kGq2gax9N4ItXkmdxmaNZTAgBVWtteZx264TAWnAech9hlGrtI/LwzDX4lBtQl2Ex01L7/mH+907b+Yfbq5uqks+BD5YeuNyjf60GI5PrKgyRR6A3KxP0F306RDrRapzDk8BZj+ShM1bd/RUok1fsAcT9gTyjhOqZ5RgeL67iUQzZEA4KlD7bMWHgAiOqg99/vi9tHNr40NTV5MDmyifMTHX6ke9+ShNO91RI6SlGCmofQngbylbOqU0hWLwCdvSGTXNwtdO6uw/ai5R0Mh8FW9XmIdn6oK4muBZd07/1sd+5RH0UHhiQh6hKnbFBH5cHvrt3KL3XupLMmK6b2kITwmP1978uBpohD3UHFJN2vHjyZUI6WWonSKWYQtI9gu8v5qqLCiZOYEJ9CuGpj6UfxcXFrGA9CCViEh5rRhzPyA4Yf/dkIFADEqDy8cWt31NfTPE5l6Vy1hk6N+6uGtXmuOCmxAg5rSJHjpJBpWNArt4XBItjk4IxYnfoKIbgMbeTJDfPObRlmZs4XpDmjTOMpVItU6frR3t2sWsuSJNWZek8aAKuiWH52J58c7zMfnYzN9117XaYGm9QoT8f8sPd5kqiWLo8UoxIik4pdXrx0tEbJIwGbqiFYbdBXYdA+nsmW/CmCARUxtAlY8p62TclGoR0wfiUKGyp9BeDxVb629FdSuvsfri+Hkp1BdxPqG8128WHq7F1Pdw+JJ7PcGiU5algDmfeSMSNfKTjeKjmLDJMUQ2yz+NgK0T/m6Ie1vL3wVutOfgpgzVNd6lu5yV5OBIYiQN1IFMToG5PPX0nebLz8fTYdjd1xECXLwCsgDNOdANzw9FjcQCmlMETRNsHl08+khzV84uQwUM65SZsuWRAQuWwEl/AD4d7BqIXQelI8tyJQsiksxvK0RYvnDnqfUFgkAGBRlP4P2B09OJiug69nVhbm5x1xnf+z0fFkzUXGGdI+PL0aMM8D0Unl1owz0yvJYdmfguyPg1JvehKdcwuTt+P2jBtDyMmcosnOWNd8IHIS1yvDGgAUReFgetkYvvwv7fmlNZmcbudXwpczK691fDK5rElMvptiAhqgc6BbSx1/1GE/SrFJEt4NosxJuiHRDB51X+vWYWpmR6KR6Pa+9T29y/Vv1w8="}); \ No newline at end of file diff --git a/public/css/animate.css b/public/css/animate.css new file mode 100755 index 00000000..db9eca25 --- /dev/null +++ b/public/css/animate.css @@ -0,0 +1,3272 @@ +@charset "UTF-8"; + +/*! +Animate.css - http://daneden.me/animate +Licensed under the MIT license - http://opensource.org/licenses/MIT + +Copyright (c) 2015 Daniel Eden +*/ + +.animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +.animated.infinite { + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} + +.animated.hinge { + -webkit-animation-duration: 2s; + animation-duration: 2s; +} + +.animated.bounceIn, +.animated.bounceOut { + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +.animated.flipOutX, +.animated.flipOutY { + -webkit-animation-duration: .75s; + animation-duration: .75s; +} + +@-webkit-keyframes bounce { + from, 20%, 53%, 80%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 40%, 43% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0,-4px,0); + transform: translate3d(0,-4px,0); + } +} + +@keyframes bounce { + from, 20%, 53%, 80%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 40%, 43% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -30px, 0); + transform: translate3d(0, -30px, 0); + } + + 70% { + -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -15px, 0); + transform: translate3d(0, -15px, 0); + } + + 90% { + -webkit-transform: translate3d(0,-4px,0); + transform: translate3d(0,-4px,0); + } +} + +.bounce { + -webkit-animation-name: bounce; + animation-name: bounce; + -webkit-transform-origin: center bottom; + transform-origin: center bottom; +} + +@-webkit-keyframes flash { + from, 50%, 100% { + opacity: 1; + } + + 25%, 75% { + opacity: 0; + } +} + +@keyframes flash { + from, 50%, 100% { + opacity: 1; + } + + 25%, 75% { + opacity: 0; + } +} + +.flash { + -webkit-animation-name: flash; + animation-name: flash; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes pulse { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes pulse { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 50% { + -webkit-transform: scale3d(1.05, 1.05, 1.05); + transform: scale3d(1.05, 1.05, 1.05); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.pulse { + -webkit-animation-name: pulse; + animation-name: pulse; +} + +@-webkit-keyframes rubberBand { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes rubberBand { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(.95, 1.05, 1); + transform: scale3d(.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, .95, 1); + transform: scale3d(1.05, .95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.rubberBand { + -webkit-animation-name: rubberBand; + animation-name: rubberBand; +} + +@-webkit-keyframes shake { + from, 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, 30%, 50%, 70%, 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, 40%, 60%, 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +@keyframes shake { + from, 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 10%, 30%, 50%, 70%, 90% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 20%, 40%, 60%, 80% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } +} + +.shake { + -webkit-animation-name: shake; + animation-name: shake; +} + +@-webkit-keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + 100% { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +@keyframes swing { + 20% { + -webkit-transform: rotate3d(0, 0, 1, 15deg); + transform: rotate3d(0, 0, 1, 15deg); + } + + 40% { + -webkit-transform: rotate3d(0, 0, 1, -10deg); + transform: rotate3d(0, 0, 1, -10deg); + } + + 60% { + -webkit-transform: rotate3d(0, 0, 1, 5deg); + transform: rotate3d(0, 0, 1, 5deg); + } + + 80% { + -webkit-transform: rotate3d(0, 0, 1, -5deg); + transform: rotate3d(0, 0, 1, -5deg); + } + + 100% { + -webkit-transform: rotate3d(0, 0, 1, 0deg); + transform: rotate3d(0, 0, 1, 0deg); + } +} + +.swing { + -webkit-transform-origin: top center; + transform-origin: top center; + -webkit-animation-name: swing; + animation-name: swing; +} + +@-webkit-keyframes tada { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, 20% { + -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + } + + 30%, 50%, 70%, 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, 60%, 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes tada { + from { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 10%, 20% { + -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); + } + + 30%, 50%, 70%, 90% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40%, 60%, 80% { + -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.tada { + -webkit-animation-name: tada; + animation-name: tada; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes wobble { + from { + -webkit-transform: none; + transform: none; + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes wobble { + from { + -webkit-transform: none; + transform: none; + } + + 15% { + -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.wobble { + -webkit-animation-name: wobble; + animation-name: wobble; +} + +@-webkit-keyframes jello { + from, 11.1%, 100% { + -webkit-transform: none; + transform: none; + } + + 22.2% { + -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); + transform: skewX(-12.5deg) skewY(-12.5deg); + } + + 33.3% { + -webkit-transform: skewX(6.25deg) skewY(6.25deg); + transform: skewX(6.25deg) skewY(6.25deg); + } + + 44.4% { + -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); + transform: skewX(-3.125deg) skewY(-3.125deg); + } + + 55.5% { + -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); + transform: skewX(1.5625deg) skewY(1.5625deg); + } + + 66.6% { + -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); + transform: skewX(-0.78125deg) skewY(-0.78125deg); + } + + 77.7% { + -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); + transform: skewX(0.390625deg) skewY(0.390625deg); + } + + 88.8% { + -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + } +} + +@keyframes jello { + from, 11.1%, 100% { + -webkit-transform: none; + transform: none; + } + + 22.2% { + -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); + transform: skewX(-12.5deg) skewY(-12.5deg); + } + + 33.3% { + -webkit-transform: skewX(6.25deg) skewY(6.25deg); + transform: skewX(6.25deg) skewY(6.25deg); + } + + 44.4% { + -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); + transform: skewX(-3.125deg) skewY(-3.125deg); + } + + 55.5% { + -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); + transform: skewX(1.5625deg) skewY(1.5625deg); + } + + 66.6% { + -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); + transform: skewX(-0.78125deg) skewY(-0.78125deg); + } + + 77.7% { + -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); + transform: skewX(0.390625deg) skewY(0.390625deg); + } + + 88.8% { + -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + transform: skewX(-0.1953125deg) skewY(-0.1953125deg); + } +} + +.jello { + -webkit-animation-name: jello; + animation-name: jello; + -webkit-transform-origin: center; + transform-origin: center; +} + +@-webkit-keyframes bounceIn { + from, 20%, 40%, 60%, 80%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +@keyframes bounceIn { + from, 20%, 40%, 60%, 80%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 20% { + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(1.03, 1.03, 1.03); + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + -webkit-transform: scale3d(.97, .97, .97); + transform: scale3d(.97, .97, .97); + } + + 100% { + opacity: 1; + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } +} + +.bounceIn { + -webkit-animation-name: bounceIn; + animation-name: bounceIn; +} + +@-webkit-keyframes bounceInDown { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInDown { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -3000px, 0); + transform: translate3d(0, -3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, 25px, 0); + transform: translate3d(0, 25px, 0); + } + + 75% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, 5px, 0); + transform: translate3d(0, 5px, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInDown { + -webkit-animation-name: bounceInDown; + animation-name: bounceInDown; +} + +@-webkit-keyframes bounceInLeft { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInLeft { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + 0% { + opacity: 0; + -webkit-transform: translate3d(-3000px, 0, 0); + transform: translate3d(-3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(25px, 0, 0); + transform: translate3d(25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(5px, 0, 0); + transform: translate3d(5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInLeft { + -webkit-animation-name: bounceInLeft; + animation-name: bounceInLeft; +} + +@-webkit-keyframes bounceInRight { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + from { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +@keyframes bounceInRight { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + from { + opacity: 0; + -webkit-transform: translate3d(3000px, 0, 0); + transform: translate3d(3000px, 0, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + + 75% { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10px, 0, 0); + } + + 90% { + -webkit-transform: translate3d(-5px, 0, 0); + transform: translate3d(-5px, 0, 0); + } + + 100% { + -webkit-transform: none; + transform: none; + } +} + +.bounceInRight { + -webkit-animation-name: bounceInRight; + animation-name: bounceInRight; +} + +@-webkit-keyframes bounceInUp { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + from { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceInUp { + from, 60%, 75%, 90%, 100% { + -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + } + + from { + opacity: 0; + -webkit-transform: translate3d(0, 3000px, 0); + transform: translate3d(0, 3000px, 0); + } + + 60% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 75% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 90% { + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.bounceInUp { + -webkit-animation-name: bounceInUp; + animation-name: bounceInUp; +} + +@-webkit-keyframes bounceOut { + 20% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 50%, 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } +} + +@keyframes bounceOut { + 20% { + -webkit-transform: scale3d(.9, .9, .9); + transform: scale3d(.9, .9, .9); + } + + 50%, 55% { + opacity: 1; + -webkit-transform: scale3d(1.1, 1.1, 1.1); + transform: scale3d(1.1, 1.1, 1.1); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } +} + +.bounceOut { + -webkit-animation-name: bounceOut; + animation-name: bounceOut; +} + +@-webkit-keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes bounceOutDown { + 20% { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.bounceOutDown { + -webkit-animation-name: bounceOutDown; + animation-name: bounceOutDown; +} + +@-webkit-keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes bounceOutLeft { + 20% { + opacity: 1; + -webkit-transform: translate3d(20px, 0, 0); + transform: translate3d(20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.bounceOutLeft { + -webkit-animation-name: bounceOutLeft; + animation-name: bounceOutLeft; +} + +@-webkit-keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes bounceOutRight { + 20% { + opacity: 1; + -webkit-transform: translate3d(-20px, 0, 0); + transform: translate3d(-20px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.bounceOutRight { + -webkit-animation-name: bounceOutRight; + animation-name: bounceOutRight; +} + +@-webkit-keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes bounceOutUp { + 20% { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 40%, 45% { + opacity: 1; + -webkit-transform: translate3d(0, 20px, 0); + transform: translate3d(0, 20px, 0); + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.bounceOutUp { + -webkit-animation-name: bounceOutUp; + animation-name: bounceOutUp; +} + +@-webkit-keyframes fadeIn { + from { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +.fadeIn { + -webkit-animation-name: fadeIn; + animation-name: fadeIn; +} + +@-webkit-keyframes fadeInDown { + from { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInDown { + from { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInDown { + -webkit-animation-name: fadeInDown; + animation-name: fadeInDown; +} + +@-webkit-keyframes fadeInDownBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInDownBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInDownBig { + -webkit-animation-name: fadeInDownBig; + animation-name: fadeInDownBig; +} + +@-webkit-keyframes fadeInLeft { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInLeft { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInLeft { + -webkit-animation-name: fadeInLeft; + animation-name: fadeInLeft; +} + +@-webkit-keyframes fadeInLeftBig { + from { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInLeftBig { + from { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInLeftBig { + -webkit-animation-name: fadeInLeftBig; + animation-name: fadeInLeftBig; +} + +@-webkit-keyframes fadeInRight { + from { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInRight { + from { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInRight { + -webkit-animation-name: fadeInRight; + animation-name: fadeInRight; +} + +@-webkit-keyframes fadeInRightBig { + from { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInRightBig { + from { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInRightBig { + -webkit-animation-name: fadeInRightBig; + animation-name: fadeInRightBig; +} + +@-webkit-keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInUp { + -webkit-animation-name: fadeInUp; + animation-name: fadeInUp; +} + +@-webkit-keyframes fadeInUpBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInUpBig { + from { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.fadeInUpBig { + -webkit-animation-name: fadeInUpBig; + animation-name: fadeInUpBig; +} + +@-webkit-keyframes fadeOut { + from { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +@keyframes fadeOut { + from { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +.fadeOut { + -webkit-animation-name: fadeOut; + animation-name: fadeOut; +} + +@-webkit-keyframes fadeOutDown { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +@keyframes fadeOutDown { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +.fadeOutDown { + -webkit-animation-name: fadeOutDown; + animation-name: fadeOutDown; +} + +@-webkit-keyframes fadeOutDownBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +@keyframes fadeOutDownBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, 2000px, 0); + transform: translate3d(0, 2000px, 0); + } +} + +.fadeOutDownBig { + -webkit-animation-name: fadeOutDownBig; + animation-name: fadeOutDownBig; +} + +@-webkit-keyframes fadeOutLeft { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes fadeOutLeft { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +.fadeOutLeft { + -webkit-animation-name: fadeOutLeft; + animation-name: fadeOutLeft; +} + +@-webkit-keyframes fadeOutLeftBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +@keyframes fadeOutLeftBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(-2000px, 0, 0); + transform: translate3d(-2000px, 0, 0); + } +} + +.fadeOutLeftBig { + -webkit-animation-name: fadeOutLeftBig; + animation-name: fadeOutLeftBig; +} + +@-webkit-keyframes fadeOutRight { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +@keyframes fadeOutRight { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +.fadeOutRight { + -webkit-animation-name: fadeOutRight; + animation-name: fadeOutRight; +} + +@-webkit-keyframes fadeOutRightBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +@keyframes fadeOutRightBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(2000px, 0, 0); + transform: translate3d(2000px, 0, 0); + } +} + +.fadeOutRightBig { + -webkit-animation-name: fadeOutRightBig; + animation-name: fadeOutRightBig; +} + +@-webkit-keyframes fadeOutUp { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +@keyframes fadeOutUp { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +.fadeOutUp { + -webkit-animation-name: fadeOutUp; + animation-name: fadeOutUp; +} + +@-webkit-keyframes fadeOutUpBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +@keyframes fadeOutUpBig { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(0, -2000px, 0); + transform: translate3d(0, -2000px, 0); + } +} + +.fadeOutUpBig { + -webkit-animation-name: fadeOutUpBig; + animation-name: fadeOutUpBig; +} + +@-webkit-keyframes flip { + from { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(.95, .95, .95); + transform: perspective(400px) scale3d(.95, .95, .95); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +@keyframes flip { + from { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + transform: perspective(400px) rotate3d(0, 1, 0, -360deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 40% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } + + 50% { + -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 80% { + -webkit-transform: perspective(400px) scale3d(.95, .95, .95); + transform: perspective(400px) scale3d(.95, .95, .95); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } +} + +.animated.flip { + -webkit-backface-visibility: visible; + backface-visibility: visible; + -webkit-animation-name: flip; + animation-name: flip; +} + +@-webkit-keyframes flipInX { + from { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInX { + from { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInX { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInX; + animation-name: flipInX; +} + +@-webkit-keyframes flipInY { + from { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +@keyframes flipInY { + from { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + opacity: 0; + } + + 40% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + transform: perspective(400px) rotate3d(0, 1, 0, -20deg); + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; + } + + 60% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + transform: perspective(400px) rotate3d(0, 1, 0, 10deg); + opacity: 1; + } + + 80% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + transform: perspective(400px) rotate3d(0, 1, 0, -5deg); + } + + 100% { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } +} + +.flipInY { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipInY; + animation-name: flipInY; +} + +@-webkit-keyframes flipOutX { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutX { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + opacity: 0; + } +} + +.flipOutX { + -webkit-animation-name: flipOutX; + animation-name: flipOutX; + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; +} + +@-webkit-keyframes flipOutY { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +@keyframes flipOutY { + from { + -webkit-transform: perspective(400px); + transform: perspective(400px); + } + + 30% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + transform: perspective(400px) rotate3d(0, 1, 0, -15deg); + opacity: 1; + } + + 100% { + -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + transform: perspective(400px) rotate3d(0, 1, 0, 90deg); + opacity: 0; + } +} + +.flipOutY { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; + -webkit-animation-name: flipOutY; + animation-name: flipOutY; +} + +@-webkit-keyframes lightSpeedIn { + from { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + opacity: 1; + } + + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes lightSpeedIn { + from { + -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); + transform: translate3d(100%, 0, 0) skewX(-30deg); + opacity: 0; + } + + 60% { + -webkit-transform: skewX(20deg); + transform: skewX(20deg); + opacity: 1; + } + + 80% { + -webkit-transform: skewX(-5deg); + transform: skewX(-5deg); + opacity: 1; + } + + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.lightSpeedIn { + -webkit-animation-name: lightSpeedIn; + animation-name: lightSpeedIn; + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; +} + +@-webkit-keyframes lightSpeedOut { + from { + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +@keyframes lightSpeedOut { + from { + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); + transform: translate3d(100%, 0, 0) skewX(30deg); + opacity: 0; + } +} + +.lightSpeedOut { + -webkit-animation-name: lightSpeedOut; + animation-name: lightSpeedOut; + -webkit-animation-timing-function: ease-in; + animation-timing-function: ease-in; +} + +@-webkit-keyframes rotateIn { + from { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateIn { + from { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, -200deg); + transform: rotate3d(0, 0, 1, -200deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateIn { + -webkit-animation-name: rotateIn; + animation-name: rotateIn; +} + +@-webkit-keyframes rotateInDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInDownLeft { + -webkit-animation-name: rotateInDownLeft; + animation-name: rotateInDownLeft; +} + +@-webkit-keyframes rotateInDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInDownRight { + -webkit-animation-name: rotateInDownRight; + animation-name: rotateInDownRight; +} + +@-webkit-keyframes rotateInUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInUpLeft { + -webkit-animation-name: rotateInUpLeft; + animation-name: rotateInUpLeft; +} + +@-webkit-keyframes rotateInUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes rotateInUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -90deg); + transform: rotate3d(0, 0, 1, -90deg); + opacity: 0; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.rotateInUpRight { + -webkit-animation-name: rotateInUpRight; + animation-name: rotateInUpRight; +} + +@-webkit-keyframes rotateOut { + from { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +@keyframes rotateOut { + from { + -webkit-transform-origin: center; + transform-origin: center; + opacity: 1; + } + + 100% { + -webkit-transform-origin: center; + transform-origin: center; + -webkit-transform: rotate3d(0, 0, 1, 200deg); + transform: rotate3d(0, 0, 1, 200deg); + opacity: 0; + } +} + +.rotateOut { + -webkit-animation-name: rotateOut; + animation-name: rotateOut; +} + +@-webkit-keyframes rotateOutDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, 45deg); + transform: rotate3d(0, 0, 1, 45deg); + opacity: 0; + } +} + +.rotateOutDownLeft { + -webkit-animation-name: rotateOutDownLeft; + animation-name: rotateOutDownLeft; +} + +@-webkit-keyframes rotateOutDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutDownRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutDownRight { + -webkit-animation-name: rotateOutDownRight; + animation-name: rotateOutDownRight; +} + +@-webkit-keyframes rotateOutUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +@keyframes rotateOutUpLeft { + from { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: left bottom; + transform-origin: left bottom; + -webkit-transform: rotate3d(0, 0, 1, -45deg); + transform: rotate3d(0, 0, 1, -45deg); + opacity: 0; + } +} + +.rotateOutUpLeft { + -webkit-animation-name: rotateOutUpLeft; + animation-name: rotateOutUpLeft; +} + +@-webkit-keyframes rotateOutUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +@keyframes rotateOutUpRight { + from { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + opacity: 1; + } + + 100% { + -webkit-transform-origin: right bottom; + transform-origin: right bottom; + -webkit-transform: rotate3d(0, 0, 1, 90deg); + transform: rotate3d(0, 0, 1, 90deg); + opacity: 0; + } +} + +.rotateOutUpRight { + -webkit-animation-name: rotateOutUpRight; + animation-name: rotateOutUpRight; +} + +@-webkit-keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +@keyframes hinge { + 0% { + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 20%, 60% { + -webkit-transform: rotate3d(0, 0, 1, 80deg); + transform: rotate3d(0, 0, 1, 80deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + + 40%, 80% { + -webkit-transform: rotate3d(0, 0, 1, 60deg); + transform: rotate3d(0, 0, 1, 60deg); + -webkit-transform-origin: top left; + transform-origin: top left; + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + opacity: 1; + } + + 100% { + -webkit-transform: translate3d(0, 700px, 0); + transform: translate3d(0, 700px, 0); + opacity: 0; + } +} + +.hinge { + -webkit-animation-name: hinge; + animation-name: hinge; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollIn { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes rollIn { + from { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); + } + + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.rollIn { + -webkit-animation-name: rollIn; + animation-name: rollIn; +} + +/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ + +@-webkit-keyframes rollOut { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +@keyframes rollOut { + from { + opacity: 1; + } + + 100% { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); + } +} + +.rollOut { + -webkit-animation-name: rollOut; + animation-name: rollOut; +} + +@-webkit-keyframes zoomIn { + from { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 50% { + opacity: 1; + } +} + +@keyframes zoomIn { + from { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 50% { + opacity: 1; + } +} + +.zoomIn { + -webkit-animation-name: zoomIn; + animation-name: zoomIn; +} + +@-webkit-keyframes zoomInDown { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInDown { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInDown { + -webkit-animation-name: zoomInDown; + animation-name: zoomInDown; +} + +@-webkit-keyframes zoomInLeft { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInLeft { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInLeft { + -webkit-animation-name: zoomInLeft; + animation-name: zoomInLeft; +} + +@-webkit-keyframes zoomInRight { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInRight { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInRight { + -webkit-animation-name: zoomInRight; + animation-name: zoomInRight; +} + +@-webkit-keyframes zoomInUp { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomInUp { + from { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 60% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomInUp { + -webkit-animation-name: zoomInUp; + animation-name: zoomInUp; +} + +@-webkit-keyframes zoomOut { + from { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 100% { + opacity: 0; + } +} + +@keyframes zoomOut { + from { + opacity: 1; + } + + 50% { + opacity: 0; + -webkit-transform: scale3d(.3, .3, .3); + transform: scale3d(.3, .3, .3); + } + + 100% { + opacity: 0; + } +} + +.zoomOut { + -webkit-animation-name: zoomOut; + animation-name: zoomOut; +} + +@-webkit-keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomOutDown { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomOutDown { + -webkit-animation-name: zoomOutDown; + animation-name: zoomOutDown; +} + +@-webkit-keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); + transform: scale(.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +@keyframes zoomOutLeft { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); + transform: scale(.1) translate3d(-2000px, 0, 0); + -webkit-transform-origin: left center; + transform-origin: left center; + } +} + +.zoomOutLeft { + -webkit-animation-name: zoomOutLeft; + animation-name: zoomOutLeft; +} + +@-webkit-keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(2000px, 0, 0); + transform: scale(.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +@keyframes zoomOutRight { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); + } + + 100% { + opacity: 0; + -webkit-transform: scale(.1) translate3d(2000px, 0, 0); + transform: scale(.1) translate3d(2000px, 0, 0); + -webkit-transform-origin: right center; + transform-origin: right center; + } +} + +.zoomOutRight { + -webkit-animation-name: zoomOutRight; + animation-name: zoomOutRight; +} + +@-webkit-keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +@keyframes zoomOutUp { + 40% { + opacity: 1; + -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); + -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); + } + + 100% { + opacity: 0; + -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); + -webkit-transform-origin: center bottom; + transform-origin: center bottom; + -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); + } +} + +.zoomOutUp { + -webkit-animation-name: zoomOutUp; + animation-name: zoomOutUp; +} + +@-webkit-keyframes slideInDown { + from { + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInDown { + from { + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInDown { + -webkit-animation-name: slideInDown; + animation-name: slideInDown; +} + +@-webkit-keyframes slideInLeft { + from { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInLeft { + from { + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInLeft { + -webkit-animation-name: slideInLeft; + animation-name: slideInLeft; +} + +@-webkit-keyframes slideInRight { + from { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInRight { + from { + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInRight { + -webkit-animation-name: slideInRight; + animation-name: slideInRight; +} + +@-webkit-keyframes slideInUp { + from { + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +@keyframes slideInUp { + from { + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + visibility: visible; + } + + 100% { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.slideInUp { + -webkit-animation-name: slideInUp; + animation-name: slideInUp; +} + +@-webkit-keyframes slideOutDown { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +@keyframes slideOutDown { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0); + } +} + +.slideOutDown { + -webkit-animation-name: slideOutDown; + animation-name: slideOutDown; +} + +@-webkit-keyframes slideOutLeft { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes slideOutLeft { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } +} + +.slideOutLeft { + -webkit-animation-name: slideOutLeft; + animation-name: slideOutLeft; +} + +@-webkit-keyframes slideOutRight { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +@keyframes slideOutRight { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } +} + +.slideOutRight { + -webkit-animation-name: slideOutRight; + animation-name: slideOutRight; +} + +@-webkit-keyframes slideOutUp { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +@keyframes slideOutUp { + from { + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + + 100% { + visibility: hidden; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } +} + +.slideOutUp { + -webkit-animation-name: slideOutUp; + animation-name: slideOutUp; +} \ No newline at end of file diff --git a/public/css/atoms/buttons.css b/public/css/atoms/buttons.css new file mode 100644 index 00000000..d3bfc616 --- /dev/null +++ b/public/css/atoms/buttons.css @@ -0,0 +1,11 @@ +button, +.button { + background-color: white; + border: solid 1px black; + border-radius: 0.75rem; + font-size: 1rem; + font-weight: bold; + height: 3rem; + line-height: 3rem; + padding: 0 1rem; +} \ No newline at end of file diff --git a/public/css/autocomplete.css b/public/css/autocomplete.css new file mode 100644 index 00000000..078b5edc --- /dev/null +++ b/public/css/autocomplete.css @@ -0,0 +1,225 @@ +/** resets **/ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + outline: none; + -webkit-font-smoothing: antialiased; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { height: 101%; } +body { + background: #f0f0f0 url('images/bg.gif'); + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + color: #313131; + font-size: 62.5%; + line-height: 1; +} + +::selection { background: #a4dcec; } +::-moz-selection { background: #a4dcec; } +::-webkit-selection { background: #a4dcec; } + +::-webkit-input-placeholder { /* WebKit browsers */ + color: #ccc; + font-style: italic; +} +:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ + color: #ccc; + font-style: italic; +} +::-moz-placeholder { /* Mozilla Firefox 19+ */ + color: #ccc; + font-style: italic; +} +:-ms-input-placeholder { /* Internet Explorer 10+ */ + color: #ccc !important; + font-style: italic; +} + +br { display: block; line-height: 2.2em; } + +article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } +ol, ul { list-style: none; } + +input, textarea { + -webkit-font-smoothing: antialiased; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + outline: none; +} + +blockquote, q { quotes: none; } +blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } +strong { font-weight: bold; } + +table { border-collapse: collapse; border-spacing: 0; } +img { border: 0; max-width: 100%; } + +#topbar { + background: #4f4a41; + padding: 10px 0 10px 0; + text-align: center; +} + +#topbar a { + color: #fff; + font-size:1.3em; + line-height: 1.25em; + text-decoration: none; + opacity: 0.5; + font-weight: bold; +} + +#topbar a:hover { + opacity: 1; +} + +/** typography **/ + +h1 { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 2.5em; + line-height: 1.5em; + letter-spacing: -0.05em; + margin-bottom: 20px; + padding: .1em 0; + color: #444; + position: relative; + overflow: hidden; + white-space: nowrap; + text-align: center; +} +h1:before, +h1:after { + content: ""; + position: relative; + display: inline-block; + width: 50%; + height: 1px; + vertical-align: middle; + background: #f0f0f0; +} +h1:before { + left: -.5em; + margin: 0 0 0 -50%; +} +h1:after { + left: .5em; + margin: 0 -50% 0 0; +} +h1 > span { + display: inline-block; + vertical-align: middle; + white-space: normal; +} + +a { color: #5a9352; text-decoration: none; } +a:hover { text-decoration: underline; } + +.center { display: block; text-align: center; } + + +/** page structure **/ +#w { + display: block; + width: 750px; + margin: 0 auto; + padding-top: 30px; +} + +#content { + display: block; + width: 100%; + background: #fff; + padding: 25px 20px; + padding-bottom: 35px; + -webkit-box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px 0px; + -moz-box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px 0px; + box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 2px 0px; +} + +#searchfield { display: block; width: 100%; text-align: center; margin-bottom: 35px; } + +#searchfield form { + display: inline-block; + background: #eeefed; + padding: 0; + margin: 0; + padding: 5px; + border-radius: 3px; + margin: 5px 0 0 0; +} +#searchfield form .biginput { + width: 600px; + height: 40px; + padding: 0 10px 0 10px; + background-color: #fff; + border: 1px solid #c8c8c8; + border-radius: 3px; + color: #aeaeae; + font-weight:normal; + font-size: 1.5em; + -webkit-transition: all 0.2s linear; + -moz-transition: all 0.2s linear; + transition: all 0.2s linear; +} +#searchfield form .biginput:focus { + color: #858585; +} + + + +.flatbtn { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + display: inline-block; + outline: 0; + border: 0; + color: #f3faef; + text-decoration: none; + background-color: #6bb642; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + font-size: 1.2em; + font-weight: bold; + padding: 12px 22px 12px 22px; + line-height: normal; + text-align: center; + vertical-align: middle; + cursor: pointer; + text-transform: uppercase; + text-shadow: 0 1px 0 rgba(0,0,0,0.3); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 0 rgba(15, 15, 15, 0.3); + -moz-box-shadow: 0 1px 0 rgba(15, 15, 15, 0.3); + box-shadow: 0 1px 0 rgba(15, 15, 15, 0.3); +} +.flatbtn:hover { + color: #fff; + background-color: #73c437; +} +.flatbtn:active { + -webkit-box-shadow: inset 0 1px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow:inset 0 1px 5px rgba(0, 0, 0, 0.1); + box-shadow:inset 0 1px 5px rgba(0, 0, 0, 0.1); +} + + + +.autocomplete-suggestions { border: 1px solid #999; background: #fff; cursor: default; overflow: auto; } +.autocomplete-suggestion { padding: 10px 5px; font-size: 1.2em; white-space: nowrap; overflow: hidden; } +.autocomplete-selected { background: #f0f0f0; } +.autocomplete-suggestions strong { font-weight: normal; color: #3399ff; } \ No newline at end of file diff --git a/public/css/base.css b/public/css/base.css new file mode 100644 index 00000000..1d428868 --- /dev/null +++ b/public/css/base.css @@ -0,0 +1,181 @@ +form legend { + font-size: 1.8em; + padding-bottom: 10px; +} + +label { + cursor: pointer; +} + +.left { + text-align: left; +} + +p { + display: block; + font-size: 1.2em; + line-height: 1.5em; + margin-bottom: 22px; +} + +label { + font-size: 1.2em; +} + +.large { + font-size: 32px; +} + +.medium { + font-size: 24px !important; +} + + +.visible { + display: block !important; +} + +.main_content ul li { + list-style: disc; + padding-bottom:10px; + margin-bottom:10px; +} + +.main_content ul { + padding-bottom: 10px; + padding-left: 15px; +} + +.hint { + font-size: 1em; + font-style: italic; +} + +.error { + color: rgb(255, 2, 2) !important; +} + +/* TODO: better style for warning? */ +.warning { + color: rgb(255, 2, 2) !important; +} + +.warning a { + color: #337ab7 !important; +} + +.errorInput { + border: 2px solid red !important; + color: rgb(255, 2, 2) !important; +} + +.red { + color: red; +} + +.hidden { + display: none; + margin: 0px; + padding: 0px; +} + +.bigUl { + font-size: 22px; + margin: 0px; + padding: 0px !important; +} + +.bigUl li{ + list-style: none; + margin: 0px; + padding: 0px; +} + +.control-group { + padding-top: 10px; + padding-bottom: 10px; +} +.controls { + padding-bottom: 10px; +} +.form-control {} + +.threeDform { + color: black; + background-color: #f5f5f5; + border: solid; + border-width: 1px; + box-shadow: 10px 10px 5px #888888; + padding-top: 10px; + padding-bottom: 5px; + padding-left: 20px; + padding-right: 20px; + width: 100%; + margin-bottom: 30px; +} + +.forkBtn { + cursor: pointer; + font-size: 34px; + padding: 5px; +} + +.example { + font-size: 16px; +} + +.well { + background-color: #F5F5F5; + border: 1px solid #E1E1E1; + padding: 17px 17px 0px 17px; + padding-top: 15px; + /* padding-bottom: 15px; */ + padding-left: 20px; + padding-right: 20px; + width: 100%; + /* margin-bottom: 30px; */ +} + +.emailReminder { + display: block; +} + +.emailReminder .content { + color: black; + padding-top: 20px; + padding-bottom: 20px; + padding-left: 20px; + margin-bottom: 20px; + border: 1px solid #E1E1E1; + background-color: #F5F5F5; +} + +.emailReminder h2 { + padding: 0px; + margin: 0px; +} + +.closeWarning { + cursor: pointer; +} + +.boxAlert { + border: solid; + border-width: 1px; + box-shadow: 0px 7px 10px 10px #888888; + padding-top: 15px; + padding-bottom: 15px; + padding-right: 20px; + margin-bottom: 30px; + z-index: 99; + position: relative; + padding-left: 20px; +} + +.upgradeWarning { + font-size: 22px; +} +.planCall { + font-size: 22px; +} + diff --git a/public/css/bootstrap.css b/public/css/bootstrap.css deleted file mode 100644 index 4af8905e..00000000 --- a/public/css/bootstrap.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.0 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:before,:after{color:#000!important;text-shadow:none!important;background:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px;line-height:1.42857143 \0}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px;line-height:1.5 \0}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px;line-height:1.33 \0}_:-ms-fullscreen,:root input[type=date],_:-ms-fullscreen,:root input[type=time],_:-ms-fullscreen,:root input[type=datetime-local],_:-ms-fullscreen,:root input[type=month]{line-height:1.42857143}_:-ms-fullscreen.input-sm,:root input[type=date].input-sm,_:-ms-fullscreen.input-sm,:root input[type=time].input-sm,_:-ms-fullscreen.input-sm,:root input[type=datetime-local].input-sm,_:-ms-fullscreen.input-sm,:root input[type=month].input-sm{line-height:1.5}_:-ms-fullscreen.input-lg,:root input[type=date].input-lg,_:-ms-fullscreen.input-lg,:root input[type=time].input-lg,_:-ms-fullscreen.input-lg,:root input[type=datetime-local].input-lg,_:-ms-fullscreen.input-lg,:root input[type=month].input-lg{line-height:1.33}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm,.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,select.form-group-sm .form-control{height:30px;line-height:30px}textarea.input-sm,textarea.form-group-sm .form-control,select[multiple].input-sm,select[multiple].form-group-sm .form-control{height:auto}.input-lg,.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,select.form-group-lg .form-control{height:46px;line-height:46px}textarea.input-lg,textarea.form-group-lg .form-control,select[multiple].input-lg,select[multiple].form-group-lg .form-control{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none;visibility:hidden}.collapse.in{display:block;visibility:visible}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=radio],[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none;visibility:hidden}.tab-content>.active{display:block;visibility:visible}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important;visibility:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-size:12px;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.carousel-inner>.item.next,.carousel-inner>.item.active.right{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/public/css/bootstrap.min.css b/public/css/bootstrap.min.css new file mode 100755 index 00000000..d65c66b1 --- /dev/null +++ b/public/css/bootstrap.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/public/css/csshake.min.css b/public/css/csshake.min.css new file mode 100644 index 00000000..843ae531 --- /dev/null +++ b/public/css/csshake.min.css @@ -0,0 +1 @@ +.shake,.shake-little,.shake-slow,.shake-hard,.shake-horizontal,.shake-vertical,.shake-rotate,.shake-opacity,.shake-crazy,.shake-chunk{display:inline-block;transform-origin:center center}.shake-freeze,.shake-constant.shake-constant--hover:hover,.shake-trigger:hover .shake-constant.shake-constant--hover{animation-play-state:paused}.shake-freeze:hover,.shake-trigger:hover .shake-freeze,.shake:hover,.shake-trigger:hover .shake,.shake-little:hover,.shake-trigger:hover .shake-little,.shake-slow:hover,.shake-trigger:hover .shake-slow,.shake-hard:hover,.shake-trigger:hover .shake-hard,.shake-horizontal:hover,.shake-trigger:hover .shake-horizontal,.shake-vertical:hover,.shake-trigger:hover .shake-vertical,.shake-rotate:hover,.shake-trigger:hover .shake-rotate,.shake-opacity:hover,.shake-trigger:hover .shake-opacity,.shake-crazy:hover,.shake-trigger:hover .shake-crazy,.shake-chunk:hover,.shake-trigger:hover .shake-chunk{animation-play-state:running}@keyframes shake{2%{transform:translate(1.5px, 1.5px) rotate(-0.5deg)}4%{transform:translate(-1.5px, 1.5px) rotate(1.5deg)}6%{transform:translate(1.5px, 2.5px) rotate(-0.5deg)}8%{transform:translate(1.5px, -0.5px) rotate(0.5deg)}10%{transform:translate(2.5px, 0.5px) rotate(0.5deg)}12%{transform:translate(-1.5px, -0.5px) rotate(1.5deg)}14%{transform:translate(1.5px, 1.5px) rotate(1.5deg)}16%{transform:translate(0.5px, 0.5px) rotate(-0.5deg)}18%{transform:translate(-1.5px, -1.5px) rotate(1.5deg)}20%{transform:translate(1.5px, 2.5px) rotate(1.5deg)}22%{transform:translate(0.5px, 2.5px) rotate(1.5deg)}24%{transform:translate(2.5px, 1.5px) rotate(-0.5deg)}26%{transform:translate(0.5px, 1.5px) rotate(1.5deg)}28%{transform:translate(-0.5px, -1.5px) rotate(-0.5deg)}30%{transform:translate(-0.5px, 1.5px) rotate(-0.5deg)}32%{transform:translate(1.5px, 1.5px) rotate(-0.5deg)}34%{transform:translate(-1.5px, 0.5px) rotate(1.5deg)}36%{transform:translate(2.5px, 0.5px) rotate(-0.5deg)}38%{transform:translate(-0.5px, -0.5px) rotate(0.5deg)}40%{transform:translate(0.5px, -1.5px) rotate(-0.5deg)}42%{transform:translate(0.5px, 1.5px) rotate(1.5deg)}44%{transform:translate(2.5px, 0.5px) rotate(-0.5deg)}46%{transform:translate(2.5px, 0.5px) rotate(1.5deg)}48%{transform:translate(1.5px, 1.5px) rotate(-0.5deg)}50%{transform:translate(-0.5px, -0.5px) rotate(1.5deg)}52%{transform:translate(2.5px, -1.5px) rotate(0.5deg)}54%{transform:translate(0.5px, 2.5px) rotate(1.5deg)}56%{transform:translate(0.5px, 2.5px) rotate(-0.5deg)}58%{transform:translate(1.5px, 0.5px) rotate(-0.5deg)}60%{transform:translate(1.5px, 1.5px) rotate(-0.5deg)}62%{transform:translate(2.5px, -1.5px) rotate(1.5deg)}64%{transform:translate(2.5px, 1.5px) rotate(0.5deg)}66%{transform:translate(1.5px, 2.5px) rotate(1.5deg)}68%{transform:translate(-0.5px, 0.5px) rotate(0.5deg)}70%{transform:translate(-1.5px, 2.5px) rotate(1.5deg)}72%{transform:translate(1.5px, 2.5px) rotate(1.5deg)}74%{transform:translate(-0.5px, 2.5px) rotate(0.5deg)}76%{transform:translate(1.5px, 2.5px) rotate(0.5deg)}78%{transform:translate(2.5px, 0.5px) rotate(0.5deg)}80%{transform:translate(-1.5px, -0.5px) rotate(0.5deg)}82%{transform:translate(-0.5px, -1.5px) rotate(0.5deg)}84%{transform:translate(1.5px, 1.5px) rotate(-0.5deg)}86%{transform:translate(-0.5px, 2.5px) rotate(0.5deg)}88%{transform:translate(-1.5px, 2.5px) rotate(0.5deg)}90%{transform:translate(-0.5px, -0.5px) rotate(-0.5deg)}92%{transform:translate(2.5px, 1.5px) rotate(-0.5deg)}94%{transform:translate(1.5px, 2.5px) rotate(0.5deg)}96%{transform:translate(0.5px, 2.5px) rotate(-0.5deg)}98%{transform:translate(2.5px, 0.5px) rotate(1.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}}.shake:hover,.shake-trigger:hover .shake,.shake.shake-freeze,.shake.shake-constant{animation:shake 100ms ease-in-out infinite}@keyframes shake-little{2%{transform:translate(1px, 1px) rotate(0.5deg)}4%{transform:translate(1px, 1px) rotate(0.5deg)}6%{transform:translate(1px, 1px) rotate(0.5deg)}8%{transform:translate(1px, 1px) rotate(0.5deg)}10%{transform:translate(1px, 1px) rotate(0.5deg)}12%{transform:translate(1px, 0px) rotate(0.5deg)}14%{transform:translate(0px, 1px) rotate(0.5deg)}16%{transform:translate(1px, 0px) rotate(0.5deg)}18%{transform:translate(1px, 0px) rotate(0.5deg)}20%{transform:translate(0px, 0px) rotate(0.5deg)}22%{transform:translate(0px, 0px) rotate(0.5deg)}24%{transform:translate(0px, 1px) rotate(0.5deg)}26%{transform:translate(0px, 1px) rotate(0.5deg)}28%{transform:translate(0px, 1px) rotate(0.5deg)}30%{transform:translate(1px, 1px) rotate(0.5deg)}32%{transform:translate(0px, 1px) rotate(0.5deg)}34%{transform:translate(0px, 1px) rotate(0.5deg)}36%{transform:translate(1px, 0px) rotate(0.5deg)}38%{transform:translate(1px, 1px) rotate(0.5deg)}40%{transform:translate(1px, 0px) rotate(0.5deg)}42%{transform:translate(1px, 1px) rotate(0.5deg)}44%{transform:translate(0px, 0px) rotate(0.5deg)}46%{transform:translate(1px, 0px) rotate(0.5deg)}48%{transform:translate(1px, 1px) rotate(0.5deg)}50%{transform:translate(0px, 0px) rotate(0.5deg)}52%{transform:translate(1px, 1px) rotate(0.5deg)}54%{transform:translate(0px, 0px) rotate(0.5deg)}56%{transform:translate(0px, 1px) rotate(0.5deg)}58%{transform:translate(1px, 1px) rotate(0.5deg)}60%{transform:translate(0px, 0px) rotate(0.5deg)}62%{transform:translate(0px, 0px) rotate(0.5deg)}64%{transform:translate(0px, 0px) rotate(0.5deg)}66%{transform:translate(0px, 1px) rotate(0.5deg)}68%{transform:translate(1px, 1px) rotate(0.5deg)}70%{transform:translate(0px, 0px) rotate(0.5deg)}72%{transform:translate(0px, 0px) rotate(0.5deg)}74%{transform:translate(0px, 0px) rotate(0.5deg)}76%{transform:translate(0px, 1px) rotate(0.5deg)}78%{transform:translate(0px, 0px) rotate(0.5deg)}80%{transform:translate(1px, 1px) rotate(0.5deg)}82%{transform:translate(0px, 0px) rotate(0.5deg)}84%{transform:translate(1px, 0px) rotate(0.5deg)}86%{transform:translate(1px, 1px) rotate(0.5deg)}88%{transform:translate(1px, 1px) rotate(0.5deg)}90%{transform:translate(1px, 1px) rotate(0.5deg)}92%{transform:translate(1px, 1px) rotate(0.5deg)}94%{transform:translate(0px, 1px) rotate(0.5deg)}96%{transform:translate(0px, 1px) rotate(0.5deg)}98%{transform:translate(0px, 1px) rotate(0.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-little:hover,.shake-trigger:hover .shake-little,.shake-little.shake-freeze,.shake-little.shake-constant{animation:shake-little 100ms ease-in-out infinite}@keyframes shake-slow{2%{transform:translate(2px, -9px) rotate(2.5deg)}4%{transform:translate(-4px, 5px) rotate(-2.5deg)}6%{transform:translate(-8px, 8px) rotate(3.5deg)}8%{transform:translate(-7px, 4px) rotate(-1.5deg)}10%{transform:translate(7px, 0px) rotate(1.5deg)}12%{transform:translate(3px, 8px) rotate(-0.5deg)}14%{transform:translate(4px, 4px) rotate(0.5deg)}16%{transform:translate(-4px, -4px) rotate(3.5deg)}18%{transform:translate(-8px, -7px) rotate(3.5deg)}20%{transform:translate(-9px, 8px) rotate(2.5deg)}22%{transform:translate(-9px, -5px) rotate(-2.5deg)}24%{transform:translate(4px, -7px) rotate(-2.5deg)}26%{transform:translate(-1px, 3px) rotate(1.5deg)}28%{transform:translate(-3px, -7px) rotate(3.5deg)}30%{transform:translate(6px, -9px) rotate(2.5deg)}32%{transform:translate(8px, -5px) rotate(-2.5deg)}34%{transform:translate(7px, 8px) rotate(1.5deg)}36%{transform:translate(2px, 5px) rotate(-2.5deg)}38%{transform:translate(-6px, 0px) rotate(2.5deg)}40%{transform:translate(9px, 7px) rotate(-2.5deg)}42%{transform:translate(-2px, -2px) rotate(-0.5deg)}44%{transform:translate(0px, -6px) rotate(-2.5deg)}46%{transform:translate(-5px, 2px) rotate(1.5deg)}48%{transform:translate(-8px, -7px) rotate(3.5deg)}50%{transform:translate(-5px, -6px) rotate(-2.5deg)}52%{transform:translate(8px, 1px) rotate(-2.5deg)}54%{transform:translate(-1px, -1px) rotate(-2.5deg)}56%{transform:translate(5px, -1px) rotate(2.5deg)}58%{transform:translate(-6px, -8px) rotate(-2.5deg)}60%{transform:translate(5px, 5px) rotate(3.5deg)}62%{transform:translate(-4px, -2px) rotate(1.5deg)}64%{transform:translate(-5px, 7px) rotate(3.5deg)}66%{transform:translate(7px, 4px) rotate(0.5deg)}68%{transform:translate(-5px, -2px) rotate(-2.5deg)}70%{transform:translate(1px, 3px) rotate(-1.5deg)}72%{transform:translate(-6px, 0px) rotate(2.5deg)}74%{transform:translate(1px, 9px) rotate(2.5deg)}76%{transform:translate(10px, -5px) rotate(-2.5deg)}78%{transform:translate(-5px, 4px) rotate(3.5deg)}80%{transform:translate(-6px, 1px) rotate(0.5deg)}82%{transform:translate(9px, 10px) rotate(2.5deg)}84%{transform:translate(-1px, 5px) rotate(-1.5deg)}86%{transform:translate(4px, 1px) rotate(2.5deg)}88%{transform:translate(-5px, -7px) rotate(1.5deg)}90%{transform:translate(-8px, -2px) rotate(0.5deg)}92%{transform:translate(10px, -9px) rotate(-0.5deg)}94%{transform:translate(7px, 6px) rotate(-0.5deg)}96%{transform:translate(6px, 1px) rotate(-2.5deg)}98%{transform:translate(5px, 0px) rotate(1.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-slow:hover,.shake-trigger:hover .shake-slow,.shake-slow.shake-freeze,.shake-slow.shake-constant{animation:shake-slow 5s ease-in-out infinite}@keyframes shake-hard{2%{transform:translate(2px, -5px) rotate(2.5deg)}4%{transform:translate(4px, 6px) rotate(-1.5deg)}6%{transform:translate(-5px, 3px) rotate(-2.5deg)}8%{transform:translate(-8px, 7px) rotate(3.5deg)}10%{transform:translate(-2px, -8px) rotate(3.5deg)}12%{transform:translate(-9px, -1px) rotate(1.5deg)}14%{transform:translate(1px, -8px) rotate(-0.5deg)}16%{transform:translate(-3px, 10px) rotate(-1.5deg)}18%{transform:translate(9px, -4px) rotate(0.5deg)}20%{transform:translate(4px, 8px) rotate(2.5deg)}22%{transform:translate(3px, 2px) rotate(-0.5deg)}24%{transform:translate(-5px, 6px) rotate(2.5deg)}26%{transform:translate(-7px, -6px) rotate(0.5deg)}28%{transform:translate(3px, 0px) rotate(2.5deg)}30%{transform:translate(8px, -8px) rotate(2.5deg)}32%{transform:translate(-9px, -8px) rotate(2.5deg)}34%{transform:translate(-9px, 3px) rotate(2.5deg)}36%{transform:translate(-2px, 7px) rotate(2.5deg)}38%{transform:translate(8px, 7px) rotate(-1.5deg)}40%{transform:translate(4px, 0px) rotate(-1.5deg)}42%{transform:translate(-4px, -9px) rotate(-0.5deg)}44%{transform:translate(0px, -4px) rotate(2.5deg)}46%{transform:translate(4px, 2px) rotate(2.5deg)}48%{transform:translate(10px, -9px) rotate(2.5deg)}50%{transform:translate(3px, -6px) rotate(2.5deg)}52%{transform:translate(1px, 6px) rotate(0.5deg)}54%{transform:translate(3px, -1px) rotate(-1.5deg)}56%{transform:translate(-1px, -9px) rotate(0.5deg)}58%{transform:translate(7px, -4px) rotate(-0.5deg)}60%{transform:translate(2px, 0px) rotate(2.5deg)}62%{transform:translate(-5px, 3px) rotate(0.5deg)}64%{transform:translate(6px, -8px) rotate(3.5deg)}66%{transform:translate(1px, -3px) rotate(2.5deg)}68%{transform:translate(10px, 1px) rotate(1.5deg)}70%{transform:translate(0px, 7px) rotate(-0.5deg)}72%{transform:translate(-9px, 6px) rotate(3.5deg)}74%{transform:translate(8px, 0px) rotate(-0.5deg)}76%{transform:translate(0px, 5px) rotate(0.5deg)}78%{transform:translate(6px, 6px) rotate(-0.5deg)}80%{transform:translate(4px, 3px) rotate(-2.5deg)}82%{transform:translate(8px, -2px) rotate(3.5deg)}84%{transform:translate(0px, -8px) rotate(1.5deg)}86%{transform:translate(-2px, -8px) rotate(2.5deg)}88%{transform:translate(10px, -7px) rotate(2.5deg)}90%{transform:translate(2px, 10px) rotate(-0.5deg)}92%{transform:translate(-9px, 4px) rotate(2.5deg)}94%{transform:translate(-3px, 1px) rotate(1.5deg)}96%{transform:translate(-2px, -1px) rotate(0.5deg)}98%{transform:translate(7px, -9px) rotate(3.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-hard:hover,.shake-trigger:hover .shake-hard,.shake-hard.shake-freeze,.shake-hard.shake-constant{animation:shake-hard 100ms ease-in-out infinite}@keyframes shake-horizontal{2%{transform:translate(4px, 0) rotate(0)}4%{transform:translate(7px, 0) rotate(0)}6%{transform:translate(-2px, 0) rotate(0)}8%{transform:translate(-7px, 0) rotate(0)}10%{transform:translate(-1px, 0) rotate(0)}12%{transform:translate(-2px, 0) rotate(0)}14%{transform:translate(1px, 0) rotate(0)}16%{transform:translate(-8px, 0) rotate(0)}18%{transform:translate(-5px, 0) rotate(0)}20%{transform:translate(9px, 0) rotate(0)}22%{transform:translate(-5px, 0) rotate(0)}24%{transform:translate(6px, 0) rotate(0)}26%{transform:translate(2px, 0) rotate(0)}28%{transform:translate(2px, 0) rotate(0)}30%{transform:translate(-5px, 0) rotate(0)}32%{transform:translate(-7px, 0) rotate(0)}34%{transform:translate(10px, 0) rotate(0)}36%{transform:translate(1px, 0) rotate(0)}38%{transform:translate(-2px, 0) rotate(0)}40%{transform:translate(4px, 0) rotate(0)}42%{transform:translate(-8px, 0) rotate(0)}44%{transform:translate(5px, 0) rotate(0)}46%{transform:translate(9px, 0) rotate(0)}48%{transform:translate(6px, 0) rotate(0)}50%{transform:translate(-9px, 0) rotate(0)}52%{transform:translate(7px, 0) rotate(0)}54%{transform:translate(-9px, 0) rotate(0)}56%{transform:translate(-7px, 0) rotate(0)}58%{transform:translate(-8px, 0) rotate(0)}60%{transform:translate(3px, 0) rotate(0)}62%{transform:translate(-7px, 0) rotate(0)}64%{transform:translate(6px, 0) rotate(0)}66%{transform:translate(-4px, 0) rotate(0)}68%{transform:translate(-2px, 0) rotate(0)}70%{transform:translate(6px, 0) rotate(0)}72%{transform:translate(-9px, 0) rotate(0)}74%{transform:translate(7px, 0) rotate(0)}76%{transform:translate(2px, 0) rotate(0)}78%{transform:translate(-8px, 0) rotate(0)}80%{transform:translate(2px, 0) rotate(0)}82%{transform:translate(2px, 0) rotate(0)}84%{transform:translate(-4px, 0) rotate(0)}86%{transform:translate(-7px, 0) rotate(0)}88%{transform:translate(4px, 0) rotate(0)}90%{transform:translate(-6px, 0) rotate(0)}92%{transform:translate(-8px, 0) rotate(0)}94%{transform:translate(-3px, 0) rotate(0)}96%{transform:translate(4px, 0) rotate(0)}98%{transform:translate(-8px, 0) rotate(0)}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-horizontal:hover,.shake-trigger:hover .shake-horizontal,.shake-horizontal.shake-freeze,.shake-horizontal.shake-constant{animation:shake-horizontal 100ms ease-in-out infinite}@keyframes shake-vertical{2%{transform:translate(0, 6px) rotate(0)}4%{transform:translate(0, -1px) rotate(0)}6%{transform:translate(0, -7px) rotate(0)}8%{transform:translate(0, -1px) rotate(0)}10%{transform:translate(0, 9px) rotate(0)}12%{transform:translate(0, 1px) rotate(0)}14%{transform:translate(0, 3px) rotate(0)}16%{transform:translate(0, 6px) rotate(0)}18%{transform:translate(0, 0px) rotate(0)}20%{transform:translate(0, 2px) rotate(0)}22%{transform:translate(0, 1px) rotate(0)}24%{transform:translate(0, 3px) rotate(0)}26%{transform:translate(0, 4px) rotate(0)}28%{transform:translate(0, 0px) rotate(0)}30%{transform:translate(0, -8px) rotate(0)}32%{transform:translate(0, 6px) rotate(0)}34%{transform:translate(0, 6px) rotate(0)}36%{transform:translate(0, -4px) rotate(0)}38%{transform:translate(0, 2px) rotate(0)}40%{transform:translate(0, -8px) rotate(0)}42%{transform:translate(0, -9px) rotate(0)}44%{transform:translate(0, -3px) rotate(0)}46%{transform:translate(0, 0px) rotate(0)}48%{transform:translate(0, -7px) rotate(0)}50%{transform:translate(0, 0px) rotate(0)}52%{transform:translate(0, 3px) rotate(0)}54%{transform:translate(0, -4px) rotate(0)}56%{transform:translate(0, 3px) rotate(0)}58%{transform:translate(0, -9px) rotate(0)}60%{transform:translate(0, 9px) rotate(0)}62%{transform:translate(0, -6px) rotate(0)}64%{transform:translate(0, 0px) rotate(0)}66%{transform:translate(0, -4px) rotate(0)}68%{transform:translate(0, 1px) rotate(0)}70%{transform:translate(0, 5px) rotate(0)}72%{transform:translate(0, 0px) rotate(0)}74%{transform:translate(0, -6px) rotate(0)}76%{transform:translate(0, -3px) rotate(0)}78%{transform:translate(0, 3px) rotate(0)}80%{transform:translate(0, 6px) rotate(0)}82%{transform:translate(0, 2px) rotate(0)}84%{transform:translate(0, -3px) rotate(0)}86%{transform:translate(0, 1px) rotate(0)}88%{transform:translate(0, 1px) rotate(0)}90%{transform:translate(0, 10px) rotate(0)}92%{transform:translate(0, -2px) rotate(0)}94%{transform:translate(0, -2px) rotate(0)}96%{transform:translate(0, -6px) rotate(0)}98%{transform:translate(0, -9px) rotate(0)}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-vertical:hover,.shake-trigger:hover .shake-vertical,.shake-vertical.shake-freeze,.shake-vertical.shake-constant{animation:shake-vertical 100ms ease-in-out infinite}@keyframes shake-rotate{2%{transform:translate(0, 0) rotate(3.5deg)}4%{transform:translate(0, 0) rotate(4.5deg)}6%{transform:translate(0, 0) rotate(1.5deg)}8%{transform:translate(0, 0) rotate(2.5deg)}10%{transform:translate(0, 0) rotate(3.5deg)}12%{transform:translate(0, 0) rotate(0.5deg)}14%{transform:translate(0, 0) rotate(-5.5deg)}16%{transform:translate(0, 0) rotate(-1.5deg)}18%{transform:translate(0, 0) rotate(1.5deg)}20%{transform:translate(0, 0) rotate(6.5deg)}22%{transform:translate(0, 0) rotate(3.5deg)}24%{transform:translate(0, 0) rotate(6.5deg)}26%{transform:translate(0, 0) rotate(-0.5deg)}28%{transform:translate(0, 0) rotate(7.5deg)}30%{transform:translate(0, 0) rotate(6.5deg)}32%{transform:translate(0, 0) rotate(-3.5deg)}34%{transform:translate(0, 0) rotate(-1.5deg)}36%{transform:translate(0, 0) rotate(3.5deg)}38%{transform:translate(0, 0) rotate(7.5deg)}40%{transform:translate(0, 0) rotate(-0.5deg)}42%{transform:translate(0, 0) rotate(3.5deg)}44%{transform:translate(0, 0) rotate(7.5deg)}46%{transform:translate(0, 0) rotate(7.5deg)}48%{transform:translate(0, 0) rotate(3.5deg)}50%{transform:translate(0, 0) rotate(0.5deg)}52%{transform:translate(0, 0) rotate(2.5deg)}54%{transform:translate(0, 0) rotate(5.5deg)}56%{transform:translate(0, 0) rotate(2.5deg)}58%{transform:translate(0, 0) rotate(-4.5deg)}60%{transform:translate(0, 0) rotate(-4.5deg)}62%{transform:translate(0, 0) rotate(7.5deg)}64%{transform:translate(0, 0) rotate(0.5deg)}66%{transform:translate(0, 0) rotate(2.5deg)}68%{transform:translate(0, 0) rotate(2.5deg)}70%{transform:translate(0, 0) rotate(5.5deg)}72%{transform:translate(0, 0) rotate(5.5deg)}74%{transform:translate(0, 0) rotate(-2.5deg)}76%{transform:translate(0, 0) rotate(7.5deg)}78%{transform:translate(0, 0) rotate(2.5deg)}80%{transform:translate(0, 0) rotate(-6.5deg)}82%{transform:translate(0, 0) rotate(-0.5deg)}84%{transform:translate(0, 0) rotate(2.5deg)}86%{transform:translate(0, 0) rotate(5.5deg)}88%{transform:translate(0, 0) rotate(0.5deg)}90%{transform:translate(0, 0) rotate(-0.5deg)}92%{transform:translate(0, 0) rotate(-1.5deg)}94%{transform:translate(0, 0) rotate(-0.5deg)}96%{transform:translate(0, 0) rotate(0.5deg)}98%{transform:translate(0, 0) rotate(-4.5deg)}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-rotate:hover,.shake-trigger:hover .shake-rotate,.shake-rotate.shake-freeze,.shake-rotate.shake-constant{animation:shake-rotate 100ms ease-in-out infinite}@keyframes shake-opacity{10%{transform:translate(-4px, 4px) rotate(-1.5deg);opacity:0.25}20%{transform:translate(-1px, 2px) rotate(0.5deg);opacity:1}30%{transform:translate(2px, -4px) rotate(-1.5deg);opacity:0.03}40%{transform:translate(-1px, -2px) rotate(1.5deg);opacity:0.55}50%{transform:translate(5px, -4px) rotate(1.5deg);opacity:0.09}60%{transform:translate(-1px, 1px) rotate(-1.5deg);opacity:0.97}70%{transform:translate(4px, 1px) rotate(0.5deg);opacity:0.96}80%{transform:translate(3px, 2px) rotate(2.5deg);opacity:0.83}90%{transform:translate(-2px, -4px) rotate(-1.5deg);opacity:0.09}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-opacity:hover,.shake-trigger:hover .shake-opacity,.shake-opacity.shake-freeze,.shake-opacity.shake-constant{animation:shake-opacity 0.5s ease-in-out infinite}@keyframes shake-crazy{10%{transform:translate(-10px, -19px) rotate(6deg);opacity:0.47}20%{transform:translate(-11px, 10px) rotate(5deg);opacity:0.82}30%{transform:translate(17px, -3px) rotate(-9deg);opacity:0.34}40%{transform:translate(11px, 19px) rotate(5deg);opacity:0.4}50%{transform:translate(-11px, 13px) rotate(1deg);opacity:0.97}60%{transform:translate(17px, -16px) rotate(7deg);opacity:0.24}70%{transform:translate(-10px, -12px) rotate(-6deg);opacity:0.56}80%{transform:translate(13px, -19px) rotate(-4deg);opacity:0.96}90%{transform:translate(-18px, -11px) rotate(3deg);opacity:0.49}0%,100%{transform:translate(0, 0) rotate(0)}}.shake-crazy:hover,.shake-trigger:hover .shake-crazy,.shake-crazy.shake-freeze,.shake-crazy.shake-constant{animation:shake-crazy 100ms ease-in-out infinite}@keyframes shake-chunk{2%{transform:translate(5px, 2px) rotate(-12deg)}4%{transform:translate(-6px, 3px) rotate(1deg)}6%{transform:translate(3px, 6px) rotate(14deg)}8%{transform:translate(1px, 8px) rotate(1deg)}10%{transform:translate(-5px, 10px) rotate(0deg)}12%{transform:translate(-11px, 2px) rotate(7deg)}14%{transform:translate(4px, 15px) rotate(11deg)}16%{transform:translate(4px, -8px) rotate(15deg)}18%{transform:translate(-5px, 10px) rotate(1deg)}20%{transform:translate(-1px, 3px) rotate(15deg)}22%{transform:translate(-8px, 5px) rotate(-6deg)}24%{transform:translate(-1px, -9px) rotate(8deg)}26%{transform:translate(9px, 11px) rotate(-13deg)}28%{transform:translate(-7px, 4px) rotate(9deg)}30%{transform:translate(8px, 14px) rotate(9deg)}32%{transform:translate(-4px, 11px) rotate(-11deg)}34%{transform:translate(14px, 11px) rotate(-8deg)}36%{transform:translate(-13px, -8px) rotate(13deg)}38%{transform:translate(-12px, 1px) rotate(-13deg)}0%,40%,100%{transform:translate(0, 0) rotate(0)}}.shake-chunk:hover,.shake-trigger:hover .shake-chunk,.shake-chunk.shake-freeze,.shake-chunk.shake-constant{animation:shake-chunk 4s ease-in-out infinite} \ No newline at end of file diff --git a/public/css/custom.css b/public/css/custom.css deleted file mode 100644 index 31d35c5b..00000000 --- a/public/css/custom.css +++ /dev/null @@ -1,3 +0,0 @@ -input[disabled] { - color: grey; -} \ No newline at end of file diff --git a/public/css/font-awesome.css b/public/css/font-awesome.css deleted file mode 100644 index ced07047..00000000 --- a/public/css/font-awesome.css +++ /dev/null @@ -1,1672 +0,0 @@ -/*! - * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */ -/* FONT PATH - * -------------------------- */ -@font-face { - font-family: 'FontAwesome'; - src: url('../fonts/fontawesome-webfont.eot?v=4.2.0'); - src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg'); - font-weight: normal; - font-style: normal; -} -.fa { - display: inline-block; - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -/* makes the font 33% larger relative to the icon container */ -.fa-lg { - font-size: 1.33333333em; - line-height: 0.75em; - vertical-align: -15%; -} -.fa-2x { - font-size: 2em; -} -.fa-3x { - font-size: 3em; -} -.fa-4x { - font-size: 4em; -} -.fa-5x { - font-size: 5em; -} -.fa-fw { - width: 1.28571429em; - text-align: center; -} -.fa-ul { - padding-left: 0; - margin-left: 2.14285714em; - list-style-type: none; -} -.fa-ul > li { - position: relative; -} -.fa-li { - position: absolute; - left: -2.14285714em; - width: 2.14285714em; - top: 0.14285714em; - text-align: center; -} -.fa-li.fa-lg { - left: -1.85714286em; -} -.fa-border { - padding: .2em .25em .15em; - border: solid 0.08em #eeeeee; - border-radius: .1em; -} -.pull-right { - float: right; -} -.pull-left { - float: left; -} -.fa.pull-left { - margin-right: .3em; -} -.fa.pull-right { - margin-left: .3em; -} -.fa-spin { - -webkit-animation: fa-spin 2s infinite linear; - animation: fa-spin 2s infinite linear; -} -@-webkit-keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -@keyframes fa-spin { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(359deg); - transform: rotate(359deg); - } -} -.fa-rotate-90 { - filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); -} -.fa-rotate-180 { - filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); - -webkit-transform: rotate(180deg); - -ms-transform: rotate(180deg); - transform: rotate(180deg); -} -.fa-rotate-270 { - filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); - -webkit-transform: rotate(270deg); - -ms-transform: rotate(270deg); - transform: rotate(270deg); -} -.fa-flip-horizontal { - filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); - -webkit-transform: scale(-1, 1); - -ms-transform: scale(-1, 1); - transform: scale(-1, 1); -} -.fa-flip-vertical { - filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); - -webkit-transform: scale(1, -1); - -ms-transform: scale(1, -1); - transform: scale(1, -1); -} -:root .fa-rotate-90, -:root .fa-rotate-180, -:root .fa-rotate-270, -:root .fa-flip-horizontal, -:root .fa-flip-vertical { - filter: none; -} -.fa-stack { - position: relative; - display: inline-block; - width: 2em; - height: 2em; - line-height: 2em; - vertical-align: middle; -} -.fa-stack-1x, -.fa-stack-2x { - position: absolute; - left: 0; - width: 100%; - text-align: center; -} -.fa-stack-1x { - line-height: inherit; -} -.fa-stack-2x { - font-size: 2em; -} -.fa-inverse { - color: #ffffff; -} -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen - readers do not read off random characters that represent icons */ -.fa-glass:before { - content: "\f000"; -} -.fa-music:before { - content: "\f001"; -} -.fa-search:before { - content: "\f002"; -} -.fa-envelope-o:before { - content: "\f003"; -} -.fa-heart:before { - content: "\f004"; -} -.fa-star:before { - content: "\f005"; -} -.fa-star-o:before { - content: "\f006"; -} -.fa-user:before { - content: "\f007"; -} -.fa-film:before { - content: "\f008"; -} -.fa-th-large:before { - content: "\f009"; -} -.fa-th:before { - content: "\f00a"; -} -.fa-th-list:before { - content: "\f00b"; -} -.fa-check:before { - content: "\f00c"; -} -.fa-remove:before, -.fa-close:before, -.fa-times:before { - content: "\f00d"; -} -.fa-search-plus:before { - content: "\f00e"; -} -.fa-search-minus:before { - content: "\f010"; -} -.fa-power-off:before { - content: "\f011"; -} -.fa-signal:before { - content: "\f012"; -} -.fa-gear:before, -.fa-cog:before { - content: "\f013"; -} -.fa-trash-o:before { - content: "\f014"; -} -.fa-home:before { - content: "\f015"; -} -.fa-file-o:before { - content: "\f016"; -} -.fa-clock-o:before { - content: "\f017"; -} -.fa-road:before { - content: "\f018"; -} -.fa-download:before { - content: "\f019"; -} -.fa-arrow-circle-o-down:before { - content: "\f01a"; -} -.fa-arrow-circle-o-up:before { - content: "\f01b"; -} -.fa-inbox:before { - content: "\f01c"; -} -.fa-play-circle-o:before { - content: "\f01d"; -} -.fa-rotate-right:before, -.fa-repeat:before { - content: "\f01e"; -} -.fa-refresh:before { - content: "\f021"; -} -.fa-list-alt:before { - content: "\f022"; -} -.fa-lock:before { - content: "\f023"; -} -.fa-flag:before { - content: "\f024"; -} -.fa-headphones:before { - content: "\f025"; -} -.fa-volume-off:before { - content: "\f026"; -} -.fa-volume-down:before { - content: "\f027"; -} -.fa-volume-up:before { - content: "\f028"; -} -.fa-qrcode:before { - content: "\f029"; -} -.fa-barcode:before { - content: "\f02a"; -} -.fa-tag:before { - content: "\f02b"; -} -.fa-tags:before { - content: "\f02c"; -} -.fa-book:before { - content: "\f02d"; -} -.fa-bookmark:before { - content: "\f02e"; -} -.fa-print:before { - content: "\f02f"; -} -.fa-camera:before { - content: "\f030"; -} -.fa-font:before { - content: "\f031"; -} -.fa-bold:before { - content: "\f032"; -} -.fa-italic:before { - content: "\f033"; -} -.fa-text-height:before { - content: "\f034"; -} -.fa-text-width:before { - content: "\f035"; -} -.fa-align-left:before { - content: "\f036"; -} -.fa-align-center:before { - content: "\f037"; -} -.fa-align-right:before { - content: "\f038"; -} -.fa-align-justify:before { - content: "\f039"; -} -.fa-list:before { - content: "\f03a"; -} -.fa-dedent:before, -.fa-outdent:before { - content: "\f03b"; -} -.fa-indent:before { - content: "\f03c"; -} -.fa-video-camera:before { - content: "\f03d"; -} -.fa-photo:before, -.fa-image:before, -.fa-picture-o:before { - content: "\f03e"; -} -.fa-pencil:before { - content: "\f040"; -} -.fa-map-marker:before { - content: "\f041"; -} -.fa-adjust:before { - content: "\f042"; -} -.fa-tint:before { - content: "\f043"; -} -.fa-edit:before, -.fa-pencil-square-o:before { - content: "\f044"; -} -.fa-share-square-o:before { - content: "\f045"; -} -.fa-check-square-o:before { - content: "\f046"; -} -.fa-arrows:before { - content: "\f047"; -} -.fa-step-backward:before { - content: "\f048"; -} -.fa-fast-backward:before { - content: "\f049"; -} -.fa-backward:before { - content: "\f04a"; -} -.fa-play:before { - content: "\f04b"; -} -.fa-pause:before { - content: "\f04c"; -} -.fa-stop:before { - content: "\f04d"; -} -.fa-forward:before { - content: "\f04e"; -} -.fa-fast-forward:before { - content: "\f050"; -} -.fa-step-forward:before { - content: "\f051"; -} -.fa-eject:before { - content: "\f052"; -} -.fa-chevron-left:before { - content: "\f053"; -} -.fa-chevron-right:before { - content: "\f054"; -} -.fa-plus-circle:before { - content: "\f055"; -} -.fa-minus-circle:before { - content: "\f056"; -} -.fa-times-circle:before { - content: "\f057"; -} -.fa-check-circle:before { - content: "\f058"; -} -.fa-question-circle:before { - content: "\f059"; -} -.fa-info-circle:before { - content: "\f05a"; -} -.fa-crosshairs:before { - content: "\f05b"; -} -.fa-times-circle-o:before { - content: "\f05c"; -} -.fa-check-circle-o:before { - content: "\f05d"; -} -.fa-ban:before { - content: "\f05e"; -} -.fa-arrow-left:before { - content: "\f060"; -} -.fa-arrow-right:before { - content: "\f061"; -} -.fa-arrow-up:before { - content: "\f062"; -} -.fa-arrow-down:before { - content: "\f063"; -} -.fa-mail-forward:before, -.fa-share:before { - content: "\f064"; -} -.fa-expand:before { - content: "\f065"; -} -.fa-compress:before { - content: "\f066"; -} -.fa-plus:before { - content: "\f067"; -} -.fa-minus:before { - content: "\f068"; -} -.fa-asterisk:before { - content: "\f069"; -} -.fa-exclamation-circle:before { - content: "\f06a"; -} -.fa-gift:before { - content: "\f06b"; -} -.fa-leaf:before { - content: "\f06c"; -} -.fa-fire:before { - content: "\f06d"; -} -.fa-eye:before { - content: "\f06e"; -} -.fa-eye-slash:before { - content: "\f070"; -} -.fa-warning:before, -.fa-exclamation-triangle:before { - content: "\f071"; -} -.fa-plane:before { - content: "\f072"; -} -.fa-calendar:before { - content: "\f073"; -} -.fa-random:before { - content: "\f074"; -} -.fa-comment:before { - content: "\f075"; -} -.fa-magnet:before { - content: "\f076"; -} -.fa-chevron-up:before { - content: "\f077"; -} -.fa-chevron-down:before { - content: "\f078"; -} -.fa-retweet:before { - content: "\f079"; -} -.fa-shopping-cart:before { - content: "\f07a"; -} -.fa-folder:before { - content: "\f07b"; -} -.fa-folder-open:before { - content: "\f07c"; -} -.fa-arrows-v:before { - content: "\f07d"; -} -.fa-arrows-h:before { - content: "\f07e"; -} -.fa-bar-chart-o:before, -.fa-bar-chart:before { - content: "\f080"; -} -.fa-twitter-square:before { - content: "\f081"; -} -.fa-facebook-square:before { - content: "\f082"; -} -.fa-camera-retro:before { - content: "\f083"; -} -.fa-key:before { - content: "\f084"; -} -.fa-gears:before, -.fa-cogs:before { - content: "\f085"; -} -.fa-comments:before { - content: "\f086"; -} -.fa-thumbs-o-up:before { - content: "\f087"; -} -.fa-thumbs-o-down:before { - content: "\f088"; -} -.fa-star-half:before { - content: "\f089"; -} -.fa-heart-o:before { - content: "\f08a"; -} -.fa-sign-out:before { - content: "\f08b"; -} -.fa-linkedin-square:before { - content: "\f08c"; -} -.fa-thumb-tack:before { - content: "\f08d"; -} -.fa-external-link:before { - content: "\f08e"; -} -.fa-sign-in:before { - content: "\f090"; -} -.fa-trophy:before { - content: "\f091"; -} -.fa-github-square:before { - content: "\f092"; -} -.fa-upload:before { - content: "\f093"; -} -.fa-lemon-o:before { - content: "\f094"; -} -.fa-phone:before { - content: "\f095"; -} -.fa-square-o:before { - content: "\f096"; -} -.fa-bookmark-o:before { - content: "\f097"; -} -.fa-phone-square:before { - content: "\f098"; -} -.fa-twitter:before { - content: "\f099"; -} -.fa-facebook:before { - content: "\f09a"; -} -.fa-github:before { - content: "\f09b"; -} -.fa-unlock:before { - content: "\f09c"; -} -.fa-credit-card:before { - content: "\f09d"; -} -.fa-rss:before { - content: "\f09e"; -} -.fa-hdd-o:before { - content: "\f0a0"; -} -.fa-bullhorn:before { - content: "\f0a1"; -} -.fa-bell:before { - content: "\f0f3"; -} -.fa-certificate:before { - content: "\f0a3"; -} -.fa-hand-o-right:before { - content: "\f0a4"; -} -.fa-hand-o-left:before { - content: "\f0a5"; -} -.fa-hand-o-up:before { - content: "\f0a6"; -} -.fa-hand-o-down:before { - content: "\f0a7"; -} -.fa-arrow-circle-left:before { - content: "\f0a8"; -} -.fa-arrow-circle-right:before { - content: "\f0a9"; -} -.fa-arrow-circle-up:before { - content: "\f0aa"; -} -.fa-arrow-circle-down:before { - content: "\f0ab"; -} -.fa-globe:before { - content: "\f0ac"; -} -.fa-wrench:before { - content: "\f0ad"; -} -.fa-tasks:before { - content: "\f0ae"; -} -.fa-filter:before { - content: "\f0b0"; -} -.fa-briefcase:before { - content: "\f0b1"; -} -.fa-arrows-alt:before { - content: "\f0b2"; -} -.fa-group:before, -.fa-users:before { - content: "\f0c0"; -} -.fa-chain:before, -.fa-link:before { - content: "\f0c1"; -} -.fa-cloud:before { - content: "\f0c2"; -} -.fa-flask:before { - content: "\f0c3"; -} -.fa-cut:before, -.fa-scissors:before { - content: "\f0c4"; -} -.fa-copy:before, -.fa-files-o:before { - content: "\f0c5"; -} -.fa-paperclip:before { - content: "\f0c6"; -} -.fa-save:before, -.fa-floppy-o:before { - content: "\f0c7"; -} -.fa-square:before { - content: "\f0c8"; -} -.fa-navicon:before, -.fa-reorder:before, -.fa-bars:before { - content: "\f0c9"; -} -.fa-list-ul:before { - content: "\f0ca"; -} -.fa-list-ol:before { - content: "\f0cb"; -} -.fa-strikethrough:before { - content: "\f0cc"; -} -.fa-underline:before { - content: "\f0cd"; -} -.fa-table:before { - content: "\f0ce"; -} -.fa-magic:before { - content: "\f0d0"; -} -.fa-truck:before { - content: "\f0d1"; -} -.fa-pinterest:before { - content: "\f0d2"; -} -.fa-pinterest-square:before { - content: "\f0d3"; -} -.fa-google-plus-square:before { - content: "\f0d4"; -} -.fa-google-plus:before { - content: "\f0d5"; -} -.fa-money:before { - content: "\f0d6"; -} -.fa-caret-down:before { - content: "\f0d7"; -} -.fa-caret-up:before { - content: "\f0d8"; -} -.fa-caret-left:before { - content: "\f0d9"; -} -.fa-caret-right:before { - content: "\f0da"; -} -.fa-columns:before { - content: "\f0db"; -} -.fa-unsorted:before, -.fa-sort:before { - content: "\f0dc"; -} -.fa-sort-down:before, -.fa-sort-desc:before { - content: "\f0dd"; -} -.fa-sort-up:before, -.fa-sort-asc:before { - content: "\f0de"; -} -.fa-envelope:before { - content: "\f0e0"; -} -.fa-linkedin:before { - content: "\f0e1"; -} -.fa-rotate-left:before, -.fa-undo:before { - content: "\f0e2"; -} -.fa-legal:before, -.fa-gavel:before { - content: "\f0e3"; -} -.fa-dashboard:before, -.fa-tachometer:before { - content: "\f0e4"; -} -.fa-comment-o:before { - content: "\f0e5"; -} -.fa-comments-o:before { - content: "\f0e6"; -} -.fa-flash:before, -.fa-bolt:before { - content: "\f0e7"; -} -.fa-sitemap:before { - content: "\f0e8"; -} -.fa-umbrella:before { - content: "\f0e9"; -} -.fa-paste:before, -.fa-clipboard:before { - content: "\f0ea"; -} -.fa-lightbulb-o:before { - content: "\f0eb"; -} -.fa-exchange:before { - content: "\f0ec"; -} -.fa-cloud-download:before { - content: "\f0ed"; -} -.fa-cloud-upload:before { - content: "\f0ee"; -} -.fa-user-md:before { - content: "\f0f0"; -} -.fa-stethoscope:before { - content: "\f0f1"; -} -.fa-suitcase:before { - content: "\f0f2"; -} -.fa-bell-o:before { - content: "\f0a2"; -} -.fa-coffee:before { - content: "\f0f4"; -} -.fa-cutlery:before { - content: "\f0f5"; -} -.fa-file-text-o:before { - content: "\f0f6"; -} -.fa-building-o:before { - content: "\f0f7"; -} -.fa-hospital-o:before { - content: "\f0f8"; -} -.fa-ambulance:before { - content: "\f0f9"; -} -.fa-medkit:before { - content: "\f0fa"; -} -.fa-fighter-jet:before { - content: "\f0fb"; -} -.fa-beer:before { - content: "\f0fc"; -} -.fa-h-square:before { - content: "\f0fd"; -} -.fa-plus-square:before { - content: "\f0fe"; -} -.fa-angle-double-left:before { - content: "\f100"; -} -.fa-angle-double-right:before { - content: "\f101"; -} -.fa-angle-double-up:before { - content: "\f102"; -} -.fa-angle-double-down:before { - content: "\f103"; -} -.fa-angle-left:before { - content: "\f104"; -} -.fa-angle-right:before { - content: "\f105"; -} -.fa-angle-up:before { - content: "\f106"; -} -.fa-angle-down:before { - content: "\f107"; -} -.fa-desktop:before { - content: "\f108"; -} -.fa-laptop:before { - content: "\f109"; -} -.fa-tablet:before { - content: "\f10a"; -} -.fa-mobile-phone:before, -.fa-mobile:before { - content: "\f10b"; -} -.fa-circle-o:before { - content: "\f10c"; -} -.fa-quote-left:before { - content: "\f10d"; -} -.fa-quote-right:before { - content: "\f10e"; -} -.fa-spinner:before { - content: "\f110"; -} -.fa-circle:before { - content: "\f111"; -} -.fa-mail-reply:before, -.fa-reply:before { - content: "\f112"; -} -.fa-github-alt:before { - content: "\f113"; -} -.fa-folder-o:before { - content: "\f114"; -} -.fa-folder-open-o:before { - content: "\f115"; -} -.fa-smile-o:before { - content: "\f118"; -} -.fa-frown-o:before { - content: "\f119"; -} -.fa-meh-o:before { - content: "\f11a"; -} -.fa-gamepad:before { - content: "\f11b"; -} -.fa-keyboard-o:before { - content: "\f11c"; -} -.fa-flag-o:before { - content: "\f11d"; -} -.fa-flag-checkered:before { - content: "\f11e"; -} -.fa-terminal:before { - content: "\f120"; -} -.fa-code:before { - content: "\f121"; -} -.fa-mail-reply-all:before, -.fa-reply-all:before { - content: "\f122"; -} -.fa-star-half-empty:before, -.fa-star-half-full:before, -.fa-star-half-o:before { - content: "\f123"; -} -.fa-location-arrow:before { - content: "\f124"; -} -.fa-crop:before { - content: "\f125"; -} -.fa-code-fork:before { - content: "\f126"; -} -.fa-unlink:before, -.fa-chain-broken:before { - content: "\f127"; -} -.fa-question:before { - content: "\f128"; -} -.fa-info:before { - content: "\f129"; -} -.fa-exclamation:before { - content: "\f12a"; -} -.fa-superscript:before { - content: "\f12b"; -} -.fa-subscript:before { - content: "\f12c"; -} -.fa-eraser:before { - content: "\f12d"; -} -.fa-puzzle-piece:before { - content: "\f12e"; -} -.fa-microphone:before { - content: "\f130"; -} -.fa-microphone-slash:before { - content: "\f131"; -} -.fa-shield:before { - content: "\f132"; -} -.fa-calendar-o:before { - content: "\f133"; -} -.fa-fire-extinguisher:before { - content: "\f134"; -} -.fa-rocket:before { - content: "\f135"; -} -.fa-maxcdn:before { - content: "\f136"; -} -.fa-chevron-circle-left:before { - content: "\f137"; -} -.fa-chevron-circle-right:before { - content: "\f138"; -} -.fa-chevron-circle-up:before { - content: "\f139"; -} -.fa-chevron-circle-down:before { - content: "\f13a"; -} -.fa-html5:before { - content: "\f13b"; -} -.fa-css3:before { - content: "\f13c"; -} -.fa-anchor:before { - content: "\f13d"; -} -.fa-unlock-alt:before { - content: "\f13e"; -} -.fa-bullseye:before { - content: "\f140"; -} -.fa-ellipsis-h:before { - content: "\f141"; -} -.fa-ellipsis-v:before { - content: "\f142"; -} -.fa-rss-square:before { - content: "\f143"; -} -.fa-play-circle:before { - content: "\f144"; -} -.fa-ticket:before { - content: "\f145"; -} -.fa-minus-square:before { - content: "\f146"; -} -.fa-minus-square-o:before { - content: "\f147"; -} -.fa-level-up:before { - content: "\f148"; -} -.fa-level-down:before { - content: "\f149"; -} -.fa-check-square:before { - content: "\f14a"; -} -.fa-pencil-square:before { - content: "\f14b"; -} -.fa-external-link-square:before { - content: "\f14c"; -} -.fa-share-square:before { - content: "\f14d"; -} -.fa-compass:before { - content: "\f14e"; -} -.fa-toggle-down:before, -.fa-caret-square-o-down:before { - content: "\f150"; -} -.fa-toggle-up:before, -.fa-caret-square-o-up:before { - content: "\f151"; -} -.fa-toggle-right:before, -.fa-caret-square-o-right:before { - content: "\f152"; -} -.fa-euro:before, -.fa-eur:before { - content: "\f153"; -} -.fa-gbp:before { - content: "\f154"; -} -.fa-dollar:before, -.fa-usd:before { - content: "\f155"; -} -.fa-rupee:before, -.fa-inr:before { - content: "\f156"; -} -.fa-cny:before, -.fa-rmb:before, -.fa-yen:before, -.fa-jpy:before { - content: "\f157"; -} -.fa-ruble:before, -.fa-rouble:before, -.fa-rub:before { - content: "\f158"; -} -.fa-won:before, -.fa-krw:before { - content: "\f159"; -} -.fa-bitcoin:before, -.fa-btc:before { - content: "\f15a"; -} -.fa-file:before { - content: "\f15b"; -} -.fa-file-text:before { - content: "\f15c"; -} -.fa-sort-alpha-asc:before { - content: "\f15d"; -} -.fa-sort-alpha-desc:before { - content: "\f15e"; -} -.fa-sort-amount-asc:before { - content: "\f160"; -} -.fa-sort-amount-desc:before { - content: "\f161"; -} -.fa-sort-numeric-asc:before { - content: "\f162"; -} -.fa-sort-numeric-desc:before { - content: "\f163"; -} -.fa-thumbs-up:before { - content: "\f164"; -} -.fa-thumbs-down:before { - content: "\f165"; -} -.fa-youtube-square:before { - content: "\f166"; -} -.fa-youtube:before { - content: "\f167"; -} -.fa-xing:before { - content: "\f168"; -} -.fa-xing-square:before { - content: "\f169"; -} -.fa-youtube-play:before { - content: "\f16a"; -} -.fa-dropbox:before { - content: "\f16b"; -} -.fa-stack-overflow:before { - content: "\f16c"; -} -.fa-instagram:before { - content: "\f16d"; -} -.fa-flickr:before { - content: "\f16e"; -} -.fa-adn:before { - content: "\f170"; -} -.fa-bitbucket:before { - content: "\f171"; -} -.fa-bitbucket-square:before { - content: "\f172"; -} -.fa-tumblr:before { - content: "\f173"; -} -.fa-tumblr-square:before { - content: "\f174"; -} -.fa-long-arrow-down:before { - content: "\f175"; -} -.fa-long-arrow-up:before { - content: "\f176"; -} -.fa-long-arrow-left:before { - content: "\f177"; -} -.fa-long-arrow-right:before { - content: "\f178"; -} -.fa-apple:before { - content: "\f179"; -} -.fa-windows:before { - content: "\f17a"; -} -.fa-android:before { - content: "\f17b"; -} -.fa-linux:before { - content: "\f17c"; -} -.fa-dribbble:before { - content: "\f17d"; -} -.fa-skype:before { - content: "\f17e"; -} -.fa-foursquare:before { - content: "\f180"; -} -.fa-trello:before { - content: "\f181"; -} -.fa-female:before { - content: "\f182"; -} -.fa-male:before { - content: "\f183"; -} -.fa-gittip:before { - content: "\f184"; -} -.fa-sun-o:before { - content: "\f185"; -} -.fa-moon-o:before { - content: "\f186"; -} -.fa-archive:before { - content: "\f187"; -} -.fa-bug:before { - content: "\f188"; -} -.fa-vk:before { - content: "\f189"; -} -.fa-weibo:before { - content: "\f18a"; -} -.fa-renren:before { - content: "\f18b"; -} -.fa-pagelines:before { - content: "\f18c"; -} -.fa-stack-exchange:before { - content: "\f18d"; -} -.fa-arrow-circle-o-right:before { - content: "\f18e"; -} -.fa-arrow-circle-o-left:before { - content: "\f190"; -} -.fa-toggle-left:before, -.fa-caret-square-o-left:before { - content: "\f191"; -} -.fa-dot-circle-o:before { - content: "\f192"; -} -.fa-wheelchair:before { - content: "\f193"; -} -.fa-vimeo-square:before { - content: "\f194"; -} -.fa-turkish-lira:before, -.fa-try:before { - content: "\f195"; -} -.fa-plus-square-o:before { - content: "\f196"; -} -.fa-space-shuttle:before { - content: "\f197"; -} -.fa-slack:before { - content: "\f198"; -} -.fa-envelope-square:before { - content: "\f199"; -} -.fa-wordpress:before { - content: "\f19a"; -} -.fa-openid:before { - content: "\f19b"; -} -.fa-institution:before, -.fa-bank:before, -.fa-university:before { - content: "\f19c"; -} -.fa-mortar-board:before, -.fa-graduation-cap:before { - content: "\f19d"; -} -.fa-yahoo:before { - content: "\f19e"; -} -.fa-google:before { - content: "\f1a0"; -} -.fa-reddit:before { - content: "\f1a1"; -} -.fa-reddit-square:before { - content: "\f1a2"; -} -.fa-stumbleupon-circle:before { - content: "\f1a3"; -} -.fa-stumbleupon:before { - content: "\f1a4"; -} -.fa-delicious:before { - content: "\f1a5"; -} -.fa-digg:before { - content: "\f1a6"; -} -.fa-pied-piper:before { - content: "\f1a7"; -} -.fa-pied-piper-alt:before { - content: "\f1a8"; -} -.fa-drupal:before { - content: "\f1a9"; -} -.fa-joomla:before { - content: "\f1aa"; -} -.fa-language:before { - content: "\f1ab"; -} -.fa-fax:before { - content: "\f1ac"; -} -.fa-building:before { - content: "\f1ad"; -} -.fa-child:before { - content: "\f1ae"; -} -.fa-paw:before { - content: "\f1b0"; -} -.fa-spoon:before { - content: "\f1b1"; -} -.fa-cube:before { - content: "\f1b2"; -} -.fa-cubes:before { - content: "\f1b3"; -} -.fa-behance:before { - content: "\f1b4"; -} -.fa-behance-square:before { - content: "\f1b5"; -} -.fa-steam:before { - content: "\f1b6"; -} -.fa-steam-square:before { - content: "\f1b7"; -} -.fa-recycle:before { - content: "\f1b8"; -} -.fa-automobile:before, -.fa-car:before { - content: "\f1b9"; -} -.fa-cab:before, -.fa-taxi:before { - content: "\f1ba"; -} -.fa-tree:before { - content: "\f1bb"; -} -.fa-spotify:before { - content: "\f1bc"; -} -.fa-deviantart:before { - content: "\f1bd"; -} -.fa-soundcloud:before { - content: "\f1be"; -} -.fa-database:before { - content: "\f1c0"; -} -.fa-file-pdf-o:before { - content: "\f1c1"; -} -.fa-file-word-o:before { - content: "\f1c2"; -} -.fa-file-excel-o:before { - content: "\f1c3"; -} -.fa-file-powerpoint-o:before { - content: "\f1c4"; -} -.fa-file-photo-o:before, -.fa-file-picture-o:before, -.fa-file-image-o:before { - content: "\f1c5"; -} -.fa-file-zip-o:before, -.fa-file-archive-o:before { - content: "\f1c6"; -} -.fa-file-sound-o:before, -.fa-file-audio-o:before { - content: "\f1c7"; -} -.fa-file-movie-o:before, -.fa-file-video-o:before { - content: "\f1c8"; -} -.fa-file-code-o:before { - content: "\f1c9"; -} -.fa-vine:before { - content: "\f1ca"; -} -.fa-codepen:before { - content: "\f1cb"; -} -.fa-jsfiddle:before { - content: "\f1cc"; -} -.fa-life-bouy:before, -.fa-life-buoy:before, -.fa-life-saver:before, -.fa-support:before, -.fa-life-ring:before { - content: "\f1cd"; -} -.fa-circle-o-notch:before { - content: "\f1ce"; -} -.fa-ra:before, -.fa-rebel:before { - content: "\f1d0"; -} -.fa-ge:before, -.fa-empire:before { - content: "\f1d1"; -} -.fa-git-square:before { - content: "\f1d2"; -} -.fa-git:before { - content: "\f1d3"; -} -.fa-hacker-news:before { - content: "\f1d4"; -} -.fa-tencent-weibo:before { - content: "\f1d5"; -} -.fa-qq:before { - content: "\f1d6"; -} -.fa-wechat:before, -.fa-weixin:before { - content: "\f1d7"; -} -.fa-send:before, -.fa-paper-plane:before { - content: "\f1d8"; -} -.fa-send-o:before, -.fa-paper-plane-o:before { - content: "\f1d9"; -} -.fa-history:before { - content: "\f1da"; -} -.fa-circle-thin:before { - content: "\f1db"; -} -.fa-header:before { - content: "\f1dc"; -} -.fa-paragraph:before { - content: "\f1dd"; -} -.fa-sliders:before { - content: "\f1de"; -} -.fa-share-alt:before { - content: "\f1e0"; -} -.fa-share-alt-square:before { - content: "\f1e1"; -} -.fa-bomb:before { - content: "\f1e2"; -} -.fa-soccer-ball-o:before, -.fa-futbol-o:before { - content: "\f1e3"; -} -.fa-tty:before { - content: "\f1e4"; -} -.fa-binoculars:before { - content: "\f1e5"; -} -.fa-plug:before { - content: "\f1e6"; -} -.fa-slideshare:before { - content: "\f1e7"; -} -.fa-twitch:before { - content: "\f1e8"; -} -.fa-yelp:before { - content: "\f1e9"; -} -.fa-newspaper-o:before { - content: "\f1ea"; -} -.fa-wifi:before { - content: "\f1eb"; -} -.fa-calculator:before { - content: "\f1ec"; -} -.fa-paypal:before { - content: "\f1ed"; -} -.fa-google-wallet:before { - content: "\f1ee"; -} -.fa-cc-visa:before { - content: "\f1f0"; -} -.fa-cc-mastercard:before { - content: "\f1f1"; -} -.fa-cc-discover:before { - content: "\f1f2"; -} -.fa-cc-amex:before { - content: "\f1f3"; -} -.fa-cc-paypal:before { - content: "\f1f4"; -} -.fa-cc-stripe:before { - content: "\f1f5"; -} -.fa-bell-slash:before { - content: "\f1f6"; -} -.fa-bell-slash-o:before { - content: "\f1f7"; -} -.fa-trash:before { - content: "\f1f8"; -} -.fa-copyright:before { - content: "\f1f9"; -} -.fa-at:before { - content: "\f1fa"; -} -.fa-eyedropper:before { - content: "\f1fb"; -} -.fa-paint-brush:before { - content: "\f1fc"; -} -.fa-birthday-cake:before { - content: "\f1fd"; -} -.fa-area-chart:before { - content: "\f1fe"; -} -.fa-pie-chart:before { - content: "\f200"; -} -.fa-line-chart:before { - content: "\f201"; -} -.fa-lastfm:before { - content: "\f202"; -} -.fa-lastfm-square:before { - content: "\f203"; -} -.fa-toggle-off:before { - content: "\f204"; -} -.fa-toggle-on:before { - content: "\f205"; -} -.fa-bicycle:before { - content: "\f206"; -} -.fa-bus:before { - content: "\f207"; -} -.fa-ioxhost:before { - content: "\f208"; -} -.fa-angellist:before { - content: "\f209"; -} -.fa-cc:before { - content: "\f20a"; -} -.fa-shekel:before, -.fa-sheqel:before, -.fa-ils:before { - content: "\f20b"; -} -.fa-meanpath:before { - content: "\f20c"; -} \ No newline at end of file diff --git a/public/css/font-awesome.min.css b/public/css/font-awesome.min.css new file mode 100755 index 00000000..ee4e9782 --- /dev/null +++ b/public/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"} diff --git a/public/css/github.css b/public/css/github.css new file mode 100644 index 00000000..d21a816b --- /dev/null +++ b/public/css/github.css @@ -0,0 +1 @@ +@keyframes fade-in{0%{opacity:0}100%{opacity:1}}@keyframes fade{10%{transform:scale(1, 1)}35%{transform:scale(1, 1.7)}40%{transform:scale(1, 1.7)}50%{opacity:1}60%{transform:scale(1, 1)}100%{transform:scale(1, 1);opacity:0}}[data-language] code,[class^="lang"] code,pre [data-language],pre [class^="lang"]{opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";animation:fade-in 50ms ease-in-out 2s forwards}[data-language] code.rainbow,[class^="lang"] code.rainbow,pre [data-language].rainbow,pre [class^="lang"].rainbow{animation:none;transition:opacity 50ms ease-in-out}[data-language] code.loading,[class^="lang"] code.loading,pre [data-language].loading,pre [class^="lang"].loading{animation:none}[data-language] code.rainbow-show,[class^="lang"] code.rainbow-show,pre [data-language].rainbow-show,pre [class^="lang"].rainbow-show{opacity:1}pre{position:relative}pre.loading .preloader div{animation-play-state:running}pre.loading .preloader div:nth-of-type(1){background:#0081f5;animation:fade 1.5s 300ms linear infinite}pre.loading .preloader div:nth-of-type(2){background:#5000f5;animation:fade 1.5s 438ms linear infinite}pre.loading .preloader div:nth-of-type(3){background:#9000f5;animation:fade 1.5s 577ms linear infinite}pre.loading .preloader div:nth-of-type(4){background:#f50419;animation:fade 1.5s 715ms linear infinite}pre.loading .preloader div:nth-of-type(5){background:#f57900;animation:fade 1.5s 853ms linear infinite}pre.loading .preloader div:nth-of-type(6){background:#f5e600;animation:fade 1.5s 992ms linear infinite}pre.loading .preloader div:nth-of-type(7){background:#00f50c;animation:fade 1.5s 1130ms linear infinite}pre .preloader{position:absolute;top:12px;left:10px}pre .preloader div{width:12px;height:12px;border-radius:4px;display:inline-block;margin-right:4px;opacity:0;animation-play-state:paused;animation-fill-mode:forwards}pre{border:1px solid #ccc;word-wrap:break-word;padding:6px 10px;line-height:19px;margin-bottom:20px}pre code{border:0;padding:0;margin:0;border-radius:0}code{border:1px solid #eaeaea;margin:0px 2px;padding:0px 5px;font-size:12px}pre,code{font-family:Consolas, 'Liberation Mono', Courier, monospace;color:#333;background:#f8f8f8;border-radius:3px}pre,pre code{font-size:13px}pre .comment{color:#998}pre .support{color:#0086B3}pre .tag,pre .tag-name{color:navy}pre .keyword,pre .css-property,pre .vendor-fix,pre .sass,pre .class,pre .id,pre .css-value,pre .entity.function,pre .storage.function{font-weight:bold}pre .css-property,pre .css-value,pre .vendor-fix,pre .support.namespace{color:#333}pre .constant.numeric,pre .keyword.unit,pre .hex-color{font-weight:normal;color:#099}pre .entity.class{color:#458}pre .entity.id,pre .entity.function{color:#900}pre .attribute,pre .variable{color:teal}pre .string,pre .support.value{font-weight:normal;color:#d14}pre .regexp{color:#009926} \ No newline at end of file diff --git a/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png b/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png deleted file mode 100755 index fa61af9b..00000000 Binary files a/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_flat_75_ffffff_40x100.png b/public/css/images/ui-bg_flat_75_ffffff_40x100.png deleted file mode 100755 index cacf9b48..00000000 Binary files a/public/css/images/ui-bg_flat_75_ffffff_40x100.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png deleted file mode 100755 index f8b999ef..00000000 Binary files a/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_65_ffffff_1x400.png b/public/css/images/ui-bg_glass_65_ffffff_1x400.png deleted file mode 100755 index 1a77e449..00000000 Binary files a/public/css/images/ui-bg_glass_65_ffffff_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_75_dadada_1x400.png b/public/css/images/ui-bg_glass_75_dadada_1x400.png deleted file mode 100755 index a062b46d..00000000 Binary files a/public/css/images/ui-bg_glass_75_dadada_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png deleted file mode 100755 index ffc09b84..00000000 Binary files a/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_glass_95_fef1ec_1x400.png b/public/css/images/ui-bg_glass_95_fef1ec_1x400.png deleted file mode 100755 index b52de613..00000000 Binary files a/public/css/images/ui-bg_glass_95_fef1ec_1x400.png and /dev/null differ diff --git a/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png deleted file mode 100755 index 41077175..00000000 Binary files a/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png and /dev/null differ diff --git a/public/css/jquery-ui-slider-pips.css b/public/css/jquery-ui-slider-pips.css deleted file mode 100755 index 695bfab8..00000000 --- a/public/css/jquery-ui-slider-pips.css +++ /dev/null @@ -1,387 +0,0 @@ - - - - /* HORIZONTAL */ - - /* increase bottom margin to fit the pips */ - .ui-slider-horizontal.ui-slider-pips { - margin-bottom: 1.4em; - } - - /* default hide the labels and pips that arnt visible */ - /* we just use css to hide incase we want to show certain */ - /* labels/pips individually later */ - .ui-slider-pips .ui-slider-label, - .ui-slider-pips .ui-slider-pip-hide { - display: none; - } - - /* now we show any labels that we've set to show in the options */ - .ui-slider-pips .ui-slider-pip-label .ui-slider-label { - display: block; - } - - /* PIP/LABEL WRAPPER */ - /* position each pip absolutely just below the default slider */ - /* and also prevent accidental selection */ - .ui-slider-pips .ui-slider-pip { - width: 2em; - height: 1em; - line-height: 1em; - position: absolute; - font-size: 18px; - color: #999; - overflow: visible; - text-align: center; - top: 20px; - left: 20px; - margin-left: -1em; - cursor: pointer; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - .ui-state-disabled.ui-slider-pips .ui-slider-pip { - cursor: default; - } - - /* little pip/line position & size */ - .ui-slider-pips .ui-slider-line { - background: #999; - width: 1px; - height: 3px; - position: absolute; - left: 50%; - } - - /* the text label postion & size */ - /* it overflows so no need for width to be accurate */ - .ui-slider-pips .ui-slider-label { - position: absolute; - top: 5px; - left: 50%; - margin-left: -1em; - width: 2em; - } - - /* make it easy to see when we hover a label */ - .ui-slider-pips:not(.ui-slider-disabled) .ui-slider-pip:hover .ui-slider-label { - color: black; - font-weight: bold; - } - - - - - /* VERTICAL */ - - /* vertical slider needs right-margin, not bottom */ - .ui-slider-vertical.ui-slider-pips { - margin-bottom: 1em; - margin-right: 2em; - } - - /* align vertical pips left and to right of the slider */ - .ui-slider-vertical.ui-slider-pips .ui-slider-pip { - text-align: left; - top: auto; - left: 20px; - margin-left: 0; - margin-bottom: -0.5em; - } - - /* vertical line/pip should be horizontal instead */ - .ui-slider-vertical.ui-slider-pips .ui-slider-line { - width: 3px; - height: 1px; - position: absolute; - top: 50%; - left: 0; - } - - .ui-slider-vertical.ui-slider-pips .ui-slider-label { - top: 50%; - left: 0.5em; - margin-left: 0; - margin-top: -0.5em; - width: 2em; - } - - - - - /* FLOATING HORIZTONAL TOOLTIPS */ - - /* remove the godawful looking focus outline on handle and float */ - .ui-slider-float .ui-slider-handle:focus, - .ui-slider-float .ui-slider-handle.ui-state-focus .ui-slider-tip-label, - .ui-slider-float .ui-slider-handle:focus .ui-slider-tip, - .ui-slider-float .ui-slider-handle.ui-state-focus .ui-slider-tip-label, - .ui-slider-float .ui-slider-handle:focus .ui-slider-tip-label - .ui-slider-float .ui-slider-handle.ui-state-focus .ui-slider-tip-label { - outline: none; - } - - /* style tooltips on handles and on labels */ - /* also has a nice transition */ - .ui-slider-float .ui-slider-tip, - .ui-slider-float .ui-slider-tip-label { - - position: absolute; - visibility: hidden; - top: -40px; - display: block; - width: 34px; - margin-left: -18px; - left: 50%; - height: 20px; - line-height: 20px; - background: white; - border-radius: 3px; - border: 1px solid #888; - text-align: center; - font-size: 12px; - opacity: 0; - color: #333; - - -webkit-transition-duration: 200ms, 200ms, 0; - -moz-transition-duration: 200ms, 200ms, 0; - -ms-transition-duration: 200ms, 200ms, 0; - transition-duration: 200ms, 200ms, 0; - - -webkit-transition-property: opacity, top, visibility; - -moz-transition-property: opacity, top, visibility; - -ms-transition-property: opacity, top, visibility; - transition-property: opacity, top, visibility; - - -webkit-transition-delay: 0, 0, 200ms; - -moz-transition-delay: 0, 0, 200ms; - -ms-transition-delay: 0, 0, 200ms; - transition-delay: 0, 0, 200ms; - } - - /* show the tooltip on hover or focus */ - /* also switch transition delay around */ - .ui-slider-float .ui-slider-handle:hover .ui-slider-tip, - .ui-slider-float .ui-slider-handle.ui-state-hover .ui-slider-tip, - .ui-slider-float .ui-slider-handle:focus .ui-slider-tip, - .ui-slider-float .ui-slider-handle.ui-state-focus .ui-slider-tip, - .ui-slider-float .ui-slider-handle.ui-state-active .ui-slider-tip, - .ui-slider-float .ui-slider-pip:hover .ui-slider-tip-label { - - opacity: 1; - top: -30px; - visibility: visible; - - -webkit-transition-delay:200ms, 200ms, 0; - -moz-transition-delay:200ms, 200ms, 0; - -ms-transition-delay:200ms, 200ms, 0; - transition-delay:200ms, 200ms, 0; - - } - - /* put label tooltips below slider */ - .ui-slider-float .ui-slider-pip .ui-slider-tip-label { - top: 42px; - } - - .ui-slider-float .ui-slider-pip:hover .ui-slider-tip-label { - top: 32px; - font-weight: normal; - } - - /* give the tooltip a css triangle arrow */ - .ui-slider-float .ui-slider-tip:after, - .ui-slider-float .ui-slider-pip .ui-slider-tip-label:after { - content: " "; - width: 0; - height: 0; - border: 5px solid rgba(255,255,255,0); - border-top-color: rgba(255,255,255,1); - position: absolute; - bottom: -10px; - left: 50%; - margin-left: -5px; - } - - /* put a 1px border on the tooltip arrow to match tooltip border */ - .ui-slider-float .ui-slider-tip:before, - .ui-slider-float .ui-slider-pip .ui-slider-tip-label:before { - content: " "; - width: 0; - height: 0; - border: 5px solid rgba(255,255,255,0); - border-top-color: #888; - position: absolute; - bottom: -11px; - left: 50%; - margin-left: -5px; - } - - /* switch the arrow to top on labels */ - .ui-slider-float .ui-slider-pip .ui-slider-tip-label:after { - border: 5px solid rgba(255,255,255,0); - border-bottom-color: rgba(255,255,255,1); - top: -10px; - } - - .ui-slider-float .ui-slider-pip .ui-slider-tip-label:before { - border: 5px solid rgba(255,255,255,0); - border-bottom-color: #888; - top: -11px; - } - - - - - /* FLOATING VERTICAL TOOLTIPS */ - - /* tooltip floats to left of handle */ - .ui-slider-vertical.ui-slider-float .ui-slider-tip, - .ui-slider-vertical.ui-slider-float .ui-slider-tip-label { - - top: 50%; - margin-top: -11px; - width: 34px; - margin-left: 0px; - left: -60px; - color: #333; - - -webkit-transition-duration: 200ms, 200ms, 0; - -moz-transition-duration: 200ms, 200ms, 0; - -ms-transition-duration: 200ms, 200ms, 0; - transition-duration: 200ms, 200ms, 0; - - -webkit-transition-property: opacity, left, visibility; - -moz-transition-property: opacity, left, visibility; - -ms-transition-property: opacity, left, visibility; - transition-property: opacity, left, visibility; - - -webkit-transition-delay: 0, 0, 200ms; - -moz-transition-delay: 0, 0, 200ms; - -ms-transition-delay: 0, 0, 200ms; - transition-delay: 0, 0, 200ms; - - } - - - - .ui-slider-vertical.ui-slider-float .ui-slider-handle:hover .ui-slider-tip, - .ui-slider-vertical.ui-slider-float .ui-slider-handle.ui-state-hover .ui-slider-tip, - .ui-slider-vertical.ui-slider-float .ui-slider-handle:focus .ui-slider-tip, - .ui-slider-vertical.ui-slider-float .ui-slider-handle.ui-state-focus .ui-slider-tip, - .ui-slider-vertical.ui-slider-float .ui-slider-handle.ui-state-active .ui-slider-tip, - .ui-slider-vertical.ui-slider-float .ui-slider-pip:hover .ui-slider-tip-label { - top: 50%; - margin-top: -11px; - left: -50px; - } - - /* put label tooltips to right of slider */ - .ui-slider-vertical.ui-slider-float .ui-slider-pip .ui-slider-tip-label { - left: 47px; - } - - .ui-slider-vertical.ui-slider-float .ui-slider-pip:hover .ui-slider-tip-label { - left: 37px; - } - - /* give the tooltip a css triangle arrow */ - .ui-slider-vertical.ui-slider-float .ui-slider-tip:after, - .ui-slider-vertical.ui-slider-float .ui-slider-pip .ui-slider-tip-label:after { - border: 5px solid rgba(255,255,255,0); - border-left-color: rgba(255,255,255,1); - border-top-color: transparent; - position: absolute; - bottom: 50%; - margin-bottom: -5px; - right: -10px; - margin-left: 0; - top: auto; - left: auto; - } - - .ui-slider-vertical.ui-slider-float .ui-slider-tip:before, - .ui-slider-vertical.ui-slider-float .ui-slider-pip .ui-slider-tip-label:before { - border: 5px solid rgba(255,255,255,0); - border-left-color: #888; - border-top-color: transparent; - position: absolute; - bottom: 50%; - margin-bottom: -5px; - right: -11px; - margin-left: 0; - top: auto; - left: auto; - } - - .ui-slider-vertical.ui-slider-float .ui-slider-pip .ui-slider-tip-label:after { - border: 5px solid rgba(255,255,255,0); - border-right-color: rgba(255,255,255,1); - right: auto; - left: -10px; - } - - .ui-slider-vertical.ui-slider-float .ui-slider-pip .ui-slider-tip-label:before { - border: 5px solid rgba(255,255,255,0); - border-right-color: #888; - right: auto; - left: -11px; - } - - - - - - - - - - - - - - - /* SELECTED STATES */ - /* Comment out this chuck of code if you don't want to have - the new label colours shown */ - - .ui-slider-pips [class*=ui-slider-pip-initial] { - - font-weight: bold; - color: #14CA82; - - } - - .ui-slider-pips .ui-slider-pip-initial-1 { - - } - - .ui-slider-pips .ui-slider-pip-initial-2 { - color: #1897C9; - } - - - - .ui-slider-pips [class*=i-slider-pip-selected] { - - font-weight: bold; - color: #FF7A00; - - } - - .ui-slider-pips .ui-slider-pip-selected-1 { - - } - - .ui-slider-pips .ui-slider-pip-selected-2 { - color: #E70081; - } - - - - diff --git a/public/css/jquery-ui.css b/public/css/jquery-ui.css index 8cdd0a3c..90aa9a11 100755 --- a/public/css/jquery-ui.css +++ b/public/css/jquery-ui.css @@ -1,695 +1,1225 @@ -/*! jQuery UI - v1.11.2 - 2014-11-13 -* http://jqueryui.com -* Includes: core.css, button.css, slider.css, tabs.css, theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px -* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ - -/* Layout helpers -----------------------------------*/ -.ui-helper-hidden { - display: none; -} -.ui-helper-hidden-accessible { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} -.ui-helper-reset { - margin: 0; - padding: 0; - border: 0; - outline: 0; - line-height: 1.3; - text-decoration: none; - font-size: 100%; - list-style: none; -} -.ui-helper-clearfix:before, -.ui-helper-clearfix:after { - content: ""; - display: table; - border-collapse: collapse; -} -.ui-helper-clearfix:after { - clear: both; -} -.ui-helper-clearfix { - min-height: 0; /* support: IE7 */ -} -.ui-helper-zfix { - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - opacity: 0; - filter:Alpha(Opacity=0); /* support: IE8 */ -} - -.ui-front { - z-index: 100; -} - - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { - cursor: default !important; -} - - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - display: block; - text-indent: -99999px; - overflow: hidden; - background-repeat: no-repeat; -} - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.ui-button { - display: inline-block; - position: relative; - padding: 0; - line-height: normal; - margin-right: .1em; - cursor: pointer; - vertical-align: middle; - text-align: center; - overflow: visible; /* removes extra width in IE */ -} -.ui-button, -.ui-button:link, -.ui-button:visited, -.ui-button:hover, -.ui-button:active { - text-decoration: none; -} -/* to make room for the icon, a width needs to be set here */ -.ui-button-icon-only { - width: 2.2em; -} -/* button elements seem to need a little more width */ -button.ui-button-icon-only { - width: 2.4em; -} -.ui-button-icons-only { - width: 3.4em; -} -button.ui-button-icons-only { - width: 3.7em; -} - -/* button text element */ -.ui-button .ui-button-text { - display: block; - line-height: normal; -} -.ui-button-text-only .ui-button-text { - padding: .4em 1em; -} -.ui-button-icon-only .ui-button-text, -.ui-button-icons-only .ui-button-text { - padding: .4em; - text-indent: -9999999px; -} -.ui-button-text-icon-primary .ui-button-text, -.ui-button-text-icons .ui-button-text { - padding: .4em 1em .4em 2.1em; -} -.ui-button-text-icon-secondary .ui-button-text, -.ui-button-text-icons .ui-button-text { - padding: .4em 2.1em .4em 1em; -} -.ui-button-text-icons .ui-button-text { - padding-left: 2.1em; - padding-right: 2.1em; -} -/* no icon support for input elements, provide padding by default */ -input.ui-button { - padding: .4em 1em; -} - -/* button icon element(s) */ -.ui-button-icon-only .ui-icon, -.ui-button-text-icon-primary .ui-icon, -.ui-button-text-icon-secondary .ui-icon, -.ui-button-text-icons .ui-icon, -.ui-button-icons-only .ui-icon { - position: absolute; - top: 50%; - margin-top: -8px; -} -.ui-button-icon-only .ui-icon { - left: 50%; - margin-left: -8px; -} -.ui-button-text-icon-primary .ui-button-icon-primary, -.ui-button-text-icons .ui-button-icon-primary, -.ui-button-icons-only .ui-button-icon-primary { - left: .5em; -} -.ui-button-text-icon-secondary .ui-button-icon-secondary, -.ui-button-text-icons .ui-button-icon-secondary, -.ui-button-icons-only .ui-button-icon-secondary { - right: .5em; -} - -/* button sets */ -.ui-buttonset { - margin-right: 7px; -} -.ui-buttonset .ui-button { - margin-left: 0; - margin-right: -.3em; -} - -/* workarounds */ -/* reset extra padding in Firefox, see h5bp.com/l */ -input.ui-button::-moz-focus-inner, -button.ui-button::-moz-focus-inner { - border: 0; - padding: 0; -} -.ui-slider { - position: relative; - text-align: left; -} -.ui-slider .ui-slider-handle { - position: absolute; - z-index: 2; - width: 1.2em; - height: 1.2em; - cursor: default; - -ms-touch-action: none; - touch-action: none; -} -.ui-slider .ui-slider-range { - position: absolute; - z-index: 1; - font-size: .7em; - display: block; - border: 0; - background-position: 0 0; -} - -/* support: IE8 - See #6727 */ -.ui-slider.ui-state-disabled .ui-slider-handle, -.ui-slider.ui-state-disabled .ui-slider-range { - filter: inherit; -} - -.ui-slider-horizontal { - height: .8em; -} -.ui-slider-horizontal .ui-slider-handle { - top: -.3em; - margin-left: -.6em; -} -.ui-slider-horizontal .ui-slider-range { - top: 0; - height: 100%; -} -.ui-slider-horizontal .ui-slider-range-min { - left: 0; -} -.ui-slider-horizontal .ui-slider-range-max { - right: 0; -} - -.ui-slider-vertical { - width: .8em; - height: 100px; -} -.ui-slider-vertical .ui-slider-handle { - left: -.3em; - margin-left: 0; - margin-bottom: -.6em; -} -.ui-slider-vertical .ui-slider-range { - left: 0; - width: 100%; -} -.ui-slider-vertical .ui-slider-range-min { - bottom: 0; -} -.ui-slider-vertical .ui-slider-range-max { - top: 0; -} -.ui-tabs { - position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ - padding: .2em; -} -.ui-tabs .ui-tabs-nav { - margin: 0; - padding: .2em .2em 0; -} -.ui-tabs .ui-tabs-nav li { - list-style: none; - float: left; - position: relative; - top: 0; - margin: 1px .2em 0 0; - border-bottom-width: 0; - padding: 0; - white-space: nowrap; -} -.ui-tabs .ui-tabs-nav .ui-tabs-anchor { - float: left; - padding: .5em 1em; - text-decoration: none; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active { - margin-bottom: -1px; - padding-bottom: 1px; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { - cursor: text; -} -.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { - cursor: pointer; -} -.ui-tabs .ui-tabs-panel { - display: block; - border-width: 0; - padding: 1em 1.4em; - background: none; -} - -/* Component containers -----------------------------------*/ -.ui-widget { - font-family: Verdana,Arial,sans-serif; - font-size: 1.1em; -} -.ui-widget .ui-widget { - font-size: 1em; -} -.ui-widget input, -.ui-widget select, -.ui-widget textarea, -.ui-widget button { - font-family: Verdana,Arial,sans-serif; - font-size: 1em; -} -.ui-widget-content { - border: 1px solid #aaaaaa; - background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x; - color: #222222; -} -.ui-widget-content a { - color: #222222; -} -.ui-widget-header { - border: 1px solid #aaaaaa; - background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x; - color: #222222; - font-weight: bold; -} -.ui-widget-header a { - color: #222222; -} - -/* Interaction states -----------------------------------*/ -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default { - border: 1px solid #d3d3d3; - background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #555555; -} -.ui-state-default a, -.ui-state-default a:link, -.ui-state-default a:visited { - color: #555555; - text-decoration: none; -} -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-widget-header .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus { - border: 1px solid #999999; - background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #212121; -} -.ui-state-hover a, -.ui-state-hover a:hover, -.ui-state-hover a:link, -.ui-state-hover a:visited, -.ui-state-focus a, -.ui-state-focus a:hover, -.ui-state-focus a:link, -.ui-state-focus a:visited { - color: #212121; - text-decoration: none; -} -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active { - border: 1px solid #aaaaaa; - background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; - font-weight: normal; - color: #212121; -} -.ui-state-active a, -.ui-state-active a:link, -.ui-state-active a:visited { - color: #212121; - text-decoration: none; -} - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { - border: 1px solid #fcefa1; - background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x; - color: #363636; -} -.ui-state-highlight a, -.ui-widget-content .ui-state-highlight a, -.ui-widget-header .ui-state-highlight a { - color: #363636; -} -.ui-state-error, -.ui-widget-content .ui-state-error, -.ui-widget-header .ui-state-error { - border: 1px solid #cd0a0a; - background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x; - color: #cd0a0a; -} -.ui-state-error a, -.ui-widget-content .ui-state-error a, -.ui-widget-header .ui-state-error a { - color: #cd0a0a; -} -.ui-state-error-text, -.ui-widget-content .ui-state-error-text, -.ui-widget-header .ui-state-error-text { - color: #cd0a0a; -} -.ui-priority-primary, -.ui-widget-content .ui-priority-primary, -.ui-widget-header .ui-priority-primary { - font-weight: bold; -} -.ui-priority-secondary, -.ui-widget-content .ui-priority-secondary, -.ui-widget-header .ui-priority-secondary { - opacity: .7; - filter:Alpha(Opacity=70); /* support: IE8 */ - font-weight: normal; -} -.ui-state-disabled, -.ui-widget-content .ui-state-disabled, -.ui-widget-header .ui-state-disabled { - opacity: .35; - filter:Alpha(Opacity=35); /* support: IE8 */ - background-image: none; -} -.ui-state-disabled .ui-icon { - filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ -} - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - width: 16px; - height: 16px; -} -.ui-icon, -.ui-widget-content .ui-icon { - background-image: url("images/ui-icons_222222_256x240.png"); -} -.ui-widget-header .ui-icon { - background-image: url("images/ui-icons_222222_256x240.png"); -} -.ui-state-default .ui-icon { - background-image: url("images/ui-icons_888888_256x240.png"); -} -.ui-state-hover .ui-icon, -.ui-state-focus .ui-icon { - background-image: url("images/ui-icons_454545_256x240.png"); -} -.ui-state-active .ui-icon { - background-image: url("images/ui-icons_454545_256x240.png"); -} -.ui-state-highlight .ui-icon { - background-image: url("images/ui-icons_2e83ff_256x240.png"); -} -.ui-state-error .ui-icon, -.ui-state-error-text .ui-icon { - background-image: url("images/ui-icons_cd0a0a_256x240.png"); -} - -/* positioning */ -.ui-icon-blank { background-position: 16px 16px; } -.ui-icon-carat-1-n { background-position: 0 0; } -.ui-icon-carat-1-ne { background-position: -16px 0; } -.ui-icon-carat-1-e { background-position: -32px 0; } -.ui-icon-carat-1-se { background-position: -48px 0; } -.ui-icon-carat-1-s { background-position: -64px 0; } -.ui-icon-carat-1-sw { background-position: -80px 0; } -.ui-icon-carat-1-w { background-position: -96px 0; } -.ui-icon-carat-1-nw { background-position: -112px 0; } -.ui-icon-carat-2-n-s { background-position: -128px 0; } -.ui-icon-carat-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -64px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -64px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 0 -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-on { background-position: -96px -144px; } -.ui-icon-radio-off { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, -.ui-corner-top, -.ui-corner-left, -.ui-corner-tl { - border-top-left-radius: 4px; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-right, -.ui-corner-tr { - border-top-right-radius: 4px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-left, -.ui-corner-bl { - border-bottom-left-radius: 4px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-right, -.ui-corner-br { - border-bottom-right-radius: 4px; -} - -/* Overlays */ -.ui-widget-overlay { - background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; - opacity: .3; - filter: Alpha(Opacity=30); /* support: IE8 */ -} -.ui-widget-shadow { - margin: -8px 0 0 -8px; - padding: 8px; - background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; - opacity: .3; - filter: Alpha(Opacity=30); /* support: IE8 */ - border-radius: 8px; -} +/*! jQuery UI - v1.11.4 - 2015-03-11 +* http://jqueryui.com +* Includes: core.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, draggable.css, menu.css, progressbar.css, resizable.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); /* support: IE8 */ +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin: 2px 0 0 0; + padding: .5em .5em .5em .7em; + min-height: 0; /* support: IE7 */ + font-size: 100%; +} +.ui-accordion .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-icons .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-header .ui-accordion-header-icon { + position: absolute; + left: .5em; + top: 50%; + margin-top: -8px; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} +.ui-button { + display: inline-block; + position: relative; + padding: 0; + line-height: normal; + margin-right: .1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: visible; /* removes extra width in IE */ +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +/* to make room for the icon, a width needs to be set here */ +.ui-button-icon-only { + width: 2.2em; +} +/* button elements seem to need a little more width */ +button.ui-button-icon-only { + width: 2.4em; +} +.ui-button-icons-only { + width: 3.4em; +} +button.ui-button-icons-only { + width: 3.7em; +} + +/* button text element */ +.ui-button .ui-button-text { + display: block; + line-height: normal; +} +.ui-button-text-only .ui-button-text { + padding: .4em 1em; +} +.ui-button-icon-only .ui-button-text, +.ui-button-icons-only .ui-button-text { + padding: .4em; + text-indent: -9999999px; +} +.ui-button-text-icon-primary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 1em .4em 2.1em; +} +.ui-button-text-icon-secondary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 2.1em .4em 1em; +} +.ui-button-text-icons .ui-button-text { + padding-left: 2.1em; + padding-right: 2.1em; +} +/* no icon support for input elements, provide padding by default */ +input.ui-button { + padding: .4em 1em; +} + +/* button icon element(s) */ +.ui-button-icon-only .ui-icon, +.ui-button-text-icon-primary .ui-icon, +.ui-button-text-icon-secondary .ui-icon, +.ui-button-text-icons .ui-icon, +.ui-button-icons-only .ui-icon { + position: absolute; + top: 50%; + margin-top: -8px; +} +.ui-button-icon-only .ui-icon { + left: 50%; + margin-left: -8px; +} +.ui-button-text-icon-primary .ui-button-icon-primary, +.ui-button-text-icons .ui-button-icon-primary, +.ui-button-icons-only .ui-button-icon-primary { + left: .5em; +} +.ui-button-text-icon-secondary .ui-button-icon-secondary, +.ui-button-text-icons .ui-button-icon-secondary, +.ui-button-icons-only .ui-button-icon-secondary { + right: .5em; +} + +/* button sets */ +.ui-buttonset { + margin-right: 7px; +} +.ui-buttonset .ui-button { + margin-left: 0; + margin-right: -.3em; +} + +/* workarounds */ +/* reset extra padding in Firefox, see h5bp.com/l */ +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 45%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} +.ui-dialog { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + padding: .2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: .4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: .1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 20px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: .5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: .5em; + padding: .3em 1em .5em .4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: .5em .4em .5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-se { + width: 12px; + height: 12px; + right: -5px; + bottom: -5px; + background-position: 16px 16px; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} +.ui-draggable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-menu { + list-style: none; + padding: 0; + margin: 0; + display: block; + outline: none; +} +.ui-menu .ui-menu { + position: absolute; +} +.ui-menu .ui-menu-item { + position: relative; + margin: 0; + padding: 3px 1em 3px .4em; + cursor: pointer; + min-height: 0; /* support: IE7 */ + /* support: IE10, see #8844 */ + list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); +} +.ui-menu .ui-menu-divider { + margin: 5px 0; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-state-focus, +.ui-menu .ui-state-active { + margin: -1px; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item { + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: 0; + bottom: 0; + left: .2em; + margin: auto 0; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + left: auto; + right: 0; +} +.ui-progressbar { + height: 2em; + text-align: left; + overflow: hidden; +} +.ui-progressbar .ui-progressbar-value { + margin: -1px; + height: 100%; +} +.ui-progressbar .ui-progressbar-overlay { + background: url("data:image/gif;base64,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"); + height: 100%; + filter: alpha(opacity=25); /* support: IE8 */ + opacity: 0.25; +} +.ui-progressbar-indeterminate .ui-progressbar-value { + background-image: none; +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.ui-selectable { + -ms-touch-action: none; + touch-action: none; +} +.ui-selectable-helper { + position: absolute; + z-index: 100; + border: 1px dotted black; +} +.ui-selectmenu-menu { + padding: 0; + margin: 0; + position: absolute; + top: 0; + left: 0; + display: none; +} +.ui-selectmenu-menu .ui-menu { + overflow: auto; + /* Support: IE7 */ + overflow-x: hidden; + padding-bottom: 1px; +} +.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { + font-size: 1em; + font-weight: bold; + line-height: 1.5; + padding: 2px 0.4em; + margin: 0.5em 0 0 0; + height: auto; + border: 0; +} +.ui-selectmenu-open { + display: block; +} +.ui-selectmenu-button { + display: inline-block; + overflow: hidden; + position: relative; + text-decoration: none; + cursor: pointer; +} +.ui-selectmenu-button span.ui-icon { + right: 0.5em; + left: auto; + margin-top: -8px; + position: absolute; + top: 50%; +} +.ui-selectmenu-button span.ui-selectmenu-text { + text-align: left; + padding: 0.4em 2.1em 0.4em 1em; + display: block; + line-height: 1.4; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; + -ms-touch-action: none; + touch-action: none; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* support: IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} +.ui-sortable-handle { + -ms-touch-action: none; + touch-action: none; +} +.ui-spinner { + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} +.ui-spinner-input { + border: none; + background: none; + color: inherit; + padding: 0; + margin: .2em 0; + vertical-align: middle; + margin-left: .4em; + margin-right: 22px; +} +.ui-spinner-button { + width: 16px; + height: 50%; + font-size: .5em; + padding: 0; + margin: 0; + text-align: center; + position: absolute; + cursor: default; + display: block; + overflow: hidden; + right: 0; +} +/* more specificity required here to override default borders */ +.ui-spinner a.ui-spinner-button { + border-top: none; + border-bottom: none; + border-right: none; +} +/* vertically center icon */ +.ui-spinner .ui-icon { + position: absolute; + margin-top: -8px; + top: 50%; + left: 0; +} +.ui-spinner-up { + top: 0; +} +.ui-spinner-down { + bottom: 0; +} + +/* TR overrides */ +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position: -65px -16px; +} +.ui-tabs { + position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + padding: .2em; +} +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 0; + margin: 1px .2em 0 0; + border-bottom-width: 0; + padding: 0; + white-space: nowrap; +} +.ui-tabs .ui-tabs-nav .ui-tabs-anchor { + float: left; + padding: .5em 1em; + text-decoration: none; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active { + margin-bottom: -1px; + padding-bottom: 1px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { + cursor: text; +} +.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { + cursor: pointer; +} +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} +.ui-tooltip { + padding: 8px; + position: absolute; + z-index: 9999; + max-width: 300px; + -webkit-box-shadow: 0 0 5px #aaa; + box-shadow: 0 0 5px #aaa; +} +body .ui-tooltip { + border-width: 2px; +} + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Verdana,Arial,sans-serif; + font-size: 1.1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Verdana,Arial,sans-serif; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #aaaaaa; + background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x; + color: #222222; +} +.ui-widget-content a { + color: #222222; +} +.ui-widget-header { + border: 1px solid #aaaaaa; + background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x; + color: #222222; + font-weight: bold; +} +.ui-widget-header a { + color: #222222; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #d3d3d3; + background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #555555; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #555555; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #999999; + background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #212121; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited { + color: #212121; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #aaaaaa; + background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #212121; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #212121; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fcefa1; + background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x; + color: #363636; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a; + background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x; + color: #cd0a0a; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #cd0a0a; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #cd0a0a; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); /* support: IE8 */ + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); /* support: IE8 */ + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-widget-header .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-state-default .ui-icon { + background-image: url("images/ui-icons_888888_256x240.png"); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url("images/ui-icons_454545_256x240.png"); +} +.ui-state-active .ui-icon { + background-image: url("images/ui-icons_454545_256x240.png"); +} +.ui-state-highlight .ui-icon { + background-image: url("images/ui-icons_2e83ff_256x240.png"); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url("images/ui-icons_cd0a0a_256x240.png"); +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px; +} + +/* Overlays */ +.ui-widget-overlay { + background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; + opacity: .3; + filter: Alpha(Opacity=30); /* support: IE8 */ +} +.ui-widget-shadow { + margin: -8px 0 0 -8px; + padding: 8px; + background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x; + opacity: .3; + filter: Alpha(Opacity=30); /* support: IE8 */ + border-radius: 8px; +} diff --git a/public/css/jquery.fancybox.css b/public/css/jquery.fancybox.css new file mode 100755 index 00000000..370d9dd0 --- /dev/null +++ b/public/css/jquery.fancybox.css @@ -0,0 +1,275 @@ +/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */ +.fancybox-wrap, +.fancybox-skin, +.fancybox-outer, +.fancybox-inner, +.fancybox-image, +.fancybox-wrap iframe, +.fancybox-wrap object, +.fancybox-nav, +.fancybox-nav span, +.fancybox-tmp +{ + padding: 0; + margin: 0; + border: 0; + outline: none; + vertical-align: top; +} + +.fancybox-wrap { + position: absolute; + top: 0; + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + z-index: 8020; +} + +.fancybox-skin { + position: relative; + background: #f9f9f9; + color: #444; + text-shadow: none; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.fancybox-opened { + z-index: 8030; +} + +.fancybox-opened .fancybox-skin { + -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); +} + +.fancybox-outer, .fancybox-inner { + position: relative; +} + +.fancybox-inner { + overflow: hidden; +} + +.fancybox-type-iframe .fancybox-inner { + -webkit-overflow-scrolling: touch; +} + +.fancybox-error { + color: #444; + font: 14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; + margin: 0; + padding: 15px; + white-space: nowrap; +} + +.fancybox-image, .fancybox-iframe { + display: block; + width: 100%; + height: 100%; +} + +.fancybox-image { + max-width: 100%; + max-height: 100%; +} + +#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span { + background-image: url(../images/fancybox/fancybox_sprite.png); +} + +#fancybox-loading { + position: fixed; + top: 50%; + left: 50%; + margin-top: -22px; + margin-left: -22px; + background-position: 0 -108px; + opacity: 0.8; + cursor: pointer; + z-index: 8060; +} + +#fancybox-loading div { + width: 44px; + height: 44px; + background: url(../images/fancybox/fancybox_loading.gif) center center no-repeat; +} + +.fancybox-close { + position: absolute; + top: -18px; + right: -18px; + width: 36px; + height: 36px; + cursor: pointer; + z-index: 8040; +} + +.fancybox-nav { + position: absolute; + top: 0; + width: 40%; + height: 100%; + cursor: pointer; + text-decoration: none; + background: transparent url(../images/fancybox/blank.gif); /* helps IE */ + -webkit-tap-highlight-color: rgba(0,0,0,0); + z-index: 8040; +} + +.fancybox-prev { + left: 0; +} + +.fancybox-next { + right: 0; +} + +.fancybox-nav span { + position: absolute; + top: 50%; + width: 36px; + height: 34px; + margin-top: -18px; + cursor: pointer; + z-index: 8040; + visibility: hidden; +} + +.fancybox-prev span { + left: 10px; + background-position: 0 -36px; +} + +.fancybox-next span { + right: 10px; + background-position: 0 -72px; +} + +.fancybox-nav:hover span { + visibility: visible; +} + +.fancybox-tmp { + position: absolute; + top: -99999px; + left: -99999px; + max-width: 99999px; + max-height: 99999px; + overflow: visible !important; +} + +/* Overlay helper */ + +.fancybox-lock { + overflow: visible !important; + width: auto; +} + +.fancybox-lock body { + overflow: hidden !important; +} + +.fancybox-lock-test { + overflow-y: hidden !important; +} + +.fancybox-overlay { + position: absolute; + top: 0; + left: 0; + overflow: hidden; + display: none; + z-index: 8010; + background: url(../images/fancybox/fancybox_overlay.png); +} + +.fancybox-overlay-fixed { + position: fixed; + bottom: 0; + right: 0; +} + +.fancybox-lock .fancybox-overlay { + overflow: auto; + overflow-y: scroll; +} + +/* Title helper */ + +.fancybox-title { + visibility: hidden; + font: normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; + position: relative; + text-shadow: none; + z-index: 8050; +} + +.fancybox-opened .fancybox-title { + visibility: visible; +} + +.fancybox-title-float-wrap { + position: absolute; + bottom: 0; + right: 50%; + margin-bottom: -35px; + z-index: 8050; + text-align: center; +} + +.fancybox-title-float-wrap .child { + display: inline-block; + margin-right: -100%; + padding: 2px 20px; + background: transparent; /* Fallback for web browsers that doesn't support RGBa */ + background: rgba(0, 0, 0, 0.8); + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; + text-shadow: 0 1px 2px #222; + color: #FFF; + font-weight: bold; + line-height: 24px; + white-space: nowrap; +} + +.fancybox-title-outside-wrap { + position: relative; + margin-top: 10px; + color: #fff; +} + +.fancybox-title-inside-wrap { + padding-top: 10px; +} + +.fancybox-title-over-wrap { + position: absolute; + bottom: 0; + left: 0; + color: #fff; + padding: 10px; + background: #000; + background: rgba(0, 0, 0, .8); +} + +/*Retina graphics!*/ +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), + only screen and (min--moz-device-pixel-ratio: 1.5), + only screen and (min-device-pixel-ratio: 1.5){ + + #fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span { + background-image: url(../images/fancybox/fancybox_sprite@2x.png); + background-size: 44px 152px; /*The size of the normal image, half the size of the hi-res image*/ + } + + #fancybox-loading div { + background-image: url(../images/fancybox/fancybox_loading@2x.gif); + background-size: 24px 24px; /*The size of the normal image, half the size of the hi-res image*/ + } +} \ No newline at end of file diff --git a/public/css/jquery.growl.css b/public/css/jquery.growl.css new file mode 100644 index 00000000..aed5923e --- /dev/null +++ b/public/css/jquery.growl.css @@ -0,0 +1,85 @@ +/* jQuery Growl + * Copyright 2015 Kevin Sylvestre + * 1.3.2 + */ +#growls { + z-index: 50000; + position: fixed; } + #growls.default { + top: 10px; + right: 10px; } + #growls.tl { + top: 10px; + left: 10px; } + #growls.tr { + top: 10px; + right: 10px; } + #growls.bl { + bottom: 10px; + left: 10px; } + #growls.br { + bottom: 10px; + right: 10px; } + #growls.tc { + top: 10px; + right: 10px; + left: 10px; } + #growls.bc { + bottom: 10px; + right: 10px; + left: 10px; } + #growls.tc .growl, #growls.bc .growl { + margin-left: auto; + margin-right: auto; } + +.growl { + opacity: 0.8; + filter: alpha(opacity=80); + position: relative; + border-radius: 4px; + -webkit-transition: all 0.4s ease-in-out; + -moz-transition: all 0.4s ease-in-out; + transition: all 0.4s ease-in-out; } + .growl.growl-incoming { + opacity: 0; + filter: alpha(opacity=0); } + .growl.growl-outgoing { + opacity: 0; + filter: alpha(opacity=0); } + .growl.growl-small { + width: 200px; + padding: 5px; + margin: 5px; } + .growl.growl-medium { + width: 250px; + padding: 10px; + margin: 10px; } + .growl.growl-large { + width: 300px; + padding: 15px; + margin: 15px; } + .growl.growl-default { + color: #FFF; + background: #7f8c8d; } + .growl.growl-error { + color: #FFF; + background: #C0392B; } + .growl.growl-notice { + color: #FFF; + background: #2ECC71; } + .growl.growl-warning { + color: #FFF; + background: #F39C12; } + .growl .growl-close { + cursor: pointer; + float: right; + font-size: 14px; + line-height: 18px; + font-weight: normal; + font-family: helvetica, verdana, sans-serif; } + .growl .growl-title { + font-size: 18px; + line-height: 24px; } + .growl .growl-message { + font-size: 14px; + line-height: 18px; } \ No newline at end of file diff --git a/public/css/main.css b/public/css/main.css new file mode 100755 index 00000000..730d09ef --- /dev/null +++ b/public/css/main.css @@ -0,0 +1,288 @@ +/*! HTML5 Boilerplate v5.2.0 | MIT License | https://html5boilerplate.com/ */ + +/* + * What follows is the result of much research on cross-browser styling. + * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, + * Kroc Camen, and the H5BP dev community and team. + */ + +/* ========================================================================== + Base styles: opinionated defaults + ========================================================================== */ + +html { + color: #222; + font-size: 1em; + line-height: 1.4; +} + +/* + * Remove text-shadow in selection highlight: + * https://twitter.com/miketaylr/status/12228805301 + * + * These selection rule sets have to be separate. + * Customize the background color to match your design. + */ + +::-moz-selection { + background: #b3d4fc; + text-shadow: none; +} + +::selection { + background: #b3d4fc; + text-shadow: none; +} + +/* + * A better looking default horizontal rule + */ + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; +} + +/* + * Remove the gap between audio, canvas, iframes, + * images, videos and the bottom of their containers: + * https://github.com/h5bp/html5-boilerplate/issues/440 + */ + +audio, +canvas, +iframe, +img, +svg, +video { + vertical-align: middle; +} + +/* + * Remove default fieldset styles. + */ + +fieldset { + border: 0; + margin: 0; + padding: 0; +} + +/* + * Allow only vertical resizing of textareas. + */ + +textarea { + resize: vertical; +} + +/* ========================================================================== + Browser Upgrade Prompt + ========================================================================== */ + +.browserupgrade { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +/* ========================================================================== + Author's custom styles + ========================================================================== */ + + + + + + + + + + + + + + + + + +/* ========================================================================== + Helper classes + ========================================================================== */ + +/* + * Hide visually and from screen readers: + */ + +.hidden { + display: none !important; +} + +/* + * Hide only visually, but have it available for screen readers: + * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility + */ + +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* + * Extends the .visuallyhidden class to allow the element + * to be focusable when navigated to via the keyboard: + * https://www.drupal.org/node/897638 + */ + +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; +} + +/* + * Hide visually and from screen readers, but maintain layout + */ + +.invisible { + visibility: hidden; +} + +/* + * Clearfix: contain floats + * + * For modern browsers + * 1. The space content is one way to avoid an Opera bug when the + * `contenteditable` attribute is included anywhere else in the document. + * Otherwise it causes space to appear at the top and bottom of elements + * that receive the `clearfix` class. + * 2. The use of `table` rather than `block` is only necessary if using + * `:before` to contain the top-margins of child elements. + */ + +.clearfix:before, +.clearfix:after { + content: " "; /* 1 */ + display: table; /* 2 */ +} + +.clearfix:after { + clear: both; +} + +/* ========================================================================== + EXAMPLE Media Queries for Responsive Design. + These examples override the primary ('mobile first') styles. + Modify as content requires. + ========================================================================== */ + +@media only screen and (min-width: 35em) { + /* Style adjustments for viewports that meet the condition */ +} + +@media print, + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 1.25dppx), + (min-resolution: 120dpi) { + /* Style adjustments for high resolution devices */ +} + +/* ========================================================================== + Print styles. + Inlined to avoid the additional HTTP request: + http://www.phpied.com/delay-loading-your-print-css/ + ========================================================================== */ + +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; /* Black prints faster: + http://www.sanbeiji.com/archives/953 */ + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + /* + * Don't show links that are fragment identifiers, + * or use the `javascript:` pseudo protocol + */ + + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + /* + * Printing Tables: + * http://css-discuss.incutio.com/wiki/Printing_Tables + */ + + thead { + display: table-header-group; + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} + +h2 { + font-size: 1.6em; +} + +h3 { + font-size: 1.4em; +} \ No newline at end of file diff --git a/public/css/meanmenu.min.css b/public/css/meanmenu.min.css new file mode 100755 index 00000000..0fc480d0 --- /dev/null +++ b/public/css/meanmenu.min.css @@ -0,0 +1,144 @@ +a.meanmenu-reveal{display:none} +.mean-container .mean-bar { + background: #0F99DE; + float: left; + min-height: 42px; + padding: 4px 0 0; + position: relative; + width: 100%; + z-index: 9998; +} +.mean-container a.meanmenu-reveal{width:22px;height:22px;padding:13px 13px 11px;top:0;right:0;cursor:pointer;color:#fff;text-decoration:none;font-size:16px;text-indent:-9999em;line-height:22px;font-size:1px;display:block;font-family:Arial,Helvetica,sans-serif;font-weight:700;float: right;} +.mean-container a.meanmenu-reveal span{display:block;background:#fff;height:3px;margin-top:3px} +.mean-container .mean-nav{float:left;width:100%;background:#0c1923} +.mean-container .mean-nav ul{padding:0;margin:0;width:100%;list-style-type:none} +.mean-container .mean-nav ul li { + background: #f8f8f8 none repeat scroll 0 0; + float: left; + position: relative; + width: 100%; + border-top: 1px solid#ddd; +} +.mean-container .mean-nav ul li a { + background: #f8f8f8 none repeat scroll 0 0; + color: #666666; + display: block; + float: left; + font-size: 12px; + margin: 0; + padding: 1em 5%; + text-align: left; + text-decoration: none; + text-transform: uppercase; + width: 90%; + font-weight: bold; +} +.mobile-menu-area { + background: #262626; +} +.mean-container .mean-nav ul li li a { + border-top: 1px solid rgba(255, 255, 255, 0.25); + opacity: 0.75; + padding: 1em 10%; + text-shadow: none !important; + visibility: visible; + width: 80%; + font-weight: normal; + text-transform: capitalize; + color: #444; +} +.mean-container .mean-nav ul li.mean-last a{border-bottom:0;margin-bottom:0} +.mean-container .mean-nav ul li li li a{width:70%;padding:1em 15%} +.mean-container .mean-nav ul li li li li a{width:60%;padding:1em 20%} +.mean-container .mean-nav ul li li li li li a{width:50%;padding:1em 25%} +.mean-container .mean-nav ul li a:hover { + background: #f8f8f8 none repeat scroll 0 0; + color: #F47E1E; +} +.mean-container .mean-nav ul li a.mean-expand{ + width:26px; + height: 15px; + margin-top: 1px; + padding: 12px !important; + text-align:center; + position:absolute; + right:0;top:0; + z-index:2; + font-weight:700; + background:rgba(255,255,255,.1); + border:0!important; + background: #F8F8F8; + } +.mean-container .mean-nav ul li a.mean-expand:hover { + background: #f8f8f8; +} +.mean-container .mean-push{float:left;width:100%;padding:0;margin:0;clear:both} +.mean-nav .wrapper{width:100%;padding:0;margin:0} +.mean-container .mean-bar,.mean-container .mean-bar *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box} +.mean-remove{display:none!important} +.mean-container .mean-bar::after { + color: #ffffff; + content: "MENU"; + font-size: 21px; + left: 5%; + position: absolute; + top: 12px; +} + + +/* ------------------------------- +39. Mobile menu +----------------------------------*/ +.mean-container .mean-bar::after { + display: none; +} +.mean-container .mean-nav ul li a:hover { + color: #2bcdc1; + background: none; +} +.mean-container a.meanmenu-reveal span { + background: #fff; +} +.mobile-menu-area { + background: #fff none repeat scroll 0 0; + border-top: 3px solid #2bcdc1; +} +.mean-container .mean-nav ul li a { + background: #444; + color: #fff; + display: block; +} +.mean-container .mean-nav ul li a.mean-expand { + background: none; + top: -4px; +} +.mean-container .mean-nav ul li a.mean-expand:hover { + background: none; +} +.mean-container .mean-nav ul li { + background: #444; +} +.mean-container .mean-nav ul li li a { + color: #fff; + opacity: .90; + border-top: 0; +} +.mean-container a.meanmenu-reveal { + color: #444; +} +.meanmenu-reveal.meanclose:hover { + color: #2bcdc1 !important; +} +.mean-container a.meanmenu-reveal:hover span { + background: #2bcdc1; +} +.mean-container .mean-bar::before { + color: #fff; + content: "MENU"; + font-size: 18px; + font-weight: 700; + left: 10px; + position: absolute; + top: 14px; +} + diff --git a/public/css/nivo-slider.css b/public/css/nivo-slider.css new file mode 100755 index 00000000..d806fef7 --- /dev/null +++ b/public/css/nivo-slider.css @@ -0,0 +1,113 @@ +/* + * jQuery Nivo Slider v3.2 + * http://nivo.dev7studios.com + * + * Copyright 2012, Dev7studios + * Free to use and abuse under the MIT license. + * http://www.opensource.org/licenses/mit-license.php + */ + +/* The Nivo Slider styles */ +.nivoSlider { + position:relative; + width:100%; + height:auto; + overflow: hidden; +} +.nivoSlider img { + position:absolute; + top:0px; + left:0px; + max-width: none; +} +.nivo-main-image { + display: block !important; + position: relative !important; + width: 100% !important; +} + +/* If an image is wrapped in a link */ +.nivoSlider a.nivo-imageLink { + position:absolute; + top:0px; + left:0px; + width:100%; + height:100%; + border:0; + padding:0; + margin:0; + z-index:6; + display:none; + background:white; + filter:alpha(opacity=0); + opacity:0; +} +/* The slices and boxes in the Slider */ +.nivo-slice { + display:block; + position:absolute; + z-index:5; + height:100%; + top:0; +} +.nivo-box { + display:block; + position:absolute; + z-index:5; + overflow:hidden; +} +.nivo-box img { display:block; } + +/* Caption styles */ +.nivo-caption { + position:absolute; + left:0px; + bottom:0px; + background:#000; + color:#fff; + width:100%; + z-index:8; + padding: 5px 10px; + opacity: 0.8; + overflow: hidden; + display: none; + -moz-opacity: 0.8; + filter:alpha(opacity=8); + -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */ + -moz-box-sizing: border-box; /* Firefox, other Gecko */ + box-sizing: border-box; /* Opera/IE 8+ */ +} +.nivo-caption p { + padding:5px; + margin:0; +} +.nivo-caption a { + display:inline !important; +} +.nivo-html-caption { + display:none; +} +/* Direction nav styles (e.g. Next & Prev) */ +.nivo-directionNav a { + position:absolute; + top:45%; + z-index:9; + cursor:pointer; +} +.nivo-prevNav { + left:0px; +} +.nivo-nextNav { + right:0px; +} +/* Control nav styles (e.g. 1,2,3...) */ +.nivo-controlNav { + text-align:center; + padding: 15px 0; +} +.nivo-controlNav a { + cursor:pointer; +} +.nivo-controlNav a.active { + font-weight:bold; +} \ No newline at end of file diff --git a/public/css/normalize.css b/public/css/normalize.css new file mode 100755 index 00000000..5e5e3c89 --- /dev/null +++ b/public/css/normalize.css @@ -0,0 +1,424 @@ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS and IE text size adjust after device orientation change, + * without disabling user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability of focused elements when they are also in an + * active/hover state. + */ + +a:active, +a:hover { + outline: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + box-sizing: content-box; /* 2 */ +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/public/css/organisms/mobileNav.css b/public/css/organisms/mobileNav.css new file mode 100644 index 00000000..2d9cc65a --- /dev/null +++ b/public/css/organisms/mobileNav.css @@ -0,0 +1,89 @@ +.mobileNavCheckbox { + display: none; +} +.mobileNavButton { + cursor: pointer; + font-size: 1rem; + margin: 0; + padding: 0 1rem; + position: absolute; + right: 1rem; + text-align: center; + top: 1.5rem; + z-index: 99999; +} +.mobileNavCheckbox:checked ~ .mobileNavButton { + position: fixed; +} +.mobileNavButton-icon { + background-color: #000; + display: inline-block; + height: 0.25rem; + position: relative; + top: -.1875rem; + transition-duration: 0s; + transition-delay: 0.2s; + width: 1.25rem; +} +.mobileNavButton-icon::before, +.mobileNavButton-icon::after { + background-color: #000; + content: ''; + display: block; + height: 0.25rem; + position: absolute; + transition-delay: 0.2s, 0s; + transition-duration: 0.2s; + transition-property: margin, transform; + width: 1.25rem; +} +.mobileNavButton-icon::before { + margin-top: 0.375rem; +} +.mobileNavButton-icon::after { + margin-top: -0.375rem; +} +.mobileNavCheckbox:checked ~ .mobileNavButton .mobileNavButton-icon { + background-color: transparent; +} +.mobileNavCheckbox:checked ~ .mobileNavButton .mobileNavButton-icon::before, +.mobileNavCheckbox:checked ~ .mobileNavButton .mobileNavButton-icon::after { + margin-top: 0; + transition-delay: 0s, 0.2s; +} +.mobileNavCheckbox:checked ~ .mobileNavButton .mobileNavButton-icon::before { + transform: rotate(45deg); +} +.mobileNavCheckbox:checked ~ .mobileNavButton .mobileNavButton-icon::after { + transform: rotate(-45deg); +} +.mobileNav { + background-color: #9DFCF0; + display: block; + height: 100%; + position: fixed; + text-align: left; + top: 0; + transform: translate(-100%, 0); + transition: all 0.3s ease-in-out; + width: 100%; + z-index: 88888; +} +.mobileNavCheckbox:checked ~ .mobileNav { + transform: translate(0, 0); +} +.mobileNav-list { + list-style-type: none; + margin: 0 auto; + max-width: 32rem; + padding: 6rem 2rem 0; +} +.mobileNav-listItem { + margin-bottom: 1.5rem; +} +.mobileNav-listItem:last-of-type { + padding-top: 1.5rem; +} +.mobileNav-link { + +} \ No newline at end of file diff --git a/public/css/organisms/topNav.css b/public/css/organisms/topNav.css new file mode 100644 index 00000000..a532ad78 --- /dev/null +++ b/public/css/organisms/topNav.css @@ -0,0 +1,23 @@ +.topNav { + display: flex; + align-items: center; + justify-content: space-between; + height: 6rem; + padding: 0 1rem; +} +.topNav-logo { + display: block; + height: 1.5rem; + width: 6.75rem; +} +.topNav-list { + display: flex; + margin-right: 8rem; +} +.topNav-listItem { + display: block; + margin-left: 1rem; +} +.topNav-link { + +} \ No newline at end of file diff --git a/public/css/owl.carousel.css b/public/css/owl.carousel.css new file mode 100755 index 00000000..4e3c17c3 --- /dev/null +++ b/public/css/owl.carousel.css @@ -0,0 +1,71 @@ +/* + * Core Owl Carousel CSS File + * v1.3.3 + */ + +/* clearfix */ +.owl-carousel .owl-wrapper:after { + content: "."; + display: block; + clear: both; + visibility: hidden; + line-height: 0; + height: 0; +} +/* display none until init */ +.owl-carousel{ + display: none; + position: relative; + width: 100%; + -ms-touch-action: pan-y; +} +.owl-carousel .owl-wrapper{ + display: none; + position: relative; + -webkit-transform: translate3d(0px, 0px, 0px); +} +.owl-carousel .owl-wrapper-outer{ + overflow: hidden; + position: relative; + width: 100%; +} +.owl-carousel .owl-wrapper-outer.autoHeight{ + -webkit-transition: height 500ms ease-in-out; + -moz-transition: height 500ms ease-in-out; + -ms-transition: height 500ms ease-in-out; + -o-transition: height 500ms ease-in-out; + transition: height 500ms ease-in-out; +} + +.owl-carousel .owl-item{ + float: left; +} +.owl-controls .owl-page, +.owl-controls .owl-buttons div{ + cursor: pointer; +} +.owl-controls { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +/* mouse grab icon */ +.grabbing { + cursor:url(grabbing.png) 8 8, move; +} + +/* fix */ +.owl-carousel .owl-wrapper, +.owl-carousel .owl-item{ + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; + -ms-backface-visibility: hidden; + -webkit-transform: translate3d(0,0,0); + -moz-transform: translate3d(0,0,0); + -ms-transform: translate3d(0,0,0); +} + diff --git a/public/css/owl.theme.css b/public/css/owl.theme.css new file mode 100755 index 00000000..97729753 --- /dev/null +++ b/public/css/owl.theme.css @@ -0,0 +1,79 @@ +/* +* Owl Carousel Owl Demo Theme +* v1.3.3 +*/ + +.owl-theme .owl-controls{ + margin-top: 10px; + text-align: center; +} + +/* Styling Next and Prev buttons */ + +.owl-theme .owl-controls .owl-buttons div{ + color: #FFF; + display: inline-block; + zoom: 1; + *display: inline;/*IE7 life-saver */ + margin: 5px; + padding: 3px 10px; + font-size: 12px; + -webkit-border-radius: 30px; + -moz-border-radius: 30px; + border-radius: 30px; + background: #869791; + filter: Alpha(Opacity=50);/*IE7 fix*/ + opacity: 0.5; +} +/* Clickable class fix problem with hover on touch devices */ +/* Use it for non-touch hover action */ +.owl-theme .owl-controls.clickable .owl-buttons div:hover{ + filter: Alpha(Opacity=100);/*IE7 fix*/ + opacity: 1; + text-decoration: none; +} + +/* Styling Pagination*/ + +.owl-theme .owl-controls .owl-page{ + display: inline-block; + zoom: 1; + *display: inline;/*IE7 life-saver */ +} +.owl-theme .owl-controls .owl-page span{ + display: block; + width: 12px; + height: 12px; + margin: 5px 7px; + filter: Alpha(Opacity=50);/*IE7 fix*/ + opacity: 0.5; + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + border-radius: 20px; + background: #869791; +} + +.owl-theme .owl-controls .owl-page.active span, +.owl-theme .owl-controls.clickable .owl-page:hover span{ + filter: Alpha(Opacity=100);/*IE7 fix*/ + opacity: 1; +} + +/* If PaginationNumbers is true */ + +.owl-theme .owl-controls .owl-page span.owl-numbers{ + height: auto; + width: auto; + color: #FFF; + padding: 2px 10px; + font-size: 12px; + -webkit-border-radius: 30px; + -moz-border-radius: 30px; + border-radius: 30px; +} + +/* preloading images */ +.owl-item.loading{ + min-height: 150px; + background: url(AjaxLoader.gif) no-repeat center center +} \ No newline at end of file diff --git a/public/css/owl.transitions.css b/public/css/owl.transitions.css new file mode 100755 index 00000000..b2d55d5b --- /dev/null +++ b/public/css/owl.transitions.css @@ -0,0 +1,163 @@ +/* + * Owl Carousel CSS3 Transitions + * v1.3.2 + */ + +.owl-origin { + -webkit-perspective: 1200px; + -webkit-perspective-origin-x : 50%; + -webkit-perspective-origin-y : 50%; + -moz-perspective : 1200px; + -moz-perspective-origin-x : 50%; + -moz-perspective-origin-y : 50%; + perspective : 1200px; +} +/* fade */ +.owl-fade-out { + z-index: 10; + -webkit-animation: fadeOut .7s both ease; + -moz-animation: fadeOut .7s both ease; + animation: fadeOut .7s both ease; +} +.owl-fade-in { + -webkit-animation: fadeIn .7s both ease; + -moz-animation: fadeIn .7s both ease; + animation: fadeIn .7s both ease; +} +/* backSlide */ +.owl-backSlide-out { + -webkit-animation: backSlideOut 1s both ease; + -moz-animation: backSlideOut 1s both ease; + animation: backSlideOut 1s both ease; +} +.owl-backSlide-in { + -webkit-animation: backSlideIn 1s both ease; + -moz-animation: backSlideIn 1s both ease; + animation: backSlideIn 1s both ease; +} +/* goDown */ +.owl-goDown-out { + -webkit-animation: scaleToFade .7s ease both; + -moz-animation: scaleToFade .7s ease both; + animation: scaleToFade .7s ease both; +} +.owl-goDown-in { + -webkit-animation: goDown .6s ease both; + -moz-animation: goDown .6s ease both; + animation: goDown .6s ease both; +} +/* scaleUp */ +.owl-fadeUp-in { + -webkit-animation: scaleUpFrom .5s ease both; + -moz-animation: scaleUpFrom .5s ease both; + animation: scaleUpFrom .5s ease both; +} + +.owl-fadeUp-out { + -webkit-animation: scaleUpTo .5s ease both; + -moz-animation: scaleUpTo .5s ease both; + animation: scaleUpTo .5s ease both; +} +/* Keyframes */ +/*empty*/ +@-webkit-keyframes empty { + 0% {opacity: 1} +} +@-moz-keyframes empty { + 0% {opacity: 1} +} +@keyframes empty { + 0% {opacity: 1} +} +@-webkit-keyframes fadeIn { + 0% { opacity:0; } + 100% { opacity:1; } +} +@-moz-keyframes fadeIn { + 0% { opacity:0; } + 100% { opacity:1; } +} +@keyframes fadeIn { + 0% { opacity:0; } + 100% { opacity:1; } +} +@-webkit-keyframes fadeOut { + 0% { opacity:1; } + 100% { opacity:0; } +} +@-moz-keyframes fadeOut { + 0% { opacity:1; } + 100% { opacity:0; } +} +@keyframes fadeOut { + 0% { opacity:1; } + 100% { opacity:0; } +} +@-webkit-keyframes backSlideOut { + 25% { opacity: .5; -webkit-transform: translateZ(-500px); } + 75% { opacity: .5; -webkit-transform: translateZ(-500px) translateX(-200%); } + 100% { opacity: .5; -webkit-transform: translateZ(-500px) translateX(-200%); } +} +@-moz-keyframes backSlideOut { + 25% { opacity: .5; -moz-transform: translateZ(-500px); } + 75% { opacity: .5; -moz-transform: translateZ(-500px) translateX(-200%); } + 100% { opacity: .5; -moz-transform: translateZ(-500px) translateX(-200%); } +} +@keyframes backSlideOut { + 25% { opacity: .5; transform: translateZ(-500px); } + 75% { opacity: .5; transform: translateZ(-500px) translateX(-200%); } + 100% { opacity: .5; transform: translateZ(-500px) translateX(-200%); } +} +@-webkit-keyframes backSlideIn { + 0%, 25% { opacity: .5; -webkit-transform: translateZ(-500px) translateX(200%); } + 75% { opacity: .5; -webkit-transform: translateZ(-500px); } + 100% { opacity: 1; -webkit-transform: translateZ(0) translateX(0); } +} +@-moz-keyframes backSlideIn { + 0%, 25% { opacity: .5; -moz-transform: translateZ(-500px) translateX(200%); } + 75% { opacity: .5; -moz-transform: translateZ(-500px); } + 100% { opacity: 1; -moz-transform: translateZ(0) translateX(0); } +} +@keyframes backSlideIn { + 0%, 25% { opacity: .5; transform: translateZ(-500px) translateX(200%); } + 75% { opacity: .5; transform: translateZ(-500px); } + 100% { opacity: 1; transform: translateZ(0) translateX(0); } +} +@-webkit-keyframes scaleToFade { + to { opacity: 0; -webkit-transform: scale(.8); } +} +@-moz-keyframes scaleToFade { + to { opacity: 0; -moz-transform: scale(.8); } +} +@keyframes scaleToFade { + to { opacity: 0; transform: scale(.8); } +} +@-webkit-keyframes goDown { + from { -webkit-transform: translateY(-100%); } +} +@-moz-keyframes goDown { + from { -moz-transform: translateY(-100%); } +} +@keyframes goDown { + from { transform: translateY(-100%); } +} + +@-webkit-keyframes scaleUpFrom { + from { opacity: 0; -webkit-transform: scale(1.5); } +} +@-moz-keyframes scaleUpFrom { + from { opacity: 0; -moz-transform: scale(1.5); } +} +@keyframes scaleUpFrom { + from { opacity: 0; transform: scale(1.5); } +} + +@-webkit-keyframes scaleUpTo { + to { opacity: 0; -webkit-transform: scale(1.5); } +} +@-moz-keyframes scaleUpTo { + to { opacity: 0; -moz-transform: scale(1.5); } +} +@keyframes scaleUpTo { + to { opacity: 0; transform: scale(1.5); } +} \ No newline at end of file diff --git a/public/css/responsive.css b/public/css/responsive.css new file mode 100755 index 00000000..d65b6e01 --- /dev/null +++ b/public/css/responsive.css @@ -0,0 +1,494 @@ +/* Normal desktop :992px. */ +@media (min-width: 992px) and (max-width: 1169px) { +.mobile_memu_area{ + display:none; +} +.new_pro_left { + width: 100%; +} +.new_pro_right { + width: 100%; +} +.payment { + text-align: center; +} +.longwidth { + left: -166px; + width: 924px; +} +.men_longwidth { + left: -245px; + width: 882px; +} +.Footwear_longwidth { + left: -304px; + width: 882px; +} +.Jewellery_longwidth { + left: -402px; + width: 882px; +} +.home-2 .longwidth { + left: -72px; + width: 938px; +} +.home-2 .men_longwidth { + left: -155px; + width: 882px; +} +.home-2 .Footwear_longwidth { + left: -215px; + width: 882px; +} +.home-2 .Jewellery_longwidth { + left: -315px; + width: 882px; +} +.blog_text { + padding-left: 6px; +} +.blog_title { + margin-bottom: 0px; +} +.blog_title a { + font-size: 11px; +} +.product_review_area { + padding-top: 10px; +} +.disbutton .newsl_button button span { + padding: 5px 14px; +} +.top_menu_right ul ul { + width:auto; +} +.single_ad.lmiddle_widget img { + width: 100%; +} +.slide1-text { + margin-top:0px; +} +.cap-title h3 span { + font-size: 51px; +} +.cap-title.slide2 > h3 { + margin-top: 40px; +} +} + + +/* Tablet desktop :768px. */ +@media (min-width: 768px) and (max-width: 991px) { + .header_nav{ + display:none; +} +.mobile_memu_area{ + display:block; +} +.top_menu > ul,.top_menu_right > ul{ + text-align:center; +} +.pull-right{ + float:none !important; +} +.logo { + text-align: center; +} +.search { + margin: 0px auto; + width: 100%; +} +.shoping_cart_area { +float:none; +} +.text_c{ + text-align:center; +} +.my-cart { + padding: 1px 0 0 0px; +} +.single_model { + width: 100%; + margin-top:30px; + text-align:center; +} +.cart { + overflow-x: scroll; +} +.card_control ul li .input-box { + width: 100%; +} +.payment { + text-align: left; +} +.page { + width: 55%; +} +.sorter { + float: left; + width: 45%; +} +.min_width { + min-width: 156px; +} +.thumnail-image { + padding-left: 43px; +} +.home-2 .over_view { + margin-top: -11px; +} +.home-2 .view_more{ + padding:4px 7px; +} +.col_right { + padding-top: 10px; + padding:0px; +} +.shopdes { + padding: 0; +} +.single_model { + width: 50%; +} +.single_iner > img { + width: 100%; +} +.product_name a { + font-size: 33px; +} +.shoping_cart_area { +margin-top:0px; +} +.home-3 .search { + margin: 20px auto 0px; +} +.pdleft0 { + padding-left: 15px; +} +.home-3 .logo { + padding: 15px 0 10px; + text-align: center; +} +.slide1-text { + margin-top:0px; +} +.cap-title h3 span { + line-height: 39px; +} +.cap-dec { + margin-bottom: 5px; +} +.cap-title h3 span { + font-size: 37px; +} +.cap-title.slide2 > h3 { + margin-top: 40px; +} +} + + +/* small mobile :320px. */ +@media (max-width: 767px) { +.container {width:100%} +.header_nav{ + display:none; +} +.shoping_cart_area{ + margin-top:0px; +} +.mobile_memu_area{ + display:block; +} +.header_top { + display: none; +} +.search { + margin: 0px auto; + width: 100%; +} +.pdleft0 { + padding-left: 15px; +} +.home-3 .search { + margin: 10px auto; +} +.home-3 .logo { + padding: 15px 0 10px; + text-align: center; +} +.single_ads { + width: 100%; + margin-top:20px; +} +.nav_style li { + display: block; + padding: 10px; +} +.nav_style li a::after { + right: 28px; +} +.nav_style li a::after { + opacity: 0; +} +.counterdown { + display: none; +} +.stoc_content_inner { + padding: 0px; + width: 100%; +} +.title::before { + opacity: 0; +} +.Bestseller_product_area { + padding:0px; +} +.single_model { + width: 100%; + margin-top:30px; +} +.new_pro_left { + width: 100%; +} +.new_pro_right { + width: 100%; +} +.footer_menu > ul { + text-align: center; +} +.footer_copyright { + text-align: center; +} +.payment { + text-align: center; +} +.home-2 .single_ad { + margin-bottom: 10px; + text-align:center; +} +.cart { + overflow-x: scroll; +} +.card_control ul li .input-box { + width: 100%; +} +.disbutton .newsl_button button span { + padding:5px 20px; +} +.form_control ul li:nth-child(1) .field { + float: left; + width: 100%; +} +.form_control ul li:nth-child(1) .field.email { + float: left; + width: 100%; +} +.page { + width: 100%; +} +.sorter { + width: 100%; + margin-top:20px; +} +.toolbar-form { + float:none; +} +.thumnail-image { + padding-left: 36px; +} +.the-comment .comment-box { + margin-left: 0; +} +.nav_style { + margin-bottom: 18px; +} +.nav_style li { + padding: 0px; +} +.nav_style li a { + padding: 5px 15px; +} +.footer_title { + margin: 0; + padding: 10px 0; +} +.footer_menu li { + padding: 2px; +} +.blog_text { + padding-left: 0px; + clear: both; +} +.single_iner { + text-align: center; +} +.single_iner > img { + width: 100%; +} +.single_model:hover .shop-now { + bottom: 32%; +} +.addtocart { + padding-bottom: 10px; +} +.col_right { + padding: 0; +} +.logo { + text-align: center; +} +.slide1-text { + margin-top:0px; +} +.slide1-text.text_left { + margin-left: 10px; +} +.slide1-text.text_left .smore a { + padding: 0 25px; +} +.cap-title h2 { + margin: 10px 0px 0px; +} +.cap-title h2 span { + font-size: 12px; +} +.cap-title h3 { + margin: 0px; +} +.cap-title h3 span { + line-height: 11px; + font-size: 20px; +} +.home-3 .ads_area { + padding: 0px 0 80px; +} +.cap-dec p { + display: none; +} +.cap-dec { + margin-bottom: 0px; +} +.smore a { + padding: 2px 25px; +} +.cap-readmore { + display: none; +} +.cap-title.slide2 > h3 { + margin-top: 40px; +} +.section-title h2, .section-titleshop h2 { + font-size: 22px; +} +.product_name a { + font-size: 30px; +} +} + +/* Large Mobile :480px. */ +@media only screen and (min-width: 480px) and (max-width: 767px) { +.container {width:450px} +.header_nav{ + display:none; +} +.mobile_memu_area{ + display:block; +} +.logo { + text-align: center; +} +.search { + width: 100%; +} +.shoping_cart_area { +float:none; +} +.text_c{ + text-align:center; +} +.my-cart { + padding: 1px 0 0 0px; +} +.single_model { + text-align: center; +} +.counter { + width: 20%; +} +.stoc_content_inner { + width: 80%; + padding: 50px 0 10px 65px; +} +.blog_left.pull-left { + width: 100%; + text-align: center; +} +.page { + width: 100%; +} +.single_ads { + text-align: center; +} +.blog_text { + clear: both; + padding-left: 69px; +} +.blog_padding { + padding-left: 0; +} +.stoc_content_inner { + padding: 0; + width: 100%; +} +.product_name a { + font-size: 30px; +} +.special_price { + margin: 0px; +} +.single_ads a { + display: inline-block; + position: relative; + width: 100%; +} +.product-datails-tab .tab-menu li { + margin-bottom: 16px; +} +.addtocart { + padding-bottom: 10px; +} +.col_right { + padding: 0; +} +.single_ad.lmiddle_widget img { + width: 100%; +} +.cap-dec p { + font-size: 9px; +} +.cap-dec { + margin-bottom: 5px; +} +.smore a { + padding: 5px 25px; +} +.slide1-text.text_left .smore a{ + padding: 5px 25px; +} +.main-slider-area { + display: block; +} +.slide1-text.text_left { + margin-left: 100px; +} +.cap-title h2 { + margin: 10px ; +} +.cap-readmore { + display: block; +} +.cap-title.slide2 > h3 { + margin-top: 40px; +} +} + diff --git a/public/fonts/FontAwesome.otf b/public/fonts/FontAwesome.otf new file mode 100755 index 00000000..681bdd4d Binary files /dev/null and b/public/fonts/FontAwesome.otf differ diff --git a/public/fonts/fontawesome-webfont.eot b/public/fonts/fontawesome-webfont.eot old mode 100644 new mode 100755 index 84677bc0..a30335d7 Binary files a/public/fonts/fontawesome-webfont.eot and b/public/fonts/fontawesome-webfont.eot differ diff --git a/public/fonts/fontawesome-webfont.svg b/public/fonts/fontawesome-webfont.svg old mode 100644 new mode 100755 index d907b25a..6fd19abc --- a/public/fonts/fontawesome-webfont.svg +++ b/public/fonts/fontawesome-webfont.svg @@ -1,6 +1,6 @@ - + @@ -147,14 +147,14 @@ - + - + @@ -275,7 +275,7 @@ - + @@ -399,7 +399,7 @@ - + @@ -411,8 +411,8 @@ - - + + @@ -438,7 +438,7 @@ - + @@ -459,7 +459,7 @@ - + @@ -483,13 +483,13 @@ - + - + @@ -513,8 +513,128 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/fontawesome-webfont.ttf b/public/fonts/fontawesome-webfont.ttf old mode 100644 new mode 100755 index 96a3639c..d7994e13 Binary files a/public/fonts/fontawesome-webfont.ttf and b/public/fonts/fontawesome-webfont.ttf differ diff --git a/public/fonts/fontawesome-webfont.woff b/public/fonts/fontawesome-webfont.woff old mode 100644 new mode 100755 index 628b6a52..6fd4ede0 Binary files a/public/fonts/fontawesome-webfont.woff and b/public/fonts/fontawesome-webfont.woff differ diff --git a/public/fonts/fontawesome-webfont.woff2 b/public/fonts/fontawesome-webfont.woff2 new file mode 100755 index 00000000..5560193c Binary files /dev/null and b/public/fonts/fontawesome-webfont.woff2 differ diff --git a/public/fonts/glyphicons-halflings-regular.eot b/public/fonts/glyphicons-halflings-regular.eot new file mode 100755 index 00000000..b93a4953 Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.eot differ diff --git a/public/fonts/glyphicons-halflings-regular.svg b/public/fonts/glyphicons-halflings-regular.svg new file mode 100755 index 00000000..94fb5490 --- /dev/null +++ b/public/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/glyphicons-halflings-regular.ttf b/public/fonts/glyphicons-halflings-regular.ttf new file mode 100755 index 00000000..1413fc60 Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.ttf differ diff --git a/public/fonts/glyphicons-halflings-regular.woff b/public/fonts/glyphicons-halflings-regular.woff new file mode 100755 index 00000000..9e612858 Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.woff differ diff --git a/public/fonts/glyphicons-halflings-regular.woff2 b/public/fonts/glyphicons-halflings-regular.woff2 new file mode 100755 index 00000000..64539b54 Binary files /dev/null and b/public/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/public/img/a.png b/public/img/a.png new file mode 100755 index 00000000..5d770cf7 Binary files /dev/null and b/public/img/a.png differ diff --git a/public/img/benevocats.png b/public/img/benevocats.png deleted file mode 100644 index d88edd7d..00000000 Binary files a/public/img/benevocats.png and /dev/null differ diff --git a/public/img/block.png b/public/img/block.png deleted file mode 100644 index 9a2b7957..00000000 Binary files a/public/img/block.png and /dev/null differ diff --git a/public/img/cat.png b/public/img/cat.png deleted file mode 100644 index 54facce7..00000000 Binary files a/public/img/cat.png and /dev/null differ diff --git a/public/img/checkmark.png b/public/img/checkmark.png new file mode 100644 index 00000000..013799ee Binary files /dev/null and b/public/img/checkmark.png differ diff --git a/public/img/d.png b/public/img/d.png new file mode 100755 index 00000000..8664b0cf Binary files /dev/null and b/public/img/d.png differ diff --git a/public/img/default.svg b/public/img/default.svg new file mode 100644 index 00000000..4759f782 --- /dev/null +++ b/public/img/default.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/img/dojocat.jpg b/public/img/dojocat.jpg deleted file mode 100644 index 1646df0d..00000000 Binary files a/public/img/dojocat.jpg and /dev/null differ diff --git a/public/img/fancybox/blank.gif b/public/img/fancybox/blank.gif new file mode 100755 index 00000000..35d42e80 Binary files /dev/null and b/public/img/fancybox/blank.gif differ diff --git a/public/img/fancybox/fancybox_buttons.png b/public/img/fancybox/fancybox_buttons.png new file mode 100755 index 00000000..e0e1ea87 Binary files /dev/null and b/public/img/fancybox/fancybox_buttons.png differ diff --git a/public/img/fancybox/fancybox_loading.gif b/public/img/fancybox/fancybox_loading.gif new file mode 100755 index 00000000..641a269c Binary files /dev/null and b/public/img/fancybox/fancybox_loading.gif differ diff --git a/public/img/fancybox/fancybox_sprite.png b/public/img/fancybox/fancybox_sprite.png new file mode 100755 index 00000000..ae597131 Binary files /dev/null and b/public/img/fancybox/fancybox_sprite.png differ diff --git a/public/img/feedLarge.png b/public/img/feedLarge.png new file mode 100755 index 00000000..d64c669c Binary files /dev/null and b/public/img/feedLarge.png differ diff --git a/public/img/feedSmall.png b/public/img/feedSmall.png new file mode 100755 index 00000000..b3c949d2 Binary files /dev/null and b/public/img/feedSmall.png differ diff --git a/public/img/foobar.png b/public/img/foobar.png deleted file mode 100644 index a340693f..00000000 Binary files a/public/img/foobar.png and /dev/null differ diff --git a/public/img/fork.png b/public/img/fork.png deleted file mode 100644 index d6c6273b..00000000 Binary files a/public/img/fork.png and /dev/null differ diff --git a/public/img/icon-timer.png b/public/img/icon-timer.png new file mode 100755 index 00000000..595c45b2 Binary files /dev/null and b/public/img/icon-timer.png differ diff --git a/public/img/littlecat.png b/public/img/littlecat.png deleted file mode 100644 index 7aa794bf..00000000 Binary files a/public/img/littlecat.png and /dev/null differ diff --git a/public/img/loading.gif b/public/img/loading.gif new file mode 100755 index 00000000..6380dd75 Binary files /dev/null and b/public/img/loading.gif differ diff --git a/public/img/loading_spinner.gif b/public/img/loading_spinner.gif new file mode 100644 index 00000000..a8c78d12 Binary files /dev/null and b/public/img/loading_spinner.gif differ diff --git a/public/img/logo 2.png b/public/img/logo 2.png new file mode 100755 index 00000000..9c6a94db Binary files /dev/null and b/public/img/logo 2.png differ diff --git a/public/img/logo-inverse.png b/public/img/logo-inverse.png new file mode 100644 index 00000000..30e4dda4 Binary files /dev/null and b/public/img/logo-inverse.png differ diff --git a/public/img/logo.jpg b/public/img/logo.jpg new file mode 100644 index 00000000..a973d0cf Binary files /dev/null and b/public/img/logo.jpg differ diff --git a/public/img/m.png b/public/img/m.png new file mode 100755 index 00000000..d9ad6270 Binary files /dev/null and b/public/img/m.png differ diff --git a/public/img/map-marker.png b/public/img/map-marker.png new file mode 100755 index 00000000..88442535 Binary files /dev/null and b/public/img/map-marker.png differ diff --git a/public/img/pa.png b/public/img/pa.png new file mode 100755 index 00000000..e151537f Binary files /dev/null and b/public/img/pa.png differ diff --git a/public/img/robotcat.png b/public/img/robotcat.png deleted file mode 100644 index 543f94e0..00000000 Binary files a/public/img/robotcat.png and /dev/null differ diff --git a/public/img/setuptocat.jpg b/public/img/setuptocat.jpg deleted file mode 100644 index 67b0b515..00000000 Binary files a/public/img/setuptocat.jpg and /dev/null differ diff --git a/public/img/setuptocat.png b/public/img/setuptocat.png deleted file mode 100644 index 54facce7..00000000 Binary files a/public/img/setuptocat.png and /dev/null differ diff --git a/public/img/slider.png b/public/img/slider.png new file mode 100755 index 00000000..e376cb57 Binary files /dev/null and b/public/img/slider.png differ diff --git a/public/img/socialite.jpg b/public/img/socialite.jpg deleted file mode 100644 index 928ab34c..00000000 Binary files a/public/img/socialite.jpg and /dev/null differ diff --git a/public/img/splash-bg.png b/public/img/splash-bg.png new file mode 100644 index 00000000..4499cb06 Binary files /dev/null and b/public/img/splash-bg.png differ diff --git a/public/img/stackvana-logo-inverse.png b/public/img/stackvana-logo-inverse.png new file mode 100644 index 00000000..924e8b91 Binary files /dev/null and b/public/img/stackvana-logo-inverse.png differ diff --git a/public/img/stackvana-logo.png b/public/img/stackvana-logo.png new file mode 100644 index 00000000..e98090d3 Binary files /dev/null and b/public/img/stackvana-logo.png differ diff --git a/public/img/stripe-secure.png b/public/img/stripe-secure.png new file mode 100644 index 00000000..1b833ca1 Binary files /dev/null and b/public/img/stripe-secure.png differ diff --git a/public/img/supportcat.png b/public/img/supportcat.png deleted file mode 100644 index dbac6e8a..00000000 Binary files a/public/img/supportcat.png and /dev/null differ diff --git a/public/img/upsidedown.jpg b/public/img/upsidedown.jpg deleted file mode 100644 index 53151d62..00000000 Binary files a/public/img/upsidedown.jpg and /dev/null differ diff --git a/public/img/upsidedowncat.jpg b/public/img/upsidedowncat.jpg deleted file mode 100644 index 679b852b..00000000 Binary files a/public/img/upsidedowncat.jpg and /dev/null differ diff --git a/public/img/upsidedowncat.png b/public/img/upsidedowncat.png deleted file mode 100644 index 93d7cdf9..00000000 Binary files a/public/img/upsidedowncat.png and /dev/null differ diff --git a/public/img/v.png b/public/img/v.png new file mode 100755 index 00000000..0f5416e3 Binary files /dev/null and b/public/img/v.png differ diff --git a/public/img/wscat.png b/public/img/wscat.png new file mode 100644 index 00000000..2a856fc6 Binary files /dev/null and b/public/img/wscat.png differ diff --git a/public/js/bootstrap.min.js b/public/js/bootstrap.min.js new file mode 100755 index 00000000..133aeecb --- /dev/null +++ b/public/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/public/js/clippy.min.js b/public/js/clippy.min.js new file mode 100644 index 00000000..5c910aba --- /dev/null +++ b/public/js/clippy.min.js @@ -0,0 +1,1067 @@ +var clippy = {}; + +/****** + * + * + * @constructor + */ +clippy.Agent = function (path, data, sounds) { + this.path = path; + + this._queue = new clippy.Queue($.proxy(this._onQueueEmpty, this)); + + this._el = $('
').hide(); + + $(document.body).append(this._el); + + this._animator = new clippy.Animator(this._el, path, data, sounds); + + this._balloon = new clippy.Balloon(this._el); + + this._setupEvents(); +}; + +clippy.Agent.prototype = { + + /**************************** API ************************************/ + + /*** + * + * @param {Number} x + * @param {Number} y + */ + gestureAt:function (x, y) { + var d = this._getDirection(x, y); + var gAnim = 'Gesture' + d; + var lookAnim = 'Look' + d; + + var animation = this.hasAnimation(gAnim) ? gAnim : lookAnim; + return this.play(animation); + }, + + /*** + * + * @param {Boolean=} fast + * + */ + hide:function (fast, callback) { + this._hidden = true; + var el = this._el; + this.stop(); + if (fast) { + this._el.hide(); + this.stop(); + this.pause(); + if (callback) callback(); + return; + } + + this._addToQueue(function (complete) { + this._animator.showAnimation('Hide', $.proxy(function (name, state) { + if (state === clippy.Animator.States.EXITED) { + el.hide(); + this.pause(); + if (callback) callback(); + complete(); + } + }, this)); + }, this); + }, + + moveTo:function (x, y, duration) { + var dir = this._getDirection(x, y); + var anim = 'Move' + dir; + if (duration === undefined) duration = 1000; + + this._addToQueue(function (complete) { + // the simple case + if (duration === 0) { + this._el.css({top:y, left:x}); + this.reposition(); + complete(); + return; + } + + // no animations + if (!this.hasAnimation(anim)) { + this._el.animate({top:y, left:x}, duration, complete); + return; + } + + var callback = $.proxy(function (name, state) { + // when exited, complete + if (state === clippy.Animator.States.EXITED) { + complete(); + } + // if waiting, + if (state === clippy.Animator.States.WAITING) { + this._el.animate({top:y, left:x}, duration, $.proxy(function () { + // after we're done with the movement, do the exit animation + this._animator.exitAnimation(); + }, this)); + } + + }, this); + + this._animator.showAnimation(anim, callback); + }, this); + }, + + play:function (animation, timeout, cb) { + if (!this.hasAnimation(animation)) return false; + + if (timeout === undefined) timeout = 5000; + + this._addToQueue(function (complete) { + var completed = false; + // handle callback + var callback = function (name, state) { + if (state === clippy.Animator.States.EXITED) { + completed = true; + if (cb) cb(); + complete(); + } + }; + + // if has timeout, register a timeout function + if (timeout) { + window.setTimeout($.proxy(function () { + if (completed) return; + // exit after timeout + this._animator.exitAnimation(); + }, this), timeout) + } + + this._animator.showAnimation(animation, callback); + }, this); + + return true; + }, + + /*** + * + * @param {Boolean=} fast + */ + show:function (fast) { + this._hidden = false; + if (fast) { + this._el.show(); + this.resume(); + this._onQueueEmpty(); + return; + } + + if (this._el.css('top') === 'auto' || !this._el.css('left') === 'auto') { + var left = $(window).width() * 0.8; + var top = ($(window).height() + $(document).scrollTop()) * 0.8; + this._el.css({top:top, left:left}); + } + + this.resume(); + return this.play('Show'); + }, + + /*** + * + * @param {String} text + */ + speak:function (text, hold) { + this._addToQueue(function (complete) { + this._balloon.speak(complete, text, hold); + }, this); + }, + + /*** + * + * @param {String} text + */ + ask:function (text, choices, callback) { + this._addToQueue(function (complete) { + this._balloon.ask(complete, text, choices, callback); + }, this); + }, + + /*** + * Close the current balloon + */ + closeBalloon:function () { + this._balloon.close(); + }, + + delay:function (time) { + time = time || 250; + + this._addToQueue(function (complete) { + window.setTimeout(complete, time); + }, this); + }, + + /*** + * Skips the current animation + */ + stopCurrent:function () { + this._animator.exitAnimation(); + this._balloon.close(); + }, + + + stop:function () { + // clear the queue + this._queue.clear(); + this._animator.exitAnimation(); + this._balloon.close(); + }, + + /*** + * + * @param {String} name + * @returns {Boolean} + */ + hasAnimation:function (name) { + return this._animator.hasAnimation(name); + }, + + /*** + * Gets a list of animation names + * + * @return {Array.} + */ + animations:function () { + return this._animator.animations(); + }, + + /*** + * Play a random animation + * @return {jQuery.Deferred} + */ + animate:function () { + var animations = this.animations(); + var anim = animations[Math.floor(Math.random() * animations.length)]; + // skip idle animations + if (anim.indexOf('Idle') === 0 || anim == 'Show' || anim == 'Hide') { + return this.animate(); + } + return this.play(anim); + }, + + /**************************** Utils ************************************/ + + /*** + * + * @param {Number} x + * @param {Number} y + * @return {String} + * @private + */ + _getDirection:function (x, y) { + var offset = this._el.offset(); + var h = this._el.height(); + var w = this._el.width(); + + var centerX = (offset.left + w / 2); + var centerY = (offset.top + h / 2); + + var a = centerY - y; + var b = centerX - x; + + var r = Math.round((180 * Math.atan2(a, b)) / Math.PI); + + // Left and Right are for the character, not the screen :-/ + if (-45 <= r && r < 45) return 'Right'; + if (45 <= r && r < 135) return 'Up'; + if (135 <= r && r <= 180 || -180 <= r && r < -135) return 'Left'; + if (-135 <= r && r < -45) return 'Down'; + + // sanity check + return 'Top'; + }, + + /**************************** Queue and Idle handling ************************************/ + + /*** + * Handle empty queue. + * We need to transition the animation to an idle state + * @private + */ + _onQueueEmpty:function () { + if (this._hidden || this._isIdleAnimation()) return; + var idleAnim = this._getIdleAnimation(); + this._idleDfd = $.Deferred(); + + this._animator.showAnimation(idleAnim, $.proxy(this._onIdleComplete, this)); + }, + + _onIdleComplete:function (name, state) { + if (state === clippy.Animator.States.EXITED) { + this._idleDfd.resolve(); + + // Always play some idle animation. + this._queue.next(); + } + }, + + /*** + * Is an Idle animation currently playing? + * @return {Boolean} + * @private + */ + _isIdleAnimation:function () { + var c = this._animator.currentAnimationName; + return c && c.indexOf('Idle') == 0 && this._idleDfd && this._idleDfd.state() === 'pending'; + }, + + /** + * Gets a random Idle animation + * @return {String} + * @private + */ + _getIdleAnimation:function () { + var animations = this.animations(); + var r = []; + for (var i = 0; i < animations.length; i++) { + var a = animations[i]; + if (a.indexOf('Idle') === 0) { + r.push(a); + } + } + + // pick one + var idx = Math.floor(Math.random() * r.length); + return r[idx]; + }, + + /**************************** Events ************************************/ + + _setupEvents:function () { + $(window).on('resize', $.proxy(this.reposition, this)); + + this._el.on('mousedown', $.proxy(this._onMouseDown, this)); + + this._el.on('dblclick', $.proxy(this._onDoubleClick, this)); + }, + + _onDoubleClick:function () { + if (!this.play('ClickedOn')) { + this.animate(); + } + }, + + reposition:function () { + if (!this._el.is(':visible')) return; + var o = this._el.offset(); + var bH = this._el.outerHeight(); + var bW = this._el.outerWidth(); + + var wW = $(window).width(); + var wH = $(window).height(); + var sT = $(window).scrollTop(); + var sL = $(window).scrollLeft(); + + var top = o.top - sT; + var left = o.left - sL; + var m = 5; + if (top - m < 0) { + top = m; + } else if ((top + bH + m) > wH) { + top = wH - bH - m; + } + + if (left - m < 0) { + left = m; + } else if (left + bW + m > wW) { + left = wW - bW - m; + } + + this._el.css({left:left, top:top}); + // reposition balloon + this._balloon.reposition(); + }, + + _onMouseDown:function (e) { + e.preventDefault(); + this._startDrag(e); + }, + + + /**************************** Drag ************************************/ + + _startDrag:function (e) { + // pause animations + this.pause(); + this._balloon.hide(); + this._offset = this._calculateClickOffset(e); + + this._moveHandle = $.proxy(this._dragMove, this); + this._upHandle = $.proxy(this._finishDrag, this); + + $(window).on('mousemove', this._moveHandle); + $(window).on('mouseup', this._upHandle); + + this._dragUpdateLoop = window.setTimeout($.proxy(this._updateLocation, this), 10); + }, + + _calculateClickOffset:function (e) { + var mouseX = e.pageX; + var mouseY = e.pageY; + var o = this._el.offset(); + return { + top:mouseY - o.top, + left:mouseX - o.left + } + + }, + + _updateLocation:function () { + this._el.css({top:this._targetY, left:this._targetX}); + this._dragUpdateLoop = window.setTimeout($.proxy(this._updateLocation, this), 10); + }, + + _dragMove:function (e) { + e.preventDefault(); + var x = e.clientX - this._offset.left; + var y = e.clientY - this._offset.top; + this._targetX = x; + this._targetY = y; + }, + + _finishDrag:function () { + window.clearTimeout(this._dragUpdateLoop); + // remove handles + $(window).off('mousemove', this._moveHandle); + $(window).off('mouseup', this._upHandle); + // resume animations + this._balloon.show(); + this.reposition(); + this.resume(); + + }, + + _addToQueue:function (func, scope) { + if (scope) func = $.proxy(func, scope); + + // if we're inside an idle animation, + if (this._isIdleAnimation()) { + this._idleDfd.done($.proxy(function () { + this._queue.queue(func); + }, this)) + this._animator.exitAnimation(); + return; + } + + this._queue.queue(func); + }, + + /**************************** Pause and Resume ************************************/ + + pause:function () { + this._animator.pause(); + this._balloon.pause(); + + }, + + resume:function () { + this._animator.resume(); + this._balloon.resume(); + } + +}; + +/****** + * + * + * @constructor + */ +clippy.Animator = function (el, path, data, sounds) { + this._el = el; + this._data = data; + this._path = path; + this._currentFrameIndex = 0; + this._currentFrame = undefined; + this._exiting = false; + this._currentAnimation = undefined; + this._endCallback = undefined; + this._started = false; + this._sounds = {}; + this.currentAnimationName = undefined; + this.preloadSounds(sounds); + this._overlays = [this._el]; + var curr = this._el; + + this._setupElement(this._el); + for (var i = 1; i < this._data.overlayCount; i++) { + var inner = this._setupElement($('
')); + + curr.append(inner); + this._overlays.push(inner); + curr = inner; + } +}; + +clippy.Animator.prototype = { + _setupElement:function (el) { + var frameSize = this._data.framesize; + el.css('display', "none"); + el.css({width:frameSize[0], height:frameSize[1]}); + el.css('background', "url('" + this._path + "/map.png') no-repeat"); + + return el; + }, + + animations:function () { + var r = []; + var d = this._data.animations; + for (var n in d) { + r.push(n); + } + return r; + }, + + preloadSounds:function (sounds) { + + for (var i = 0; i < this._data.sounds.length; i++) { + var snd = this._data.sounds[i]; + var uri = sounds[snd]; + if (!uri) continue; + this._sounds[snd] = new Audio(uri); + + } + }, + hasAnimation:function (name) { + return !!this._data.animations[name]; + }, + + exitAnimation:function () { + this._exiting = true; + }, + + + showAnimation:function (animationName, stateChangeCallback) { + this._exiting = false; + + if (!this.hasAnimation(animationName)) { + return false; + } + + this._currentAnimation = this._data.animations[animationName]; + this.currentAnimationName = animationName; + + + if (!this._started) { + this._step(); + this._started = true; + } + + this._currentFrameIndex = 0; + this._currentFrame = undefined; + this._endCallback = stateChangeCallback; + + return true; + }, + + + _draw:function () { + var images = []; + if (this._currentFrame) images = this._currentFrame.images || []; + + for (var i = 0; i < this._overlays.length; i++) { + if (i < images.length) { + var xy = images[i]; + var bg = -xy[0] + 'px ' + -xy[1] + 'px'; + this._overlays[i].css({'background-position':bg, 'display':'block'}); + } + else { + this._overlays[i].css('display', 'none'); + } + + } + }, + + _getNextAnimationFrame:function () { + if (!this._currentAnimation) return undefined; + // No current frame. start animation. + if (!this._currentFrame) return 0; + var currentFrame = this._currentFrame; + var branching = this._currentFrame.branching; + + + if (this._exiting && currentFrame.exitBranch !== undefined) { + return currentFrame.exitBranch; + } + else if (branching) { + var rnd = Math.random() * 100; + for (var i = 0; i < branching.branches.length; i++) { + var branch = branching.branches[i]; + if (rnd <= branch.weight) { + return branch.frameIndex; + } + + rnd -= branch.weight; + } + } + + return this._currentFrameIndex + 1; + }, + + _playSound:function () { + var s = this._currentFrame.sound; + if (!s) return; + var audio = this._sounds[s]; + if (audio) audio.play(); + }, + + _atLastFrame:function () { + return this._currentFrameIndex >= this._currentAnimation.frames.length - 1; + }, + + _step:function () { + if (!this._currentAnimation) return; + var newFrameIndex = Math.min(this._getNextAnimationFrame(), this._currentAnimation.frames.length - 1); + var frameChanged = !this._currentFrame || this._currentFrameIndex !== newFrameIndex; + this._currentFrameIndex = newFrameIndex; + + // always switch frame data, unless we're at the last frame of an animation with a useExitBranching flag. + if (!(this._atLastFrame() && this._currentAnimation.useExitBranching)) { + this._currentFrame = this._currentAnimation.frames[this._currentFrameIndex]; + } + + this._draw(); + this._playSound(); + + this._loop = window.setTimeout($.proxy(this._step, this), this._currentFrame.duration); + + + // fire events if the frames changed and we reached an end + if (this._endCallback && frameChanged && this._atLastFrame()) { + if (this._currentAnimation.useExitBranching && !this._exiting) { + this._endCallback(this.currentAnimationName, clippy.Animator.States.WAITING); + } + else { + this._endCallback(this.currentAnimationName, clippy.Animator.States.EXITED); + } + } + }, + + /*** + * Pause animation execution + */ + pause:function () { + window.clearTimeout(this._loop); + }, + + /*** + * Resume animation + */ + resume:function () { + this._step(); + } +}; + +clippy.Animator.States = { WAITING:1, EXITED:0 }; + +/****** + * + * + * @constructor + */ +clippy.Balloon = function (targetEl) { + this._targetEl = targetEl; + + this._hidden = true; + this._setup(); +}; + +clippy.Balloon.prototype = { + WORD_SPEAK_TIME:200, + CLOSE_BALLOON_DELAY:2000, + + _setup:function () { + this._balloon = $('
').hide(); + this._content = this._balloon.find('.clippy-content'); + + $(document.body).append(this._balloon); + }, + + reposition:function () { + var sides = ['top-left', 'top-right', 'bottom-left', 'bottom-right']; + + for (var i = 0; i < sides.length; i++) { + var s = sides[i]; + this._position(s); + if (!this._isOut()) break; + } + }, + + _BALLOON_MARGIN:15, + + /*** + * + * @param side + * @private + */ + _position:function (side) { + var o = this._targetEl.offset(); + var h = this._targetEl.height(); + var w = this._targetEl.width(); + o.top -= $(window).scrollTop(); + o.left -= $(window).scrollLeft(); + + var bH = this._balloon.outerHeight(); + var bW = this._balloon.outerWidth(); + + this._balloon.removeClass('clippy-top-left'); + this._balloon.removeClass('clippy-top-right'); + this._balloon.removeClass('clippy-bottom-right'); + this._balloon.removeClass('clippy-bottom-left'); + + var left, top; + side = 'top-left'; + switch (side) { + case 'top-left': + // right side of the balloon next to the right side of the agent + left = o.left + w - bW - 90; + top = o.top - bH - this._BALLOON_MARGIN + 40; + break; + case 'top-right': + // left side of the balloon next to the left side of the agent + left = o.left; + top = o.top - bH - this._BALLOON_MARGIN; + break; + case 'bottom-right': + // right side of the balloon next to the right side of the agent + left = o.left; + top = o.top + h + this._BALLOON_MARGIN; + break; + case 'bottom-left': + // left side of the balloon next to the left side of the agent + left = o.left + w - bW; + top = o.top + h + this._BALLOON_MARGIN; + break; + } + + this._balloon.css({top:top, left:left}); + this._balloon.addClass('clippy-' + side); + }, + + _isOut:function () { + var o = this._balloon.offset(); + var bH = this._balloon.outerHeight(); + var bW = this._balloon.outerWidth(); + + var wW = $(window).width(); + var wH = $(window).height(); + var sT = $(document).scrollTop(); + var sL = $(document).scrollLeft(); + + var top = o.top - sT; + var left = o.left - sL; + var m = 5; + if (top - m < 0 || left - m < 0) return true; + if ((top + bH + m) > wH || (left + bW + m) > wW) return true; + + return false; + }, + + speak:function (complete, text, hold) { + this._hidden = false; + this.show(); + var c = this._content; + // set height to auto + c.height('auto'); + c.width('auto'); + // add the text + c.text(text); + // set height + c.height(c.height()); + c.width(c.width()); + c.text(''); + this.reposition(); + + this._complete = complete; + this._sayWords(text, [], hold, complete); + }, + + ask:function (complete, text, choiceTexts, callback) { + choices = [] + for (var i in choiceTexts) { + d = $('
').text(choiceTexts[i]) + choices.push(d); + } + + this._hidden = false; + this.show(); + var c = this._content; + c.height('auto'); + c.width('auto'); + c.text(text); + for (var i in choices) { + c.append(choices[i]); + } + c.height(c.height()); + c.width(c.width()); + c.text(''); + this.reposition(); + + this._complete = complete; + this._sayWords(text, choices, true, complete, callback); + }, + + show:function () { + if (this._hidden) return; + this._balloon.show(); + }, + + hide:function () { + this._balloon.hide(); + }, + + _sayWords:function (text, choices, hold, complete, callback) { + this._active = true; + this._hold = hold; + var words = text.split(/[^\S-]/); + var time = this.WORD_SPEAK_TIME; + var el = this._content; + var idx = 1; + + this._addWord = $.proxy(function () { + if (!this._active) return; + if (idx <= words.length) { + el.text(words.slice(0, idx).join(' ')); + idx++; + this._loop = window.setTimeout($.proxy(this._addWord, this), time); + } else { + for (var i in choices) { + el.append(choices[i]); + } + self = this; + $(".clippy-choice").click(function() { + self.close(true); + if (callback) { + callback($(this).text()); + } + }); + delete this._addWord; + this._active = false; + if (!this._hold) { + complete(); + delete this._complete; + this.close(); + } + } + }, this); + + this._addWord(); + }, + + close:function (fast) { + if (this._active) { + this._hold = false; + return; + } + if (this._hold) { + this._hold = false; + if (this._complete) { + this._complete(); + delete this._complete; + } + } + if (!this._hidden) { + if (fast) { + this._balloon.hide(); + this._hidden = true; + } else { + this._hiding = window.setTimeout($.proxy(this._finishHideBalloon, this), this.CLOSE_BALLOON_DELAY); + } + } + }, + + _finishHideBalloon:function () { + if (this._active) return; + this._balloon.hide(); + this._hidden = true; + this._hiding = null; + }, + + pause:function () { + window.clearTimeout(this._loop); + if (this._hiding) { + window.clearTimeout(this._hiding); + this._hiding = null; + } + }, + + resume:function () { + if (this._addWord) { + this._addWord(); + } else if (!this._hold && !this._hidden) { + this._hiding = window.setTimeout($.proxy(this._finishHideBalloon, this), this.CLOSE_BALLOON_DELAY); + } + } +}; + + +clippy.BASE_PATH = 'agents/'; + +clippy.load = function (name, successCb, failCb, path) { + path = path || clippy.BASE_PATH + name; + + var mapDfd = clippy.load._loadMap(path); + var agentDfd = clippy.load._loadAgent(name, path); + var soundsDfd = clippy.load._loadSounds(name, path); + + var data; + agentDfd.done(function (d) { + data = d; + }); + + var sounds; + + soundsDfd.done(function (d) { + sounds = d; + }); + + // wrapper to the success callback + var cb = function () { + var a = new clippy.Agent(path, data,sounds); + successCb(a); + }; + + $.when(mapDfd, agentDfd, soundsDfd).done(cb).fail(failCb); +}; + +clippy.load._maps = {}; +clippy.load._loadMap = function (path) { + var dfd = clippy.load._maps[path]; + if (dfd) return dfd; + + // set dfd if not defined + dfd = clippy.load._maps[path] = $.Deferred(); + + var src = path + '/map.png'; + var img = new Image(); + + img.onload = dfd.resolve; + img.onerror = dfd.reject; + + // start loading the map; + img.setAttribute('src', src); + + return dfd.promise(); +}; + +clippy.load._sounds = {}; + +clippy.load._loadSounds = function (name, path) { + var dfd = clippy.load._sounds[name]; + if (dfd) return dfd; + + // set dfd if not defined + dfd = clippy.load._sounds[name] = $.Deferred(); + + var audio = document.createElement('audio'); + var canPlayMp3 = !!audio.canPlayType && "" != audio.canPlayType('audio/mpeg'); + var canPlayOgg = !!audio.canPlayType && "" != audio.canPlayType('audio/ogg; codecs="vorbis"'); + + if (!canPlayMp3 && !canPlayOgg) { + dfd.resolve({}); + } else { + var src = path + (canPlayMp3 ? '/sounds-mp3.js' : '/sounds-ogg.js'); + // load + clippy.load._loadScript(src); + } + + return dfd.promise() +}; + +clippy.load._data = {}; +clippy.load._loadAgent = function (name, path) { + var dfd = clippy.load._data[name]; + if (dfd) return dfd; + + dfd = clippy.load._getAgentDfd(name); + + var src = path + '/agent.js'; + + clippy.load._loadScript(src); + + return dfd.promise(); +}; + +clippy.load._loadScript = function (src) { + var script = document.createElement('script'); + script.setAttribute('src', src); + script.setAttribute('async', 'async'); + script.setAttribute('type', 'text/javascript'); + + var dochead = document.head || document.getElementsByTagName('head')[0]; + dochead.appendChild(script); +}; + +clippy.load._getAgentDfd = function (name) { + var dfd = clippy.load._data[name]; + if (!dfd) { + dfd = clippy.load._data[name] = $.Deferred(); + } + return dfd; +}; + +clippy.ready = function (name, data) { + var dfd = clippy.load._getAgentDfd(name); + dfd.resolve(data); +}; + +clippy.soundsReady = function (name, data) { + var dfd = clippy.load._sounds[name]; + if (!dfd) { + dfd = clippy.load._sounds[name] = $.Deferred(); + } + + dfd.resolve(data); +}; + +/****** + * Tiny Queue + * + * @constructor + */ +clippy.Queue = function (onEmptyCallback) { + this._queue = []; + this._onEmptyCallback = onEmptyCallback; +}; + +clippy.Queue.prototype = { + /*** + * + * @param {function(Function)} func + * @returns {jQuery.Deferred} + */ + queue:function (func) { + this._queue.push(func); + this.next(); + }, + + next:function () { + if (this._active) return; + + // stop if nothing left in queue + if (!this._queue.length) { + this._onEmptyCallback(); + return; + } + + var f = this._queue.shift(); + this._active = true; + + // execute function + var completeFunction = $.proxy(this._finish, this); + f(completeFunction); + }, + + _finish:function() { + this._active = false; + this.next(); + }, + + clear:function () { + this._queue = []; + }, +}; \ No newline at end of file diff --git a/public/js/codemirror.css b/public/js/codemirror.css new file mode 100644 index 00000000..89dfc2f6 --- /dev/null +++ b/public/js/codemirror.css @@ -0,0 +1,343 @@ +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + margin-top: 10px; + margin-bottom: 10px; + font-family: monospace; + height: 200px; + color: black; + /* + border: solid; + border-width: 1px; + box-shadow: 10px 10px 5px #888888; + */ + background-color: #F5F5F5; + border: 1px solid #E1E1E1; + +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror div.CodeMirror-cursor { + border-left: 1px solid black; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.CodeMirror.cm-fat-cursor div.CodeMirror-cursor { + width: auto; + border: 0; + background: #7e7; +} +.CodeMirror.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} + +.cm-animate-fat-cursor { + width: auto; + border: 0; + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; + background-color: #7e7; +} +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +div.CodeMirror-overwrite div.CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-ruler { + border-left: 1px solid #ccc; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3 {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + height: 210px; + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 30px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -30px; margin-right: -30px; + padding-bottom: 30px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 30px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actuall scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + margin-bottom: -30px; + /* Hack to make IE7 behave */ + *zoom:1; + *display:inline; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; +} +.CodeMirror-wrap pre { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + overflow: auto; +} + +.CodeMirror-widget {} + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} +.CodeMirror-measure pre { position: static; } + +.CodeMirror div.CodeMirror-cursor { + position: absolute; + border-right: none; + width: 0; +} + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background: #ffa; + background: rgba(255, 255, 0, .4); +} + +/* IE7 hack to prevent it from returning funny offsetTops on the spans */ +.CodeMirror span { *vertical-align: text-bottom; } + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } diff --git a/public/js/codemirror.js b/public/js/codemirror.js new file mode 100644 index 00000000..e9e2c1f3 --- /dev/null +++ b/public/js/codemirror.js @@ -0,0 +1,8788 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// This is CodeMirror (http://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + module.exports = mod(); + else if (typeof define == "function" && define.amd) // AMD + return define([], mod); + else // Plain browser env + this.CodeMirror = mod(); +})(function() { + "use strict"; + + // BROWSER SNIFFING + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + + var gecko = /gecko\/\d/i.test(navigator.userAgent); + var ie_upto10 = /MSIE \d/.test(navigator.userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent); + var ie = ie_upto10 || ie_11up; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); + var webkit = /WebKit\//.test(navigator.userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); + var chrome = /Chrome\//.test(navigator.userAgent); + var presto = /Opera\//.test(navigator.userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent); + var phantom = /PhantomJS/.test(navigator.userAgent); + + var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent); + var mac = ios || /Mac/.test(navigator.platform); + var windows = /win/i.test(navigator.platform); + + var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) presto_version = Number(presto_version[1]); + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + // EDITOR CONSTRUCTOR + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + setGuttersForLineNumbers(options); + + var doc = options.value; + if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator); + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input); + display.wrapper.CodeMirror = this; + updateGutters(this); + themeChanged(this); + if (options.lineWrapping) + this.display.wrapper.className += " CodeMirror-wrap"; + if (options.autofocus && !mobile) display.input.focus(); + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + var cm = this; + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20); + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || cm.hasFocus()) + setTimeout(bind(onFocus, this), 20); + else + onBlur(this); + + for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) + optionHandlers[opt](this, options[opt], Init); + maybeUpdateLineNumberWidth(this); + if (options.finishInit) options.finishInit(this); + for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + display.lineDiv.style.textRendering = "auto"; + } + + // DISPLAY CONSTRUCTOR + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = elt("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) d.scroller.draggable = true; + + if (place) { + if (place.appendChild) place.appendChild(d.wrapper); + else place(d.wrapper); + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + input.init(d); + } + + // STATE UPDATES + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function(line) { + if (line.stateAfter) line.stateAfter = null; + if (line.styles) line.styles = null; + }); + cm.doc.frontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) regChange(cm); + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function(){updateScrollbars(cm);}, 100); + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function(line) { + if (lineIsHidden(cm.doc, line)) return 0; + + var widgetsHeight = 0; + if (line.widgets) for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) widgetsHeight += line.widgets[i].height; + } + + if (wrapping) + return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th; + else + return widgetsHeight + th; + }; + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function(line) { + var estHeight = est(line); + if (estHeight != line.height) updateLineHeight(line, estHeight); + }); + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + function guttersChanged(cm) { + updateGutters(cm); + regChange(cm); + setTimeout(function(){alignHorizontally(cm);}, 20); + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function updateGutters(cm) { + var gutters = cm.display.gutters, specs = cm.options.gutters; + removeChildren(gutters); + for (var i = 0; i < specs.length; ++i) { + var gutterClass = specs[i]; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); + if (gutterClass == "CodeMirror-linenumbers") { + cm.display.lineGutter = gElt; + gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = i ? "" : "none"; + updateGutterSpace(cm); + } + + function updateGutterSpace(cm) { + var width = cm.display.gutters.offsetWidth; + cm.display.sizer.style.marginLeft = width + "px"; + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) return 0; + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found = merged.find(0, true); + len -= cur.text.length - found.from.ch; + cur = found.to.line; + len += cur.text.length - found.to.ch; + } + return len; + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function(line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // Make sure the gutters options contains the element + // "CodeMirror-linenumbers" when the lineNumbers option is true. + function setGuttersForLineNumbers(options) { + var found = indexOf(options.gutters, "CodeMirror-linenumbers"); + if (found == -1 && options.lineNumbers) { + options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); + } else if (found > -1 && !options.lineNumbers) { + options.gutters = options.gutters.slice(0); + options.gutters.splice(found, 1); + } + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + }; + } + + function NativeScrollbars(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + place(vert); place(horiz); + + on(vert, "scroll", function() { + if (vert.clientHeight) scroll(vert.scrollTop, "vertical"); + }); + on(horiz, "scroll", function() { + if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal"); + }); + + this.checkedOverlay = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; + } + + NativeScrollbars.prototype = copyObj({ + update: function(measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedOverlay && measure.clientHeight > 0) { + if (sWidth == 0) this.overlayHack(); + this.checkedOverlay = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}; + }, + setScrollLeft: function(pos) { + if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos; + }, + setScrollTop: function(pos) { + if (this.vert.scrollTop != pos) this.vert.scrollTop = pos; + }, + overlayHack: function() { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.minHeight = this.vert.style.minWidth = w; + var self = this; + var barMouseDown = function(e) { + if (e_target(e) != self.vert && e_target(e) != self.horiz) + operation(self.cm, onMouseDown)(e); + }; + on(this.vert, "mousedown", barMouseDown); + on(this.horiz, "mousedown", barMouseDown); + }, + clear: function() { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + } + }, NativeScrollbars.prototype); + + function NullScrollbars() {} + + NullScrollbars.prototype = copyObj({ + update: function() { return {bottom: 0, right: 0}; }, + setScrollLeft: function() {}, + setScrollTop: function() {}, + clear: function() {} + }, NullScrollbars.prototype); + + CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); + } + + cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function() { + if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0); + }); + node.setAttribute("cm-not-content", "true"); + }, function(pos, axis) { + if (axis == "horizontal") setScrollLeft(cm, pos); + else setScrollTop(cm, pos); + }, cm); + if (cm.display.scrollbars.addClass) + addClass(cm.display.wrapper, cm.display.scrollbars.addClass); + } + + function updateScrollbars(cm, measure) { + if (!measure) measure = measureForScrollbars(cm); + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + updateHeightsInViewport(cm); + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else d.scrollbarFiller.style.display = ""; + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else d.gutterFiller.style.display = ""; + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)}; + } + + // LINE NUMBERS + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return; + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) if (!view[i].hidden) { + if (cm.options.fixedGutter && view[i].gutter) + view[i].gutter.style.left = left; + var align = view[i].alignable; + if (align) for (var j = 0; j < align.length; j++) + align[j].style.left = left; + } + if (cm.options.fixedGutter) + display.gutters.style.left = (comp + gutterW) + "px"; + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) return false; + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm); + return true; + } + return false; + } + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)); + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; + } + + // DISPLAY DRAWING + + function DisplayUpdate(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + } + + DisplayUpdate.prototype.signal = function(emitter, type) { + if (hasHandler(emitter, type)) + this.events.push(arguments); + }; + DisplayUpdate.prototype.finish = function() { + for (var i = 0; i < this.events.length; i++) + signal.apply(null, this.events[i]); + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false; + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + return false; + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom); + if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo); + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + return false; + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var focused = activeElt(); + if (toUpdate > 4) display.lineDiv.style.display = "none"; + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) display.lineDiv.style.display = ""; + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + if (focused && activeElt() != focused && focused.offsetHeight) focused.focus(); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true; + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + break; + } + if (!updateDisplayIfNeeded(cm, update)) break; + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + setDocumentHeight(cm, barMeasure); + updateScrollbars(cm, barMeasure); + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + setDocumentHeight(cm, barMeasure); + updateScrollbars(cm, barMeasure); + update.finish(); + } + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + var total = measure.docHeight + cm.display.barHeight; + cm.display.heightForcer.style.top = total + "px"; + cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px"; + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], height; + if (cur.hidden) continue; + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + } + var diff = cur.line.height - height; + if (height < 2) height = textHeight(display); + if (diff > .001 || diff < -.001) { + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) for (var j = 0; j < cur.rest.length; j++) + updateWidgetHeight(cur.rest[j]); + } + } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) for (var i = 0; i < line.widgets.length; ++i) + line.widgets[i].height = line.widgets[i].node.offsetHeight; + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; + width[cm.options.gutters[i]] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth}; + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + node.style.display = "none"; + else + node.parentNode.removeChild(node); + return next; + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) { + } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) cur = rm(cur); + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false; + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) cur = rm(cur); + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") updateLineText(cm, lineView); + else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims); + else if (type == "class") updateLineClasses(lineView); + else if (type == "widget") updateLineWidgets(cm, lineView, dims); + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + lineView.text.parentNode.replaceChild(lineView.node, lineView.text); + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) lineView.node.style.zIndex = 2; + } + return lineView.node; + } + + function updateLineBackground(lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) cls += " CodeMirror-linebackground"; + if (lineView.background) { + if (cls) lineView.background.className = cls; + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built; + } + return buildLineContent(cm, lineView); + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) lineView.node = built.pre; + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(lineView) { + updateLineBackground(lineView); + if (lineView.line.wrapClass) + ensureLineWrapped(lineView).className = lineView.line.wrapClass; + else if (lineView.node != lineView.text) + lineView.node.className = ""; + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + + "px; width: " + dims.gutterTotalWidth + "px"); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " + + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"); + cm.display.input.setUneditable(gutterWrap); + wrap.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + gutterWrap.className += " " + lineView.line.gutterClass; + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " + + cm.display.lineNumInnerWidth + "px")); + if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) { + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) + gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + + dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")); + } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) lineView.alignable = null; + for (var node = lineView.node.firstChild, next; node; node = next) { + var next = node.nextSibling; + if (node.className == "CodeMirror-linewidget") + lineView.node.removeChild(node); + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) lineView.bgClass = built.bgClass; + if (built.textClass) lineView.textClass = built.textClass; + + updateLineClasses(lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node; + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) + insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) return; + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true"); + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + wrap.insertBefore(node, lineView.gutter || lineView.text); + else + wrap.appendChild(node); + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"; + } + } + + // POSITION OBJECT + + // A Pos instance represents a position within the text. + var Pos = CodeMirror.Pos = function(line, ch) { + if (!(this instanceof Pos)) return new Pos(line, ch); + this.line = line; this.ch = ch; + }; + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; }; + + function copyPos(x) {return Pos(x.line, x.ch);} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b; } + + // INPUT HANDLING + + function ensureFocus(cm) { + if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } + } + + function isReadOnly(cm) { + return cm.options.readOnly || cm.doc.cantEdit; + } + + // This will be set to an array of strings when copying, so that, + // when pasting, we know what kind of selections the copied text + // was made out of. + var lastCopied = null; + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) sel = doc.sel; + + var paste = cm.state.pasteIncoming || origin == "paste"; + var textLines = doc.splitLines(inserted), multiPaste = null; + // When pasing N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.length; i++) + multiPaste.push(doc.splitLines(lastCopied[i])); + } + } else if (textLines.length == sel.ranges.length) { + multiPaste = map(textLines, function(l) { return [l]; }); + } + } + + // Normal behavior is to insert the new text into every selection + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + from = Pos(from.line, from.ch - deleted); + else if (cm.state.overwrite && !paste) // Handle overwrite + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); + } + var updateInput = cm.curOp.updateInput; + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + triggerElectric(cm, inserted); + + ensureCursorVisible(cm); + cm.curOp.updateInput = updateInput; + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = false; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("text/plain"); + if (pasted) { + e.preventDefault(); + runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); }); + return true; + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) return; + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue; + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break; + } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + indented = indentLine(cm, range.head.line, "smart"); + } + if (indented) signalLater(cm, "electricInput", cm, range.head.line); + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges}; + } + + function disableBrowserMagic(field) { + field.setAttribute("autocorrect", "off"); + field.setAttribute("autocapitalize", "off"); + field.setAttribute("spellcheck", "false"); + } + + // TEXTAREA INPUT STYLE + + function TextareaInput(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Tracks when input.reset has punted to just putting a short + // string into the textarea instead of the full selection. + this.inaccurateSelection = false; + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + }; + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) te.style.width = "1000px"; + else te.setAttribute("wrap", "off"); + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) te.style.border = "1px solid black"; + disableBrowserMagic(te); + return div; + } + + TextareaInput.prototype = copyObj({ + init: function(display) { + var input = this, cm = this.cm; + + // Wraps and hides input textarea + var div = this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + var te = this.textarea = div.firstChild; + display.wrapper.insertBefore(div, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) te.style.width = "0px"; + + on(te, "input", function() { + if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null; + input.poll(); + }); + + on(te, "paste", function(e) { + if (handlePaste(e, cm)) return true; + + cm.state.pasteIncoming = true; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (cm.somethingSelected()) { + lastCopied = cm.getSelections(); + if (input.inaccurateSelection) { + input.prevInput = ""; + input.inaccurateSelection = false; + te.value = lastCopied.join("\n"); + selectInput(te); + } + } else if (!cm.options.lineWiseCopyCut) { + return; + } else { + var ranges = copyableRanges(cm); + lastCopied = ranges.text; + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") cm.state.cutIncoming = true; + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function(e) { + if (eventInWidget(display, e)) return; + cm.state.pasteIncoming = true; + input.focus(); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function(e) { + if (!eventInWidget(display, e)) e_preventDefault(e); + }); + + on(te, "compositionstart", function() { + var start = cm.getCursor("from"); + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function() { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }, + + prepareSelection: function() { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result; + }, + + showSelection: function(drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }, + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + reset: function(typing) { + if (this.contextMenuPending) return; + var minimal, selected, cm = this.cm, doc = cm.doc; + if (cm.somethingSelected()) { + this.prevInput = ""; + var range = doc.sel.primary(); + minimal = hasCopyEvent && + (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000); + var content = minimal ? "-" : selected || cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) selectInput(this.textarea); + if (ie && ie_version >= 9) this.hasSelection = content; + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) this.hasSelection = null; + } + this.inaccurateSelection = minimal; + }, + + getField: function() { return this.textarea; }, + + supportsTouch: function() { return false; }, + + focus: function() { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }, + + blur: function() { this.textarea.blur(); }, + + resetPosition: function() { + this.wrapper.style.top = this.wrapper.style.left = 0; + }, + + receivedFocus: function() { this.slowPoll(); }, + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + slowPoll: function() { + var input = this; + if (input.pollingFast) return; + input.polling.set(this.cm.options.pollInterval, function() { + input.poll(); + if (input.cm.state.focused) input.slowPoll(); + }); + }, + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + fastPoll: function() { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }, + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + poll: function() { + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq) + return false; + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) return false; + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false; + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) prevInput = "\u200b"; + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; + + var self = this; + runInOp(cm, function() { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, self.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = ""; + else self.prevInput = text; + + if (self.composing) { + self.composing.range.clear(); + self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true; + }, + + ensurePolled: function() { + if (this.pollingFast && this.poll()) this.pollingFast = false; + }, + + onKeyPress: function() { + if (ie && ie_version >= 9) this.hasSelection = null; + this.fastPoll(); + }, + + onContextMenu: function(e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) return; // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); + + var oldCSS = te.style.cssText; + input.wrapper.style.position = "absolute"; + te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " + + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + + "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) window.scrollTo(null, oldScrollY); + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) te.value = input.prevInput = " "; + input.contextMenuPending = true; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + input.contextMenuPending = false; + input.wrapper.style.position = "relative"; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) prepareSelectAllHack(); + var i = 0, poll = function() { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") + operation(cm, commands.selectAll)(cm); + else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500); + else display.input.reset(); + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) prepareSelectAllHack(); + if (captureRightClick) { + e_stop(e); + var mouseup = function() { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }, + + setUneditable: nothing, + + needsContentAttribute: false + }, TextareaInput.prototype); + + // CONTENTEDITABLE INPUT STYLE + + function ContentEditableInput(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.gracePeriod = false; + } + + ContentEditableInput.prototype = copyObj({ + init: function(display) { + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = "true"; + disableBrowserMagic(div); + + on(div, "paste", function(e) { handlePaste(e, cm); }) + + on(div, "compositionstart", function(e) { + var data = e.data; + input.composing = {sel: cm.doc.sel, data: data, startData: data}; + if (!data) return; + var prim = cm.doc.sel.primary(); + var line = cm.getLine(prim.head.line); + var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)); + if (found > -1 && found <= prim.head.ch) + input.composing.sel = simpleSelection(Pos(prim.head.line, found), + Pos(prim.head.line, found + data.length)); + }); + on(div, "compositionupdate", function(e) { + input.composing.data = e.data; + }); + on(div, "compositionend", function(e) { + var ours = input.composing; + if (!ours) return; + if (e.data != ours.startData && !/\u200b/.test(e.data)) + ours.data = e.data; + // Need a small delay to prevent other code (input event, + // selection polling) from doing damage when fired right after + // compositionend. + setTimeout(function() { + if (!ours.handled) + input.applyComposition(ours); + if (input.composing == ours) + input.composing = null; + }, 50); + }); + + on(div, "touchstart", function() { + input.forceCompositionEnd(); + }); + + on(div, "input", function() { + if (input.composing) return; + if (!input.pollContent()) + runInOp(input.cm, function() {regChange(cm);}); + }); + + function onCopyCut(e) { + if (cm.somethingSelected()) { + lastCopied = cm.getSelections(); + if (e.type == "cut") cm.replaceSelection("", null, "cut"); + } else if (!cm.options.lineWiseCopyCut) { + return; + } else { + var ranges = copyableRanges(cm); + lastCopied = ranges.text; + if (e.type == "cut") { + cm.operation(function() { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + // iOS exposes the clipboard API, but seems to discard content inserted into it + if (e.clipboardData && !ios) { + e.preventDefault(); + e.clipboardData.clearData(); + e.clipboardData.setData("text/plain", lastCopied.join("\n")); + } else { + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.join("\n"); + var hadFocus = document.activeElement; + selectInput(te); + setTimeout(function() { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + }, 50); + } + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }, + + prepareSelection: function() { + var result = prepareSelection(this.cm, false); + result.focus = this.cm.state.focused; + return result; + }, + + showSelection: function(info) { + if (!info || !this.cm.display.view.length) return; + if (info.focus) this.showPrimarySelection(); + this.showMultipleSelections(info); + }, + + showPrimarySelection: function() { + var sel = window.getSelection(), prim = this.cm.doc.sel.primary(); + var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), prim.from()) == 0 && + cmp(maxPos(curAnchor, curFocus), prim.to()) == 0) + return; + + var start = posToDOM(this.cm, prim.from()); + var end = posToDOM(this.cm, prim.to()); + if (!start && !end) return; + + var view = this.cm.display.view; + var old = sel.rangeCount && sel.getRangeAt(0); + if (!start) { + start = {node: view[0].measure.map[2], offset: 0}; + } else if (!end) { // FIXME dangerously hacky + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + try { var rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + sel.removeAllRanges(); + sel.addRange(rng); + if (old && sel.anchorNode == null) sel.addRange(old); + else if (gecko) this.startGracePeriod(); + } + this.rememberSelection(); + }, + + startGracePeriod: function() { + var input = this; + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function() { + input.gracePeriod = false; + if (input.selectionChanged()) + input.cm.operation(function() { input.cm.curOp.selectionChanged = true; }); + }, 20); + }, + + showMultipleSelections: function(info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }, + + rememberSelection: function() { + var sel = window.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }, + + selectionInEditor: function() { + var sel = window.getSelection(); + if (!sel.rangeCount) return false; + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node); + }, + + focus: function() { + if (this.cm.options.readOnly != "nocursor") this.div.focus(); + }, + blur: function() { this.div.blur(); }, + getField: function() { return this.div; }, + + supportsTouch: function() { return true; }, + + receivedFocus: function() { + var input = this; + if (this.selectionInEditor()) + this.pollSelection(); + else + runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; }); + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }, + + selectionChanged: function() { + var sel = window.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset; + }, + + pollSelection: function() { + if (!this.composing && !this.gracePeriod && this.selectionChanged()) { + var sel = window.getSelection(), cm = this.cm; + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) runInOp(cm, function() { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) cm.curOp.selectionChanged = true; + }); + } + }, + + pollContent: function() { + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false; + + var fromIndex; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + var fromLine = lineNo(display.view[0].line); + var fromNode = display.view[0].node; + } else { + var fromLine = lineNo(display.view[fromIndex].line); + var fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + if (toIndex == display.view.length - 1) { + var toLine = display.viewTo - 1; + var toNode = display.lineDiv.lastChild; + } else { + var toLine = lineNo(display.view[toIndex + 1].line) - 1; + var toNode = display.view[toIndex + 1].node.previousSibling; + } + + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else break; + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + ++cutFront; + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + ++cutEnd; + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd); + newText[0] = newText[0].slice(cutFront); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true; + } + }, + + ensurePolled: function() { + this.forceCompositionEnd(); + }, + reset: function() { + this.forceCompositionEnd(); + }, + forceCompositionEnd: function() { + if (!this.composing || this.composing.handled) return; + this.applyComposition(this.composing); + this.composing.handled = true; + this.div.blur(); + this.div.focus(); + }, + applyComposition: function(composing) { + if (composing.data && composing.data != composing.startData) + operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel); + }, + + setUneditable: function(node) { + node.setAttribute("contenteditable", "false"); + }, + + onKeyPress: function(e) { + e.preventDefault(); + operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); + }, + + onContextMenu: nothing, + resetPosition: nothing, + + needsContentAttribute: true + }, ContentEditableInput.prototype); + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) return null; + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result; + } + + function badPos(pos, bad) { if (bad) pos.bad = true; return pos; } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true); + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) return null; + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break; + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + return locateNodeInLineView(lineView, node, offset); + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true); + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad); + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) offset = textNode.nodeValue.length; + } + while (topNode.parentNode != wrapper) topNode = topNode.parentNode; + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)]; + return Pos(line, ch); + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) return badPos(found, bad); + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + return badPos(Pos(found.line, found.ch - dist), bad); + else + dist += after.textContent.length; + } + for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + return badPos(Pos(found.line, found.ch + dist), bad); + else + dist += after.textContent.length; + } + } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(); + function recognizeMarker(id) { return function(marker) { return marker.id == id; }; } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText != null) { + if (cmText == "") cmText = node.textContent.replace(/\u200b/g, ""); + text += cmText; + return; + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find())) + text += getBetween(cm.doc, range.from, range.to).join(lineSep); + return; + } + if (node.getAttribute("contenteditable") == "false") return; + for (var i = 0; i < node.childNodes.length; i++) + walk(node.childNodes[i]); + if (/^(pre|div|p)$/i.test(node.nodeName)) + closing = true; + } else if (node.nodeType == 3) { + var val = node.nodeValue; + if (!val) return; + if (closing) { + text += lineSep; + closing = false; + } + text += val; + } + } + for (;;) { + walk(from); + if (from == to) break; + from = from.nextSibling; + } + return text; + } + + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // SELECTION / CURSOR + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + function Selection(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + } + + Selection.prototype = { + primary: function() { return this.ranges[this.primIndex]; }, + equals: function(other) { + if (other == this) return true; + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false; + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false; + } + return true; + }, + deepCopy: function() { + for (var out = [], i = 0; i < this.ranges.length; i++) + out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); + return new Selection(out, this.primIndex); + }, + somethingSelected: function() { + for (var i = 0; i < this.ranges.length; i++) + if (!this.ranges[i].empty()) return true; + return false; + }, + contains: function(pos, end) { + if (!end) end = pos; + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + return i; + } + return -1; + } + }; + + function Range(anchor, head) { + this.anchor = anchor; this.head = head; + } + + Range.prototype = { + from: function() { return minPos(this.anchor, this.head); }, + to: function() { return maxPos(this.anchor, this.head); }, + empty: function() { + return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch; + } + }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(ranges, primIndex) { + var prim = ranges[primIndex]; + ranges.sort(function(a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + if (cmp(prev.to(), cur.from()) >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) --primIndex; + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex); + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0); + } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));} + function clipPos(doc, pos) { + if (pos.line < doc.first) return Pos(doc.first, 0); + var last = doc.first + doc.size - 1; + if (pos.line > last) return Pos(last, getLine(doc, last).text.length); + return clipToLen(pos, getLine(doc, pos.line).text.length); + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) return Pos(pos.line, linelen); + else if (ch < 0) return Pos(pos.line, 0); + else return pos; + } + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} + function clipPosArray(doc, array) { + for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]); + return out; + } + + // SELECTION UPDATES + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(doc, range, head, other) { + if (doc.cm && doc.cm.display.shift || doc.extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head); + } else { + return new Range(other || head, head); + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options) { + setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + for (var out = [], i = 0; i < doc.sel.ranges.length; i++) + out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null); + var newSel = normalizeSelection(out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); + } + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); + if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1); + else return sel; + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + sel = filterSelectionChange(doc, sel); + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm) + ensureCursorVisible(doc.cm); + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) return; + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) out = sel.ranges.slice(0, i); + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(out, sel.primIndex) : sel; + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, bias, mayClear) { + var flipped = false, curPos = pos; + var dir = bias || 1; + doc.cantEdit = false; + search: for (;;) { + var line = getLine(doc, curPos.line); + if (line.markedSpans) { + for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) && + (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) break; + else {--i; continue;} + } + } + if (!m.atomic) continue; + var newPos = m.find(dir < 0 ? -1 : 1); + if (cmp(newPos, curPos) == 0) { + newPos.ch += dir; + if (newPos.ch < 0) { + if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1)); + else newPos = null; + } else if (newPos.ch > line.text.length) { + if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0); + else newPos = null; + } + if (!newPos) { + if (flipped) { + // Driven in a corner -- no valid cursor position found at all + // -- try again *with* clearing, if we didn't already + if (!mayClear) return skipAtomic(doc, pos, bias, true); + // Otherwise, turn off editing until further notice, and return the start of the doc + doc.cantEdit = true; + return Pos(doc.first, 0); + } + flipped = true; newPos = pos; dir = -dir; + } + } + curPos = newPos; + continue search; + } + } + } + return curPos; + } + } + + // SELECTION DRAWING + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (primary === false && i == doc.sel.primIndex) continue; + var range = doc.sel.ranges[i]; + var collapsed = range.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + drawSelectionCursor(cm, range, curFragment); + if (!collapsed) + drawSelectionRange(cm, range, selFragment); + } + return result; + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, range, output) { + var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + + function add(left, top, width, bottom) { + if (top < 0) top = 0; + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + + "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) + + "px; height: " + (bottom - top) + "px")); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias); + } + + iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { + var leftPos = coords(from, "left"), rightPos, left, right; + if (from == to) { + rightPos = leftPos; + left = right = leftPos.left; + } else { + rightPos = coords(to - 1, "right"); + if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } + left = leftPos.left; + right = rightPos.right; + } + if (fromArg == null && from == 0) left = leftSide; + if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part + add(left, leftPos.top, null, leftPos.bottom); + left = leftSide; + if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); + } + if (toArg == null && to == lineLen) right = rightSide; + if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) + start = leftPos; + if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) + end = rightPos; + if (left < leftSide + 1) left = leftSide; + add(left, rightPos.top, right - left, rightPos.bottom); + }); + return {start: start, end: end}; + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + add(leftSide, leftEnd.bottom, null, rightStart.top); + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) return; + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + display.blinker = setInterval(function() { + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); + else if (cm.options.cursorBlinkRate < 0) + display.cursorDiv.style.visibility = "hidden"; + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) + cm.state.highlight.set(time, bind(highlightWorker, cm)); + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.frontier < doc.first) doc.frontier = doc.first; + if (doc.frontier >= cm.display.viewTo) return; + var end = +new Date + cm.options.workTime; + var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); + var changedLines = []; + + doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) { + if (doc.frontier >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var highlighted = highlightLine(cm, line, state, true); + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) line.styleClasses = newCls; + else if (oldCls) line.styleClasses = null; + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i]; + if (ischange) changedLines.push(doc.frontier); + line.stateAfter = copyState(doc.mode, state); + } else { + processLine(cm, line.text, state); + line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; + } + ++doc.frontier; + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true; + } + }); + if (changedLines.length) runInOp(cm, function() { + for (var i = 0; i < changedLines.length; i++) + regLineChange(cm, changedLines[i], "text"); + }); + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) return doc.first; + var line = getLine(doc, search - 1); + if (line.stateAfter && (!precise || search <= doc.frontier)) return search; + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline; + } + + function getStateBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) return true; + var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter; + if (!state) state = startState(doc.mode); + else state = copyState(doc.mode, state); + doc.iter(pos, n, function(line) { + processLine(cm, line.text, state); + var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo; + line.stateAfter = save ? copyState(doc.mode, state) : null; + ++pos; + }); + if (precise) doc.frontier = pos; + return state; + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop;} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;} + function paddingH(display) { + if (display.cachedPaddingH) return display.cachedPaddingH; + var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data; + return data; + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth; + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight; + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + heights.push((cur.bottom + next.top) / 2 - rect.top); + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + return {map: lineView.measure.map, cache: lineView.measure.cache}; + for (var i = 0; i < lineView.rest.length; i++) + if (lineView.rest[i] == line) + return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]}; + for (var i = 0; i < lineView.rest.length; i++) + if (lineNo(lineView.rest[i]) > lineN) + return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true}; + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view; + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias); + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + return cm.display.view[findViewIndex(cm, lineN)]; + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + return ext; + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + view = updateExternalMeasurement(cm, line); + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + }; + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) ch = -1; + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + prepared.rect = prepared.view.text.getBoundingClientRect(); + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) prepared.cache[key] = found; + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom}; + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + var mStart = map[i], mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) collapse = "right"; + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + collapse = bias; + if (bias == "left" && start == 0) + while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } + if (bias == "right" && start == mEnd - mStart) + while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } + break; + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}; + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start; + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end; + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) { + rect = node.parentNode.getBoundingClientRect(); + } else if (ie && cm.options.lineWrapping) { + var rects = range(node, start, end).getClientRects(); + if (rects.length) + rect = rects[bias == "right" ? rects.length - 1 : 0]; + else + rect = nullRect; + } else { + rect = range(node, start, end).getBoundingClientRect() || nullRect; + } + if (rect.left || rect.right || start == 0) break; + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect); + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) collapse = bias = "right"; + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + rect = rects[bias == "right" ? rects.length - 1 : 0]; + else + rect = node.getBoundingClientRect(); + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; + else + rect = nullRect; + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + for (var i = 0; i < heights.length - 1; i++) + if (mid < heights[i]) break; + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) result.bogus = true; + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result; + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + return rect; + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY}; + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) + lineView.measure.caches[i] = {}; + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + clearLineMeasurementCacheFor(cm.display.view[i]); + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) cm.display.maxLineChanged = true; + cm.display.lineNumChars = null; + } + + function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; } + function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"/null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context) { + if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { + var size = widgetHeight(lineObj.widgets[i]); + rect.top += size; rect.bottom += size; + } + if (context == "line") return rect; + if (!context) context = "local"; + var yOff = heightAtLine(lineObj); + if (context == "local") yOff += paddingTop(cm.display); + else yOff -= cm.display.viewOffset; + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect; + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"/null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") return coords; + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) lineObj = getLine(cm.doc, pos.line); + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context); + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj); + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) m.left = m.right; else m.right = m.left; + return intoCoordSystem(cm, lineObj, m, context); + } + function getBidi(ch, partPos) { + var part = order[partPos], right = part.level % 2; + if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { + part = order[--partPos]; + ch = bidiRight(part) - (part.level % 2 ? 0 : 1); + right = true; + } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) { + part = order[++partPos]; + ch = bidiLeft(part) - part.level % 2; + right = false; + } + if (right && ch == part.to && ch > part.from) return get(ch - 1); + return get(ch, right); + } + var order = getOrder(lineObj), ch = pos.ch; + if (!order) return get(ch); + var partPos = getBidiPartAt(order, ch); + var val = getBidi(ch, partPos); + if (bidiOther != null) val.other = getBidi(ch, bidiOther); + return val; + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0, pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch; + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height}; + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, outside, xRel) { + var pos = Pos(line, ch); + pos.xRel = xRel; + if (outside) pos.outside = true; + return pos; + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) return PosWithInfo(doc.first, 0, true, -1); + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1); + if (x < 0) x = 0; + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var merged = collapsedSpanAtEnd(lineObj); + var mergedPos = merged && merged.find(0, true); + if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) + lineN = lineNo(lineObj = mergedPos.to.line); + else + return found; + } + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + var innerOff = y - heightAtLine(lineObj); + var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth; + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + + function getX(ch) { + var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure); + wrongLine = true; + if (innerOff > sp.bottom) return sp.left - adjust; + else if (innerOff < sp.top) return sp.left + adjust; + else wrongLine = false; + return sp.left; + } + + var bidi = getOrder(lineObj), dist = lineObj.text.length; + var from = lineLeft(lineObj), to = lineRight(lineObj); + var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine; + + if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1); + // Do a binary search between these bounds. + for (;;) { + if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { + var ch = x < fromX || x - fromX <= toX - x ? from : to; + var xDiff = x - (ch == from ? fromX : toX); + while (isExtendingChar(lineObj.text.charAt(ch))) ++ch; + var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside, + xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0); + return pos; + } + var step = Math.ceil(dist / 2), middle = from + step; + if (bidi) { + middle = from; + for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); + } + var middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;} + else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;} + } + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) return display.cachedTextHeight; + if (measureText == null) { + measureText = elt("pre"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) display.cachedTextHeight = height; + removeChildren(display.measure); + return height || 1; + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) return display.cachedCharWidth; + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor]); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) display.cachedCharWidth = width; + return width || 10; + } + + // OPERATIONS + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var operationGroup = null; + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: null, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId // Unique ID + }; + if (operationGroup) { + operationGroup.ops.push(cm.curOp); + } else { + cm.curOp.ownsGroup = operationGroup = { + ops: [cm.curOp], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + callbacks[i](); + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm); + } + } while (i < callbacks.length); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp, group = op.ownsGroup; + if (!group) return; + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + for (var i = 0; i < group.ops.length; i++) + group.ops[i].cm.curOp = null; + endOperations(group); + } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_R1(ops[i]); + for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W1(ops[i]); + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_R2(ops[i]); + for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W2(ops[i]); + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_finish(ops[i]); + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) findMaxLine(cm); + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) updateHeightsInViewport(cm); + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + op.preparedSelection = display.input.prepareSelection(); + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); + cm.display.maxLineChanged = false; + } + + if (op.preparedSelection) + cm.display.input.showSelection(op.preparedSelection); + if (op.updatedDisplay) + setDocumentHeight(cm, op.barMeasure); + if (op.updatedDisplay || op.startHeight != cm.doc.height) + updateScrollbars(cm, op.barMeasure); + + if (op.selectionChanged) restartBlink(cm); + + if (cm.state.focused && op.updateInput) + cm.display.input.reset(op.typing); + if (op.focus && op.focus == activeElt()) ensureFocus(op.cm); + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) postUpdateDisplay(cm, op.update); + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + display.wheelStartX = display.wheelStartY = null; + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) { + doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)); + display.scrollbars.setScrollTop(doc.scrollTop); + display.scroller.scrollTop = doc.scrollTop; + } + if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) { + doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft)); + display.scrollbars.setScrollLeft(doc.scrollLeft); + display.scroller.scrollLeft = doc.scrollLeft; + alignHorizontally(cm); + } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) for (var i = 0; i < hidden.length; ++i) + if (!hidden[i].lines.length) signal(hidden[i], "hide"); + if (unhidden) for (var i = 0; i < unhidden.length; ++i) + if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); + + if (display.wrapper.offsetHeight) + doc.scrollTop = cm.display.scroller.scrollTop; + + // Fire change events, and delayed event handlers + if (op.changeObjs) + signal(cm, "changes", cm, op.changeObjs); + if (op.update) + op.update.finish(); + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) return f(); + startOperation(cm); + try { return f(); } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) return f.apply(cm, arguments); + startOperation(cm); + try { return f.apply(cm, arguments); } + finally { endOperation(cm); } + }; + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) return f.apply(this, arguments); + startOperation(this); + try { return f.apply(this, arguments); } + finally { endOperation(this); } + }; + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) return f.apply(this, arguments); + startOperation(cm); + try { return f.apply(this, arguments); } + finally { endOperation(cm); } + }; + } + + // VIEW TRACKING + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array; + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) from = cm.doc.first; + if (to == null) to = cm.doc.first + cm.doc.size; + if (!lendiff) lendiff = 0; + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + display.updateLineNumbers = from; + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + resetView(cm); + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut = viewCuttingPoint(cm, from, from, -1); + if (cut) { + display.view = display.view.slice(0, cut.index); + display.viewTo = cut.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + ext.lineN += lendiff; + else if (from < ext.lineN + ext.size) + display.externalMeasured = null; + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + display.externalMeasured = null; + + if (line < display.viewFrom || line >= display.viewTo) return; + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) return; + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) arr.push(type); + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) return null; + n -= cm.display.viewFrom; + if (n < 0) return null; + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) return i; + } + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + return {index: index, lineN: newN}; + for (var i = 0, n = cm.display.viewFrom; i < index; i++) + n += view[i].size; + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) return null; + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) return null; + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN}; + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); + else if (display.viewFrom < from) + display.view = display.view.slice(findViewIndex(cm, from)); + display.viewFrom = from; + if (display.viewTo < to) + display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); + else if (display.viewTo > to) + display.view = display.view.slice(0, findViewIndex(cm, to)); + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty; + } + return dirty; + } + + // EVENT HANDLERS + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + on(d.scroller, "dblclick", operation(cm, function(e) { + if (signalDOMEvent(cm, e)) return; + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); + else + on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); }); + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + }; + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) return false; + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1; + } + function farAway(touch, other) { + if (other.left == null) return true; + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20; + } + on(d.scroller, "touchstart", function(e) { + if (!isMouseLikeTouchEvent(e)) { + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function() { + if (d.activeTouch) d.activeTouch.moved = true; + }); + on(d.scroller, "touchend", function(e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + range = new Range(pos, pos); + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + range = cm.findWordAt(pos); + else // Triple tap + range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function() { + if (d.scroller.clientHeight) { + setScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); + on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + simple: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);}, + start: function(e){onDragStart(cm, e);}, + drop: operation(cm, onDrop) + }; + + var inp = d.input.getField(); + on(inp, "keyup", function(e) { onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", bind(onFocus, cm)); + on(inp, "blur", bind(onBlur, cm)); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != CodeMirror.Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.simple); + toggle(cm.display.scroller, "dragover", funcs.simple); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) + return; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + // MOUSE EVENTS + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + return true; + } + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null; + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e) { return null; } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords; + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return; + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function(){display.scroller.draggable = true;}, 100); + } + return; + } + if (clickInGutter(cm, e)) return; + var start = posFromMouse(cm, e); + window.focus(); + + switch (e_button(e)) { + case 1: + if (start) + leftButtonDown(cm, e, start); + else if (e_target(e) == display.scroller) + e_preventDefault(e); + break; + case 2: + if (webkit) cm.state.lastMiddleDown = +new Date; + if (start) extendSelection(cm.doc, start); + setTimeout(function() {display.input.focus();}, 20); + e_preventDefault(e); + break; + case 3: + if (captureRightClick) onContextMenu(cm, e); + else delayBlurEvent(cm); + break; + } + } + + var lastClick, lastDoubleClick; + function leftButtonDown(cm, e, start) { + if (ie) setTimeout(bind(ensureFocus, cm), 0); + else cm.curOp.focus = activeElt(); + + var now = +new Date, type; + if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { + type = "triple"; + } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { + type = "double"; + lastDoubleClick = {time: now, pos: start}; + } else { + type = "single"; + lastClick = {time: now, pos: start}; + } + + var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained; + if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && + type == "single" && (contained = sel.contains(start)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) && + (cmp(contained.to(), start) > 0 || start.xRel < 0)) + leftButtonStartDrag(cm, e, start, modifier); + else + leftButtonSelect(cm, e, start, type, modifier); + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, e, start, modifier) { + var display = cm.display, startTime = +new Date; + var dragEnd = operation(cm, function(e2) { + if (webkit) display.scroller.draggable = false; + cm.state.draggingText = false; + off(document, "mouseup", dragEnd); + off(display.scroller, "drop", dragEnd); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + if (!modifier && +new Date - 200 < startTime) + extendSelection(cm.doc, start); + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if (webkit || ie && ie_version == 9) + setTimeout(function() {document.body.focus(); display.input.focus();}, 20); + else + display.input.focus(); + } + }); + // Let the drag handler handle this. + if (webkit) display.scroller.draggable = true; + cm.state.draggingText = dragEnd; + // IE's approach to draggable + if (display.scroller.dragDrop) display.scroller.dragDrop(); + on(document, "mouseup", dragEnd); + on(display.scroller, "drop", dragEnd); + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, e, start, type, addNew) { + var display = cm.display, doc = cm.doc; + e_preventDefault(e); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (addNew && !e.shiftKey) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + ourRange = ranges[ourIndex]; + else + ourRange = new Range(start, start); + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (e.altKey) { + type = "rect"; + if (!addNew) ourRange = new Range(start, start); + start = posFromMouse(cm, e, true, true); + ourIndex = -1; + } else if (type == "double") { + var word = cm.findWordAt(start); + if (cm.display.shift || doc.extend) + ourRange = extendRange(doc, ourRange, word.anchor, word.head); + else + ourRange = word; + } else if (type == "triple") { + var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))); + if (cm.display.shift || doc.extend) + ourRange = extendRange(doc, ourRange, line.anchor, line.head); + else + ourRange = line; + } else { + ourRange = extendRange(doc, ourRange, start); + } + + if (!addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) { + setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0)); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) return; + lastPos = pos; + + if (type == "rect") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); + else if (text.length > leftPos) + ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); + } + if (!ranges.length) ranges.push(new Range(start, start)); + setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var anchor = oldRange.anchor, head = pos; + if (type != "single") { + if (type == "double") + var range = cm.findWordAt(pos); + else + var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))); + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + } + var ranges = startSel.ranges.slice(0); + ranges[ourIndex] = new Range(clipPos(doc, anchor), head); + setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, type == "rect"); + if (!cur) return; + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150); + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) setTimeout(operation(cm, function() { + if (counter != curCount) return; + display.scroller.scrollTop += outside; + extend(e); + }), 50); + } + } + + function done(e) { + counter = Infinity; + e_preventDefault(e); + display.input.focus(); + off(document, "mousemove", move); + off(document, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function(e) { + if (!e_button(e)) done(e); + else extend(e); + }); + var up = operation(cm, done); + on(document, "mousemove", move); + on(document, "mouseup", up); + } + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent, signalfn) { + try { var mX = e.clientX, mY = e.clientY; } + catch(e) { return false; } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false; + if (prevent) e_preventDefault(e); + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e); + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.options.gutters[i]; + signalfn(cm, type, cm, line, gutter, e); + return e_defaultPrevented(e); + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true, signalLater); + } + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + return; + e_preventDefault(e); + if (ie) lastDrop = +new Date; + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || isReadOnly(cm)) return; + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var loadFile = function(file, i) { + var reader = new FileReader; + reader.onload = operation(cm, function() { + text[i] = reader.result; + if (++read == n) { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); + } + }); + reader.readAsText(file); + }; + for (var i = 0; i < n; ++i) loadFile(files[i], i); + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function() {cm.display.input.focus();}, 20); + return; + } + try { + var text = e.dataTransfer.getData("Text"); + if (text) { + if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey)) + var selected = cm.listSelections(); + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) for (var i = 0; i < selected.length; ++i) + replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag"); + cm.replaceSelection(text, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; + + e.dataTransfer.setData("Text", cm.getSelection()); + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) img.parentNode.removeChild(img); + } + } + + // SCROLL EVENTS + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function setScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) return; + cm.doc.scrollTop = val; + if (!gecko) updateDisplaySimple(cm, {top: val}); + if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (gecko) updateDisplaySimple(cm); + startWorker(cm, 100); + } + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller) { + if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return; + val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; + cm.display.scrollbars.setScrollLeft(val); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) wheelPixelsPerUnit = -.53; + else if (gecko) wheelPixelsPerUnit = 15; + else if (chrome) wheelPixelsPerUnit = -.7; + else if (safari) wheelPixelsPerUnit = -1/3; + + var wheelEventDelta = function(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail; + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail; + else if (dy == null) dy = e.wheelDelta; + return {x: dx, y: dy}; + }; + CodeMirror.wheelEventPixels = function(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta; + }; + + function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + if (!(dx && scroll.scrollWidth > scroll.clientWidth || + dy && scroll.scrollHeight > scroll.clientHeight)) return; + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer; + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { + if (dy) + setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))); + setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth))); + e_preventDefault(e); + display.wheelStartX = null; // Abort measurement, if in progress + return; + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && wheelPixelsPerUnit != null) { + var pixels = dy * wheelPixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) top = Math.max(0, top + pixels - 50); + else bot = Math.min(cm.doc.height, bot + pixels + 50); + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function() { + if (display.wheelStartX == null) return; + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) return; + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // KEY EVENTS + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) return false; + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (isReadOnly(cm)) cm.state.suppressEdits = true; + if (dropShift) cm.display.shift = false; + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done; + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) return result; + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm); + } + + var stopSeq = new Delayed; + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) return "handled"; + stopSeq.set(50, function() { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); + name = seq + " " + name; + } + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + cm.state.keySeq = name; + if (result == "handled") + signalLater(cm, "keyHandled", cm, name, e); + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + if (seq && !result && /\'$/.test(name)) { + e_preventDefault(e); + return true; + } + return !!result; + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) return false; + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);}) + || dispatchKey(cm, name, e, function(b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + return doHandleBinding(cm, b); + }); + } else { + return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); }); + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, + function(b) { return doHandleBinding(cm, b, true); }); + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + cm.curOp.focus = activeElt(); + if (signalDOMEvent(cm, e)) return; + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false; + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + cm.replaceSelection("", null, "cut"); + } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + showCrossHair(cm); + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) this.doc.sel.shift = false; + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return; + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return; + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + if (handleCharBinding(cm, e, ch)) return; + cm.display.input.onKeyPress(e); + } + + // FOCUS/BLUR EVENTS + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function() { + if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + onBlur(cm); + } + }, 100); + } + + function onFocus(cm) { + if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false; + + if (cm.options.readOnly == "nocursor") return; + if (!cm.state.focused) { + signal(cm, "focus", cm); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm) { + if (cm.state.delayingBlurEvent) return; + + if (cm.state.focused) { + signal(cm, "blur", cm); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150); + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return; + cm.display.input.onContextMenu(e); + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) return false; + return gutterEvent(cm, e, "gutterContextMenu", false, signal); + } + + // UPDATING + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + var changeEnd = CodeMirror.changeEnd = function(change) { + if (!change.text) return change.to; + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); + }; + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) return pos; + if (cmp(pos, change.to) <= 0) return changeEnd(change); + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch; + return Pos(line, ch); + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(out, doc.sel.primIndex); + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + return Pos(nw.line, pos.ch - old.ch + nw.ch); + else + return Pos(nw.line + (pos.line - old.line), pos.ch); + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex); + } + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function() { this.canceled = true; } + }; + if (update) obj.update = function(from, to, text, origin) { + if (from) this.from = clipPos(doc, from); + if (to) this.to = clipPos(doc, to); + if (text) this.text = text; + if (origin !== undefined) this.origin = origin; + }; + signal(doc, "beforeChange", doc, obj); + if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); + + if (obj.canceled) return null; + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}; + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly); + if (doc.cm.state.suppressEdits) return; + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) return; + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}); + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return; + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function(doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + if (doc.cm && doc.cm.state.suppressEdits) return; + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + for (var i = 0; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + break; + } + if (i == source.length) return; + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return; + } + selAfter = event; + } + else break; + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + for (var i = event.changes.length - 1; i >= 0; --i) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return; + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function(doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) return; + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function(range) { + return new Range(Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch)); + }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + regLineChange(doc.cm, l, "gutter"); + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans); + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return; + } + if (change.from.line > doc.lastLine()) return; + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) selAfter = computeSelAfterChange(doc, change); + if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); + else updateDoc(doc, change, spans); + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function(line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true; + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + signalCursorActivity(cm); + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function(line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) cm.curOp.updateMaxLine = true; + } + + // Adjust frontier, schedule worker + doc.frontier = Math.min(doc.frontier, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + regChange(cm); + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + regLineChange(cm, from.line, "text"); + else + regChange(cm, from.line, to.line + 1, lendiff); + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) signalLater(cm, "change", cm, obj); + if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + if (!to) to = from; + if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } + if (typeof code == "string") code = doc.splitLines(code); + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, coords) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) return; + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (coords.top + box.top < 0) doScroll = true; + else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " + + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " + + coords.left + "px; width: 2px;"); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) margin = 0; + for (var limit = 0; limit < 5; limit++) { + var changed = false, coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left), + Math.min(coords.top, endCoords.top) - margin, + Math.max(coords.left, endCoords.left), + Math.max(coords.bottom, endCoords.bottom) + margin); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + setScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true; + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true; + } + if (!changed) break; + } + return coords; + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, x1, y1, x2, y2) { + var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2); + if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop); + if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft); + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, x1, y1, x2, y2) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (y1 < 0) y1 = 0; + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (y2 - y1 > screen) y2 = y1 + screen; + var docBottom = cm.doc.height + paddingVert(display); + var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; + if (y1 < screentop) { + result.scrollTop = atTop ? 0 : y1; + } else if (y2 > screentop + screen) { + var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen); + if (newTop != screentop) result.scrollTop = newTop; + } + + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; + var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); + var tooWide = x2 - x1 > screenw; + if (tooWide) x2 = x1 + screenw; + if (x1 < 10) + result.scrollLeft = 0; + else if (x1 < screenleft) + result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)); + else if (x2 > screenw + screenleft - 3) + result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw; + return result; + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollPos(cm, left, top) { + if (left != null || top != null) resolveScrollToPos(cm); + if (left != null) + cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left; + if (top != null) + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(), from = cur, to = cur; + if (!cm.options.lineWrapping) { + from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur; + to = Pos(cur.line, cur.ch + 1); + } + cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + var sPos = calculateScrollPos(cm, Math.min(from.left, to.left), + Math.min(from.top, to.top) - range.margin, + Math.max(from.right, to.right), + Math.max(from.bottom, to.bottom) + range.margin); + cm.scrollTo(sPos.scrollLeft, sPos.scrollTop); + } + } + + // API UTILITIES + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) how = "add"; + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) how = "prev"; + else state = getStateBefore(cm, n); + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) line.stateAfter = null; + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) return; + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); + else indentation = 0; + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} + if (pos < indentation) indentString += spaceStr(indentation - pos); + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true; + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i, new Range(pos, pos)); + break; + } + } + } + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); + else no = lineNo(handle); + if (no == null) return null; + if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType); + return line; + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break; + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function() { + for (var i = kill.length - 1; i >= 0; i--) + replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); + ensureCursorVisible(cm); + }); + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "char", "column" (like char, but doesn't + // cross line boundaries), "word" (across next word), or "group" (to + // the start of next group of word or non-word-non-whitespace + // chars). The visually param controls whether, in right-to-left + // text, direction 1 means to move towards the next index in the + // string, or towards the character to the right of the current + // position. The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var line = pos.line, ch = pos.ch, origDir = dir; + var lineObj = getLine(doc, line); + var possible = true; + function findNextLine() { + var l = line + dir; + if (l < doc.first || l >= doc.first + doc.size) return (possible = false); + line = l; + return lineObj = getLine(doc, l); + } + function moveOnce(boundToLine) { + var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); + if (next == null) { + if (!boundToLine && findNextLine()) { + if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); + else ch = dir < 0 ? lineObj.text.length : 0; + } else return (possible = false); + } else ch = next; + return true; + } + + if (unit == "char") moveOnce(); + else if (unit == "column") moveOnce(true); + else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) break; + var cur = lineObj.text.charAt(ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) type = "s"; + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce();} + break; + } + + if (type) sawType = type; + if (dir > 0 && !moveOnce(!first)) break; + } + } + var result = skipAtomic(doc, Pos(line, ch), origDir, true); + if (!possible) result.hitSide = true; + return result; + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display)); + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + for (;;) { + var target = coordsChar(cm, x, y); + if (!target.outside) break; + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; } + y += dir * 5; + } + return target; + } + + // EDITOR METHODS + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") return; + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + operation(this, optionHandlers[option])(this, value, old); + }, + + getOption: function(option) {return this.options[option];}, + getDoc: function() {return this.doc;}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true; + } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) throw new Error("Overlays may not be stateful."); + this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque}); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return; + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; + else dir = dir ? "add" : "subtract"; + } + if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + indentLine(this, j, how); + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) ensureCursorVisible(this); + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise); + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true); + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) type = styles[2]; + else for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; + else if (styles[mid * 2 + 1] < ch) before = mid + 1; + else { type = styles[mid * 2 + 2]; break; } + } + var cut = type ? type.indexOf("cm-overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1); + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) return mode; + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0]; + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) return found; + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) found.push(help[mode[type]]); + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) found.push(val); + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i = 0; i < help._global.length; i++) { + var cur = help._global[i]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + found.push(cur.val); + } + return found; + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getStateBefore(this, line + 1, precise); + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) pos = range.head; + else if (typeof start == "object") pos = clipPos(this.doc, start); + else pos = start ? range.from() : range.to(); + return cursorCoords(this, pos, mode || "page"); + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page"); + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top); + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset); + }, + heightAtLine: function(line, mode) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) line = this.doc.first; + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top + + (end ? this.doc.height - heightAtLine(lineObj) : 0); + }, + + defaultTextHeight: function() { return textHeight(this.display); }, + defaultCharWidth: function() { return charWidth(this.display); }, + + setGutterMarker: methodOp(function(line, gutterID, value) { + return changeLine(this.doc, line, "gutter", function(line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) line.gutterMarkers = null; + return true; + }); + }), + + clearGutter: methodOp(function(gutterID) { + var cm = this, doc = cm.doc, i = doc.first; + doc.iter(function(line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + line.gutterMarkers[gutterID] = null; + regLineChange(cm, i, "gutter"); + if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; + } + ++i; + }); + }), + + lineInfo: function(line) { + if (typeof line == "number") { + if (!isLine(this.doc, line)) return null; + var n = line; + line = getLine(this.doc, line); + if (!line) return null; + } else { + var n = lineNo(line); + if (n == null) return null; + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets}; + }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + top = pos.top - node.offsetHeight; + else if (pos.bottom + node.offsetHeight <= vspace) + top = pos.bottom; + if (left + node.offsetWidth > hspace) + left = hspace - node.offsetWidth; + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") left = 0; + else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2; + node.style.left = left + "px"; + } + if (scroll) + scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight); + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + return commands[cmd](this); + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) break; + } + return cur; + }, + + moveH: methodOp(function(dir, unit) { + var cm = this; + cm.extendSelectionsBy(function(range) { + if (cm.display.shift || cm.doc.extend || range.empty()) + return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually); + else + return dir < 0 ? range.from() : range.to(); + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + doc.replaceSelection("", null, "+delete"); + else + deleteNearSelection(this, function(range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}; + }); + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) x = coords.left; + else coords.left = x; + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) break; + } + return cur; + }, + + moveV: methodOp(function(dir, unit) { + var cm = this, doc = this.doc, goals = []; + var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function(range) { + if (collapse) + return dir < 0 ? range.from() : range.to(); + var headPos = cursorCoords(cm, range.head, "div"); + if (range.goalColumn != null) headPos.left = range.goalColumn; + goals.push(headPos.left); + var pos = findPosV(cm, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top); + return pos; + }, sel_move); + if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) + doc.sel.ranges[i].goalColumn = goals[i]; + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function(ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} + : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + while (start > 0 && check(line.charAt(start - 1))) --start; + while (end < line.length && check(line.charAt(end))) ++end; + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)); + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) return; + if (this.state.overwrite = !this.state.overwrite) + addClass(this.display.cursorDiv, "CodeMirror-overwrite"); + else + rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt(); }, + + scrollTo: methodOp(function(x, y) { + if (x != null || y != null) resolveScrollToPos(this); + if (x != null) this.curOp.scrollLeft = x; + if (y != null) this.curOp.scrollTop = y; + }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)}; + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) margin = this.options.cursorScrollMargin; + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) range.to = range.from; + range.margin = margin || 0; + + if (range.from.line != null) { + resolveScrollToPos(this); + this.curOp.scrollToPos = range; + } else { + var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left), + Math.min(range.from.top, range.to.top) - range.margin, + Math.max(range.from.right, range.to.right), + Math.max(range.from.bottom, range.to.bottom) + range.margin); + this.scrollTo(sPos.scrollLeft, sPos.scrollTop); + } + }), + + setSize: methodOp(function(width, height) { + var cm = this; + function interpret(val) { + return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; + } + if (width != null) cm.display.wrapper.style.width = interpret(width); + if (height != null) cm.display.wrapper.style.height = interpret(height); + if (cm.options.lineWrapping) clearLineMeasurementCache(this); + var lineNo = cm.display.viewFrom; + cm.doc.iter(lineNo, cm.display.viewTo, function(line) { + if (line.widgets) for (var i = 0; i < line.widgets.length; i++) + if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; } + ++lineNo; + }); + cm.curOp.forceUpdate = true; + signal(cm, "refresh", this); + }), + + operation: function(f){return runInOp(this, f);}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + estimateLineHeights(this); + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + this.scrollTo(doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old; + }), + + getInputField: function(){return this.display.input.getField();}, + getWrapperElement: function(){return this.display.wrapper;}, + getScrollerElement: function(){return this.display.scroller;}, + getGutterElement: function(){return this.display.gutters;} + }; + eventMixin(CodeMirror); + + // OPTION DEFAULTS + + // The default configuration options. + var defaults = CodeMirror.defaults = {}; + // Functions to run when options are changed. + var optionHandlers = CodeMirror.optionHandlers = {}; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) optionHandlers[name] = + notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle; + } + + // Passed to option handlers when there is no old value. + var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}}; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function(cm, val) { + cm.setValue(val); + }, true); + option("mode", null, function(cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function(cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + option("lineSeparator", null, function(cm, val) { + cm.doc.lineSep = val; + if (!val) return; + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function(line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) break; + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) + }); + option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != CodeMirror.Init) cm.refresh(); + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function() { + throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME + }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function(cm) { + themeChanged(cm); + guttersChanged(cm); + }, true); + option("keyMap", "default", function(cm, val, old) { + var next = getKeyMap(val); + var prev = old != CodeMirror.Init && getKeyMap(old); + if (prev && prev.detach) prev.detach(cm, next); + if (next.attach) next.attach(cm, prev || null); + }); + option("extraKeys", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function(cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("fixedGutter", true, function(cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true); + option("scrollbarStyle", "native", function(cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function(cm) { + setGuttersForLineNumbers(cm.options); + guttersChanged(cm); + }, true); + option("firstLineNumber", 1, guttersChanged, true); + option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + + option("readOnly", false, function(cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + cm.display.disabled = true; + } else { + cm.display.disabled = false; + if (!val) cm.display.input.reset(); + } + }); + option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true); + option("dragDrop", true, dragDropChanged); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;}); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function(cm){cm.refresh();}, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function(cm, val) { + if (!val) cm.display.input.resetPosition(); + }); + + option("tabindex", null, function(cm, val) { + cm.display.input.getField().tabIndex = val || ""; + }); + option("autofocus", null); + + // MODE DEFINITION AND QUERYING + + // Known modes, by name and by MIME + var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name, mode) { + if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; + if (arguments.length > 2) + mode.dependencies = Array.prototype.slice.call(arguments, 2); + modes[name] = mode; + }; + + CodeMirror.defineMIME = function(mime, spec) { + mimeModes[mime] = spec; + }; + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + CodeMirror.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") found = {name: found}; + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return CodeMirror.resolveMode("application/xml"); + } + if (typeof spec == "string") return {name: spec}; + else return spec || {name: "null"}; + }; + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + CodeMirror.getMode = function(options, spec) { + var spec = CodeMirror.resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) return CodeMirror.getMode(options, "text/plain"); + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) continue; + if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop]; + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) modeObj.helperType = spec.helperType; + if (spec.modeProps) for (var prop in spec.modeProps) + modeObj[prop] = spec.modeProps[prop]; + + return modeObj; + }; + + // Minimal default mode. + CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; + }); + CodeMirror.defineMIME("text/plain", "null"); + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = CodeMirror.modeExtensions = {}; + CodeMirror.extendMode = function(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + }; + + // EXTENSIONS + + CodeMirror.defineExtension = function(name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function(name, func) { + Doc.prototype[name] = func; + }; + CodeMirror.defineOption = option; + + var initHooks = []; + CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; + + var helpers = CodeMirror.helpers = {}; + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []}; + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + + // MODE STATE HANDLING + + // Utility functions for working with state. Exported because nested + // modes need to do this for their inner modes. + + var copyState = CodeMirror.copyState = function(mode, state) { + if (state === true) return state; + if (mode.copyState) return mode.copyState(state); + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) val = val.concat([]); + nstate[n] = val; + } + return nstate; + }; + + var startState = CodeMirror.startState = function(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; + }; + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + CodeMirror.innerMode = function(mode, state) { + while (mode.innerMode) { + var info = mode.innerMode(state); + if (!info || info.mode == mode) break; + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state}; + }; + + // STANDARD COMMANDS + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = CodeMirror.commands = { + selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);}, + singleSelection: function(cm) { + cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); + }, + killLine: function(cm) { + deleteNearSelection(cm, function(range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + return {from: range.head, to: Pos(range.head.line + 1, 0)}; + else + return {from: range.head, to: Pos(range.head.line, len)}; + } else { + return {from: range.from(), to: range.to()}; + } + }); + }, + deleteLine: function(cm) { + deleteNearSelection(cm, function(range) { + return {from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; + }); + }, + delLineLeft: function(cm) { + deleteNearSelection(cm, function(range) { + return {from: Pos(range.from().line, 0), to: range.from()}; + }); + }, + delWrappedLineLeft: function(cm) { + deleteNearSelection(cm, function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()}; + }); + }, + delWrappedLineRight: function(cm) { + deleteNearSelection(cm, function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos }; + }); + }, + undo: function(cm) {cm.undo();}, + redo: function(cm) {cm.redo();}, + undoSelection: function(cm) {cm.undoSelection();}, + redoSelection: function(cm) {cm.redoSelection();}, + goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, + goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, + goLineStart: function(cm) { + cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1}); + }, + goLineStartSmart: function(cm) { + cm.extendSelectionsBy(function(range) { + return lineStartSmart(cm, range.head); + }, {origin: "+move", bias: 1}); + }, + goLineEnd: function(cm) { + cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1}); + }, + goLineRight: function(cm) { + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + }, sel_move); + }, + goLineLeft: function(cm) { + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div"); + }, sel_move); + }, + goLineLeftSmart: function(cm) { + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head); + return pos; + }, sel_move); + }, + goLineUp: function(cm) {cm.moveV(-1, "line");}, + goLineDown: function(cm) {cm.moveV(1, "line");}, + goPageUp: function(cm) {cm.moveV(-1, "page");}, + goPageDown: function(cm) {cm.moveV(1, "page");}, + goCharLeft: function(cm) {cm.moveH(-1, "char");}, + goCharRight: function(cm) {cm.moveH(1, "char");}, + goColumnLeft: function(cm) {cm.moveH(-1, "column");}, + goColumnRight: function(cm) {cm.moveH(1, "column");}, + goWordLeft: function(cm) {cm.moveH(-1, "word");}, + goGroupRight: function(cm) {cm.moveH(1, "group");}, + goGroupLeft: function(cm) {cm.moveH(-1, "group");}, + goWordRight: function(cm) {cm.moveH(1, "word");}, + delCharBefore: function(cm) {cm.deleteH(-1, "char");}, + delCharAfter: function(cm) {cm.deleteH(1, "char");}, + delWordBefore: function(cm) {cm.deleteH(-1, "word");}, + delWordAfter: function(cm) {cm.deleteH(1, "word");}, + delGroupBefore: function(cm) {cm.deleteH(-1, "group");}, + delGroupAfter: function(cm) {cm.deleteH(1, "group");}, + indentAuto: function(cm) {cm.indentSelection("smart");}, + indentMore: function(cm) {cm.indentSelection("add");}, + indentLess: function(cm) {cm.indentSelection("subtract");}, + insertTab: function(cm) {cm.replaceSelection("\t");}, + insertSoftTab: function(cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(new Array(tabSize - col % tabSize + 1).join(" ")); + } + cm.replaceSelections(spaces); + }, + defaultTab: function(cm) { + if (cm.somethingSelected()) cm.indentSelection("add"); + else cm.execCommand("insertTab"); + }, + transposeChars: function(cm) { + runInOp(cm, function() { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose"); + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); + }, + newlineAndIndent: function(cm) { + runInOp(cm, function() { + var len = cm.listSelections().length; + for (var i = 0; i < len; i++) { + var range = cm.listSelections()[i]; + cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input"); + cm.indentLine(range.from().line + 1, null, true); + ensureCursorVisible(cm); + } + }); + }, + toggleOverwrite: function(cm) {cm.toggleOverwrite();} + }; + + + // STANDARD KEYMAPS + + var keyMap = CodeMirror.keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + fallthrough: "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", + "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + fallthrough: ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/), name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) cmd = true; + else if (/^a(lt)?$/i.test(mod)) alt = true; + else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true; + else if (/^s(hift)$/i.test(mod)) shift = true; + else throw new Error("Unrecognized modifier name: " + mod); + } + if (alt) name = "Alt-" + name; + if (ctrl) name = "Ctrl-" + name; + if (cmd) name = "Cmd-" + name; + if (shift) name = "Shift-" + name; + return name; + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + CodeMirror.normalizeKeyMap = function(keymap) { + var copy = {}; + for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue; + if (value == "...") { delete keymap[keyname]; continue; } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val, name; + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) copy[name] = val; + else if (prev != val) throw new Error("Inconsistent bindings for " + name); + } + delete keymap[keyname]; + } + for (var prop in copy) keymap[prop] = copy[prop]; + return keymap; + }; + + var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) return "nothing"; + if (found === "...") return "multi"; + if (found != null && handle(found)) return "handled"; + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + return lookupKey(key, map.fallthrough, handle, context); + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) return result; + } + } + }; + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + var isModifierKey = CodeMirror.isModifierKey = function(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; + }; + + // Look up the name of a key as indicated by an event object. + var keyName = CodeMirror.keyName = function(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) return false; + var base = keyNames[event.keyCode], name = base; + if (name == null || event.altGraphKey) return false; + if (event.altKey && base != "Alt") name = "Alt-" + name; + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name; + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name; + if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name; + return name; + }; + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val; + } + + // FROMTEXTAREA + + CodeMirror.fromTextArea = function(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + options.tabindex = textarea.tabIndex; + if (!options.placeholder && textarea.placeholder) + options.placeholder = textarea.placeholder; + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form, realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function() { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function(cm) { + cm.save = save; + cm.getTextArea = function() { return textarea; }; + cm.toTextArea = function() { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (typeof textarea.form.submit == "function") + textarea.form.submit = realSubmit; + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function(node) { + textarea.parentNode.insertBefore(node, textarea.nextSibling); + }, options); + return cm; + }; + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = CodeMirror.StringStream = function(string, tabSize) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + }; + + StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == this.lineStart;}, + peek: function() {return this.string.charAt(this.pos) || undefined;}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); + }, + indentation: function() { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); + }, + match: function(pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) return null; + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } + }; + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + var nextMarkerId = 0; + + var TextMarker = CodeMirror.TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + eventMixin(TextMarker); + + // Clear the marker. + TextMarker.prototype.clear = function() { + if (this.explicitlyCleared) return; + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) startOperation(cm); + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) signalLater(this, "clear", found.from, found.to); + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text"); + else if (cm) { + if (span.to != null) max = lineNo(line); + if (span.from != null) min = lineNo(line); + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + updateLineHeight(line, textHeight(cm.display)); + } + if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { + var visual = visualLine(this.lines[i]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } + + if (min != null && cm && this.collapsed) regChange(cm, min, max + 1); + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) reCheckSelection(cm.doc); + } + if (cm) signalLater(cm, "markerCleared", cm, this); + if (withOp) endOperation(cm); + if (this.parent) this.parent.clear(); + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function(side, lineObj) { + if (side == null && this.type == "bookmark") side = 1; + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) return from; + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) return to; + } + } + return from && {from: from, to: to}; + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function() { + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) return; + runInOp(cm, function() { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + updateLineHeight(line, line.height + dHeight); + } + }); + }; + + TextMarker.prototype.attachLine = function(line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); + } + this.lines.push(line); + }; + TextMarker.prototype.detachLine = function(line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) return markTextShared(doc, from, to, options, type); + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type); + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) copyObj(options, marker, false); + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + return marker; + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true"); + if (options.insertLeft) marker.widgetNode.insertLeft = true; + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + throw new Error("Inserting collapsed marker partially overlapping an existing one"); + sawCollapsedSpans = true; + } + + if (marker.addToHistory) + addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function(line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + updateMaxLine = true; + if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0); + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null)); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) { + if (lineIsHidden(doc, line)) updateLineHeight(line, 0); + }); + + if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); }); + + if (marker.readOnly) { + sawReadOnlySpans = true; + if (doc.history.done.length || doc.history.undone.length) + doc.clearHistory(); + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) cm.curOp.updateMaxLine = true; + if (marker.collapsed) + regChange(cm, from.line, to.line + 1); + else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) + for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text"); + if (marker.atomic) reCheckSelection(cm.doc); + signalLater(cm, "markerAdded", cm, marker); + } + return marker; + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + markers[i].parent = this; + }; + eventMixin(SharedTextMarker); + + SharedTextMarker.prototype.clear = function() { + if (this.explicitlyCleared) return; + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + this.markers[i].clear(); + signalLater(this, "clear"); + }; + SharedTextMarker.prototype.find = function(side, lineObj) { + return this.primary.find(side, lineObj); + }; + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function(doc) { + if (widget) options.widgetNode = widget.cloneNode(true); + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + if (doc.linked[i].isParent) return; + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary); + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), + function(m) { return m.parent; }); + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], linked = [marker.primary.doc];; + linkedDocs(marker.primary.doc, function(d) { linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + } + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) return span; + } + } + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + for (var r, i = 0; i < spans.length; ++i) + if (spans[i] != span) (r || (r = [])).push(spans[i]); + return r; + } + // Add a span to a line. + function addMarkedSpan(line, span) { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); + (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } + return nw; + } + function markedSpansAfter(old, endCh, isInsert) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); + (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } + return nw; + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) return null; + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) return null; + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) span.to = startCh; + else if (sameLine) span.to = found.to == null ? null : found.to + offset; + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i = 0; i < last.length; ++i) { + var span = last[i]; + if (span.to != null) span.to += offset; + if (span.from == null) { + var found = getMarkedSpanFor(first, span.marker); + if (!found) { + span.from = offset; + if (sameLine) (first || (first = [])).push(span); + } + } else { + span.from += offset; + if (sameLine) (first || (first = [])).push(span); + } + } + } + // Make sure we didn't create any zero-length spans + if (first) first = clearEmptySpans(first); + if (last && last != first) last = clearEmptySpans(last); + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + for (var i = 0; i < first.length; ++i) + if (first[i].to == null) + (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)); + for (var i = 0; i < gap; ++i) + newMarkers.push(gapMarkers); + newMarkers.push(last); + } + return newMarkers; + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + spans.splice(i--, 1); + } + if (!spans.length) return null; + return spans; + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) return stretched; + if (!stretched) return old; + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + if (oldCur[k].marker == span.marker) continue spans; + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old; + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function(line) { + if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + (markers || (markers = [])).push(mark); + } + }); + if (!markers) return null; + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue; + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + newParts.push({from: p.from, to: m.from}); + if (dto > 0 || !mk.inclusiveRight && !dto) + newParts.push({from: m.to, to: p.to}); + parts.splice.apply(parts, newParts); + j += newParts.length - 1; + } + } + return parts; + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) return; + for (var i = 0; i < spans.length; ++i) + spans[i].marker.detachLine(line); + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) return; + for (var i = 0; i < spans.length; ++i) + spans[i].marker.attachLine(line); + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) return lenDiff; + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) return -fromCmp; + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) return toCmp; + return b.id - a.id; + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) for (var sp, i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + found = sp.marker; + } + return found; + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) continue; + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; + if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) || + fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight))) + return true; + } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + line = merged.find(-1, true).line; + return line; + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line; + (lines || (lines = [])).push(line); + } + return lines; + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) return lineN; + return lineNo(vis); + } + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) return lineN; + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) return lineN; + while (merged = collapsedSpanAtEnd(line)) + line = merged.find(1, true).line; + return lineNo(line) + 1; + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) for (var sp, i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) continue; + if (sp.from == null) return true; + if (sp.marker.widgetNode) continue; + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + return true; + } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)); + } + if (span.marker.inclusiveRight && span.to == line.text.length) + return true; + for (var sp, i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) return true; + } + } + + // LINE WIDGETS + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = CodeMirror.LineWidget = function(doc, node, options) { + if (options) for (var opt in options) if (options.hasOwnProperty(opt)) + this[opt] = options[opt]; + this.doc = doc; + this.node = node; + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + addToScrollPos(cm, null, diff); + } + + LineWidget.prototype.clear = function() { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) return; + for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); + if (!ws.length) line.widgets = null; + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) runInOp(cm, function() { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + }; + LineWidget.prototype.changed = function() { + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) return; + updateLineHeight(line, line.height + diff); + if (cm) runInOp(cm, function() { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + }); + }; + + function widgetHeight(widget) { + if (widget.height != null) return widget.height; + var cm = widget.doc.cm; + if (!cm) return 0; + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; + if (widget.noHScroll) + parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.offsetHeight; + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) cm.display.alignWidgets = true; + changeLine(doc, handle, "widget", function(line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) widgets.push(widget); + else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) addToScrollPos(cm, null, widget.height); + cm.curOp.forceUpdate = true; + } + return true; + }); + return widget; + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + eventMixin(Line); + Line.prototype.lineNo = function() { return lineNo(this); }; + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) line.stateAfter = null; + if (line.styles) line.styles = null; + if (line.order != null) line.order = null; + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) updateLineHeight(line, estHeight); + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + function extractLineClasses(type, output) { + if (type) for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) break; + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + output[prop] = lineClass[2]; + else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) + output[prop] += " " + lineClass[2]; + } + return type; + } + + function callBlankLine(mode, state) { + if (mode.blankLine) return mode.blankLine(state); + if (!mode.innerMode) return; + var inner = CodeMirror.innerMode(mode, state); + if (inner.mode.blankLine) return inner.mode.blankLine(inner.state); + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode; + var style = mode.token(stream, state); + if (stream.pos > stream.start) return style; + } + throw new Error("Mode " + mode.name + " failed to advance stream."); + } + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + function getObj(copy) { + return {start: stream.start, end: stream.pos, + string: stream.current(), + type: style || null, + state: copy ? copyState(doc.mode, state) : state}; + } + + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize), tokens; + if (asArray) tokens = []; + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, state); + if (asArray) tokens.push(getObj(true)); + } + return asArray ? tokens : getObj(); + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses); + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) processLine(cm, text, state, stream.pos); + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) style = "m-" + (style ? mName + " " + style : mName); + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 50000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 characters + var pos = Math.min(stream.pos, curStart + 50000); + f(pos, curStyle); + curStart = pos; + } + } + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, state, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, state, function(end, style) { + st.push(end, style); + }, lineClasses, forceToEnd); + + // Run overlays, adjust style array. + for (var o = 0; o < cm.state.overlays.length; ++o) { + var overlay = cm.state.overlays[o], i = 1, at = 0; + runMode(cm, line.text, overlay.mode, true, function(end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + st.splice(i, 1, end, st[i+1], i_end); + i += 2; + at = Math.min(end, i_end); + } + if (!style) return; + if (overlay.opaque) { + st.splice(start, i - start, end, "cm-overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style; + } + } + }, lineClasses); + } + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}; + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); + line.styles = result.styles; + if (result.classes) line.styleClasses = result.classes; + else if (line.styleClasses) line.styleClasses = null; + if (updateFrontier === cm.doc.frontier) cm.doc.frontier++; + } + return line.styles; + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, state, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize); + stream.start = stream.pos = startAt || 0; + if (text == "") callBlankLine(mode, state); + while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) { + readToken(mode, stream, state); + stream.start = stream.pos; + } + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) return null; + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")); + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = elt("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order; + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) + builder.addToken = buildTokenBadBidi(builder.addToken, order); + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); + if (line.styleClasses.textClass) + builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map); + (lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className)) + builder.content.className = "cm-tab-wrap-hack"; + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); + + return builder; + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token; + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, title, css) { + if (!text) return; + var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + if (!special.test(text)) { + builder.col += text.length; + var content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) mustWrap = true; + builder.pos += text.length; + } else { + var content = document.createDocumentFragment(), pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); + else content.appendChild(txt); + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) break; + pos += skipped + 1; + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt.setAttribute("role", "presentation"); + txt.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + var txt = content.appendChild(elt("span", m[0] == "\r" ? "␍" : "␤", "cm-invalidchar")); + txt.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + var txt = builder.cm.options.specialCharPlaceholder(m[0]); + txt.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); + else content.appendChild(txt); + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt); + builder.pos++; + } + } + if (style || startStyle || endStyle || mustWrap || css) { + var fullStyle = style || ""; + if (startStyle) fullStyle += startStyle; + if (endStyle) fullStyle += endStyle; + var token = elt("span", [content], fullStyle, css); + if (title) token.title = title; + return builder.content.appendChild(token); + } + builder.content.appendChild(content); + } + + function splitSpaces(old) { + var out = " "; + for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0"; + out += " "; + return out; + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function(builder, text, style, startStyle, endStyle, title, css) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + for (var i = 0; i < order.length; i++) { + var part = order[i]; + if (part.to > start && part.from <= start) break; + } + if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css); + inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + }; + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) builder.map.push(builder.pos, builder.pos + size, widget); + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + widget = builder.content.appendChild(document.createElement("span")); + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i = 1; i < styles.length; i+=2) + builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)); + return; + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = title = css = ""; + collapsed = null; nextChange = Infinity; + var foundBookmarks = []; + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) spanStyle += " " + m.className; + if (m.css) css = m.css; + if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; + if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle; + if (m.title && !title) title = m.title; + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + collapsed = sp; + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) return; + if (collapsed.to == pos) collapsed = false; + } + if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j) + buildCollapsedSpan(builder, 0, foundBookmarks[j]); + } + if (pos >= len) break; + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore); + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null;} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + for (var i = start, result = []; i < end; ++i) + result.push(new Line(text[i], spansFor(i), estimateHeight)); + return result; + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) doc.remove(from.line, nlines); + if (added.length) doc.insert(from.line, added); + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added = linesFor(1, text.length - 1); + added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added = linesFor(1, text.length - 1); + if (nlines > 1) doc.remove(from.line + 1, nlines - 1); + doc.insert(from.line + 1, added); + } + + signalLater(doc, "change", doc, change); + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + for (var i = 0, height = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length; }, + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) lines[i].parent = this; + }, + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + if (op(this.lines[at])) return true; + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size; }, + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) break; + at = 0; + } else at -= sz; + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines); + }, + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + while (child.lines.length > 50) { + var spilled = child.lines.splice(child.lines.length - 25, 25); + var newleaf = new LeafChunk(spilled); + child.height -= newleaf.height; + this.children.splice(i + 1, 0, newleaf); + newleaf.parent = this; + } + this.maybeSpill(); + } + break; + } + at -= sz; + } + }, + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) return; + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10); + me.parent.maybeSpill(); + }, + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) return true; + if ((n -= used) == 0) break; + at = 0; + } else at -= sz; + } + } + }; + + var nextDocId = 0; + var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) { + if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep); + if (firstLine == null) firstLine = 0; + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.frontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + + if (typeof text == "string") text = this.splitLines(text); + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) this.iterN(from - this.first, to - from, op); + else this.iterN(this.first, this.first + this.size, from); + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) height += lines[i].height; + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) return lines; + return lines.join(lineSep || this.lineSeparator()); + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + setSelection(this, simpleSelection(top)); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) return lines; + return lines.join(lineSep || this.lineSeparator()); + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, + + getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, + getLineNumber: function(line) {return lineNo(line);}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") line = getLine(this, line); + return visualLine(line); + }, + + lineCount: function() {return this.size;}, + firstLine: function() {return this.first;}, + lastLine: function() {return this.first + this.size - 1;}, + + clipPos: function(pos) {return clipPos(this, pos);}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") pos = range.head; + else if (start == "anchor") pos = range.anchor; + else if (start == "end" || start == "to" || start === false) pos = range.to(); + else pos = range.from(); + return pos; + }, + listSelections: function() { return this.sel.ranges; }, + somethingSelected: function() {return this.sel.somethingSelected();}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads, options)); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + extendSelections(this, map(this.sel.ranges, f), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) return; + for (var i = 0, out = []; i < ranges.length; i++) + out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head)); + if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex); + setSelection(this, normalizeSelection(out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) return lines; + else return lines.join(lineSep || this.lineSeparator()); + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()); + parts[i] = sel; + } + return parts; + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + dup[i] = code; + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i = changes.length - 1; i >= 0; i--) + makeChange(this, changes[i]); + if (newSel) setSelectionReplaceHistory(this, newSel); + else if (this.cm) ensureCursorVisible(this.cm); + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend;}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done; + for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone; + return {undo: done, redo: undone}; + }, + clearHistory: function() {this.history = new History(this.history.maxGeneration);}, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; + return this.history.generation; + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration); + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)}; + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history.maxGeneration); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) line[prop] = cls; + else if (classTest(cls).test(line[prop])) return false; + else line[prop] += " " + cls; + return true; + }); + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) return false; + else if (cls == null) line[prop] = null; + else { + var found = cur.match(classTest(cls)); + if (!found) return false; + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true; + }); + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options); + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, "range"); + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark"); + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + markers.push(span.marker.parent || span.marker); + } + return markers; + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function(line) { + var spans = line.markedSpans; + if (spans) for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(lineNo == from.line && from.ch > span.to || + span.from == null && lineNo != from.line|| + lineNo == to.line && span.from > to.ch) && + (!filter || filter(span.marker))) + found.push(span.marker.parent || span.marker); + } + ++lineNo; + }); + return found; + }, + getAllMarks: function() { + var markers = []; + this.iter(function(line) { + var sps = line.markedSpans; + if (sps) for (var i = 0; i < sps.length; ++i) + if (sps[i].from != null) markers.push(sps[i].marker); + }); + return markers; + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first; + this.iter(function(line) { + var sz = line.text.length + 1; + if (sz > off) { ch = off; return true; } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)); + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) return 0; + this.iter(this.first, coords.line, function (line) { + index += line.text.length + 1; + }); + return index; + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc; + }, + + linkedDoc: function(options) { + if (!options) options = {}; + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) from = options.from; + if (options.to != null && options.to < to) to = options.to; + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep); + if (options.sharedHist) copy.history = this.history; + (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy; + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) other = other.doc; + if (this.linked) for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) continue; + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break; + } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode;}, + getEditor: function() {return this.cm;}, + + splitLines: function(str) { + if (this.lineSep) return str.split(this.lineSep); + return splitLinesAuto(str); + }, + lineSeparator: function() { return this.lineSep || "\n"; } + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments);}; + })(Doc.prototype[prop]); + + eventMixin(Doc); + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) continue; + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) continue; + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) throw new Error("This document is already in use."); + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + if (!cm.options.lineWrapping) findMaxLine(cm); + cm.options.mode = doc.modeOption; + regChange(cm); + } + + // LINE UTILITIES + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document."); + for (var chunk = doc; !chunk.lines;) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break; } + n -= sz; + } + } + return chunk.lines[n]; + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function(line) { + var text = line.text; + if (n == end.line) text = text.slice(0, end.ch); + if (n == start.line) text = text.slice(start.ch); + out.push(text); + ++n; + }); + return out; + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function(line) { out.push(line.text); }); + return out; + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) for (var n = line; n; n = n.parent) n.height += diff; + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) return null; + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) break; + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first; + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i = 0; i < chunk.children.length; ++i) { + var child = chunk.children[i], ch = child.height; + if (h < ch) { chunk = child; continue outer; } + h -= ch; + n += child.chunkSize(); + } + return n; + } while (!chunk.lines); + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) break; + h -= lh; + } + return n + i; + } + + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) break; + else h += line.height; + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i = 0; i < p.children.length; ++i) { + var cur = p.children[i]; + if (cur == chunk) break; + else h += cur.height; + } + } + return h; + } + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line) { + var order = line.order; + if (order == null) order = line.order = bidiOrdering(line.text); + return order; + } + + // HISTORY + + function History(startGen) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = startGen || 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true); + return histChange; + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) array.pop(); + else break; + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done); + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done); + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done); + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, ore are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + var last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + pushSelectionToHistory(doc.sel, hist.done); + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) hist.done.shift(); + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) signal(doc, "historyAdded"); + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500); + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + hist.done[hist.done.length - 1] = sel; + else + pushSelectionToHistory(sel, hist.done); + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + clearSelectionEvents(hist.undone); + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + dest.push(sel); + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) { + if (line.markedSpans) + (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) return null; + for (var i = 0, out; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } + else if (out) out.push(spans[i]); + } + return !out ? spans : out.length ? out : null; + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) return null; + for (var i = 0, nw = []; i < change.text.length; ++i) + nw.push(removeClearedSpans(found[i])); + return nw; + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + for (var i = 0, copy = []; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue; + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m; + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } + } + } + return copy; + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue; + } + for (var j = 0; j < sub.changes.length; ++j) { + var cur = sub.changes[j]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break; + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // EVENT UTILITIES + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + var e_preventDefault = CodeMirror.e_preventDefault = function(e) { + if (e.preventDefault) e.preventDefault(); + else e.returnValue = false; + }; + var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) { + if (e.stopPropagation) e.stopPropagation(); + else e.cancelBubble = true; + }; + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false; + } + var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);}; + + function e_target(e) {return e.target || e.srcElement;} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) b = 1; + else if (e.button & 2) b = 3; + else if (e.button & 4) b = 2; + } + if (mac && e.ctrlKey && b == 1) b = 3; + return b; + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var on = CodeMirror.on = function(emitter, type, f) { + if (emitter.addEventListener) + emitter.addEventListener(type, f, false); + else if (emitter.attachEvent) + emitter.attachEvent("on" + type, f); + else { + var map = emitter._handlers || (emitter._handlers = {}); + var arr = map[type] || (map[type] = []); + arr.push(f); + } + }; + + var off = CodeMirror.off = function(emitter, type, f) { + if (emitter.removeEventListener) + emitter.removeEventListener(type, f, false); + else if (emitter.detachEvent) + emitter.detachEvent("on" + type, f); + else { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + for (var i = 0; i < arr.length; ++i) + if (arr[i] == f) { arr.splice(i, 1); break; } + } + }; + + var signal = CodeMirror.signal = function(emitter, type /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); + }; + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + function bnd(f) {return function(){f.apply(null, args);};}; + for (var i = 0; i < arr.length; ++i) + list.push(bnd(arr[i])); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) delayed[i](); + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore; + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) return; + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) + set.push(arr[i]); + } + + function hasHandler(emitter, type) { + var arr = emitter._handlers && emitter._handlers[type]; + return arr && arr.length > 0; + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // MISC UTILITIES + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 30; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + function Delayed() {this.id = null;} + Delayed.prototype.set = function(ms, f) { + clearTimeout(this.id); + this.id = setTimeout(f, ms); + }; + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) end = string.length; + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + return n + (end - i); + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + }; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) nextTab = string.length; + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + return pos + Math.min(skipped, goal - col); + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) return pos; + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + spaceStrs.push(lst(spaceStrs) + " "); + return spaceStrs[n]; + } + + function lst(arr) { return arr[arr.length-1]; } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; + else if (ie) // Suppress mysterious IE10 errors + selectInput = function(node) { try { node.select(); } catch(_e) {} }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + if (array[i] == elt) return i; + return -1; + } + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); + return out; + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) copyObj(props, inst); + return inst; + }; + + function copyObj(obj, target, overwrite) { + if (!target) target = {}; + for (var prop in obj) + if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + target[prop] = obj[prop]; + return target; + } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args);}; + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + var isWordCharBasic = CodeMirror.isWordChar = function(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); + }; + function isWordChar(ch, helper) { + if (!helper) return isWordCharBasic(ch); + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true; + return helper.test(ch); + } + + function isEmpty(obj) { + for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; + return true; + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); } + + // DOM UTILITIES + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) e.className = className; + if (style) e.style.cssText = style; + if (typeof content == "string") e.appendChild(document.createTextNode(content)); + else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); + return e; + } + + var range; + if (document.createRange) range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r; + }; + else range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r; } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r; + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + e.removeChild(e.firstChild); + return e; + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e); + } + + var contains = CodeMirror.contains = function(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + child = child.parentNode; + if (parent.contains) + return parent.contains(child); + do { + if (child.nodeType == 11) child = child.host; + if (child == parent) return true; + } while (child = child.parentNode); + }; + + function activeElt() { + var activeElement = document.activeElement; + while (activeElement && activeElement.root && activeElement.root.activeElement) + activeElement = activeElement.root.activeElement; + return activeElement; + } + // Older versions of IE throws unspecified error when touching + // document.activeElement in some cases (during loading, in iframe) + if (ie && ie_version < 11) activeElt = function() { + try { return document.activeElement; } + catch(e) { return document.body; } + }; + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); } + var rmClass = CodeMirror.rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + var addClass = CodeMirror.addClass = function(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls; + }; + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i]; + return b; + } + + // WINDOW-WIDE EVENTS + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.body.getElementsByClassName) return; + var byClass = document.body.getElementsByClassName("CodeMirror"); + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) f(cm); + } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) return; + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function() { + if (resizeTimer == null) resizeTimer = setTimeout(function() { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function() { + forEachCodeMirror(onBlur); + }); + } + + // FEATURE DETECTION + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) return false; + var div = elt('div'); + return "draggable" in div || "dragDrop" in div; + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node; + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) return badBidiRects; + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780) + var r1 = range(txt, 1, 2).getBoundingClientRect(); + return badBidiRects = (r1.right - r0.right < 3); + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) nl = string.length; + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result; + } : function(string){return string.split(/\r\n?|\n/);}; + + var hasSelection = window.getSelection ? function(te) { + try { return te.selectionStart != te.selectionEnd; } + catch(e) { return false; } + } : function(te) { + try {var range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) return false; + return range.compareEndPoints("StartToEnd", range) != 0; + }; + + var hasCopyEvent = (function() { + var e = elt("div"); + if ("oncopy" in e) return true; + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function"; + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) return badZoomedRects; + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1; + } + + // KEY NAMES + + var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 107: "=", 109: "-", 127: "Delete", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"}; + CodeMirror.keyNames = keyNames; + (function() { + // Number keys + for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i); + // Alphabetic keys + for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); + // Function keys + for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; + })(); + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) return f(from, to, "ltr"); + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); + found = true; + } + } + if (!found) f(from, to, "ltr"); + } + + function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } + function bidiRight(part) { return part.level % 2 ? part.from : part.to; } + + function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; } + function lineRight(line) { + var order = getOrder(line); + if (!order) return line.text.length; + return bidiRight(lst(order)); + } + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) lineN = lineNo(visual); + var order = getOrder(visual); + var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); + return Pos(lineN, ch); + } + function lineEnd(cm, lineN) { + var merged, line = getLine(cm.doc, lineN); + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line; + lineN = null; + } + var order = getOrder(line); + var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); + return Pos(lineN == null ? lineNo(line) : lineN, ch); + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(0, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS); + } + return start; + } + + function compareBidiLevel(order, a, b) { + var linedir = order[0].level; + if (a == linedir) return true; + if (b == linedir) return false; + return a < b; + } + var bidiOther; + function getBidiPartAt(order, pos) { + bidiOther = null; + for (var i = 0, found; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < pos && cur.to > pos) return i; + if ((cur.from == pos || cur.to == pos)) { + if (found == null) { + found = i; + } else if (compareBidiLevel(order, cur.level, order[found].level)) { + if (cur.from != cur.to) bidiOther = found; + return i; + } else { + if (cur.from != cur.to) bidiOther = i; + return found; + } + } + } + return found; + } + + function moveInLine(line, pos, dir, byUnit) { + if (!byUnit) return pos + dir; + do pos += dir; + while (pos > 0 && isExtendingChar(line.text.charAt(pos))); + return pos; + } + + // This is needed in order to move 'visually' through bi-directional + // text -- i.e., pressing left should make the cursor go left, even + // when in RTL text. The tricky part is the 'jumps', where RTL and + // LTR text touch each other. This often requires the cursor offset + // to move more than one unit, in order to visually move one unit. + function moveVisually(line, start, dir, byUnit) { + var bidi = getOrder(line); + if (!bidi) return moveLogically(line, start, dir, byUnit); + var pos = getBidiPartAt(bidi, start), part = bidi[pos]; + var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit); + + for (;;) { + if (target > part.from && target < part.to) return target; + if (target == part.from || target == part.to) { + if (getBidiPartAt(bidi, target) == pos) return target; + part = bidi[pos += dir]; + return (dir > 0) == part.level % 2 ? part.to : part.from; + } else { + part = bidi[pos += dir]; + if (!part) return null; + if ((dir > 0) == part.level % 2) + target = moveInLine(line, part.to, -1, byUnit); + else + target = moveInLine(line, part.from, 1, byUnit); + } + } + } + + function moveLogically(line, start, dir, byUnit) { + var target = start + dir; + if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir; + return target < 0 || target > line.text.length ? null : target; + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6ff + var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm"; + function charType(code) { + if (code <= 0xf7) return lowTypes.charAt(code); + else if (0x590 <= code && code <= 0x5f4) return "R"; + else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600); + else if (0x6ee <= code && code <= 0x8ac) return "r"; + else if (0x2000 <= code && code <= 0x200b) return "w"; + else if (code == 0x200c) return "b"; + else return "L"; + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + // Browsers seem to always treat the boundaries of block elements as being L. + var outerType = "L"; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str) { + if (!bidiRE.test(str)) return false; + var len = str.length, types = []; + for (var i = 0, type; i < len; ++i) + types.push(type = charType(str.charCodeAt(i))); + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i = 0, prev = outerType; i < len; ++i) { + var type = types[i]; + if (type == "m") types[i] = prev; + else prev = type; + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i = 0, cur = outerType; i < len; ++i) { + var type = types[i]; + if (type == "1" && cur == "r") types[i] = "n"; + else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i = 1, prev = types[0]; i < len - 1; ++i) { + var type = types[i]; + if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"; + else if (type == "," && prev == types[i+1] && + (prev == "1" || prev == "n")) types[i] = prev; + prev = type; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i = 0; i < len; ++i) { + var type = types[i]; + if (type == ",") types[i] = "N"; + else if (type == "%") { + for (var end = i + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i = 0, cur = outerType; i < len; ++i) { + var type = types[i]; + if (cur == "L" && type == "1") types[i] = "L"; + else if (isStrong.test(type)) cur = type; + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i = 0; i < len; ++i) { + if (isNeutral.test(types[i])) { + for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} + var before = (i ? types[i-1] : outerType) == "L"; + var after = (end < len ? types[end] : outerType) == "L"; + var replace = before || after ? "L" : "R"; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i = 0; i < len;) { + if (countsAsLeft.test(types[i])) { + var start = i; + for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} + order.push(new BidiSpan(0, start, i)); + } else { + var pos = i, at = order.length; + for (++i; i < len && types[i] != "L"; ++i) {} + for (var j = pos; j < i;) { + if (countsAsNum.test(types[j])) { + if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j)); + var nstart = j; + for (++j; j < i && countsAsNum.test(types[j]); ++j) {} + order.splice(at, 0, new BidiSpan(2, nstart, j)); + pos = j; + } else ++j; + } + if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)); + } + } + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + if (order[0].level == 2) + order.unshift(new BidiSpan(1, order[0].to, order[0].to)); + if (order[0].level != lst(order).level) + order.push(new BidiSpan(order[0].level, len, len)); + + return order; + }; + })(); + + // THE END + + CodeMirror.version = "5.5.0"; + + return CodeMirror; +}); diff --git a/public/js/d3.layout.cloud.js b/public/js/d3.layout.cloud.js new file mode 100644 index 00000000..8165a417 --- /dev/null +++ b/public/js/d3.layout.cloud.js @@ -0,0 +1,505 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g=(g.d3||(g.d3 = {}));g=(g.layout||(g.layout = {}));g.cloud = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 5, + ch = 1 << 11; + +module.exports = function() { + var size = [256, 256], + text = cloudText, + font = cloudFont, + fontSize = cloudFontSize, + fontStyle = cloudFontNormal, + fontWeight = cloudFontNormal, + rotate = cloudRotate, + padding = cloudPadding, + spiral = archimedeanSpiral, + words = [], + timeInterval = Infinity, + event = dispatch("word", "end"), + timer = null, + random = Math.random, + cloud = {}, + canvas = cloudCanvas; + + cloud.canvas = function(_) { + return arguments.length ? (canvas = functor(_), cloud) : canvas; + }; + + cloud.start = function() { + var contextAndRatio = getContext(canvas()), + board = zeroArray((size[0] >> 5) * size[1]), + bounds = null, + n = words.length, + i = -1, + tags = [], + data = words.map(function(d, i) { + d.text = text.call(this, d, i); + d.font = font.call(this, d, i); + d.style = fontStyle.call(this, d, i); + d.weight = fontWeight.call(this, d, i); + d.rotate = rotate.call(this, d, i); + d.size = ~~fontSize.call(this, d, i); + d.padding = padding.call(this, d, i); + return d; + }).sort(function(a, b) { return b.size - a.size; }); + + if (timer) clearInterval(timer); + timer = setInterval(step, 0); + step(); + + return cloud; + + function step() { + var start = Date.now(); + while (Date.now() - start < timeInterval && ++i < n && timer) { + var d = data[i]; + d.x = (size[0] * (random() + .5)) >> 1; + d.y = (size[1] * (random() + .5)) >> 1; + cloudSprite(contextAndRatio, d, data, i); + if (d.hasText && place(board, d, bounds)) { + tags.push(d); + event.word(d); + if (bounds) cloudBounds(bounds, d); + else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}]; + // Temporary hack + d.x -= size[0] >> 1; + d.y -= size[1] >> 1; + } + } + if (i >= n) { + cloud.stop(); + event.end(tags, bounds); + } + } + } + + cloud.stop = function() { + if (timer) { + clearInterval(timer); + timer = null; + } + return cloud; + }; + + function getContext(canvas) { + canvas.width = canvas.height = 1; + var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2); + canvas.width = (cw << 5) / ratio; + canvas.height = ch / ratio; + + var context = canvas.getContext("2d"); + context.fillStyle = context.strokeStyle = "red"; + context.textAlign = "center"; + + return {context: context, ratio: ratio}; + } + + function place(board, tag, bounds) { + var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}], + startX = tag.x, + startY = tag.y, + maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]), + s = spiral(size), + dt = random() < .5 ? 1 : -1, + t = -dt, + dxdy, + dx, + dy; + + while (dxdy = s(t += dt)) { + dx = ~~dxdy[0]; + dy = ~~dxdy[1]; + + if (Math.min(Math.abs(dx), Math.abs(dy)) >= maxDelta) break; + + tag.x = startX + dx; + tag.y = startY + dy; + + if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || + tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue; + // TODO only check for collisions within current bounds. + if (!bounds || !cloudCollide(tag, board, size[0])) { + if (!bounds || collideRects(tag, bounds)) { + var sprite = tag.sprite, + w = tag.width >> 5, + sw = size[0] >> 5, + lx = tag.x - (w << 4), + sx = lx & 0x7f, + msx = 32 - sx, + h = tag.y1 - tag.y0, + x = (tag.y + tag.y0) * sw + (lx >> 5), + last; + for (var j = 0; j < h; j++) { + last = 0; + for (var i = 0; i <= w; i++) { + board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0); + } + x += sw; + } + delete tag.sprite; + return true; + } + } + } + return false; + } + + cloud.timeInterval = function(_) { + return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval; + }; + + cloud.words = function(_) { + return arguments.length ? (words = _, cloud) : words; + }; + + cloud.size = function(_) { + return arguments.length ? (size = [+_[0], +_[1]], cloud) : size; + }; + + cloud.font = function(_) { + return arguments.length ? (font = functor(_), cloud) : font; + }; + + cloud.fontStyle = function(_) { + return arguments.length ? (fontStyle = functor(_), cloud) : fontStyle; + }; + + cloud.fontWeight = function(_) { + return arguments.length ? (fontWeight = functor(_), cloud) : fontWeight; + }; + + cloud.rotate = function(_) { + return arguments.length ? (rotate = functor(_), cloud) : rotate; + }; + + cloud.text = function(_) { + return arguments.length ? (text = functor(_), cloud) : text; + }; + + cloud.spiral = function(_) { + return arguments.length ? (spiral = spirals[_] || _, cloud) : spiral; + }; + + cloud.fontSize = function(_) { + return arguments.length ? (fontSize = functor(_), cloud) : fontSize; + }; + + cloud.padding = function(_) { + return arguments.length ? (padding = functor(_), cloud) : padding; + }; + + cloud.random = function(_) { + return arguments.length ? (random = _, cloud) : random; + }; + + cloud.on = function() { + var value = event.on.apply(event, arguments); + return value === event ? cloud : value; + }; + + return cloud; +}; + +function cloudText(d) { + return d.text; +} + +function cloudFont() { + return "serif"; +} + +function cloudFontNormal() { + return "normal"; +} + +function cloudFontSize(d) { + return Math.sqrt(d.value); +} + +function cloudRotate() { + return (~~(Math.random() * 6) - 3) * 30; +} + +function cloudPadding() { + return 1; +} + +// Fetches a monochrome sprite bitmap for the specified text. +// Load in batches for speed. +function cloudSprite(contextAndRatio, d, data, di) { + if (d.sprite) return; + var c = contextAndRatio.context, + ratio = contextAndRatio.ratio; + + c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio); + var x = 0, + y = 0, + maxh = 0, + n = data.length; + --di; + while (++di < n) { + d = data[di]; + c.save(); + c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font; + var w = c.measureText(d.text + "m").width * ratio, + h = d.size << 1; + if (d.rotate) { + var sr = Math.sin(d.rotate * cloudRadians), + cr = Math.cos(d.rotate * cloudRadians), + wcr = w * cr, + wsr = w * sr, + hcr = h * cr, + hsr = h * sr; + w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5; + h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr)); + } else { + w = (w + 0x1f) >> 5 << 5; + } + if (h > maxh) maxh = h; + if (x + w >= (cw << 5)) { + x = 0; + y += maxh; + maxh = 0; + } + if (y + h >= ch) break; + c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio); + if (d.rotate) c.rotate(d.rotate * cloudRadians); + c.fillText(d.text, 0, 0); + if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0); + c.restore(); + d.width = w; + d.height = h; + d.xoff = x; + d.yoff = y; + d.x1 = w >> 1; + d.y1 = h >> 1; + d.x0 = -d.x1; + d.y0 = -d.y1; + d.hasText = true; + x += w; + } + var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data, + sprite = []; + while (--di >= 0) { + d = data[di]; + if (!d.hasText) continue; + var w = d.width, + w32 = w >> 5, + h = d.y1 - d.y0; + // Zero the buffer + for (var i = 0; i < h * w32; i++) sprite[i] = 0; + x = d.xoff; + if (x == null) return; + y = d.yoff; + var seen = 0, + seenRow = -1; + for (var j = 0; j < h; j++) { + for (var i = 0; i < w; i++) { + var k = w32 * j + (i >> 5), + m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0; + sprite[k] |= m; + seen |= m; + } + if (seen) seenRow = j; + else { + d.y0++; + h--; + j--; + y++; + } + } + d.y1 = d.y0 + seenRow; + d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32); + } +} + +// Use mask-based collision detection. +function cloudCollide(tag, board, sw) { + sw >>= 5; + var sprite = tag.sprite, + w = tag.width >> 5, + lx = tag.x - (w << 4), + sx = lx & 0x7f, + msx = 32 - sx, + h = tag.y1 - tag.y0, + x = (tag.y + tag.y0) * sw + (lx >> 5), + last; + for (var j = 0; j < h; j++) { + last = 0; + for (var i = 0; i <= w; i++) { + if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0)) + & board[x + i]) return true; + } + x += sw; + } + return false; +} + +function cloudBounds(bounds, d) { + var b0 = bounds[0], + b1 = bounds[1]; + if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0; + if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0; + if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1; + if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1; +} + +function collideRects(a, b) { + return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y; +} + +function archimedeanSpiral(size) { + var e = size[0] / size[1]; + return function(t) { + return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)]; + }; +} + +function rectangularSpiral(size) { + var dy = 4, + dx = dy * size[0] / size[1], + x = 0, + y = 0; + return function(t) { + var sign = t < 0 ? -1 : 1; + // See triangular numbers: T_n = n * (n + 1) / 2. + switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) { + case 0: x += dx; break; + case 1: y += dy; break; + case 2: x -= dx; break; + default: y -= dy; break; + } + return [x, y]; + }; +} + +// TODO reuse arrays? +function zeroArray(n) { + var a = [], + i = -1; + while (++i < n) a[i] = 0; + return a; +} + +function cloudCanvas() { + return document.createElement("canvas"); +} + +function functor(d) { + return typeof d === "function" ? d : function() { return d; }; +} + +var spirals = { + archimedean: archimedeanSpiral, + rectangular: rectangularSpiral +}; + +},{"d3-dispatch":2}],2:[function(require,module,exports){ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + factory((global.dispatch = {})); +}(this, function (exports) { 'use strict'; + + function Dispatch(types) { + var i = -1, + n = types.length, + callbacksByType = {}, + callbackByName = {}, + type, + that = this; + + that.on = function(type, callback) { + type = parseType(type); + + // Return the current callback, if any. + if (arguments.length < 2) { + return (callback = callbackByName[type.name]) && callback.value; + } + + // If a type was specified… + if (type.type) { + var callbacks = callbacksByType[type.type], + callback0 = callbackByName[type.name], + i; + + // Remove the current callback, if any, using copy-on-remove. + if (callback0) { + callback0.value = null; + i = callbacks.indexOf(callback0); + callbacksByType[type.type] = callbacks = callbacks.slice(0, i).concat(callbacks.slice(i + 1)); + delete callbackByName[type.name]; + } + + // Add the new callback, if any. + if (callback) { + callback = {value: callback}; + callbackByName[type.name] = callback; + callbacks.push(callback); + } + } + + // Otherwise, if a null callback was specified, remove all callbacks with the given name. + else if (callback == null) { + for (var otherType in callbacksByType) { + if (callback = callbackByName[otherType + type.name]) { + callback.value = null; + var callbacks = callbacksByType[otherType], i = callbacks.indexOf(callback); + callbacksByType[otherType] = callbacks.slice(0, i).concat(callbacks.slice(i + 1)); + delete callbackByName[callback.name]; + } + } + } + + return that; + }; + + while (++i < n) { + type = types[i] + ""; + if (!type || (type in that)) throw new Error("illegal or duplicate type: " + type); + callbacksByType[type] = []; + that[type] = applier(type); + } + + function parseType(type) { + var i = (type += "").indexOf("."), name = type; + if (i >= 0) type = type.slice(0, i); else name += "."; + if (type && !callbacksByType.hasOwnProperty(type)) throw new Error("unknown type: " + type); + return {type: type, name: name}; + } + + function applier(type) { + return function() { + var callbacks = callbacksByType[type], // Defensive reference; copy-on-remove. + callback, + callbackValue, + i = -1, + n = callbacks.length; + + while (++i < n) { + if (callbackValue = (callback = callbacks[i]).value) { + callbackValue.apply(this, arguments); + } + } + + return that; + }; + } + } + + function dispatch() { + return new Dispatch(arguments); + } + + dispatch.prototype = Dispatch.prototype; // allow instanceof + + exports.dispatch = dispatch; + +})); +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/public/js/d3.min.js b/public/js/d3.min.js new file mode 100644 index 00000000..e4b408f0 --- /dev/null +++ b/public/js/d3.min.js @@ -0,0 +1,5 @@ +!function(){function n(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function t(n){return null!=n&&!isNaN(n)}function r(n){return{left:function(t,r,e,u){for(arguments.length<3&&(e=0),arguments.length<4&&(u=t.length);u>e;){var i=e+u>>>1;n(t[i],r)<0?e=i+1:u=i}return e},right:function(t,r,e,u){for(arguments.length<3&&(e=0),arguments.length<4&&(u=t.length);u>e;){var i=e+u>>>1;n(t[i],r)>0?u=i:e=i+1}return e}}}function e(n){return n.length}function u(n){for(var t=1;n*t%1;)t*=10;return t}function i(n,t){try{for(var r in t)Object.defineProperty(n.prototype,r,{value:t[r],enumerable:!1})}catch(e){n.prototype=t}}function o(){}function a(n){return _a+n in this}function c(n){return n=_a+n,n in this&&delete this[n]}function l(){var n=[];return this.forEach(function(t){n.push(t)}),n}function s(){var n=0;for(var t in this)t.charCodeAt(0)===ba&&++n;return n}function f(){for(var n in this)if(n.charCodeAt(0)===ba)return!1;return!0}function h(){}function g(n,t,r){return function(){var e=r.apply(t,arguments);return e===t?n:e}}function p(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var r=0,e=wa.length;e>r;++r){var u=wa[r]+t;if(u in n)return u}}function v(){}function d(){}function m(n){function t(){for(var t,e=r,u=-1,i=e.length;++ur;r++)for(var u,i=n[r],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,r);return n}function U(n){return ka(n,Ta),n}function j(n){var t,r;return function(e,u,i){var o,a=n[i].update,c=a.length;for(i!=r&&(r=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.substring(0,a));var l=Ra.get(n);return l&&(n=l,c=I),a?t?u:e:t?v:i}function O(n,t){return function(r){var e=oa.event;oa.event=r,t[0]=this.__data__;try{n.apply(this,t)}finally{oa.event=e}}}function I(n,t){var r=O(n,t);return function(n){var t=this,e=n.relatedTarget;e&&(e===t||8&e.compareDocumentPosition(t))||r.call(t,n)}}function Y(){var n=".dragsuppress-"+ ++Pa,t="click"+n,r=oa.select(fa).on("touchmove"+n,y).on("dragstart"+n,y).on("selectstart"+n,y);if(Da){var e=sa.style,u=e[Da];e[Da]="none"}return function(i){function o(){r.on(t,null)}r.on(n,null),Da&&(e[Da]=u),i&&(r.on(t,function(){y(),o()},!0),setTimeout(o,0))}}function Z(n,t){t.changedTouches&&(t=t.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var e=r.createSVGPoint();return e.x=t.clientX,e.y=t.clientY,e=e.matrixTransform(n.getScreenCTM().inverse()),[e.x,e.y]}var u=n.getBoundingClientRect();return[t.clientX-u.left-n.clientLeft,t.clientY-u.top-n.clientTop]}function V(){return oa.event.changedTouches[0].identifier}function $(){return oa.event.target}function X(){return fa}function B(n){return n>0?1:0>n?-1:0}function J(n,t,r){return(t[0]-n[0])*(r[1]-n[1])-(t[1]-n[1])*(r[0]-n[0])}function W(n){return n>1?0:-1>n?Ua:Math.acos(n)}function G(n){return n>1?Ha:-1>n?-Ha:Math.asin(n)}function K(n){return((n=Math.exp(n))-1/n)/2}function Q(n){return((n=Math.exp(n))+1/n)/2}function nt(n){return((n=Math.exp(2*n))-1)/(n+1)}function tt(n){return(n=Math.sin(n/2))*n}function rt(){}function et(n,t,r){return new ut(n,t,r)}function ut(n,t,r){this.h=n,this.s=t,this.l=r}function it(n,t,r){function e(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*e(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,r=0>r?0:r>1?1:r,o=.5>=r?r*(1+t):r+t-r*t,i=2*r-o,yt(u(n+120),u(n),u(n-120))}function ot(n,t,r){return new at(n,t,r)}function at(n,t,r){this.h=n,this.c=t,this.l=r}function ct(n,t,r){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),lt(r,Math.cos(n*=Ia)*t,Math.sin(n)*t)}function lt(n,t,r){return new st(n,t,r)}function st(n,t,r){this.l=n,this.a=t,this.b=r}function ft(n,t,r){var e=(n+16)/116,u=e+t/500,i=e-r/200;return u=gt(u)*Qa,e=gt(e)*nc,i=gt(i)*tc,yt(vt(3.2404542*u-1.5371385*e-.4985314*i),vt(-.969266*u+1.8760108*e+.041556*i),vt(.0556434*u-.2040259*e+1.0572252*i))}function ht(n,t,r){return n>0?ot(Math.atan2(r,t)*Ya,Math.sqrt(t*t+r*r),n):ot(0/0,0/0,n)}function gt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function pt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function vt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function dt(n){return yt(n>>16,255&n>>8,255&n)}function mt(n){return dt(n)+""}function yt(n,t,r){return new xt(n,t,r)}function xt(n,t,r){this.r=n,this.g=t,this.b=r}function Mt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _t(n,t,r){var e,u,i,o=0,a=0,c=0;if(e=/([a-z]+)\((.*)\)/i.exec(n))switch(u=e[2].split(","),e[1]){case"hsl":return r(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=uc.get(n))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.substring(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function bt(n,t,r){var e,u,i=Math.min(n/=255,t/=255,r/=255),o=Math.max(n,t,r),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),e=n==o?(t-r)/a+(r>t?6:0):t==o?(r-n)/a+2:(n-t)/a+4,e*=60):(e=0/0,u=c>0&&1>c?0:e),et(e,u,c)}function wt(n,t,r){n=St(n),t=St(t),r=St(r);var e=pt((.4124564*n+.3575761*t+.1804375*r)/Qa),u=pt((.2126729*n+.7151522*t+.072175*r)/nc),i=pt((.0193339*n+.119192*t+.9503041*r)/tc);return lt(116*u-16,500*(e-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function Nt(n){return n}function At(n){return function(t,r,e){return 2===arguments.length&&"function"==typeof r&&(e=r,r=null),Ct(t,r,n,e)}}function Ct(n,t,r,e){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=r.call(i,c)}catch(e){return o.error.call(i,e),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=oa.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!fa.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=oa.event;oa.event=n;try{o.progress.call(i,c)}finally{oa.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return r=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ca(arguments)))}}),i.send=function(r,e,u){if(2===arguments.length&&"function"==typeof e&&(u=e,e=null),c.open(r,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==e?null:e),i},i.abort=function(){return c.abort(),i},oa.rebind(i,o,"on"),null==e?i:i.get(Lt(e))}function Lt(n){return 1===n.length?function(t,r){n(null==t?r:null)}:n}function qt(){var n=Tt(),t=zt()-n;t>24?(isFinite(t)&&(clearTimeout(cc),cc=setTimeout(qt,t)),ac=0):(ac=1,sc(qt))}function Tt(){var n=Date.now();for(lc=ic;lc;)n>=lc.t&&(lc.f=lc.c(n-lc.t)),lc=lc.n;return n}function zt(){for(var n,t=ic,r=1/0;t;)t.f?t=n?n.n=t.n:ic=t.n:(t.t8?function(n){return n/r}:function(n){return n*r},symbol:n}}function Pt(n){var t=n.decimal,r=n.thousands,e=n.grouping,u=n.currency,i=e?function(n){for(var t=n.length,u=[],i=0,o=e[0];t>0&&o>0;)u.push(n.substring(t-=o,t+o)),o=e[i=(i+1)%e.length];return u.reverse().join(r)}:Nt;return function(n){var r=hc.exec(n),e=r[1]||" ",o=r[2]||">",a=r[3]||"",c=r[4]||"",l=r[5],s=+r[6],f=r[7],h=r[8],g=r[9],p=1,v="",d="",m=!1;switch(h&&(h=+h.substring(1)),(l||"0"===e&&"="===o)&&(l=e="0",o="=",f&&(s-=Math.floor((s-1)/4))),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=gc.get(g)||Ut;var y=l&&f;return function(n){var r=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):a;if(0>p){var c=oa.formatPrefix(n,h);n=c.scale(n),r=c.symbol+d}else n*=p;n=g(n,h);var x=n.lastIndexOf("."),M=0>x?n:n.substring(0,x),_=0>x?"":t+n.substring(x+1);!l&&f&&(M=i(M));var b=v.length+M.length+_.length+(y?0:u.length),w=s>b?new Array(b=s-b+1).join(e):"";return y&&(M=i(w+M)),u+=v,n=M+_,("<"===o?u+n+w:">"===o?w+u+n:"^"===o?w.substring(0,b>>=1)+u+n+w.substring(b):u+(y?n:w+n))+r}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ht(n,t,r){function e(t){var r=n(t),e=i(r,1);return e-t>t-r?r:e}function u(r){return t(r=n(new vc(r-1)),1),r}function i(n,r){return t(n=new vc(+n),r),n}function o(n,e,i){var o=u(n),a=[];if(i>1)for(;e>o;)r(o)%i||a.push(new Date(+o)),t(o,1);else for(;e>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,r){try{vc=jt;var e=new jt;return e._=n,o(e,t,r)}finally{vc=Date}}n.floor=n,n.round=e,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ft(n);return c.floor=c,c.round=Ft(e),c.ceil=Ft(u),c.offset=Ft(i),c.range=a,n}function Ft(n){return function(t,r){try{vc=jt;var e=new jt;return e._=t,n(e,r)._}finally{vc=Date}}}function Ot(n){function t(n){function t(t){for(var r,u,i,o=[],a=-1,c=0;++aa;){if(e>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in mc?t.charAt(a++):o],!i||(e=i(n,r,e))<0)return-1}else if(u!=r.charCodeAt(e++))return-1}return e}function e(n,t,r){b.lastIndex=0;var e=b.exec(t.substring(r));return e?(n.w=w.get(e[0].toLowerCase()),r+e[0].length):-1}function u(n,t,r){M.lastIndex=0;var e=M.exec(t.substring(r));return e?(n.w=_.get(e[0].toLowerCase()),r+e[0].length):-1}function i(n,t,r){E.lastIndex=0;var e=E.exec(t.substring(r));return e?(n.m=N.get(e[0].toLowerCase()),r+e[0].length):-1}function o(n,t,r){S.lastIndex=0;var e=S.exec(t.substring(r));return e?(n.m=k.get(e[0].toLowerCase()),r+e[0].length):-1}function a(n,t,e){return r(n,A.c.toString(),t,e)}function c(n,t,e){return r(n,A.x.toString(),t,e)}function l(n,t,e){return r(n,A.X.toString(),t,e)}function s(n,t,r){var e=x.get(t.substring(r,r+=2).toLowerCase());return null==e?-1:(n.p=e,r)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function r(n){try{vc=jt;var t=new vc;return t._=n,e(t)}finally{vc=Date}}var e=t(n);return r.parse=function(n){try{vc=jt;var t=e.parse(n);return t&&t._}finally{vc=Date}},r.toString=e.toString,r},t.multi=t.utc.multi=ar;var x=oa.map(),M=Yt(v),_=Zt(v),b=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),N=Zt(y);p.forEach(function(n,t){x.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+pc.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(pc.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(pc.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ir,"%":function(){return"%"}},C={a:e,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:tr,I:tr,j:nr,L:ur,m:Kt,M:rr,p:s,S:er,U:$t,w:Vt,W:Xt,x:c,X:l,y:Jt,Y:Bt,Z:Wt,"%":or};return t}function It(n,t,r){var e=0>n?"-":"",u=(e?-n:n)+"",i=u.length;return e+(r>i?new Array(r-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(oa.requote).join("|")+")","i")}function Zt(n){for(var t=new o,r=-1,e=n.length;++r68?1900:2e3)}function Kt(n,t,r){yc.lastIndex=0;var e=yc.exec(t.substring(r,r+2));return e?(n.m=e[0]-1,r+e[0].length):-1}function Qt(n,t,r){yc.lastIndex=0;var e=yc.exec(t.substring(r,r+2));return e?(n.d=+e[0],r+e[0].length):-1}function nr(n,t,r){yc.lastIndex=0;var e=yc.exec(t.substring(r,r+3));return e?(n.j=+e[0],r+e[0].length):-1}function tr(n,t,r){yc.lastIndex=0;var e=yc.exec(t.substring(r,r+2));return e?(n.H=+e[0],r+e[0].length):-1}function rr(n,t,r){yc.lastIndex=0;var e=yc.exec(t.substring(r,r+2));return e?(n.M=+e[0],r+e[0].length):-1}function er(n,t,r){yc.lastIndex=0;var e=yc.exec(t.substring(r,r+2));return e?(n.S=+e[0],r+e[0].length):-1}function ur(n,t,r){yc.lastIndex=0;var e=yc.exec(t.substring(r,r+3));return e?(n.L=+e[0],r+e[0].length):-1}function ir(n){var t=n.getTimezoneOffset(),r=t>0?"-":"+",e=~~(Ma(t)/60),u=Ma(t)%60;return r+It(e,"0",2)+It(u,"0",2)}function or(n,t,r){xc.lastIndex=0;var e=xc.exec(t.substring(r,r+1));return e?r+e[0].length:-1}function ar(n){for(var t=n.length,r=-1;++r=0?1:-1,a=o*r,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);kc.add(Math.atan2(h,f)),e=n,u=c,i=l}var t,r,e,u,i;Ec.point=function(o,a){Ec.point=n,e=(t=o)*Ia,u=Math.cos(a=(r=a)*Ia/2+Ua/4),i=Math.sin(a)},Ec.lineEnd=function(){n(t,r)}}function pr(n){var t=n[0],r=n[1],e=Math.cos(r);return[e*Math.cos(t),e*Math.sin(t),Math.sin(r)]}function vr(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function dr(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function mr(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function yr(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function xr(n){var t=Math.sqrt(vr(n,n));n[0]/=t,n[1]/=t,n[2]/=t}function Mr(n,t){var r=t[0]-n[0],e=t[1]-n[1],u=t[2]-n[2];return Oa*Oa>r*r+e*e+u*u}function _r(n){return[Math.atan2(n[1],n[0]),G(n[2])]}function br(n,t){return Ma(n[1]-t[1])Ua?2*Ua-r:r}function Sr(n,t){n*=Ia;var r=Math.cos(t*=Ia);kr(r*Math.cos(n),r*Math.sin(n),Math.sin(t))}function kr(n,t,r){++Nc,Cc+=(n-Cc)/Nc,Lc+=(t-Lc)/Nc,qc+=(r-qc)/Nc}function Er(){function n(n,u){n*=Ia;var i=Math.cos(u*=Ia),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),l=Math.atan2(Math.sqrt((l=r*c-e*a)*l+(l=e*o-t*c)*l+(l=t*a-r*o)*l),t*o+r*a+e*c);Ac+=l,Tc+=l*(t+(t=o)),zc+=l*(r+(r=a)),Rc+=l*(e+(e=c)),kr(t,r,e)}var t,r,e;jc.point=function(u,i){u*=Ia;var o=Math.cos(i*=Ia);t=o*Math.cos(u),r=o*Math.sin(u),e=Math.sin(i),jc.point=n,kr(t,r,e)}}function Nr(){jc.point=Sr}function Ar(){function n(n,t){n*=Ia;var r=Math.cos(t*=Ia),o=r*Math.cos(n),a=r*Math.sin(n),c=Math.sin(t),l=u*c-i*a,s=i*o-e*c,f=e*a-u*o,h=Math.sqrt(l*l+s*s+f*f),g=e*o+u*a+i*c,p=h&&-W(g)/h,v=Math.atan2(h,g);Dc+=p*l,Pc+=p*s,Uc+=p*f,Ac+=v,Tc+=v*(e+(e=o)),zc+=v*(u+(u=a)),Rc+=v*(i+(i=c)),kr(e,u,i)}var t,r,e,u,i;jc.point=function(o,a){t=o,r=a,jc.point=n,o*=Ia;var c=Math.cos(a*=Ia);e=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),kr(e,u,i)},jc.lineEnd=function(){n(t,r),jc.lineEnd=Nr,jc.point=Sr}}function Cr(){return!0}function Lr(n,t,r,e,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,r=n[0],e=n[t];if(Ma(e[0]-r[0])a;++a)u.point((r=n[a])[0],r[1]);return u.lineEnd(),void 0}var c=new Tr(r,n,null,!0),l=new Tr(r,null,c,!1);c.o=l,i.push(c),o.push(l),c=new Tr(e,n,null,!1),l=new Tr(e,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),qr(i),qr(o),i.length){for(var a=0,c=r,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else e(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else e(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function qr(n){if(t=n.length){for(var t,r,e=0,u=n[0];++e1&&2&t&&r.push(r.pop().concat(r.shift())),p.push(r.filter(Rr))}}var p,v,d,m=t(o),y=i.invert(e[0],e[1]),x={point:a,lineStart:l,lineEnd:s,polygonStart:function(){x.point=f,x.lineStart=h,x.lineEnd=g,p=[],v=[],o.polygonStart()},polygonEnd:function(){x.point=a,x.lineStart=l,x.lineEnd=s,p=oa.merge(p);var n=Ur(y,v);p.length?Lr(p,u,n,r,o):n&&(o.lineStart(),r(null,null,1,o),o.lineEnd()),o.polygonEnd(),p=v=null},sphere:function(){o.polygonStart(),o.lineStart(),r(null,null,1,o),o.lineEnd(),o.polygonEnd()}},M=Dr(),_=t(M);return x}}function Rr(n){return n.length>1}function Dr(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,r,e,u){var i=[t,r];arguments.length>2&&(i.index=e,i.t=u),n.push(i)},lineEnd:v,buffer:function(){var r=t;return t=[],n=null,r},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Pr(n,t){return((n=n.x)[0]<0?n[1]-Ha-Fa:Ha-n[1])-((t=t.x)[0]<0?t[1]-Ha-Fa:Ha-t[1])}function Ur(n,t){var r=n[0],e=n[1],u=[Math.sin(r),-Math.cos(r),0],i=0,o=0;kc.reset();for(var a=0,c=t.length;c>a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+Ua/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+Ua/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=_>=0?1:-1,w=b*_,S=w>Ua,k=p*x;if(kc.add(Math.atan2(k*b*Math.sin(w),v*M+k*Math.cos(w))),i+=S?_+b*ja:_,S^h>=r^m>=r){var E=dr(pr(f),pr(n));xr(E);var N=dr(u,E);xr(N);var A=(S^_>=0?-1:1)*G(N[2]);(e>A||e===A&&(E[0]||E[1]))&&(o+=S^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Fa>i||Fa>i&&0>kc)^1&o}function jr(n){var t,r=0/0,e=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Ua-Fa:-Ua,c=Ma(i-r);Ma(c-Ua)0?Ha:-Ha),n.point(u,e),n.lineEnd(),n.lineStart(),n.point(a,e),n.point(i,e),t=0):u!==a&&c>=Ua&&(Ma(r-u)Fa?Math.atan((Math.sin(t)*(i=Math.cos(e))*Math.sin(r)-Math.sin(e)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+e)/2}function Fr(n,t,r,e){var u;if(null==n)u=r*Ha,e.point(-Ua,u),e.point(0,u),e.point(Ua-Fa,u),e.point(Ua-Fa,0),e.point(Ua-Fa,-u),e.point(0,-u),e.point(-Ua,-u),e.point(-Ua,0),e.point(-Ua,u);else if(Ma(n[0]-t[0])>Fa){var i=-Ua,o=Ua-Fa,a=(n[0]Fa}function Ir(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function r(n){var r,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Ua:-Ua),h):0;if(!r&&(l=c=v)&&n.lineStart(),v!==c&&(g=e(r,p),(br(r,g)||br(p,g))&&(p[0]+=Fa,p[1]+=Fa,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=e(p,r),n.point(g[0],g[1])):(g=e(r,p),n.point(g[0],g[1]),n.lineEnd()),r=g;else if(a&&r&&o^v){var m;d&i||!(m=e(p,r,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||r&&br(r,p)||n.point(p[0],p[1]),r=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),r=null},clean:function(){return s|(l&&c)<<1}}}function e(n,t,r){var e=pr(n),u=pr(t),o=[1,0,0],a=dr(e,u),c=vr(a,a),l=a[0],s=c-l*l;if(!s)return!r&&n;var f=i*c/s,h=-i*l/s,g=dr(o,a),p=yr(o,f),v=yr(a,h);mr(p,v);var d=g,m=vr(p,d),y=vr(d,d),x=m*m-y*(vr(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=yr(d,(-m-M)/y);if(mr(_,p),_=_r(_),!r)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var N=S-w,A=Ma(N-Ua)N;if(!A&&k>E&&(b=k,k=E,E=b),C?A?k+E>0^_[1]<(Ma(_[0]-w)Ua^(w<=_[0]&&_[0]<=S)){var L=yr(d,(-m+M)/y);return mr(L,p),[_,_r(L)]}}}function u(t,r){var e=o?n:Ua-n,u=0;return-e>t?u|=1:t>e&&(u|=2),-e>r?u|=4:r>e&&(u|=8),u}var i=Math.cos(n),o=i>0,a=Ma(i)>Fa,c=be(n,6*Ia);return zr(t,r,c,o?[0,-n]:[-Ua,n-Ua],Pr)}function Yr(n,t,r,e){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=r-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=e-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Zr(n,t,r,e){function u(e,u){return Ma(e[0]-n)0?0:3:Ma(e[0]-r)0?2:1:Ma(e[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var r=u(n,1),e=u(t,1);return r!==e?r-e:0===r?t[1]-n[1]:1===r?n[0]-t[0]:2===r?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,r=d.length,e=n[1],u=0;r>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=e?i[1]>e&&J(l,i,n)>0&&++t:i[1]<=e&&J(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:r,s>1?e:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&r>=u&&i>=t&&e>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,_=b=0/0}function g(){v&&(p(y,x),M&&w&&N.rejoin(),v.push(N.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Fc,Math.min(Fc,n)),t=Math.max(-Fc,Math.min(Fc,t));var r=s(n,t);if(d&&m.push([n,t]),S)y=n,x=t,M=r,S=!1,r&&(a.lineStart(),a.point(n,t));else if(r&&w)a.point(n,t);else{var e={a:{x:_,y:b},b:{x:n,y:t}};A(e)?(w||(a.lineStart(),a.point(e.a.x,e.a.y)),a.point(e.b.x,e.b.y),r||a.lineEnd(),k=!1):r&&(a.lineStart(),a.point(n,t),k=!1)}_=n,b=t,w=r}var v,d,m,y,x,M,_,b,w,S,k,E=a,N=Dr(),A=Yr(n,t,r,e),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=N,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=oa.merge(v);var t=c([n,e]),r=k&&t,u=v.length;(r||u)&&(a.polygonStart(),r&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Lr(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Vr(n,t){this.from=n,this.to=t,this.normal=dr(n,t),this.fromNormal=dr(this.normal,n),this.toNormal=dr(this.normal,t)}function $r(n,t){var r=dr(n.normal,t.normal);xr(r);var e=vr(r,n.fromNormal),u=vr(r,n.toNormal),i=vr(r,t.fromNormal),o=vr(r,t.toNormal);return e>-Oa&&Oa>u&&i>-Oa&&Oa>o?r:Oa>e&&u>-Oa&&Oa>i&&o>-Oa?(r[0]=-r[0],r[1]=-r[1],r[2]=-r[2],r):void 0}function Xr(n,t){var r=vr(n,t.fromNormal),e=vr(n,t.toNormal);return n=vr(n,t.normal),Math.abs(n)-Oa&&Oa>e||Oa>r&&e>-Oa)}function Br(n){function t(t,r){return Ur([t,r],n)}function r(n){var r,e,i,o,a,c;return{lineStart:function(){r=null,c=1},point:function(l,s,f){f&&(l=e,s=i);var h=pr([l,s]),g=a;if(r){for(var p=new Vr(r,h),v=[],d=0,m=100;d0;++d){var y=u[d],x=$r(p,y);if(x){if(x===Oc||Mr(x,r)||Mr(x,h)||Mr(x,y.from)||Mr(x,y.to)){var M=1e-4;l=(l+3*Ua+(Math.random()<.5?M:-M))%(2*Ua)-Ua,s=Math.min(Ua/2-1e-4,Math.max(1e-4-Ua/2,s+(Math.random()<.5?M:-M))),p=new Vr(r,h=pr([l,s])),d=-1,--m,v.length=0;continue}var _=_r(x);x.distance=Wr(r,x),x.index=d,x.t=Wr(y.from,x),x[0]=_[0],x[1]=_[1],x.pop(),v.push(x)}}if(v.length){c=0,v.sort(function(n,t){return n.distance-t.distance});for(var d=0;d0;++d){var y=u[d];if(Xr(h,y)){var M=1e-4;l=(l+3*Ua+(Math.random()<.5?M:-M))%(2*Ua)-Ua,s=Math.min(Ua/2-1e-4,Math.max(1e-4-Ua/2,s+(Math.random()<.5?M:-M))),h=pr([l,s]),d=-1,--m}}(o=g=t(e=l,i=s))&&(n.lineStart(),n.point(l,s))}r=h,a=g},lineEnd:function(){a&&n.lineEnd()},clean:function(){return c|(o&&a)<<1}}}function e(t,r,e,i){if(null==t){var o=n.length;n.forEach(function(n,t){n.forEach(function(n){i.point(n[0],n[1])}),o-1>t&&(i.lineEnd(),i.lineStart())})}else if(t.index!==r.index&&null!=t.index&&null!=r.index)for(var a=t.index;a!==r.index;a=(a+e+u.length)%u.length){var c=u[a],l=_r(e>0?c.to:c.from);i.point(l[0],l[1])}}var u=[];return n=n.map(function(n){var t;return n=n.map(function(n,r){var e=pr(n=[n[0]*Ia,n[1]*Ia]);return r&&u.push(new Vr(t,e)),t=e,n}),n.pop(),n}),zr(t,r,e,n[0][0],Jr)}function Jr(n,t){return n=n.x,t=t.x,n.index-t.index||n.t-t.t}function Wr(n,t){var r=dr(n,t);return Math.atan2(Math.sqrt(vr(r,r)),vr(n,t))}function Gr(n,t){function r(r,e){return r=n(r,e),t(r[0],r[1])}return n.invert&&t.invert&&(r.invert=function(r,e){return r=t.invert(r,e),r&&n.invert(r[0],r[1])}),r}function Kr(n){var t=0,r=Ua/3,e=pe(n),u=e(t,r);return u.parallels=function(n){return arguments.length?e(t=n[0]*Ua/180,r=n[1]*Ua/180):[180*(t/Ua),180*(r/Ua)]},u}function Qr(n,t){function r(n,t){var r=Math.sqrt(i-2*u*Math.sin(t))/u;return[r*Math.sin(n*=u),o-r*Math.cos(n)]}var e=Math.sin(n),u=(e+Math.sin(t))/2,i=1+e*(2*u-e),o=Math.sqrt(i)/u;return r.invert=function(n,t){var r=o-t;return[Math.atan2(n,r)/u,G((i-(n*n+r*r)*u*u)/(2*u))]},r}function ne(){function n(n,t){Yc+=u*n-e*t,e=n,u=t}var t,r,e,u;Bc.point=function(i,o){Bc.point=n,t=e=i,r=u=o},Bc.lineEnd=function(){n(t,r)}}function te(n,t){Zc>n&&(Zc=n),n>$c&&($c=n),Vc>t&&(Vc=t),t>Xc&&(Xc=t)}function re(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=r}function r(n,t){o.push("L",n,",",t)}function e(){a.point=n}function u(){o.push("Z")}var i=ee(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:e,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=e,a.point=n},pointRadius:function(n){return i=ee(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function ee(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function ue(n,t){Cc+=n,Lc+=t,++qc}function ie(){function n(n,e){var u=n-t,i=e-r,o=Math.sqrt(u*u+i*i);Tc+=o*(t+n)/2,zc+=o*(r+e)/2,Rc+=o,ue(t=n,r=e) +}var t,r;Wc.point=function(e,u){Wc.point=n,ue(t=e,r=u)}}function oe(){Wc.point=ue}function ae(){function n(n,t){var r=n-e,i=t-u,o=Math.sqrt(r*r+i*i);Tc+=o*(e+n)/2,zc+=o*(u+t)/2,Rc+=o,o=u*n-e*t,Dc+=o*(e+n),Pc+=o*(u+t),Uc+=3*o,ue(e=n,u=t)}var t,r,e,u;Wc.point=function(i,o){Wc.point=n,ue(t=e=i,r=u=o)},Wc.lineEnd=function(){n(t,r)}}function ce(n){function t(t,r){n.moveTo(t,r),n.arc(t,r,o,0,ja)}function r(t,r){n.moveTo(t,r),a.point=e}function e(t,r){n.lineTo(t,r)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=r},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:v};return a}function le(n){function t(n){return(a?e:r)(n)}function r(t){return he(t,function(r,e){r=n(r,e),t.point(r[0],r[1])})}function e(t){function r(r,e){r=n(r,e),t.point(r[0],r[1])}function e(){x=0/0,S.point=i,t.lineStart()}function i(r,e){var i=pr([r,e]),o=n(r,e);u(x,M,y,_,b,w,x=o[0],M=o[1],y=r,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=r,t.lineEnd()}function c(){e(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function s(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:r,lineStart:e,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=e}};return S}function u(t,r,e,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,x=f-r,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=l+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=Ma(Ma(w)-1)i||Ma((y*L+x*q)/M-.5)>.3||o>a*g+c*p+l*v)&&(u(t,r,e,a,c,l,A,C,E,_/=S,b/=S,w,d,m),m.point(A,C),u(A,C,E,_,b,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Ia),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function se(n){var t=le(function(t,r){return n([t*Ya,r*Ya])});return function(n){return ve(t(n))}}function fe(n){this.stream=n}function he(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ge(n){return pe(function(){return n})()}function pe(n){function t(n){return n=a(n[0]*Ia,n[1]*Ia),[n[0]*h+c,l-n[1]*h]}function r(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Ya,n[1]*Ya]}function e(){a=Gr(o=ye(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=le(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Hc,_=Nt,b=null,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=ve(M(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Hc):Ir((b=+n)*Ia),u()):b},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zr(n[0][0],n[0][1],n[1][0],n[1][1]):Nt,u()):S},t.scale=function(n){return arguments.length?(h=+n,e()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],e()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Ia,d=n[1]%360*Ia,e()):[v*Ya,d*Ya]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Ia,y=n[1]%360*Ia,x=n.length>2?n[2]%360*Ia:0,e()):[m*Ya,y*Ya,x*Ya]},oa.rebind(t,f,"precision"),t.clipPolygon=function(n){return arguments.length?(b=null,w=n,M=null==n?Hc:Br(n),e()):w},function(){return i=n.apply(this,arguments),t.invert=i.invert&&r,e()}}function ve(n){return he(n,function(t,r){n.point(t*Ia,r*Ia)})}function de(n,t){return[n,t]}function me(n,t){return[n>Ua?n-ja:-Ua>n?n+ja:n,t]}function ye(n,t,r){return n?t||r?Gr(Me(n),_e(t,r)):Me(n):t||r?_e(t,r):me}function xe(n){return function(t,r){return t+=n,[t>Ua?t-ja:-Ua>t?t+ja:t,r]}}function Me(n){var t=xe(n);return t.invert=xe(-n),t}function _e(n,t){function r(n,t){var r=Math.cos(t),a=Math.cos(n)*r,c=Math.sin(n)*r,l=Math.sin(t),s=l*e+a*u;return[Math.atan2(c*i-s*o,a*e-l*u),G(s*i+c*o)]}var e=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return r.invert=function(n,t){var r=Math.cos(t),a=Math.cos(n)*r,c=Math.sin(n)*r,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*e+s*u),G(s*e-a*u)]},r}function be(n,t){var r=Math.cos(n),e=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=we(r,u),i=we(r,i),(o>0?i>u:u>i)&&(u+=o*ja)):(u=n+o*ja,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=_r([r,-e*Math.cos(s),-e*Math.sin(s)]))[0],l[1])}}function we(n,t){var r=pr(t);r[0]-=n,xr(r);var e=W(-r[1]);return((-r[2]<0?-e:e)+2*Math.PI-Fa)%(2*Math.PI)}function Se(n,t,r){var e=oa.range(n,t-Fa,r).concat(t);return function(n){return e.map(function(t){return[n,t]})}}function ke(n,t,r){var e=oa.range(n,t-Fa,r).concat(t);return function(n){return e.map(function(t){return[t,n]})}}function Ee(n){return n.source}function Ne(n){return n.target}function Ae(n,t,r,e){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(e),a=Math.sin(e),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(r),f=o*Math.sin(r),h=2*Math.asin(Math.sqrt(tt(e-t)+u*o*tt(r-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,r=Math.sin(h-n)*g,e=r*c+t*s,u=r*l+t*f,o=r*i+t*a;return[Math.atan2(u,e)*Ya,Math.atan2(o,Math.sqrt(e*e+u*u))*Ya]}:function(){return[n*Ya,t*Ya]};return p.distance=h,p}function Ce(){function n(n,u){var i=Math.sin(u*=Ia),o=Math.cos(u),a=Ma((n*=Ia)-t),c=Math.cos(a);Gc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=e*i-r*o*c)*a),r*i+e*o*c),t=n,r=i,e=o}var t,r,e;Kc.point=function(u,i){t=u*Ia,r=Math.sin(i*=Ia),e=Math.cos(i),Kc.point=n},Kc.lineEnd=function(){Kc.point=Kc.lineEnd=v}}function Le(n,t){function r(t,r){var e=Math.cos(t),u=Math.cos(r),i=n(e*u);return[i*u*Math.sin(t),i*Math.sin(r)]}return r.invert=function(n,r){var e=Math.sqrt(n*n+r*r),u=t(e),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,e*o),Math.asin(e&&r*i/e)]},r}function qe(n,t){function r(n,t){o>0?-Ha+Fa>t&&(t=-Ha+Fa):t>Ha-Fa&&(t=Ha-Fa);var r=o/Math.pow(u(t),i);return[r*Math.sin(i*n),o-r*Math.cos(i*n)]}var e=Math.cos(n),u=function(n){return Math.tan(Ua/4+n/2)},i=n===t?Math.sin(n):Math.log(e/Math.cos(t))/Math.log(u(t)/u(n)),o=e*Math.pow(u(n),i)/i;return i?(r.invert=function(n,t){var r=o-t,e=B(i)*Math.sqrt(n*n+r*r);return[Math.atan2(n,r)/i,2*Math.atan(Math.pow(o/e,1/i))-Ha]},r):ze}function Te(n,t){function r(n,t){var r=i-t;return[r*Math.sin(u*n),i-r*Math.cos(u*n)]}var e=Math.cos(n),u=n===t?Math.sin(n):(e-Math.cos(t))/(t-n),i=e/u+n;return Ma(u)u;u++){for(;e>1&&J(n[r[e-2]],n[r[e-1]],n[u])<=0;)--e;r[e++]=u}return r.slice(0,e)}function He(n,t){return n[0]-t[0]||n[1]-t[1]}function Fe(n,t,r){return(r[0]-t[0])*(n[1]-t[1])<(r[1]-t[1])*(n[0]-t[0])}function Oe(n,t,r,e){var u=n[0],i=r[0],o=t[0]-u,a=e[0]-i,c=n[1],l=r[1],s=t[1]-c,f=e[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Ie(n){var t=n[0],r=n[n.length-1];return!(t[0]-r[0]||t[1]-r[1])}function Ye(){su(this),this.edge=this.site=this.circle=null}function Ze(n){var t=sl.pop()||new Ye;return t.site=n,t}function Ve(n){tu(n),al.remove(n),sl.push(n),su(n)}function $e(n){var t=n.circle,r=t.x,e=t.cy,u={x:r,y:e},i=n.P,o=n.N,a=[n];Ve(n);for(var c=i;c.circle&&Ma(r-c.circle.x)s;++s)l=a[s],c=a[s-1],au(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=iu(c.site,l.site,null,u),nu(c),nu(l)}function Xe(n){for(var t,r,e,u,i=n.x,o=n.y,a=al._;a;)if(e=Be(a,o)-i,e>Fa)a=a.L;else{if(u=i-Je(a,o),!(u>Fa)){e>-Fa?(t=a.P,r=a):u>-Fa?(t=a,r=a.N):t=r=a;break}if(!a.R){t=a;break}a=a.R}var c=Ze(n);if(al.insert(t,c),t||r){if(t===r)return tu(t),r=Ze(t.site),al.insert(c,r),c.edge=r.edge=iu(t.site,c.site),nu(t),nu(r),void 0;if(!r)return c.edge=iu(t.site,c.site),void 0;tu(t),tu(r);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=r.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+s,y:(h*x-v*y)/m+f};au(r.edge,l,p,M),c.edge=iu(l,n,null,M),r.edge=iu(n,p,null,M),nu(t),nu(r)}}function Be(n,t){var r=n.site,e=r.x,u=r.y,i=u-t;if(!i)return e;var o=n.P;if(!o)return-1/0;r=o.site;var a=r.x,c=r.y,l=c-t;if(!l)return a;var s=a-e,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+e:(e+a)/2}function Je(n,t){var r=n.N;if(r)return Be(r,t);var e=n.site;return e.y===t?e.x:1/0}function We(n){this.site=n,this.edges=[]}function Ge(n){for(var t,r,e,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=ol,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),e=s.x,u=s.y,l=a[++o%c].start(),t=l.x,r=l.y,(Ma(e-t)>Fa||Ma(u-r)>Fa)&&(a.splice(o,0,new cu(ou(i.site,s,Ma(e-f)Fa?{x:f,y:Ma(t-f)Fa?{x:Ma(r-p)Fa?{x:h,y:Ma(t-h)Fa?{x:Ma(r-g)=-Oa)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=fl.pop()||new Qe;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=ll._;x;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};r={x:d,y:l}}else{if(i){if(i.ye||e>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/e,y:c};r={x:(l-u)/e,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:e*o+u};r={x:a,y:e*a+u}}else{if(i){if(i.xe;++e)if(o=s[e],o.x==r[0]){if(o.i)if(null==l[o.i+1])for(l[o.i-1]+=o.x,l.splice(o.i,1),u=e+1;i>u;++u)s[u].i--;else for(l[o.i-1]+=o.x+l[o.i+1],l.splice(o.i,2),u=e+1;i>u;++u)s[u].i-=2;else if(null==l[o.i+1])l[o.i]=o.x;else for(l[o.i]=o.x+l[o.i+1],l.splice(o.i+1,1),u=e+1;i>u;++u)s[u].i--;s.splice(e,1),i--,e--}else o.x=wu(parseFloat(r[0]),parseFloat(o.x));for(;i>e;)o=s.pop(),null==l[o.i+1]?l[o.i]=o.x:(l[o.i]=o.x+l[o.i+1],l.splice(o.i+1,1)),i--;return 1===l.length?null==l[0]?(o=s[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(e=0;i>e;++e)l[(o=s[e]).i]=o.x(n);return l.join("")}}function ku(n,t){for(var r,e=oa.interpolators.length;--e>=0&&!(r=oa.interpolators[e](n,t)););return r}function Eu(n,t){var r,e=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(r=0;a>r;++r)e.push(ku(n[r],t[r]));for(;i>r;++r)u[r]=n[r];for(;o>r;++r)u[r]=t[r];return function(n){for(r=0;a>r;++r)u[r]=e[r](n);return u}}function Nu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function Au(n){return function(t){return 1-n(1-t)}}function Cu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Lu(n){return n*n}function qu(n){return n*n*n}function Tu(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,r=t*n;return 4*(.5>n?r:3*(n-t)+r-.75)}function zu(n){return function(t){return Math.pow(t,n)}}function Ru(n){return 1-Math.cos(n*Ha)}function Du(n){return Math.pow(2,10*(n-1))}function Pu(n){return 1-Math.sqrt(1-n*n)}function Uu(n,t){var r;return arguments.length<2&&(t=.45),arguments.length?r=t/ja*Math.asin(1/n):(n=1,r=t/4),function(e){return 1+n*Math.pow(2,-10*e)*Math.sin((e-r)*ja/t)}}function ju(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Hu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Fu(n,t){n=oa.hcl(n),t=oa.hcl(t);var r=n.h,e=n.c,u=n.l,i=t.h-r,o=t.c-e,a=t.l-u;return isNaN(o)&&(o=0,e=isNaN(e)?t.c:e),isNaN(i)?(i=0,r=isNaN(r)?t.h:r):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(r+i*n,e+o*n,u+a*n)+""}}function Ou(n,t){n=oa.hsl(n),t=oa.hsl(t);var r=n.h,e=n.s,u=n.l,i=t.h-r,o=t.s-e,a=t.l-u;return isNaN(o)&&(o=0,e=isNaN(e)?t.s:e),isNaN(i)?(i=0,r=isNaN(r)?t.h:r):i>180?i-=360:-180>i&&(i+=360),function(n){return it(r+i*n,e+o*n,u+a*n)+""}}function Iu(n,t){n=oa.lab(n),t=oa.lab(t);var r=n.l,e=n.a,u=n.b,i=t.l-r,o=t.a-e,a=t.b-u;return function(n){return ft(r+i*n,e+o*n,u+a*n)+""}}function Yu(n,t){return t-=n,function(r){return Math.round(n+t*r)}}function Zu(n){var t=[n.a,n.b],r=[n.c,n.d],e=$u(t),u=Vu(t,r),i=$u(Xu(r,t,-u))||0;t[0]*r[1]180?s+=360:s-l>180&&(l+=360),u.push({i:e.push(e.pop()+"rotate(",null,")")-2,x:wu(l,s)})):s&&e.push(e.pop()+"rotate("+s+")"),f!=h?u.push({i:e.push(e.pop()+"skewX(",null,")")-2,x:wu(f,h)}):h&&e.push(e.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(r=e.push(e.pop()+"scale(",null,",",null,")"),u.push({i:r-4,x:wu(g[0],p[0])},{i:r-2,x:wu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&e.push(e.pop()+"scale("+p+")"),r=u.length,function(n){for(var t,i=-1;++ir;++r)(t=n[r][1])>u&&(e=r,u=t);return e}function di(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function yi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var r=-1,e=+n[0],u=(n[1]-e)/t,i=[];++r<=t;)i[r]=u*r+e;return i}function Mi(n){return[oa.min(n),oa.max(n)]}function _i(n,t){return n.parent==t.parent?1:2}function bi(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function wi(n){var t,r=n.children;return r&&(t=r.length)?r[t-1]:n._tree.thread}function Si(n,t){var r=n.children;if(r&&(u=r.length))for(var e,u,i=-1;++i0&&(n=e);return n}function ki(n,t){return n.x-t.x}function Ei(n,t){return t.x-n.x}function Ni(n,t){return n.depth-t.depth}function Ai(n,t){function r(n,e){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c=0;)t=u[i]._tree,t.prelim+=r,t.mod+=r,r+=t.shift+(e+=t.change)}function Li(n,t,r){n=n._tree,t=t._tree;var e=r/(t.number-n.number);n.change+=e,t.change-=e,t.shift+=r,t.prelim+=r,t.mod+=r}function qi(n,t,r){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:r}function Ti(n,t){return n.value-t.value}function zi(n,t){var r=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=r,r._pack_prev=t}function Ri(n,t){n._pack_next=t,t._pack_prev=n}function Di(n,t){var r=t.x-n.x,e=t.y-n.y,u=n.r+t.r;return.999*u*u>r*r+e*e}function Pi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((r=n.children)&&(l=r.length)){var r,e,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(r.forEach(Ui),e=r[0],e.x=-e.r,e.y=0,t(e),l>1&&(u=r[1],u.x=u.r,u.y=0,t(u),l>2))for(i=r[2],Fi(e,u,i),t(i),zi(e,i),e._pack_prev=i,zi(i,u),u=e._pack_next,o=3;l>o;o++){Fi(e,u,i=r[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(Di(a,i)){p=1;break}if(1==p)for(c=e._pack_prev;c!==a._pack_prev&&!Di(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=r[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,r.forEach(ji)}}function Ui(n){n._pack_next=n._pack_prev=n}function ji(n){delete n._pack_next,delete n._pack_prev}function Hi(n,t,r,e){var u=n.children;if(n.x=t+=e*n.x,n.y=r+=e*n.y,n.r*=e,u)for(var i=-1,o=u.length;++iu&&(r+=u/2,u=0),0>i&&(e+=i/2,i=0),{x:r,y:e,dx:u,dy:i}}function Xi(n){var t=n[0],r=n[n.length-1];return r>t?[t,r]:[r,t]}function Bi(n){return n.rangeExtent?n.rangeExtent():Xi(n.range())}function Ji(n,t,r,e){var u=r(n[0],n[1]),i=e(t[0],t[1]);return function(n){return i(u(n))}}function Wi(n,t){var r,e=0,u=n.length-1,i=n[e],o=n[u];return i>o&&(r=e,e=u,u=r,r=i,i=o,o=r),n[e]=t.floor(i),n[u]=t.ceil(o),n}function Gi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Ki(n,t,r,e){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Ki:Ji,c=e?Wu:Ju;return o=u(n,t,c,r),a=u(t,n,c,ku),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Yu)},i.clamp=function(n){return arguments.length?(e=n,u()):e},i.interpolate=function(n){return arguments.length?(r=n,u()):r},i.ticks=function(t){return eo(n,t)},i.tickFormat=function(t,r){return uo(n,t,r)},i.nice=function(t){return to(n,t),u()},i.copy=function(){return Qi(n,t,r,e)},u()}function no(n,t){return oa.rebind(n,t,"range","rangeRound","interpolate","clamp")}function to(n,t){return Wi(n,Gi(ro(n,t)[2]))}function ro(n,t){null==t&&(t=10);var r=Xi(n),e=r[1]-r[0],u=Math.pow(10,Math.floor(Math.log(e/t)/Math.LN10)),i=t/e*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),r[0]=Math.ceil(r[0]/u)*u,r[1]=Math.floor(r[1]/u)*u+.5*u,r[2]=u,r}function eo(n,t){return oa.range.apply(oa,ro(n,t))}function uo(n,t,r){var e=ro(n,t);if(r){var u=hc.exec(r);if(u.shift(),"s"===u[8]){var i=oa.formatPrefix(Math.max(Ma(e[0]),Ma(e[1])));return u[7]||(u[7]="."+io(i.scale(e[2]))),u[8]="f",r=oa.format(u.join("")),function(n){return r(i.scale(n))+i.symbol}}u[7]||(u[7]="."+oo(u[8],e)),r=u.join("")}else r=",."+io(e[2])+"f";return oa.format(r)}function io(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function oo(n,t){var r=io(t[2]);return n in kl?Math.abs(r-io(Math.max(Ma(t[0]),Ma(t[1]))))+ +("e"!==n):r-2*("%"===n)}function ao(n,t,r,e){function u(n){return(r?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return r?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(r=t[0]>=0,n.domain((e=t.map(Number)).map(u)),o):e},o.base=function(r){return arguments.length?(t=+r,n.domain(e.map(u)),o):t},o.nice=function(){var t=Wi(e.map(u),r?Math:Nl);return n.domain(t),e=t.map(i),o},o.ticks=function(){var n=Xi(e),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(r){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return El;arguments.length<2?t=El:"function"!=typeof t&&(t=oa.format(t));var e,a=Math.max(.1,n/o.ticks().length),c=r?(e=1e-12,Math.ceil):(e=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+e))<=a?t(n):""}},o.copy=function(){return ao(n.copy(),t,r,e)},no(o,n)}function co(n,t,r){function e(t){return n(u(t))}var u=lo(t),i=lo(1/t);return e.invert=function(t){return i(n.invert(t))},e.domain=function(t){return arguments.length?(n.domain((r=t.map(Number)).map(u)),e):r},e.ticks=function(n){return eo(r,n)},e.tickFormat=function(n,t){return uo(r,n,t)},e.nice=function(n){return e.domain(to(r,n))},e.exponent=function(o){return arguments.length?(u=lo(t=o),i=lo(1/t),n.domain(r.map(u)),e):t},e.copy=function(){return co(n.copy(),t,r)},no(e,n)}function lo(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function so(n,t){function r(r){return i[((u.get(r)||("range"===t.t?u.set(r,n.push(r)):0/0))-1)%i.length]}function e(t,r){return oa.range(n.length).map(function(n){return t+r*n})}var u,i,a;return r.domain=function(e){if(!arguments.length)return n;n=[],u=new o;for(var i,a=-1,c=e.length;++an?[0/0,0/0]:[n>0?i[n-1]:t[0],nt?0/0:t/i+n,[t,t+1/i]},e.copy=function(){return ho(n,t,r)},u()}function go(n,t){function r(r){return r>=r?t[oa.bisect(n,r)]:void 0}return r.domain=function(t){return arguments.length?(n=t,r):n},r.range=function(n){return arguments.length?(t=n,r):t},r.invertExtent=function(r){return r=t.indexOf(r),[n[r-1],n[r]]},r.copy=function(){return go(n,t)},r}function po(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(r){return arguments.length?(n=r.map(t),t):n},t.ticks=function(t){return eo(n,t)},t.tickFormat=function(t,r){return uo(n,t,r)},t.copy=function(){return po(n)},t}function vo(n){return n.innerRadius}function mo(n){return n.outerRadius}function yo(n){return n.startAngle}function xo(n){return n.endAngle}function Mo(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(r),p=Et(e);++f1&&u.push("H",e[0]),u.join("")}function So(n){for(var t=0,r=n.length,e=n[0],u=[e[0],",",e[1]];++t1){a=t[1],i=n[c],c++,e+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*r,o[a+1]=u*e)); +for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function Fo(n){return n.length<3?_o(n):n[0]+Co(n,Ho(n))}function Oo(n){for(var t,r,e,u=-1,i=n.length;++ur?l():(u.active=r,i.event&&i.event.start.call(n,s,t),i.tween.forEach(function(r,e){(e=e.call(n,s,t))&&v.push(e)}),oa.timer(function(){return p.c=c(e||1)?Cr:c,1},0,a),void 0)}function c(e){if(u.active!==r)return l();for(var o=e/g,a=f(o),c=v.length;c>0;)v[--c].call(n,a);return o>=1?(i.event&&i.event.end.call(n,s,t),l()):void 0}function l(){return--u.count?delete u[r]:delete n.__transition__,1}var s=n.__data__,f=i.ease,h=i.delay,g=i.duration,p=lc,v=[];return p.t=h+a,e>=h?o(e-h):(p.c=o,void 0)},0,a)}}function Qo(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function na(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function ta(n){return n.toISOString()}function ra(n,t,r){function e(t){return n(t)}function u(n,r){var e=n[1]-n[0],u=e/r,i=oa.bisect(Kl,u);return i==Kl.length?[t.year,ro(n.map(function(n){return n/31536e6}),r)[2]]:i?t[u/Kl[i-1]1?{floor:function(t){for(;r(t=n.floor(t));)t=ea(t-1);return t},ceil:function(t){for(;r(t=n.ceil(t));)t=ea(+t+1);return t}}:n))},e.ticks=function(n,t){var r=Xi(e.domain()),i=null==n?u(r,10):"number"==typeof n?u(r,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(r[0],ea(+r[1]+1),1>t?1:t)},e.tickFormat=function(){return r},e.copy=function(){return ra(n.copy(),t,r)},no(e,n)}function ea(n){return new Date(n)}function ua(n){return JSON.parse(n.responseText)}function ia(n){var t=la.createRange();return t.selectNode(la.body),t.createContextualFragment(n.responseText)}var oa={version:"3.4.4"};Date.now||(Date.now=function(){return+new Date});var aa=[].slice,ca=function(n){return aa.call(n)},la=document,sa=la.documentElement,fa=window;try{ca(sa.childNodes)[0].nodeType}catch(ha){ca=function(n){for(var t=n.length,r=new Array(t);t--;)r[t]=n[t];return r}}try{la.createElement("div").style.setProperty("opacity",0,"")}catch(ga){var pa=fa.Element.prototype,va=pa.setAttribute,da=pa.setAttributeNS,ma=fa.CSSStyleDeclaration.prototype,ya=ma.setProperty;pa.setAttribute=function(n,t){va.call(this,n,t+"")},pa.setAttributeNS=function(n,t,r){da.call(this,n,t,r+"")},ma.setProperty=function(n,t,r){ya.call(this,n,t+"",r)}}oa.ascending=n,oa.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},oa.min=function(n,t){var r,e,u=-1,i=n.length;if(1===arguments.length){for(;++u=r);)r=void 0;for(;++ue&&(r=e)}else{for(;++u=r);)r=void 0;for(;++ue&&(r=e)}return r},oa.max=function(n,t){var r,e,u=-1,i=n.length;if(1===arguments.length){for(;++u=r);)r=void 0;for(;++ur&&(r=e)}else{for(;++u=r);)r=void 0;for(;++ur&&(r=e)}return r},oa.extent=function(n,t){var r,e,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r);)r=u=void 0;for(;++ie&&(r=e),e>u&&(u=e))}else{for(;++i=r);)r=void 0;for(;++ie&&(r=e),e>u&&(u=e))}return[r,u]},oa.sum=function(n,t){var r,e=0,u=n.length,i=-1;if(1===arguments.length)for(;++i1&&(r=r.map(e)),r=r.filter(t),r.length?oa.quantile(r.sort(n),.5):void 0};var xa=r(n);oa.bisectLeft=xa.left,oa.bisect=oa.bisectRight=xa.right,oa.bisector=function(t){return r(1===t.length?function(r,e){return n(t(r),e)}:t)},oa.shuffle=function(n){for(var t,r,e=n.length;e;)r=0|Math.random()*e--,t=n[e],n[e]=n[r],n[r]=t;return n},oa.permute=function(n,t){for(var r=t.length,e=new Array(r);r--;)e[r]=n[t[r]];return e},oa.pairs=function(n){for(var t,r=0,e=n.length-1,u=n[0],i=new Array(0>e?0:e);e>r;)i[r]=[t=u,u=n[++r]];return i},oa.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,t=oa.min(arguments,e),r=new Array(t);++n=0;)for(e=n[u],t=e.length;--t>=0;)r[--o]=e[t];return r};var Ma=Math.abs;oa.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var e,i=[],o=u(Ma(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(e=n+r*++a)>t;)i.push(e/o);else for(;(e=n+r*++a)=i.length)return e?e.call(u,a):r?a.sort(r):a;for(var l,s,f,h,g=-1,p=a.length,v=i[c++],d=new o;++g=i.length)return n;var e=[],u=a[r++];return n.forEach(function(n,u){e.push({key:n,values:t(u,r)})}),u?e.sort(function(n,t){return u(n.key,t.key)}):e}var r,e,u={},i=[],a=[];return u.map=function(t,r){return n(r,t,0)},u.entries=function(r){return t(n(oa.map,r,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return a[i.length-1]=n,u},u.sortValues=function(n){return r=n,u},u.rollup=function(n){return e=n,u},u},oa.set=function(n){var t=new h;if(n)for(var r=0,e=n.length;e>r;++r)t.add(n[r]);return t},i(h,{has:a,add:function(n){return this[_a+n]=!0,n},remove:function(n){return n=_a+n,n in this&&delete this[n]},values:l,size:s,empty:f,forEach:function(n){for(var t in this)t.charCodeAt(0)===ba&&n.call(this,t.substring(1))}}),oa.behavior={},oa.rebind=function(n,t){for(var r,e=1,u=arguments.length;++e=0&&(e=n.substring(r+1),n=n.substring(0,r)),n)return arguments.length<2?this[n].on(e):this[n].on(e,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(e,null);return this}},oa.event=null,oa.requote=function(n){return n.replace(Sa,"\\$&")};var Sa=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ka={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var r in t)n[r]=t[r]},Ea=function(n,t){return t.querySelector(n)},Na=function(n,t){return t.querySelectorAll(n)},Aa=sa[p(sa,"matchesSelector")],Ca=function(n,t){return Aa.call(n,t)};"function"==typeof Sizzle&&(Ea=function(n,t){return Sizzle(n,t)[0]||null},Na=Sizzle,Ca=Sizzle.matchesSelector),oa.selection=function(){return za};var La=oa.selection.prototype=[];La.select=function(n){var t,r,e,u,i=[];n=b(n);for(var o=-1,a=this.length;++o=0&&(r=n.substring(0,t),n=n.substring(t+1)),qa.hasOwnProperty(r)?{space:qa[r],local:n}:n}},La.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var r=this.node();return n=oa.ns.qualify(n),n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}for(t in n)this.each(S(t,n[t]));return this}return this.each(S(n,t))},La.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var r=this.node(),e=(n=N(n)).length,u=-1;if(t=r.classList){for(;++ue){if("string"!=typeof n){2>e&&(t="");for(r in n)this.each(L(r,n[r],t));return this}if(2>e)return fa.getComputedStyle(this.node(),null).getPropertyValue(n);r=""}return this.each(L(n,t,r))},La.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(q(t,n[t]));return this}return this.each(q(n,t))},La.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},La.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},La.append=function(n){return n=T(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},La.insert=function(n,t){return n=T(n),t=b(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},La.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},La.data=function(n,t){function r(n,r){var e,u,i,a=n.length,f=r.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new o,y=new o,x=[];for(e=-1;++ee;++e)p[e]=z(r[e]);for(;a>e;++e)v[e]=n[e]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),l.push(g),s.push(v)}var e,u,i=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(e=this[0]).length);++ii;i++){u.push(t=[]),t.parentNode=(r=this[i]).parentNode;for(var a=0,c=r.length;c>a;a++)(e=r[a])&&n.call(e,e.__data__,a,i)&&t.push(e)}return _(u)},La.order=function(){for(var n=-1,t=this.length;++n=0;)(r=e[u])&&(i&&i!==r.nextSibling&&i.parentNode.insertBefore(r,i),i=r);return this},La.sort=function(n){n=D.apply(this,arguments);for(var t=-1,r=this.length;++tn;n++)for(var r=this[n],e=0,u=r.length;u>e;e++){var i=r[e];if(i)return i}return null},La.size=function(){var n=0;return this.each(function(){++n}),n};var Ta=[];oa.selection.enter=U,oa.selection.enter.prototype=Ta,Ta.append=La.append,Ta.empty=La.empty,Ta.node=La.node,Ta.call=La.call,Ta.size=La.size,Ta.select=function(n){for(var t,r,e,u,i,o=[],a=-1,c=this.length;++ae){if("string"!=typeof n){2>e&&(t=!1);for(r in n)this.each(F(r,n[r],t));return this}if(2>e)return(e=this.node()["__on"+n])&&e._;r=!1}return this.each(F(n,t,r))};var Ra=oa.map({mouseenter:"mouseover",mouseleave:"mouseout"});Ra.forEach(function(n){"on"+n in la&&Ra.remove(n)});var Da="onselectstart"in la?null:p(sa.style,"userSelect"),Pa=0;oa.mouse=function(n){return Z(n,x())},oa.touches=function(n,t){return arguments.length<2&&(t=x().touches),t?ca(t).map(function(t){var r=Z(n,t);return r.identifier=t.identifier,r}):[]},oa.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",i)}function t(n,t,u,i,o){return function(){function a(){var n,r,e=t(h,v);e&&(n=e[0]-x[0],r=e[1]-x[1],p|=n|r,x=e,g({type:"drag",x:e[0]+l[0],y:e[1]+l[1],dx:n,dy:r}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&oa.event.target===f),g({type:"dragend"}))}var l,s=this,f=oa.event.target,h=s.parentNode,g=r.of(s,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=oa.select(u()).on(i+d,a).on(o+d,c),y=Y(),x=t(h,v);e?(l=e.apply(s,arguments),l=[l.x-x[0],l.y-x[1]]):l=[0,0],g({type:"dragstart"})}}var r=M(n,"drag","dragstart","dragend"),e=null,u=t(v,oa.mouse,X,"mousemove","mouseup"),i=t(V,oa.touch,$,"touchmove","touchend");return n.origin=function(t){return arguments.length?(e=t,n):e},oa.rebind(n,r,"on")};var Ua=Math.PI,ja=2*Ua,Ha=Ua/2,Fa=1e-6,Oa=Fa*Fa,Ia=Ua/180,Ya=180/Ua,Za=Math.SQRT2,Va=2,$a=4;oa.interpolateZoom=function(n,t){function r(n){var t=n*y;if(m){var r=Q(v),o=i/(Va*h)*(r*nt(Za*t+v)-K(v));return[e+o*l,u+o*s,i*r/Q(Za*t+v)]}return[e+n*l,u+n*s,i*Math.exp(Za*t)]}var e=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],l=o-e,s=a-u,f=l*l+s*s,h=Math.sqrt(f),g=(c*c-i*i+$a*f)/(2*i*Va*h),p=(c*c-i*i-$a*f)/(2*c*Va*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Za;return r.duration=1e3*y,r},oa.behavior.zoom=function(){function n(n){n.on(N,l).on(Ja+".zoom",f).on(A,h).on("dblclick.zoom",g).on(L,s)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function r(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function e(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=r(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(x.range().map(function(n){return(n-S.x)/S.k}).map(x.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function l(){function n(){s=1,u(oa.mouse(e),g),a(l)}function r(){f.on(A,fa===e?h:null).on(C,null),p(s&&oa.event.target===i),c(l)}var e=this,i=oa.event.target,l=q.of(e,arguments),s=0,f=oa.select(fa).on(A,n).on(C,r),g=t(oa.mouse(e)),p=Y();H.call(e),o(l)}function s(){function n(){var n=oa.touches(g);return h=S.k,n.forEach(function(n){n.identifier in v&&(v[n.identifier]=t(n))}),n}function r(){for(var t=oa.event.changedTouches,r=0,i=t.length;i>r;++r)v[t[r].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-m){var l=o[0],s=v[l.identifier];e(2*S.k),u(l,s),y(),a(p)}m=c}else if(o.length>1){var l=o[0],f=o[1],h=l[0]-f[0],g=l[1]-f[1];d=h*h+g*g}}function i(){for(var n,t,r,i,o=oa.touches(g),c=0,l=o.length;l>c;++c,i=null)if(r=o[c],i=v[r.identifier]){if(t)break;n=r,t=i}if(i){var s=(s=r[0]-n[0])*s+(s=r[1]-n[1])*s,f=d&&Math.sqrt(s/d);n=[(n[0]+r[0])/2,(n[1]+r[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],e(f*h)}m=null,u(n,t),a(p)}function f(){if(oa.event.touches.length){for(var t=oa.event.changedTouches,r=0,e=t.length;e>r;++r)delete v[t[r].identifier];for(var u in v)return void n()}b.on(x,null),w.on(N,l).on(L,s),k(),c(p)}var h,g=this,p=q.of(g,arguments),v={},d=0,x=".zoom-"+oa.event.changedTouches[0].identifier,M="touchmove"+x,_="touchend"+x,b=oa.select(oa.event.target).on(M,i).on(_,f),w=oa.select(g).on(N,null).on(L,r),k=Y();H.call(g),r(),o(p)}function f(){var n=q.of(this,arguments);d?clearTimeout(d):(H.call(this),o(n)),d=setTimeout(function(){d=null,c(n)},50),y();var r=v||oa.mouse(this);p||(p=t(r)),e(Math.pow(2,.002*Xa())*S.k),u(r,p),a(n)}function h(){p=null}function g(){var n=q.of(this,arguments),r=oa.mouse(this),i=t(r),l=Math.log(S.k)/Math.LN2;o(n),e(Math.pow(2,oa.event.shiftKey?Math.ceil(l)-1:Math.floor(l)+1)),u(r,i),a(n),c(n)}var p,v,d,m,x,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Ba,N="mousedown.zoom",A="mousemove.zoom",C="mouseup.zoom",L="touchstart.zoom",q=M(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=q.of(this,arguments),t=S;Hl?oa.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var r=k[0],e=k[1],u=r/2,i=e/2,o=oa.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,r/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,r/t.k]);return function(t){var e=o(t),c=r/e[2];this.__chart__=S={x:u-e[0]*c,y:i-e[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Ba:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(v=t&&[+t[0],+t[1]],n):v},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,x=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},oa.rebind(n,q,"on")};var Xa,Ba=[0,1/0],Ja="onwheel"in la?(Xa=function(){return-oa.event.deltaY*(oa.event.deltaMode?120:1)},"wheel"):"onmousewheel"in la?(Xa=function(){return oa.event.wheelDelta},"mousewheel"):(Xa=function(){return-oa.event.detail},"MozMousePixelScroll");rt.prototype.toString=function(){return this.rgb()+""},oa.hsl=function(n,t,r){return 1===arguments.length?n instanceof ut?et(n.h,n.s,n.l):_t(""+n,bt,et):et(+n,+t,+r)};var Wa=ut.prototype=new rt;Wa.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),et(this.h,this.s,this.l/n)},Wa.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),et(this.h,this.s,n*this.l)},Wa.rgb=function(){return it(this.h,this.s,this.l)},oa.hcl=function(n,t,r){return 1===arguments.length?n instanceof at?ot(n.h,n.c,n.l):n instanceof st?ht(n.l,n.a,n.b):ht((n=wt((n=oa.rgb(n)).r,n.g,n.b)).l,n.a,n.b):ot(+n,+t,+r)};var Ga=at.prototype=new rt;Ga.brighter=function(n){return ot(this.h,this.c,Math.min(100,this.l+Ka*(arguments.length?n:1)))},Ga.darker=function(n){return ot(this.h,this.c,Math.max(0,this.l-Ka*(arguments.length?n:1)))},Ga.rgb=function(){return ct(this.h,this.c,this.l).rgb()},oa.lab=function(n,t,r){return 1===arguments.length?n instanceof st?lt(n.l,n.a,n.b):n instanceof at?ct(n.l,n.c,n.h):wt((n=oa.rgb(n)).r,n.g,n.b):lt(+n,+t,+r)};var Ka=18,Qa=.95047,nc=1,tc=1.08883,rc=st.prototype=new rt;rc.brighter=function(n){return lt(Math.min(100,this.l+Ka*(arguments.length?n:1)),this.a,this.b)},rc.darker=function(n){return lt(Math.max(0,this.l-Ka*(arguments.length?n:1)),this.a,this.b)},rc.rgb=function(){return ft(this.l,this.a,this.b)},oa.rgb=function(n,t,r){return 1===arguments.length?n instanceof xt?yt(n.r,n.g,n.b):_t(""+n,yt,it):yt(~~n,~~t,~~r)};var ec=xt.prototype=new rt;ec.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,r=this.g,e=this.b,u=30;return t||r||e?(t&&u>t&&(t=u),r&&u>r&&(r=u),e&&u>e&&(e=u),yt(Math.min(255,~~(t/n)),Math.min(255,~~(r/n)),Math.min(255,~~(e/n)))):yt(u,u,u)},ec.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),yt(~~(n*this.r),~~(n*this.g),~~(n*this.b))},ec.hsl=function(){return bt(this.r,this.g,this.b)},ec.toString=function(){return"#"+Mt(this.r)+Mt(this.g)+Mt(this.b)};var uc=oa.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});uc.forEach(function(n,t){uc.set(n,dt(t))}),oa.functor=Et,oa.xhr=At(Nt),oa.dsv=function(n,t){function r(n,r,i){arguments.length<3&&(i=r,r=null);var o=Ct(n,t,null==r?e:u(r),i);return o.row=function(n){return arguments.length?o.response(null==(r=n)?e:u(n)):r},o}function e(n){return r.parse(n.responseText)}function u(n){return function(t){return r.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return r.parse=function(n,t){var e;return r.parseRows(n,function(n,r){if(e)return e(n,r-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");e=t?function(n,r){return t(u(n),r)}:u})},r.parseRows=function(n,t){function r(){if(s>=l)return o;if(u)return u=!1,i;var t=s;if(34===n.charCodeAt(t)){for(var r=t;r++s;){var e=n.charCodeAt(s++),a=1;if(10===e)u=!0;else if(13===e)u=!0,10===n.charCodeAt(s)&&(++s,++a);else if(e!==c)continue;return n.substring(t,s-a)}return n.substring(t)}for(var e,u,i={},o={},a=[],l=n.length,s=0,f=0;(e=r())!==o;){for(var h=[];e!==i&&e!==o;)h.push(e),e=r();(!t||(h=t(h,f++)))&&a.push(h)}return a},r.format=function(t){if(Array.isArray(t[0]))return r.formatRows(t);var e=new h,u=[];return t.forEach(function(n){for(var t in n)e.has(t)||u.push(e.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},r.formatRows=function(n){return n.map(i).join("\n")},r},oa.csv=oa.dsv(",","text/csv"),oa.tsv=oa.dsv(" ","text/tab-separated-values"),oa.touch=function(n,t,r){if(arguments.length<3&&(r=t,t=x().changedTouches),t)for(var e,u=0,i=t.length;i>u;++u)if((e=t[u]).identifier===r)return Z(n,e)};var ic,oc,ac,cc,lc,sc=fa[p(fa,"requestAnimationFrame")]||function(n){setTimeout(n,17)};oa.timer=function(n,t,r){var e=arguments.length;2>e&&(t=0),3>e&&(r=Date.now());var u=r+t,i={c:n,t:u,f:!1,n:null};oc?oc.n=i:ic=i,oc=i,ac||(cc=clearTimeout(cc),ac=1,sc(qt))},oa.timer.flush=function(){Tt(),zt()},oa.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var fc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);oa.formatPrefix=function(n,t){var r=0;return n&&(0>n&&(n*=-1),t&&(n=oa.round(n,Rt(n,t))),r=1+Math.floor(1e-12+Math.log(n)/Math.LN10),r=Math.max(-24,Math.min(24,3*Math.floor((r-1)/3)))),fc[8+r/3]};var hc=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,gc=oa.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=oa.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),pc=oa.time={},vc=Date;jt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){dc.setUTCDate.apply(this._,arguments)},setDay:function(){dc.setUTCDay.apply(this._,arguments)},setFullYear:function(){dc.setUTCFullYear.apply(this._,arguments)},setHours:function(){dc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){dc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){dc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){dc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){dc.setUTCSeconds.apply(this._,arguments)},setTime:function(){dc.setTime.apply(this._,arguments)}};var dc=Date.prototype;pc.year=Ht(function(n){return n=pc.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),pc.years=pc.year.range,pc.years.utc=pc.year.utc.range,pc.day=Ht(function(n){var t=new vc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),pc.days=pc.day.range,pc.days.utc=pc.day.utc.range,pc.dayOfYear=function(n){var t=pc.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var r=pc[n]=Ht(function(n){return(n=pc.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var r=pc.year(n).getDay();return Math.floor((pc.dayOfYear(n)+(r+t)%7)/7)-(r!==t)});pc[n+"s"]=r.range,pc[n+"s"].utc=r.utc.range,pc[n+"OfYear"]=function(n){var r=pc.year(n).getDay();return Math.floor((pc.dayOfYear(n)+(r+t)%7)/7)}}),pc.week=pc.sunday,pc.weeks=pc.sunday.range,pc.weeks.utc=pc.sunday.utc.range,pc.weekOfYear=pc.sundayOfYear;var mc={"-":"",_:" ",0:"0"},yc=/^\s*\d+/,xc=/^%/;oa.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var Mc=oa.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}); +oa.format=Mc.numberFormat,oa.geo={},cr.prototype={s:0,t:0,add:function(n){lr(n,this.t,_c),lr(_c.s,this.s,this),this.s?this.t+=_c.t:this.s=_c.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var _c=new cr;oa.geo.stream=function(n,t){n&&bc.hasOwnProperty(n.type)?bc[n.type](n,t):sr(n,t)};var bc={Feature:function(n,t){sr(n.geometry,t)},FeatureCollection:function(n,t){for(var r=n.features,e=-1,u=r.length;++en?4*Ua+n:n,Ec.lineStart=Ec.lineEnd=Ec.point=v}};oa.geo.bounds=function(){function n(n,t){x.push(M=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,r){var e=pr([t*Ia,r*Ia]);if(m){var u=dr(m,e),i=[u[1],-u[0],0],o=dr(i,u);xr(o),o=_r(o);var c=t-p,l=c>0?1:-1,v=o[0]*Ya*l,d=Ma(c)>180;if(d^(v>l*p&&l*t>v)){var y=o[1]*Ya;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>l*p&&l*t>v)){var y=-o[1]*Ya;f>y&&(f=y)}else f>r&&(f=r),r>g&&(g=r);d?p>t?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t)}else n(t,r);m=e,p=t}function r(){_.point=t}function e(){M[0]=s,M[1]=h,_.point=n,m=null}function u(n,r){if(m){var e=n-p;y+=Ma(e)>180?e+(e>0?360:-360):e}else v=n,d=r;Ec.point(n,r),t(n,r)}function i(){Ec.lineStart()}function o(){u(v,d),Ec.lineEnd(),Ma(y)>Fa&&(s=-(h=180)),M[0]=s,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nkc?(s=-(h=180),f=-(g=90)):y>Fa?g=90:-Fa>y&&(f=-90),M[0]=s,M[1]=h}};return function(n){g=h=-(s=f=1/0),x=[],oa.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var r,e=1,u=x[0],i=[u];t>e;++e)r=x[e],l(r[0],u)||l(r[1],u)?(a(u[0],r[1])>a(u[0],u[1])&&(u[1]=r[1]),a(r[0],u[1])>a(u[0],u[1])&&(u[0]=r[0])):i.push(u=r);for(var o,r,p=-1/0,t=i.length-1,e=0,u=i[t];t>=e;u=r,++e)r=i[e],(o=a(u[1],r[0]))>p&&(p=o,s=r[0],h=u[1])}return x=M=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),oa.geo.centroid=function(n){Nc=Ac=Cc=Lc=qc=Tc=zc=Rc=Dc=Pc=Uc=0,oa.geo.stream(n,jc);var t=Dc,r=Pc,e=Uc,u=t*t+r*r+e*e;return Oa>u&&(t=Tc,r=zc,e=Rc,Fa>Ac&&(t=Cc,r=Lc,e=qc),u=t*t+r*r+e*e,Oa>u)?[0/0,0/0]:[Math.atan2(r,t)*Ya,G(e/Math.sqrt(u))*Ya]};var Nc,Ac,Cc,Lc,qc,Tc,zc,Rc,Dc,Pc,Uc,jc={sphere:v,point:Sr,lineStart:Er,lineEnd:Nr,polygonStart:function(){jc.lineStart=Ar},polygonEnd:function(){jc.lineStart=Er}};oa.geo.pointInPolygon=Ur;var Hc=zr(Or,jr,Fr,[-Ua,-Ua/2],Pr),Fc=1e9;oa.geo.clipExtent=function(){var n,t,r,e,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Zr(n=+a[0][0],t=+a[0][1],r=+a[1][0],e=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[r,e]]}};return o.extent([[0,0],[960,500]])},oa.geo.distance=function(n,t){var r,e=(t[0]-n[0])*Ia,u=n[1]*Ia,i=t[1]*Ia,o=Math.sin(e),a=Math.cos(e),c=Math.sin(u),l=Math.cos(u),s=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((r=f*o)*r+(r=l*s-c*f*a)*r),c*s+l*f*a)};var Oc={};(oa.geo.conicEqualArea=function(){return Kr(Qr)}).raw=Qr,oa.geo.albers=function(){return oa.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},oa.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,r(i,o),t||(e(i,o),t)||u(i,o),t}var t,r,e,u,i=oa.geo.albers(),o=oa.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=oa.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,r){t=[n,r]}};return n.invert=function(n){var t=i.scale(),r=i.translate(),e=(n[0]-r[0])/t,u=(n[1]-r[1])/t;return(u>=.12&&.234>u&&e>=-.425&&-.214>e?o:u>=.166&&.234>u&&e>=-.214&&-.115>e?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),r=o.stream(n),e=a.stream(n);return{point:function(n,u){t.point(n,u),r.point(n,u),e.point(n,u)},sphere:function(){t.sphere(),r.sphere(),e.sphere()},lineStart:function(){t.lineStart(),r.lineStart(),e.lineStart()},lineEnd:function(){t.lineEnd(),r.lineEnd(),e.lineEnd()},polygonStart:function(){t.polygonStart(),r.polygonStart(),e.polygonStart()},polygonEnd:function(){t.polygonEnd(),r.polygonEnd(),e.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var l=i.scale(),s=+t[0],f=+t[1];return r=i.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,e=o.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Fa,f+.12*l+Fa],[s-.214*l-Fa,f+.234*l-Fa]]).stream(c).point,u=a.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Fa,f+.166*l+Fa],[s-.115*l-Fa,f+.234*l-Fa]]).stream(c).point,n},n.scale(1070)};var Ic,Yc,Zc,Vc,$c,Xc,Bc={point:v,lineStart:v,lineEnd:v,polygonStart:function(){Yc=0,Bc.lineStart=ne},polygonEnd:function(){Bc.lineStart=Bc.lineEnd=Bc.point=v,Ic+=Ma(Yc/2)}},Jc={point:te,lineStart:v,lineEnd:v,polygonStart:v,polygonEnd:v},Wc={point:ue,lineStart:ie,lineEnd:oe,polygonStart:function(){Wc.lineStart=ae},polygonEnd:function(){Wc.point=ue,Wc.lineStart=ie,Wc.lineEnd=oe}};oa.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),oa.geo.stream(n,o)),i.result()}function t(){return o=null,n}var r,e,u,i,o,a=4.5;return n.area=function(n){return Ic=0,oa.geo.stream(n,u(Bc)),Ic},n.centroid=function(n){return Cc=Lc=qc=Tc=zc=Rc=Dc=Pc=Uc=0,oa.geo.stream(n,u(Wc)),Uc?[Dc/Uc,Pc/Uc]:Rc?[Tc/Rc,zc/Rc]:qc?[Cc/qc,Lc/qc]:[0/0,0/0]},n.bounds=function(n){return $c=Xc=-(Zc=Vc=1/0),oa.geo.stream(n,u(Jc)),[[Zc,Vc],[$c,Xc]]},n.projection=function(n){return arguments.length?(u=(r=n)?n.stream||se(n):Nt,t()):r},n.context=function(n){return arguments.length?(i=null==(e=n)?new re:new ce(n),"function"!=typeof a&&i.pointRadius(a),t()):e},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(oa.geo.albersUsa()).context(null)},oa.geo.transform=function(n){return{stream:function(t){var r=new fe(t);for(var e in n)r[e]=n[e];return r}}},fe.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},oa.geo.projection=ge,oa.geo.projectionMutator=pe,(oa.geo.equirectangular=function(){return ge(de)}).raw=de.invert=de,oa.geo.rotation=function(n){function t(t){return t=n(t[0]*Ia,t[1]*Ia),t[0]*=Ya,t[1]*=Ya,t}return n=ye(n[0]%360*Ia,n[1]*Ia,n.length>2?n[2]*Ia:0),t.invert=function(t){return t=n.invert(t[0]*Ia,t[1]*Ia),t[0]*=Ya,t[1]*=Ya,t},t},me.invert=de,oa.geo.circle=function(){function n(){var n="function"==typeof e?e.apply(this,arguments):e,t=ye(-n[0]*Ia,-n[1]*Ia,0).invert,u=[];return r(null,null,1,{point:function(n,r){u.push(n=t(n,r)),n[0]*=Ya,n[1]*=Ya}}),{type:"Polygon",coordinates:[u]}}var t,r,e=[0,0],u=6;return n.origin=function(t){return arguments.length?(e=t,n):e},n.angle=function(e){return arguments.length?(r=be((t=+e)*Ia,u*Ia),n):t},n.precision=function(e){return arguments.length?(r=be(t*Ia,(u=+e)*Ia),n):u},n.angle(90)},oa.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return oa.range(Math.ceil(i/d)*d,u,d).map(h).concat(oa.range(Math.ceil(l/m)*m,c,m).map(g)).concat(oa.range(Math.ceil(e/p)*p,r,p).filter(function(n){return Ma(n%d)>Fa}).map(s)).concat(oa.range(Math.ceil(a/v)*v,o,v).filter(function(n){return Ma(n%m)>Fa}).map(f))}var r,e,u,i,o,a,c,l,s,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],l=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[i,l],[u,c]]},n.minorExtent=function(t){return arguments.length?(e=+t[0][0],r=+t[1][0],a=+t[0][1],o=+t[1][1],e>r&&(t=e,e=r,r=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[e,a],[r,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,s=Se(a,o,90),f=ke(e,r,y),h=Se(l,c,90),g=ke(i,u,y),n):y},n.majorExtent([[-180,-90+Fa],[180,90-Fa]]).minorExtent([[-180,-80-Fa],[180,80+Fa]])},oa.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||e.apply(this,arguments),r||u.apply(this,arguments)]}}var t,r,e=Ee,u=Ne;return n.distance=function(){return oa.geo.distance(t||e.apply(this,arguments),r||u.apply(this,arguments))},n.source=function(r){return arguments.length?(e=r,t="function"==typeof r?null:r,n):e},n.target=function(t){return arguments.length?(u=t,r="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},oa.geo.interpolate=function(n,t){return Ae(n[0]*Ia,n[1]*Ia,t[0]*Ia,t[1]*Ia)},oa.geo.length=function(n){return Gc=0,oa.geo.stream(n,Kc),Gc};var Gc,Kc={sphere:v,point:v,lineStart:Ce,lineEnd:v,polygonStart:v,polygonEnd:v},Qc=Le(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(oa.geo.azimuthalEqualArea=function(){return ge(Qc)}).raw=Qc;var nl=Le(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},Nt);(oa.geo.azimuthalEquidistant=function(){return ge(nl)}).raw=nl,(oa.geo.conicConformal=function(){return Kr(qe)}).raw=qe,(oa.geo.conicEquidistant=function(){return Kr(Te)}).raw=Te;var tl=Le(function(n){return 1/n},Math.atan);(oa.geo.gnomonic=function(){return ge(tl)}).raw=tl,ze.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ha]},(oa.geo.mercator=function(){return Re(ze)}).raw=ze;var rl=Le(function(){return 1},Math.asin);(oa.geo.orthographic=function(){return ge(rl)}).raw=rl;var el=Le(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(oa.geo.stereographic=function(){return ge(el)}).raw=el,De.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ha]},(oa.geo.transverseMercator=function(){var n=Re(De),t=n.center,r=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?r([n[0],n[1],n.length>2?n[2]+90:90]):(n=r(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=De,oa.geom={},oa.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(r),i=Et(e),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(He),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=je(a),s=je(c),f=s[0]===l[0],h=s[s.length-1]===l[l.length-1],g=[];for(t=l.length-1;t>=0;--t)g.push(n[a[l[t]][2]]);for(t=+f;t=e&&l.x<=i&&l.y>=u&&l.y<=o?[[e,o],[i,o],[i,u],[e,u]]:[];s.point=n[a]}),t}function r(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Fa)*Fa,y:Math.round(o(n,t)/Fa)*Fa,i:t}})}var e=Pe,u=Ue,i=e,o=u,a=hl;return n?t(n):(t.links=function(n){return pu(r(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return pu(r(n)).cells.forEach(function(r,e){for(var u,i,o=r.site,a=r.edges.sort(Ke),c=-1,l=a.length,s=a[l-1].edge,f=s.l===o?s.r:s.l;++c=l,h=e>=s,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=xu()),f?u=l:a=l,h?o=s:c=s,i(n,t,r,e,u,o,a,c)}var s,f,h,g,p,v,d,m,y,x=Et(a),M=Et(c);if(null!=t)v=t,d=r,m=e,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)s=n[g],s.xm&&(m=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var _=+x(s=n[g],g),b=+M(s,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=xu();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){Mu(n,k,v,d,m,y)},g=-1,null==t){for(;++g=0?n.substring(0,t):n,e=t>=0?n.substring(t+1):"in";return r=vl.get(r)||pl,e=dl.get(e)||Nt,Nu(e(r.apply(null,aa.call(arguments,1))))},oa.interpolateHcl=Fu,oa.interpolateHsl=Ou,oa.interpolateLab=Iu,oa.interpolateRound=Yu,oa.transform=function(n){var t=la.createElementNS(oa.ns.prefix.svg,"g");return(oa.transform=function(n){if(null!=n){t.setAttribute("transform",n);var r=t.transform.baseVal.consolidate()}return new Zu(r?r.matrix:ml)})(n)},Zu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var ml={a:1,b:0,c:0,d:1,e:0,f:0};oa.interpolateTransform=Bu,oa.layout={},oa.layout.bundle=function(){return function(n){for(var t=[],r=-1,e=n.length;++ra*a/d){if(p>c){var l=t.charge/c;n.px-=i*l,n.py-=o*l}return!0}if(t.point&&c&&p>c){var l=t.pointCharge/c;n.px-=i*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=oa.event.x,n.py=oa.event.y,a.resume()}var r,e,u,i,o,a={},c=oa.dispatch("start","tick","end"),l=[1,1],s=.9,f=yl,h=xl,g=-30,p=Ml,v=.1,d=.64,m=[],y=[];return a.tick=function(){if((e*=.99)<.005)return c.end({type:"end",alpha:e=0}),!0;var t,r,a,f,h,p,d,x,M,_=m.length,b=y.length;for(r=0;b>r;++r)a=y[r],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(p=x*x+M*M)&&(p=e*i[r]*((p=Math.sqrt(p))-u[r])/p,x*=p,M*=p,h.x-=x*(d=f.weight/(h.weight+f.weight)),h.y-=M*d,f.x+=x*(d=1-d),f.y+=M*d);if((d=e*v)&&(x=l[0]/2,M=l[1]/2,r=-1,d))for(;++r<_;)a=m[r],a.x+=(x-a.x)*d,a.y+=(M-a.y)*d;if(g)for(ui(t=oa.geom.quadtree(m),e,o),r=-1;++r<_;)(a=m[r]).fixed||t.visit(n(a));for(r=-1;++r<_;)a=m[r],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*s,a.y-=(a.py-(a.py=a.y))*s);c.tick({type:"tick",alpha:e})},a.nodes=function(n){return arguments.length?(m=n,a):m},a.links=function(n){return arguments.length?(y=n,a):y},a.size=function(n){return arguments.length?(l=n,a):l},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(s=+n,a):s},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.chargeDistance=function(n){return arguments.length?(p=n*n,a):Math.sqrt(p)},a.gravity=function(n){return arguments.length?(v=+n,a):v},a.theta=function(n){return arguments.length?(d=n*n,a):Math.sqrt(d)},a.alpha=function(n){return arguments.length?(n=+n,e?e=n>0?n:0:n>0&&(c.start({type:"start",alpha:e=n}),oa.timer(a.tick)),a):e},a.start=function(){function n(n,e){if(!r){for(r=new Array(c),a=0;c>a;++a)r[a]=[];for(a=0;l>a;++a){var u=y[a];r[u.source.index].push(u.target),r[u.target.index].push(u.source)}}for(var i,o=r[t],a=-1,l=o.length;++at;++t)(e=m[t]).index=t,e.weight=0;for(t=0;s>t;++t)e=y[t],"number"==typeof e.source&&(e.source=m[e.source]),"number"==typeof e.target&&(e.target=m[e.target]),++e.source.weight,++e.target.weight;for(t=0;c>t;++t)e=m[t],isNaN(e.x)&&(e.x=n("x",p)),isNaN(e.y)&&(e.y=n("y",v)),isNaN(e.px)&&(e.px=e.x),isNaN(e.py)&&(e.py=e.y);if(u=[],"function"==typeof f)for(t=0;s>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;s>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;s>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;s>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return r||(r=oa.behavior.drag().origin(Nt).on("dragstart.force",ni).on("drag.force",t).on("dragend.force",ti)),arguments.length?(this.on("mouseover.force",ri).on("mouseout.force",ei).call(r),void 0):r},oa.rebind(a,c,"on")};var yl=20,xl=1,Ml=1/0;oa.layout.hierarchy=function(){function n(t,o,a){var c=u.call(r,t,o);if(t.depth=o,a.push(t),c&&(l=c.length)){for(var l,s,f=-1,h=t.children=new Array(l),g=0,p=o+1;++fg;++g)for(u.call(n,l[0][g],p=v[g],s[0][g][1]),h=1;d>h;++h)u.call(n,l[h][g],p+=s[h-1][g][1],s[h][g][1]);return a}var t=Nt,r=gi,e=pi,u=hi,i=si,o=fi;return n.values=function(r){return arguments.length?(t=r,n):t},n.order=function(t){return arguments.length?(r="function"==typeof t?t:bl.get(t)||gi,n):r},n.offset=function(t){return arguments.length?(e="function"==typeof t?t:wl.get(t)||pi,n):e},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var bl=oa.map({"inside-out":function(n){var t,r,e=n.length,u=n.map(vi),i=n.map(di),o=oa.range(e).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,l=[],s=[];for(t=0;e>t;++t)r=o[t],c>a?(a+=i[r],l.push(r)):(c+=i[r],s.push(r));return s.reverse().concat(l)},reverse:function(n){return oa.range(n.length).reverse()},"default":gi}),wl=oa.map({silhouette:function(n){var t,r,e,u=n.length,i=n[0].length,o=[],a=0,c=[];for(r=0;i>r;++r){for(t=0,e=0;u>t;t++)e+=n[t][r][1];e>a&&(a=e),o.push(e)}for(r=0;i>r;++r)c[r]=(a-o[r])/2;return c},wiggle:function(n){var t,r,e,u,i,o,a,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,r=1;h>r;++r){for(t=0,u=0;s>t;++t)u+=n[t][r][1];for(t=0,i=0,a=f[r][0]-f[r-1][0];s>t;++t){for(e=0,o=(n[t][r][1]-n[t][r-1][1])/(2*a);t>e;++e)o+=(n[e][r][1]-n[e][r-1][1])/a;i+=o*n[t][r][1]}g[r]=c-=u?i/u*a:0,l>c&&(l=c)}for(r=0;h>r;++r)g[r]-=l;return g},expand:function(n){var t,r,e,u=n.length,i=n[0].length,o=1/u,a=[];for(r=0;i>r;++r){for(t=0,e=0;u>t;t++)e+=n[t][r][1];if(e)for(t=0;u>t;t++)n[t][r][1]/=e;else for(t=0;u>t;t++)n[t][r][1]=o}for(r=0;i>r;++r)a[r]=0;return a},zero:pi});oa.layout.histogram=function(){function n(n,i){for(var o,a,c=[],l=n.map(r,this),s=e.call(this,l,i),f=u.call(this,s,l,i),i=-1,h=l.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=s[0]&&a<=s[1]&&(o=c[oa.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,r=Number,e=Mi,u=yi;return n.value=function(t){return arguments.length?(r=t,n):r},n.range=function(t){return arguments.length?(e=Et(t),n):e},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return xi(n,t)}:Et(t),n):u},n.frequency=function(r){return arguments.length?(t=!!r,n):t},n},oa.layout.tree=function(){function n(n,i){function o(n,t){var e=n.children,u=n._tree;if(e&&(i=e.length)){for(var i,a,l,s=e[0],f=s,h=-1;++h0&&(Li(qi(a,n,e),n,u),l+=u,s+=u),f+=a._tree.mod,l+=i._tree.mod,h+=c._tree.mod,s+=o._tree.mod;a&&!wi(o)&&(o._tree.thread=a,o._tree.mod+=f-s),i&&!bi(c)&&(c._tree.thread=i,c._tree.mod+=l-h,e=n)}return e}var l=t.call(this,n,i),s=l[0];Ai(s,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(s),a(s,-s._tree.prelim);var f=Si(s,Ei),h=Si(s,ki),g=Si(s,Ni),p=f.x-r(f,h)/2,v=h.x+r(h,f)/2,d=g.depth||1;return Ai(s,u?function(n){n.x*=e[0],n.y=n.depth*e[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*e[0],n.y=n.depth/d*e[1],delete n._tree}),l}var t=oa.layout.hierarchy().sort(null).value(null),r=_i,e=[1,1],u=!1;return n.separation=function(t){return arguments.length?(r=t,n):r},n.size=function(t){return arguments.length?(u=null==(e=t),n):u?null:e},n.nodeSize=function(t){return arguments.length?(u=null!=(e=t),n):u?e:null},ii(n,t)},oa.layout.pack=function(){function n(n,i){var o=r.call(this,n,i),a=o[0],c=u[0],l=u[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Ai(a,function(n){n.r=+s(n.value)}),Ai(a,Pi),e){var f=e*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;Ai(a,function(n){n.r+=f}),Ai(a,Pi),Ai(a,function(n){n.r-=f})}return Hi(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,r=oa.layout.hierarchy().sort(Ti),e=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(r){return arguments.length?(t=null==r||"function"==typeof r?r:+r,n):t},n.padding=function(t){return arguments.length?(e=+t,n):e},ii(n,r)},oa.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],l=0;Ai(c,function(n){var t=n.children;t&&t.length?(n.x=Ii(t),n.y=Oi(t)):(n.x=o?l+=r(n,o):0,n.y=0,o=n)});var s=Yi(c),f=Zi(c),h=s.x-r(s,f)/2,g=f.x+r(f,s)/2;return Ai(c,u?function(n){n.x=(n.x-c.x)*e[0],n.y=(c.y-n.y)*e[1]}:function(n){n.x=(n.x-h)/(g-h)*e[0],n.y=(1-(c.y?n.y/c.y:1))*e[1]}),a}var t=oa.layout.hierarchy().sort(null).value(null),r=_i,e=[1,1],u=!1;return n.separation=function(t){return arguments.length?(r=t,n):r},n.size=function(t){return arguments.length?(u=null==(e=t),n):u?null:e},n.nodeSize=function(t){return arguments.length?(u=null!=(e=t),n):u?e:null},ii(n,t)},oa.layout.treemap=function(){function n(n,t){for(var r,e,u=-1,i=n.length;++ut?0:t),r.area=isNaN(e)||0>=e?0:e}function t(r){var i=r.children;if(i&&i.length){var o,a,c,l=f(r),s=[],h=i.slice(),p=1/0,v="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&r.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/r.value),s.area=0;(c=h.length)>0;)s.push(o=h[c-1]),s.area+=o.area,"squarify"!==g||(a=e(s,v))<=p?(h.pop(),p=a):(s.area-=s.pop().area,u(s,v,l,!1),v=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(u(s,v,l,!0),s.length=s.area=0),i.forEach(t)}}function r(t){var e=t.children;if(e&&e.length){var i,o=f(t),a=e.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);e.forEach(r)}}function e(n,t){for(var r,e=n.area,u=0,i=1/0,o=-1,a=n.length;++or&&(i=r),r>u&&(u=r));return e*=e,t*=t,e?Math.max(t*u*p/e,e/(t*i*p)):1/0}function u(n,t,r,e){var u,i=-1,o=n.length,a=r.x,l=r.y,s=t?c(n.area/t):0;if(t==r.dx){for((e||s>r.dy)&&(s=r.dy);++ir.dx)&&(s=r.dx);++ir&&(t=1),1>r&&(n=0),function(){var r,e,u;do r=2*Math.random()-1,e=2*Math.random()-1,u=r*r+e*e;while(!u||u>1);return n+t*r*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=oa.random.normal.apply(oa,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=oa.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,r=0;n>r;r++)t+=Math.random();return t}}},oa.scale={};var Sl={floor:Nt,ceil:Nt};oa.scale.linear=function(){return Qi([0,1],[0,1],ku,!1)};var kl={s:1,g:1,p:1,r:1,e:1};oa.scale.log=function(){return ao(oa.scale.linear().domain([0,1]),10,!0,[1,10])};var El=oa.format(".0e"),Nl={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};oa.scale.pow=function(){return co(oa.scale.linear(),1,[0,1])},oa.scale.sqrt=function(){return oa.scale.pow().exponent(.5)},oa.scale.ordinal=function(){return so([],{t:"range",a:[[]]})},oa.scale.category10=function(){return oa.scale.ordinal().range(Al)},oa.scale.category20=function(){return oa.scale.ordinal().range(Cl)},oa.scale.category20b=function(){return oa.scale.ordinal().range(Ll)},oa.scale.category20c=function(){return oa.scale.ordinal().range(ql)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(mt),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(mt),Ll=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(mt),ql=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(mt);oa.scale.quantile=function(){return fo([],[])},oa.scale.quantize=function(){return ho(0,1,[0,1])},oa.scale.threshold=function(){return go([.5],[0,1])},oa.scale.identity=function(){return po([0,1])},oa.svg={},oa.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=r.apply(this,arguments),o=e.apply(this,arguments)+Tl,a=u.apply(this,arguments)+Tl,c=(o>a&&(c=o,o=a,a=c),a-o),l=Ua>c?"0":"1",s=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=zl?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*s+","+i*f+"A"+i+","+i+" 0 "+l+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+l+",0 "+n*s+","+n*f+"Z":"M"+i*s+","+i*f+"A"+i+","+i+" 0 "+l+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=vo,r=mo,e=yo,u=xo;return n.innerRadius=function(r){return arguments.length?(t=Et(r),n):t},n.outerRadius=function(t){return arguments.length?(r=Et(t),n):r},n.startAngle=function(t){return arguments.length?(e=Et(t),n):e},n.endAngle=function(t){return arguments.length?(u=Et(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+r.apply(this,arguments))/2,i=(e.apply(this,arguments)+u.apply(this,arguments))/2+Tl;return[Math.cos(i)*n,Math.sin(i)*n]},n};var Tl=-Ha,zl=ja-Fa;oa.svg.line=function(){return Mo(Nt)};var Rl=oa.map({linear:_o,"linear-closed":bo,step:wo,"step-before":So,"step-after":ko,basis:qo,"basis-open":To,"basis-closed":zo,bundle:Ro,cardinal:Ao,"cardinal-open":Eo,"cardinal-closed":No,monotone:Fo});Rl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Dl=[0,2/3,1/3,0],Pl=[0,1/3,2/3,0],Ul=[0,1/6,2/3,1/6];oa.svg.line.radial=function(){var n=Mo(Oo);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},So.reverse=ko,ko.reverse=So,oa.svg.area=function(){return Io(Nt)},oa.svg.area.radial=function(){var n=Io(Oo);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},oa.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),l=t(this,o,n,a);return"M"+c.p0+e(c.r,c.p1,c.a1-c.a0)+(r(c,l)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,l.r,l.p0)+e(l.r,l.p1,l.a1-l.a0)+u(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,r,e){var u=t.call(n,r,e),i=a.call(n,u,e),o=c.call(n,u,e)+Tl,s=l.call(n,u,e)+Tl;return{r:i,a0:o,a1:s,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(s),i*Math.sin(s)]}}function r(n,t){return n.a0==t.a0&&n.a1==t.a1}function e(n,t,r){return"A"+n+","+n+" 0 "+ +(r>Ua)+",1 "+t}function u(n,t,r,e){return"Q 0,0 "+e}var i=Ee,o=Ne,a=Yo,c=yo,l=xo;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(l=Et(t),n):l},n},oa.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=r.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(e),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=Ee,r=Ne,e=Zo;return n.source=function(r){return arguments.length?(t=Et(r),n):t},n.target=function(t){return arguments.length?(r=Et(t),n):r},n.projection=function(t){return arguments.length?(e=t,n):e},n},oa.svg.diagonal.radial=function(){var n=oa.svg.diagonal(),t=Zo,r=n.projection;return n.projection=function(n){return arguments.length?r(Vo(t=n)):t},n},oa.svg.symbol=function(){function n(n,e){return(jl.get(t.call(this,n,e))||Bo)(r.call(this,n,e))}var t=Xo,r=$o;return n.type=function(r){return arguments.length?(t=Et(r),n):t},n.size=function(t){return arguments.length?(r=Et(t),n):r},n};var jl=oa.map({circle:Bo,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Il)),r=t*Il;return"M0,"+-t+"L"+r+",0"+" 0,"+t+" "+-r+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/Ol),r=t*Ol/2;return"M0,"+r+"L"+t+","+-r+" "+-t+","+-r+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/Ol),r=t*Ol/2;return"M0,"+-r+"L"+t+","+r+" "+-t+","+r+"Z"}});oa.svg.symbolTypes=jl.keys();var Hl,Fl,Ol=Math.sqrt(3),Il=Math.tan(30*Ia),Yl=[],Zl=0;Yl.call=La.call,Yl.empty=La.empty,Yl.node=La.node,Yl.size=La.size,oa.transition=function(n){return arguments.length?Hl?n.transition():n:za.transition()},oa.transition.prototype=Yl,Yl.select=function(n){var t,r,e,u=this.id,i=[];n=b(n);for(var o=-1,a=this.length;++oi;i++){u.push(t=[]);for(var r=this[i],a=0,c=r.length;c>a;a++)(e=r[a])&&n.call(e,e.__data__,a,i)&&t.push(e)}return Jo(u,this.id)},Yl.tween=function(n,t){var r=this.id;return arguments.length<2?this.node().__transition__[r].tween.get(n):P(this,null==t?function(t){t.__transition__[r].tween.remove(n)}:function(e){e.__transition__[r].tween.set(n,t)})},Yl.attr=function(n,t){function r(){this.removeAttribute(a)}function e(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?r:(n+="",function(){var t,r=this.getAttribute(a);return r!==n&&(t=o(r,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?e:(n+="",function(){var t,r=this.getAttributeNS(a.space,a.local);return r!==n&&(t=o(r,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Bu:ku,a=oa.ns.qualify(n);return Wo(this,"attr."+n,t,a.local?i:u)},Yl.attrTween=function(n,t){function r(n,r){var e=t.call(this,n,r,this.getAttribute(u));return e&&function(n){this.setAttribute(u,e(n))}}function e(n,r){var e=t.call(this,n,r,this.getAttributeNS(u.space,u.local));return e&&function(n){this.setAttributeNS(u.space,u.local,e(n))}}var u=oa.ns.qualify(n);return this.tween("attr."+n,u.local?e:r)},Yl.style=function(n,t,r){function e(){this.style.removeProperty(n)}function u(t){return null==t?e:(t+="",function(){var e,u=fa.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(e=ku(u,t),function(t){this.style.setProperty(n,e(t),r)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(r in n)this.style(r,n[r],t);return this}r=""}return Wo(this,"style."+n,t,u)},Yl.styleTween=function(n,t,r){function e(e,u){var i=t.call(this,e,u,fa.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,e)},Yl.text=function(n){return Wo(this,"text",n,Go)},Yl.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Yl.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=oa.ease.apply(oa,arguments)),P(this,function(r){r.__transition__[t].ease=n}))},Yl.delay=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].delay:P(this,"function"==typeof n?function(r,e,u){r.__transition__[t].delay=+n.call(r,r.__data__,e,u)}:(n=+n,function(r){r.__transition__[t].delay=n}))},Yl.duration=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].duration:P(this,"function"==typeof n?function(r,e,u){r.__transition__[t].duration=Math.max(1,n.call(r,r.__data__,e,u))}:(n=Math.max(1,n),function(r){r.__transition__[t].duration=n}))},Yl.each=function(n,t){var r=this.id;if(arguments.length<2){var e=Fl,u=Hl;Hl=r,P(this,function(t,e,u){Fl=t.__transition__[r],n.call(t,t.__data__,e,u)}),Fl=e,Hl=u}else P(this,function(e){var u=e.__transition__[r];(u.event||(u.event=oa.dispatch("start","end"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,r,e,u=this.id,i=++Zl,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],l=0,s=t.length;s>l;l++)(r=t[l])&&(e=Object.create(r.__transition__[u]),e.delay+=e.duration,Ko(r,l,i,e)),n.push(r)}return Jo(o,i)},oa.svg.axis=function(){function n(n){n.each(function(){var n,l=oa.select(this),s=this.__chart__||r,f=this.__chart__=r.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):Nt:t,p=l.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Fa),d=oa.transition(p.exit()).style("opacity",Fa).remove(),m=oa.transition(p.order()).style("opacity",1),y=Bi(f),x=l.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),oa.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(e){case"bottom":n=Qo,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=Qo,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=na,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=na,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,N=E.rangeBand()/2;s=f=function(n){return E(n)+N}}else s.rangeBand?s=f:d.call(n,f);v.call(n,s),m.call(n,f)})}var t,r=oa.scale.linear(),e=Vl,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(r=t,n):r},n.orient=function(t){return arguments.length?(e=t in $l?t+"":Vl,n):e},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(r){return arguments.length?(t=r,n):t},n.tickSize=function(t){var r=arguments.length;return r?(u=+t,i=+arguments[r-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",$l={top:1,right:1,bottom:1,left:1};oa.svg.brush=function(){function n(i){i.each(function(){var i=oa.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(p,Nt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Xl[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var s,f=oa.transition(i),h=oa.transition(o);c&&(s=Bi(c),h.attr("x",s[0]).attr("width",s[1]-s[0]),r(f)),l&&(s=Bi(l),h.attr("y",s[0]).attr("height",s[1]-s[0]),e(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+f[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function e(n){n.select(".extent").attr("y",f[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1]-f[0])}function u(){function u(){32==oa.event.keyCode&&(A||(x=null,L[0]-=s[1],L[1]-=f[1],A=2),y())}function p(){32==oa.event.keyCode&&2==A&&(L[0]+=s[1],L[1]+=f[1],A=0,y())}function v(){var n=oa.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),A||(oa.event.altKey?(x||(x=[(s[0]+s[1])/2,(f[0]+f[1])/2]),L[0]=s[+(n[0]p?(u=e,e=p):u=p),v[0]!=e||v[1]!=u?(r?o=null:i=null,v[0]=e,v[1]=u,!0):void 0}function m(){v(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),oa.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),C(),w({type:"brushend"})}var x,M,_=this,b=oa.select(oa.event.target),w=a.of(_,arguments),S=oa.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,N=!/^(e|w)$/.test(k)&&l,A=b.classed("extent"),C=Y(),L=oa.mouse(_),q=oa.select(fa).on("keydown.brush",u).on("keyup.brush",p);if(oa.event.changedTouches?q.on("touchmove.brush",v).on("touchend.brush",m):q.on("mousemove.brush",v).on("mouseup.brush",m),S.interrupt().selectAll("*").interrupt(),A)L[0]=s[0]-L[0],L[1]=f[0]-L[1];else if(k){var T=+/w$/.test(k),z=+/^n/.test(k);M=[s[1-T]-L[0],f[1-z]-L[1]],L[0]=s[T],L[1]=f[z]}else oa.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),oa.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),v()}var i,o,a=M(n,"brushstart","brush","brushend"),c=null,l=null,s=[0,0],f=[0,0],h=!0,g=!0,p=Bl[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:s,y:f,i:i,j:o},r=this.__chart__||t;this.__chart__=t,Hl?oa.select(this).transition().each("start.brush",function(){i=r.i,o=r.j,s=r.x,f=r.y,n({type:"brushstart"})}).tween("brush:brush",function(){var r=Eu(s,t.x),e=Eu(f,t.y);return i=o=null,function(u){s=t.x=r(u),f=t.y=e(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,p=Bl[!c<<1|!l],n):c},n.y=function(t){return arguments.length?(l=t,p=Bl[!c<<1|!l],n):l},n.clamp=function(t){return arguments.length?(c&&l?(h=!!t[0],g=!!t[1]):c?h=!!t:l&&(g=!!t),n):c&&l?[h,g]:c?h:l?g:null},n.extent=function(t){var r,e,u,a,h;return arguments.length?(c&&(r=t[0],e=t[1],l&&(r=r[0],e=e[0]),i=[r,e],c.invert&&(r=c(r),e=c(e)),r>e&&(h=r,r=e,e=h),(r!=s[0]||e!=s[1])&&(s=[r,e])),l&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],l.invert&&(u=l(u),a=l(a)),u>a&&(h=u,u=a,a=h),(u!=f[0]||a!=f[1])&&(f=[u,a])),n):(c&&(i?(r=i[0],e=i[1]):(r=s[0],e=s[1],c.invert&&(r=c.invert(r),e=c.invert(e)),r>e&&(h=r,r=e,e=h))),l&&(o?(u=o[0],a=o[1]):(u=f[0],a=f[1],l.invert&&(u=l.invert(u),a=l.invert(a)),u>a&&(h=u,u=a,a=h))),c&&l?[[r,u],[e,a]]:c?[r,e]:l&&[u,a])},n.clear=function(){return n.empty()||(s=[0,0],f=[0,0],i=o=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!l&&f[0]==f[1]},oa.rebind(n,a,"on")};var Xl={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Jl=pc.format=Mc.timeFormat,Wl=Jl.utc,Gl=Wl("%Y-%m-%dT%H:%M:%S.%LZ");Jl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?ta:Gl,ta.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},ta.toString=Gl.toString,pc.second=Ht(function(n){return new vc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),pc.seconds=pc.second.range,pc.seconds.utc=pc.second.utc.range,pc.minute=Ht(function(n){return new vc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),pc.minutes=pc.minute.range,pc.minutes.utc=pc.minute.utc.range,pc.hour=Ht(function(n){var t=n.getTimezoneOffset()/60;return new vc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),pc.hours=pc.hour.range,pc.hours.utc=pc.hour.utc.range,pc.month=Ht(function(n){return n=pc.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),pc.months=pc.month.range,pc.months.utc=pc.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[pc.second,1],[pc.second,5],[pc.second,15],[pc.second,30],[pc.minute,1],[pc.minute,5],[pc.minute,15],[pc.minute,30],[pc.hour,1],[pc.hour,3],[pc.hour,6],[pc.hour,12],[pc.day,1],[pc.day,2],[pc.week,1],[pc.month,1],[pc.month,3],[pc.year,1]],ns=Jl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",Cr]]),ts={range:function(n,t,r){return oa.range(Math.ceil(n/r)*r,+t,r).map(ea)},floor:Nt,ceil:Nt};Ql.year=pc.year,pc.scale=function(){return ra(oa.scale.linear(),Ql,ns)};var rs=Ql.map(function(n){return[n[0].utc,n[1]]}),es=Wl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",Cr]]);rs.year=pc.year.utc,pc.scale.utc=function(){return ra(oa.scale.linear(),rs,es)},oa.text=At(function(n){return n.responseText}),oa.json=function(n,t){return Ct(n,"application/json",ua,t)},oa.html=function(n,t){return Ct(n,"text/html",ia,t)},oa.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(oa):"object"==typeof module&&module.exports?module.exports=oa:this.d3=oa}(); \ No newline at end of file diff --git a/public/js/dropzone/basic.css b/public/js/dropzone/basic.css new file mode 100755 index 00000000..b72d7dea --- /dev/null +++ b/public/js/dropzone/basic.css @@ -0,0 +1,39 @@ +/* + * The MIT License + * Copyright (c) 2012 Matias Meno + */ +.dropzone, .dropzone * { + box-sizing: border-box; } + +.dropzone { + position: relative; } + .dropzone .dz-preview { + position: relative; + display: inline-block; + width: 120px; + margin: 0.5em; } + .dropzone .dz-preview .dz-progress { + display: block; + height: 15px; + border: 1px solid #aaa; } + .dropzone .dz-preview .dz-progress .dz-upload { + display: block; + height: 100%; + width: 0; + background: green; } + .dropzone .dz-preview .dz-error-message { + color: red; + display: none; } + .dropzone .dz-preview.dz-error .dz-error-message, .dropzone .dz-preview.dz-error .dz-error-mark { + display: block; } + .dropzone .dz-preview.dz-success .dz-success-mark { + display: block; } + .dropzone .dz-preview .dz-error-mark, .dropzone .dz-preview .dz-success-mark { + position: absolute; + display: none; + left: 30px; + top: 30px; + width: 54px; + height: 58px; + left: 50%; + margin-left: -27px; } diff --git a/public/js/dropzone/dropzone-amd-module.js b/public/js/dropzone/dropzone-amd-module.js new file mode 100755 index 00000000..66e7412f --- /dev/null +++ b/public/js/dropzone/dropzone-amd-module.js @@ -0,0 +1,1743 @@ +// Uses AMD or browser globals to create a jQuery plugin. +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else { + // Browser globals + factory(jQuery); + } +} (function (jQuery) { + var module = { exports: { } }; // Fake component + + +/* + * + * More info at [www.dropzonejs.com](http://www.dropzonejs.com) + * + * Copyright (c) 2012, Matias Meno + * + * 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. + * + */ + +(function() { + var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, + __slice = [].slice, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + noop = function() {}; + + Emitter = (function() { + function Emitter() {} + + Emitter.prototype.addEventListener = Emitter.prototype.on; + + Emitter.prototype.on = function(event, fn) { + this._callbacks = this._callbacks || {}; + if (!this._callbacks[event]) { + this._callbacks[event] = []; + } + this._callbacks[event].push(fn); + return this; + }; + + Emitter.prototype.emit = function() { + var args, callback, callbacks, event, _i, _len; + event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + this._callbacks = this._callbacks || {}; + callbacks = this._callbacks[event]; + if (callbacks) { + for (_i = 0, _len = callbacks.length; _i < _len; _i++) { + callback = callbacks[_i]; + callback.apply(this, args); + } + } + return this; + }; + + Emitter.prototype.removeListener = Emitter.prototype.off; + + Emitter.prototype.removeAllListeners = Emitter.prototype.off; + + Emitter.prototype.removeEventListener = Emitter.prototype.off; + + Emitter.prototype.off = function(event, fn) { + var callback, callbacks, i, _i, _len; + if (!this._callbacks || arguments.length === 0) { + this._callbacks = {}; + return this; + } + callbacks = this._callbacks[event]; + if (!callbacks) { + return this; + } + if (arguments.length === 1) { + delete this._callbacks[event]; + return this; + } + for (i = _i = 0, _len = callbacks.length; _i < _len; i = ++_i) { + callback = callbacks[i]; + if (callback === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; + }; + + return Emitter; + + })(); + + Dropzone = (function(_super) { + var extend, resolveOption; + + __extends(Dropzone, _super); + + Dropzone.prototype.Emitter = Emitter; + + + /* + This is a list of all available events you can register on a dropzone object. + + You can register an event handler like this: + + dropzone.on("dragEnter", function() { }); + */ + + Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"]; + + Dropzone.prototype.defaultOptions = { + url: null, + method: "post", + withCredentials: false, + parallelUploads: 2, + uploadMultiple: false, + maxFilesize: 256, + paramName: "file", + createImageThumbnails: true, + maxThumbnailFilesize: 10, + thumbnailWidth: 120, + thumbnailHeight: 120, + filesizeBase: 1000, + maxFiles: null, + filesizeBase: 1000, + params: {}, + clickable: true, + ignoreHiddenFiles: true, + acceptedFiles: null, + acceptedMimeTypes: null, + autoProcessQueue: true, + autoQueue: true, + addRemoveLinks: false, + previewsContainer: null, + capture: null, + dictDefaultMessage: "Drop files here to upload", + dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.", + dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.", + dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", + dictInvalidFileType: "You can't upload files of this type.", + dictResponseError: "Server responded with {{statusCode}} code.", + dictCancelUpload: "Cancel upload", + dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", + dictRemoveFile: "Remove file", + dictRemoveFileConfirmation: null, + dictMaxFilesExceeded: "You can not upload any more files.", + accept: function(file, done) { + return done(); + }, + init: function() { + return noop; + }, + forceFallback: false, + fallback: function() { + var child, messageElement, span, _i, _len, _ref; + this.element.className = "" + this.element.className + " dz-browser-not-supported"; + _ref = this.element.getElementsByTagName("div"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + if (/(^| )dz-message($| )/.test(child.className)) { + messageElement = child; + child.className = "dz-message"; + continue; + } + } + if (!messageElement) { + messageElement = Dropzone.createElement("
"); + this.element.appendChild(messageElement); + } + span = messageElement.getElementsByTagName("span")[0]; + if (span) { + span.textContent = this.options.dictFallbackMessage; + } + return this.element.appendChild(this.getFallbackForm()); + }, + resize: function(file) { + var info, srcRatio, trgRatio; + info = { + srcX: 0, + srcY: 0, + srcWidth: file.width, + srcHeight: file.height + }; + srcRatio = file.width / file.height; + info.optWidth = this.options.thumbnailWidth; + info.optHeight = this.options.thumbnailHeight; + if ((info.optWidth == null) && (info.optHeight == null)) { + info.optWidth = info.srcWidth; + info.optHeight = info.srcHeight; + } else if (info.optWidth == null) { + info.optWidth = srcRatio * info.optHeight; + } else if (info.optHeight == null) { + info.optHeight = (1 / srcRatio) * info.optWidth; + } + trgRatio = info.optWidth / info.optHeight; + if (file.height < info.optHeight || file.width < info.optWidth) { + info.trgHeight = info.srcHeight; + info.trgWidth = info.srcWidth; + } else { + if (srcRatio > trgRatio) { + info.srcHeight = file.height; + info.srcWidth = info.srcHeight * trgRatio; + } else { + info.srcWidth = file.width; + info.srcHeight = info.srcWidth / trgRatio; + } + } + info.srcX = (file.width - info.srcWidth) / 2; + info.srcY = (file.height - info.srcHeight) / 2; + return info; + }, + + /* + Those functions register themselves to the events on init and handle all + the user interface specific stuff. Overwriting them won't break the upload + but can break the way it's displayed. + You can overwrite them if you don't like the default behavior. If you just + want to add an additional event handler, register it on the dropzone object + and don't overwrite those options. + */ + drop: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragstart: noop, + dragend: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragenter: function(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragover: function(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragleave: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + paste: noop, + reset: function() { + return this.element.classList.remove("dz-started"); + }, + addedfile: function(file) { + var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results; + if (this.element === this.previewsContainer) { + this.element.classList.add("dz-started"); + } + if (this.previewsContainer) { + file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim()); + file.previewTemplate = file.previewElement; + this.previewsContainer.appendChild(file.previewElement); + _ref = file.previewElement.querySelectorAll("[data-dz-name]"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + node.textContent = file.name; + } + _ref1 = file.previewElement.querySelectorAll("[data-dz-size]"); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + node = _ref1[_j]; + node.innerHTML = this.filesize(file.size); + } + if (this.options.addRemoveLinks) { + file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + ""); + file.previewElement.appendChild(file._removeLink); + } + removeFileEvent = (function(_this) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + if (file.status === Dropzone.UPLOADING) { + return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() { + return _this.removeFile(file); + }); + } else { + if (_this.options.dictRemoveFileConfirmation) { + return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() { + return _this.removeFile(file); + }); + } else { + return _this.removeFile(file); + } + } + }; + })(this); + _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]"); + _results = []; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + removeLink = _ref2[_k]; + _results.push(removeLink.addEventListener("click", removeFileEvent)); + } + return _results; + } + }, + removedfile: function(file) { + var _ref; + if (file.previewElement) { + if ((_ref = file.previewElement) != null) { + _ref.parentNode.removeChild(file.previewElement); + } + } + return this._updateMaxFilesReachedClass(); + }, + thumbnail: function(file, dataUrl) { + var thumbnailElement, _i, _len, _ref; + if (file.previewElement) { + file.previewElement.classList.remove("dz-file-preview"); + _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + thumbnailElement = _ref[_i]; + thumbnailElement.alt = file.name; + thumbnailElement.src = dataUrl; + } + return setTimeout(((function(_this) { + return function() { + return file.previewElement.classList.add("dz-image-preview"); + }; + })(this)), 1); + } + }, + error: function(file, message) { + var node, _i, _len, _ref, _results; + if (file.previewElement) { + file.previewElement.classList.add("dz-error"); + if (typeof message !== "String" && message.error) { + message = message.error; + } + _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]"); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + _results.push(node.textContent = message); + } + return _results; + } + }, + errormultiple: noop, + processing: function(file) { + if (file.previewElement) { + file.previewElement.classList.add("dz-processing"); + if (file._removeLink) { + return file._removeLink.textContent = this.options.dictCancelUpload; + } + } + }, + processingmultiple: noop, + uploadprogress: function(file, progress, bytesSent) { + var node, _i, _len, _ref, _results; + if (file.previewElement) { + _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]"); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + if (node.nodeName === 'PROGRESS') { + _results.push(node.value = progress); + } else { + _results.push(node.style.width = "" + progress + "%"); + } + } + return _results; + } + }, + totaluploadprogress: noop, + sending: noop, + sendingmultiple: noop, + success: function(file) { + if (file.previewElement) { + return file.previewElement.classList.add("dz-success"); + } + }, + successmultiple: noop, + canceled: function(file) { + return this.emit("error", file, "Upload canceled."); + }, + canceledmultiple: noop, + complete: function(file) { + if (file._removeLink) { + file._removeLink.textContent = this.options.dictRemoveFile; + } + if (file.previewElement) { + return file.previewElement.classList.add("dz-complete"); + } + }, + completemultiple: noop, + maxfilesexceeded: noop, + maxfilesreached: noop, + queuecomplete: noop, + previewTemplate: "
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
" + }; + + extend = function() { + var key, object, objects, target, val, _i, _len; + target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + for (_i = 0, _len = objects.length; _i < _len; _i++) { + object = objects[_i]; + for (key in object) { + val = object[key]; + target[key] = val; + } + } + return target; + }; + + function Dropzone(element, options) { + var elementOptions, fallback, _ref; + this.element = element; + this.version = Dropzone.version; + this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, ""); + this.clickableElements = []; + this.listeners = []; + this.files = []; + if (typeof this.element === "string") { + this.element = document.querySelector(this.element); + } + if (!(this.element && (this.element.nodeType != null))) { + throw new Error("Invalid dropzone element."); + } + if (this.element.dropzone) { + throw new Error("Dropzone already attached."); + } + Dropzone.instances.push(this); + this.element.dropzone = this; + elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {}; + this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {}); + if (this.options.forceFallback || !Dropzone.isBrowserSupported()) { + return this.options.fallback.call(this); + } + if (this.options.url == null) { + this.options.url = this.element.getAttribute("action"); + } + if (!this.options.url) { + throw new Error("No URL provided."); + } + if (this.options.acceptedFiles && this.options.acceptedMimeTypes) { + throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); + } + if (this.options.acceptedMimeTypes) { + this.options.acceptedFiles = this.options.acceptedMimeTypes; + delete this.options.acceptedMimeTypes; + } + this.options.method = this.options.method.toUpperCase(); + if ((fallback = this.getExistingFallback()) && fallback.parentNode) { + fallback.parentNode.removeChild(fallback); + } + if (this.options.previewsContainer !== false) { + if (this.options.previewsContainer) { + this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer"); + } else { + this.previewsContainer = this.element; + } + } + if (this.options.clickable) { + if (this.options.clickable === true) { + this.clickableElements = [this.element]; + } else { + this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable"); + } + } + this.init(); + } + + Dropzone.prototype.getAcceptedFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.accepted) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getRejectedFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (!file.accepted) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getFilesWithStatus = function(status) { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status === status) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getQueuedFiles = function() { + return this.getFilesWithStatus(Dropzone.QUEUED); + }; + + Dropzone.prototype.getUploadingFiles = function() { + return this.getFilesWithStatus(Dropzone.UPLOADING); + }; + + Dropzone.prototype.getActiveFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.init = function() { + var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1; + if (this.element.tagName === "form") { + this.element.setAttribute("enctype", "multipart/form-data"); + } + if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) { + this.element.appendChild(Dropzone.createElement("
" + this.options.dictDefaultMessage + "
")); + } + if (this.clickableElements.length) { + setupHiddenFileInput = (function(_this) { + return function() { + if (_this.hiddenFileInput) { + document.body.removeChild(_this.hiddenFileInput); + } + _this.hiddenFileInput = document.createElement("input"); + _this.hiddenFileInput.setAttribute("type", "file"); + if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) { + _this.hiddenFileInput.setAttribute("multiple", "multiple"); + } + _this.hiddenFileInput.className = "dz-hidden-input"; + if (_this.options.acceptedFiles != null) { + _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles); + } + if (_this.options.capture != null) { + _this.hiddenFileInput.setAttribute("capture", _this.options.capture); + } + _this.hiddenFileInput.style.visibility = "hidden"; + _this.hiddenFileInput.style.position = "absolute"; + _this.hiddenFileInput.style.top = "0"; + _this.hiddenFileInput.style.left = "0"; + _this.hiddenFileInput.style.height = "0"; + _this.hiddenFileInput.style.width = "0"; + document.body.appendChild(_this.hiddenFileInput); + return _this.hiddenFileInput.addEventListener("change", function() { + var file, files, _i, _len; + files = _this.hiddenFileInput.files; + if (files.length) { + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + _this.addFile(file); + } + } + return setupHiddenFileInput(); + }); + }; + })(this); + setupHiddenFileInput(); + } + this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL; + _ref1 = this.events; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + eventName = _ref1[_i]; + this.on(eventName, this.options[eventName]); + } + this.on("uploadprogress", (function(_this) { + return function() { + return _this.updateTotalUploadProgress(); + }; + })(this)); + this.on("removedfile", (function(_this) { + return function() { + return _this.updateTotalUploadProgress(); + }; + })(this)); + this.on("canceled", (function(_this) { + return function(file) { + return _this.emit("complete", file); + }; + })(this)); + this.on("complete", (function(_this) { + return function(file) { + if (_this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) { + return setTimeout((function() { + return _this.emit("queuecomplete"); + }), 0); + } + }; + })(this)); + noPropagation = function(e) { + e.stopPropagation(); + if (e.preventDefault) { + return e.preventDefault(); + } else { + return e.returnValue = false; + } + }; + this.listeners = [ + { + element: this.element, + events: { + "dragstart": (function(_this) { + return function(e) { + return _this.emit("dragstart", e); + }; + })(this), + "dragenter": (function(_this) { + return function(e) { + noPropagation(e); + return _this.emit("dragenter", e); + }; + })(this), + "dragover": (function(_this) { + return function(e) { + var efct; + try { + efct = e.dataTransfer.effectAllowed; + } catch (_error) {} + e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy'; + noPropagation(e); + return _this.emit("dragover", e); + }; + })(this), + "dragleave": (function(_this) { + return function(e) { + return _this.emit("dragleave", e); + }; + })(this), + "drop": (function(_this) { + return function(e) { + noPropagation(e); + return _this.drop(e); + }; + })(this), + "dragend": (function(_this) { + return function(e) { + return _this.emit("dragend", e); + }; + })(this) + } + } + ]; + this.clickableElements.forEach((function(_this) { + return function(clickableElement) { + return _this.listeners.push({ + element: clickableElement, + events: { + "click": function(evt) { + if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) { + return _this.hiddenFileInput.click(); + } + } + } + }); + }; + })(this)); + this.enable(); + return this.options.init.call(this); + }; + + Dropzone.prototype.destroy = function() { + var _ref; + this.disable(); + this.removeAllFiles(true); + if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) { + this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); + this.hiddenFileInput = null; + } + delete this.element.dropzone; + return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); + }; + + Dropzone.prototype.updateTotalUploadProgress = function() { + var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref; + totalBytesSent = 0; + totalBytes = 0; + activeFiles = this.getActiveFiles(); + if (activeFiles.length) { + _ref = this.getActiveFiles(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + totalBytesSent += file.upload.bytesSent; + totalBytes += file.upload.total; + } + totalUploadProgress = 100 * totalBytesSent / totalBytes; + } else { + totalUploadProgress = 100; + } + return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent); + }; + + Dropzone.prototype._getParamName = function(n) { + if (typeof this.options.paramName === "function") { + return this.options.paramName(n); + } else { + return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : ""); + } + }; + + Dropzone.prototype.getFallbackForm = function() { + var existingFallback, fields, fieldsString, form; + if (existingFallback = this.getExistingFallback()) { + return existingFallback; + } + fieldsString = "
"; + if (this.options.dictFallbackText) { + fieldsString += "

" + this.options.dictFallbackText + "

"; + } + fieldsString += "
"; + fields = Dropzone.createElement(fieldsString); + if (this.element.tagName !== "FORM") { + form = Dropzone.createElement("
"); + form.appendChild(fields); + } else { + this.element.setAttribute("enctype", "multipart/form-data"); + this.element.setAttribute("method", this.options.method); + } + return form != null ? form : fields; + }; + + Dropzone.prototype.getExistingFallback = function() { + var fallback, getFallback, tagName, _i, _len, _ref; + getFallback = function(elements) { + var el, _i, _len; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + el = elements[_i]; + if (/(^| )fallback($| )/.test(el.className)) { + return el; + } + } + }; + _ref = ["div", "form"]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + tagName = _ref[_i]; + if (fallback = getFallback(this.element.getElementsByTagName(tagName))) { + return fallback; + } + } + }; + + Dropzone.prototype.setupEventListeners = function() { + var elementListeners, event, listener, _i, _len, _ref, _results; + _ref = this.listeners; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elementListeners = _ref[_i]; + _results.push((function() { + var _ref1, _results1; + _ref1 = elementListeners.events; + _results1 = []; + for (event in _ref1) { + listener = _ref1[event]; + _results1.push(elementListeners.element.addEventListener(event, listener, false)); + } + return _results1; + })()); + } + return _results; + }; + + Dropzone.prototype.removeEventListeners = function() { + var elementListeners, event, listener, _i, _len, _ref, _results; + _ref = this.listeners; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elementListeners = _ref[_i]; + _results.push((function() { + var _ref1, _results1; + _ref1 = elementListeners.events; + _results1 = []; + for (event in _ref1) { + listener = _ref1[event]; + _results1.push(elementListeners.element.removeEventListener(event, listener, false)); + } + return _results1; + })()); + } + return _results; + }; + + Dropzone.prototype.disable = function() { + var file, _i, _len, _ref, _results; + this.clickableElements.forEach(function(element) { + return element.classList.remove("dz-clickable"); + }); + this.removeEventListeners(); + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + _results.push(this.cancelUpload(file)); + } + return _results; + }; + + Dropzone.prototype.enable = function() { + this.clickableElements.forEach(function(element) { + return element.classList.add("dz-clickable"); + }); + return this.setupEventListeners(); + }; + + Dropzone.prototype.filesize = function(size) { + var cutoff, i, selectedSize, selectedUnit, unit, units, _i, _len; + units = ['TB', 'GB', 'MB', 'KB', 'b']; + selectedSize = selectedUnit = null; + for (i = _i = 0, _len = units.length; _i < _len; i = ++_i) { + unit = units[i]; + cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; + if (size >= cutoff) { + selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); + selectedUnit = unit; + break; + } + } + selectedSize = Math.round(10 * selectedSize) / 10; + return "" + selectedSize + " " + selectedUnit; + }; + + Dropzone.prototype._updateMaxFilesReachedClass = function() { + if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + if (this.getAcceptedFiles().length === this.options.maxFiles) { + this.emit('maxfilesreached', this.files); + } + return this.element.classList.add("dz-max-files-reached"); + } else { + return this.element.classList.remove("dz-max-files-reached"); + } + }; + + Dropzone.prototype.drop = function(e) { + var files, items; + if (!e.dataTransfer) { + return; + } + this.emit("drop", e); + files = e.dataTransfer.files; + if (files.length) { + items = e.dataTransfer.items; + if (items && items.length && (items[0].webkitGetAsEntry != null)) { + this._addFilesFromItems(items); + } else { + this.handleFiles(files); + } + } + }; + + Dropzone.prototype.paste = function(e) { + var items, _ref; + if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) { + return; + } + this.emit("paste", e); + items = e.clipboardData.items; + if (items.length) { + return this._addFilesFromItems(items); + } + }; + + Dropzone.prototype.handleFiles = function(files) { + var file, _i, _len, _results; + _results = []; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + _results.push(this.addFile(file)); + } + return _results; + }; + + Dropzone.prototype._addFilesFromItems = function(items) { + var entry, item, _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) { + if (entry.isFile) { + _results.push(this.addFile(item.getAsFile())); + } else if (entry.isDirectory) { + _results.push(this._addFilesFromDirectory(entry, entry.name)); + } else { + _results.push(void 0); + } + } else if (item.getAsFile != null) { + if ((item.kind == null) || item.kind === "file") { + _results.push(this.addFile(item.getAsFile())); + } else { + _results.push(void 0); + } + } else { + _results.push(void 0); + } + } + return _results; + }; + + Dropzone.prototype._addFilesFromDirectory = function(directory, path) { + var dirReader, entriesReader; + dirReader = directory.createReader(); + entriesReader = (function(_this) { + return function(entries) { + var entry, _i, _len; + for (_i = 0, _len = entries.length; _i < _len; _i++) { + entry = entries[_i]; + if (entry.isFile) { + entry.file(function(file) { + if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') { + return; + } + file.fullPath = "" + path + "/" + file.name; + return _this.addFile(file); + }); + } else if (entry.isDirectory) { + _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name); + } + } + }; + })(this); + return dirReader.readEntries(entriesReader, function(error) { + return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0; + }); + }; + + Dropzone.prototype.accept = function(file, done) { + if (file.size > this.options.maxFilesize * 1024 * 1024) { + return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize)); + } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) { + return done(this.options.dictInvalidFileType); + } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles)); + return this.emit("maxfilesexceeded", file); + } else { + return this.options.accept.call(this, file, done); + } + }; + + Dropzone.prototype.addFile = function(file) { + file.upload = { + progress: 0, + total: file.size, + bytesSent: 0 + }; + this.files.push(file); + file.status = Dropzone.ADDED; + this.emit("addedfile", file); + this._enqueueThumbnail(file); + return this.accept(file, (function(_this) { + return function(error) { + if (error) { + file.accepted = false; + _this._errorProcessing([file], error); + } else { + file.accepted = true; + if (_this.options.autoQueue) { + _this.enqueueFile(file); + } + } + return _this._updateMaxFilesReachedClass(); + }; + })(this)); + }; + + Dropzone.prototype.enqueueFiles = function(files) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + this.enqueueFile(file); + } + return null; + }; + + Dropzone.prototype.enqueueFile = function(file) { + if (file.status === Dropzone.ADDED && file.accepted === true) { + file.status = Dropzone.QUEUED; + if (this.options.autoProcessQueue) { + return setTimeout(((function(_this) { + return function() { + return _this.processQueue(); + }; + })(this)), 0); + } + } else { + throw new Error("This file can't be queued because it has already been processed or was rejected."); + } + }; + + Dropzone.prototype._thumbnailQueue = []; + + Dropzone.prototype._processingThumbnail = false; + + Dropzone.prototype._enqueueThumbnail = function(file) { + if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) { + this._thumbnailQueue.push(file); + return setTimeout(((function(_this) { + return function() { + return _this._processThumbnailQueue(); + }; + })(this)), 0); + } + }; + + Dropzone.prototype._processThumbnailQueue = function() { + if (this._processingThumbnail || this._thumbnailQueue.length === 0) { + return; + } + this._processingThumbnail = true; + return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) { + return function() { + _this._processingThumbnail = false; + return _this._processThumbnailQueue(); + }; + })(this)); + }; + + Dropzone.prototype.removeFile = function(file) { + if (file.status === Dropzone.UPLOADING) { + this.cancelUpload(file); + } + this.files = without(this.files, file); + this.emit("removedfile", file); + if (this.files.length === 0) { + return this.emit("reset"); + } + }; + + Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) { + var file, _i, _len, _ref; + if (cancelIfNecessary == null) { + cancelIfNecessary = false; + } + _ref = this.files.slice(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) { + this.removeFile(file); + } + } + return null; + }; + + Dropzone.prototype.createThumbnail = function(file, callback) { + var fileReader; + fileReader = new FileReader; + fileReader.onload = (function(_this) { + return function() { + if (file.type === "image/svg+xml") { + _this.emit("thumbnail", file, fileReader.result); + if (callback != null) { + callback(); + } + return; + } + return _this.createThumbnailFromUrl(file, fileReader.result, callback); + }; + })(this); + return fileReader.readAsDataURL(file); + }; + + Dropzone.prototype.createThumbnailFromUrl = function(file, imageUrl, callback) { + var img; + img = document.createElement("img"); + img.onload = (function(_this) { + return function() { + var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3; + file.width = img.width; + file.height = img.height; + resizeInfo = _this.options.resize.call(_this, file); + if (resizeInfo.trgWidth == null) { + resizeInfo.trgWidth = resizeInfo.optWidth; + } + if (resizeInfo.trgHeight == null) { + resizeInfo.trgHeight = resizeInfo.optHeight; + } + canvas = document.createElement("canvas"); + ctx = canvas.getContext("2d"); + canvas.width = resizeInfo.trgWidth; + canvas.height = resizeInfo.trgHeight; + drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); + thumbnail = canvas.toDataURL("image/png"); + _this.emit("thumbnail", file, thumbnail); + if (callback != null) { + return callback(); + } + }; + })(this); + if (callback != null) { + img.onerror = callback; + } + return img.src = imageUrl; + }; + + Dropzone.prototype.processQueue = function() { + var i, parallelUploads, processingLength, queuedFiles; + parallelUploads = this.options.parallelUploads; + processingLength = this.getUploadingFiles().length; + i = processingLength; + if (processingLength >= parallelUploads) { + return; + } + queuedFiles = this.getQueuedFiles(); + if (!(queuedFiles.length > 0)) { + return; + } + if (this.options.uploadMultiple) { + return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength)); + } else { + while (i < parallelUploads) { + if (!queuedFiles.length) { + return; + } + this.processFile(queuedFiles.shift()); + i++; + } + } + }; + + Dropzone.prototype.processFile = function(file) { + return this.processFiles([file]); + }; + + Dropzone.prototype.processFiles = function(files) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.processing = true; + file.status = Dropzone.UPLOADING; + this.emit("processing", file); + } + if (this.options.uploadMultiple) { + this.emit("processingmultiple", files); + } + return this.uploadFiles(files); + }; + + Dropzone.prototype._getFilesWithXhr = function(xhr) { + var file, files; + return files = (function() { + var _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.xhr === xhr) { + _results.push(file); + } + } + return _results; + }).call(this); + }; + + Dropzone.prototype.cancelUpload = function(file) { + var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref; + if (file.status === Dropzone.UPLOADING) { + groupedFiles = this._getFilesWithXhr(file.xhr); + for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) { + groupedFile = groupedFiles[_i]; + groupedFile.status = Dropzone.CANCELED; + } + file.xhr.abort(); + for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) { + groupedFile = groupedFiles[_j]; + this.emit("canceled", groupedFile); + } + if (this.options.uploadMultiple) { + this.emit("canceledmultiple", groupedFiles); + } + } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) { + file.status = Dropzone.CANCELED; + this.emit("canceled", file); + if (this.options.uploadMultiple) { + this.emit("canceledmultiple", [file]); + } + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + resolveOption = function() { + var args, option; + option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (typeof option === 'function') { + return option.apply(this, args); + } + return option; + }; + + Dropzone.prototype.uploadFile = function(file) { + return this.uploadFiles([file]); + }; + + Dropzone.prototype.uploadFiles = function(files) { + var file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, key, method, option, progressObj, response, updateProgress, url, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; + xhr = new XMLHttpRequest(); + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.xhr = xhr; + } + method = resolveOption(this.options.method, files); + url = resolveOption(this.options.url, files); + xhr.open(method, url, true); + xhr.withCredentials = !!this.options.withCredentials; + response = null; + handleError = (function(_this) { + return function() { + var _j, _len1, _results; + _results = []; + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr)); + } + return _results; + }; + })(this); + updateProgress = (function(_this) { + return function(e) { + var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results; + if (e != null) { + progress = 100 * e.loaded / e.total; + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + file.upload = { + progress: progress, + total: e.total, + bytesSent: e.loaded + }; + } + } else { + allFilesFinished = true; + progress = 100; + for (_k = 0, _len2 = files.length; _k < _len2; _k++) { + file = files[_k]; + if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) { + allFilesFinished = false; + } + file.upload.progress = progress; + file.upload.bytesSent = file.upload.total; + } + if (allFilesFinished) { + return; + } + } + _results = []; + for (_l = 0, _len3 = files.length; _l < _len3; _l++) { + file = files[_l]; + _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent)); + } + return _results; + }; + })(this); + xhr.onload = (function(_this) { + return function(e) { + var _ref; + if (files[0].status === Dropzone.CANCELED) { + return; + } + if (xhr.readyState !== 4) { + return; + } + response = xhr.responseText; + if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) { + try { + response = JSON.parse(response); + } catch (_error) { + e = _error; + response = "Invalid JSON response from server."; + } + } + updateProgress(); + if (!((200 <= (_ref = xhr.status) && _ref < 300))) { + return handleError(); + } else { + return _this._finished(files, response, e); + } + }; + })(this); + xhr.onerror = (function(_this) { + return function() { + if (files[0].status === Dropzone.CANCELED) { + return; + } + return handleError(); + }; + })(this); + progressObj = (_ref = xhr.upload) != null ? _ref : xhr; + progressObj.onprogress = updateProgress; + headers = { + "Accept": "application/json", + "Cache-Control": "no-cache", + "X-Requested-With": "XMLHttpRequest" + }; + if (this.options.headers) { + extend(headers, this.options.headers); + } + for (headerName in headers) { + headerValue = headers[headerName]; + xhr.setRequestHeader(headerName, headerValue); + } + formData = new FormData(); + if (this.options.params) { + _ref1 = this.options.params; + for (key in _ref1) { + value = _ref1[key]; + formData.append(key, value); + } + } + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + this.emit("sending", file, xhr, formData); + } + if (this.options.uploadMultiple) { + this.emit("sendingmultiple", files, xhr, formData); + } + if (this.element.tagName === "FORM") { + _ref2 = this.element.querySelectorAll("input, textarea, select, button"); + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + input = _ref2[_k]; + inputName = input.getAttribute("name"); + inputType = input.getAttribute("type"); + if (input.tagName === "SELECT" && input.hasAttribute("multiple")) { + _ref3 = input.options; + for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { + option = _ref3[_l]; + if (option.selected) { + formData.append(inputName, option.value); + } + } + } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) { + formData.append(inputName, input.value); + } + } + } + for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) { + formData.append(this._getParamName(i), files[i], files[i].name); + } + return xhr.send(formData); + }; + + Dropzone.prototype._finished = function(files, responseText, e) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.status = Dropzone.SUCCESS; + this.emit("success", file, responseText, e); + this.emit("complete", file); + } + if (this.options.uploadMultiple) { + this.emit("successmultiple", files, responseText, e); + this.emit("completemultiple", files); + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + Dropzone.prototype._errorProcessing = function(files, message, xhr) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.status = Dropzone.ERROR; + this.emit("error", file, message, xhr); + this.emit("complete", file); + } + if (this.options.uploadMultiple) { + this.emit("errormultiple", files, message, xhr); + this.emit("completemultiple", files); + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + return Dropzone; + + })(Emitter); + + Dropzone.version = "4.0.1"; + + Dropzone.options = {}; + + Dropzone.optionsForElement = function(element) { + if (element.getAttribute("id")) { + return Dropzone.options[camelize(element.getAttribute("id"))]; + } else { + return void 0; + } + }; + + Dropzone.instances = []; + + Dropzone.forElement = function(element) { + if (typeof element === "string") { + element = document.querySelector(element); + } + if ((element != null ? element.dropzone : void 0) == null) { + throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."); + } + return element.dropzone; + }; + + Dropzone.autoDiscover = true; + + Dropzone.discover = function() { + var checkElements, dropzone, dropzones, _i, _len, _results; + if (document.querySelectorAll) { + dropzones = document.querySelectorAll(".dropzone"); + } else { + dropzones = []; + checkElements = function(elements) { + var el, _i, _len, _results; + _results = []; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + el = elements[_i]; + if (/(^| )dropzone($| )/.test(el.className)) { + _results.push(dropzones.push(el)); + } else { + _results.push(void 0); + } + } + return _results; + }; + checkElements(document.getElementsByTagName("div")); + checkElements(document.getElementsByTagName("form")); + } + _results = []; + for (_i = 0, _len = dropzones.length; _i < _len; _i++) { + dropzone = dropzones[_i]; + if (Dropzone.optionsForElement(dropzone) !== false) { + _results.push(new Dropzone(dropzone)); + } else { + _results.push(void 0); + } + } + return _results; + }; + + Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i]; + + Dropzone.isBrowserSupported = function() { + var capableBrowser, regex, _i, _len, _ref; + capableBrowser = true; + if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) { + if (!("classList" in document.createElement("a"))) { + capableBrowser = false; + } else { + _ref = Dropzone.blacklistedBrowsers; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + regex = _ref[_i]; + if (regex.test(navigator.userAgent)) { + capableBrowser = false; + continue; + } + } + } + } else { + capableBrowser = false; + } + return capableBrowser; + }; + + without = function(list, rejectedItem) { + var item, _i, _len, _results; + _results = []; + for (_i = 0, _len = list.length; _i < _len; _i++) { + item = list[_i]; + if (item !== rejectedItem) { + _results.push(item); + } + } + return _results; + }; + + camelize = function(str) { + return str.replace(/[\-_](\w)/g, function(match) { + return match.charAt(1).toUpperCase(); + }); + }; + + Dropzone.createElement = function(string) { + var div; + div = document.createElement("div"); + div.innerHTML = string; + return div.childNodes[0]; + }; + + Dropzone.elementInside = function(element, container) { + if (element === container) { + return true; + } + while (element = element.parentNode) { + if (element === container) { + return true; + } + } + return false; + }; + + Dropzone.getElement = function(el, name) { + var element; + if (typeof el === "string") { + element = document.querySelector(el); + } else if (el.nodeType != null) { + element = el; + } + if (element == null) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element."); + } + return element; + }; + + Dropzone.getElements = function(els, name) { + var e, el, elements, _i, _j, _len, _len1, _ref; + if (els instanceof Array) { + elements = []; + try { + for (_i = 0, _len = els.length; _i < _len; _i++) { + el = els[_i]; + elements.push(this.getElement(el, name)); + } + } catch (_error) { + e = _error; + elements = null; + } + } else if (typeof els === "string") { + elements = []; + _ref = document.querySelectorAll(els); + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + el = _ref[_j]; + elements.push(el); + } + } else if (els.nodeType != null) { + elements = [els]; + } + if (!((elements != null) && elements.length)) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."); + } + return elements; + }; + + Dropzone.confirm = function(question, accepted, rejected) { + if (window.confirm(question)) { + return accepted(); + } else if (rejected != null) { + return rejected(); + } + }; + + Dropzone.isValidFile = function(file, acceptedFiles) { + var baseMimeType, mimeType, validType, _i, _len; + if (!acceptedFiles) { + return true; + } + acceptedFiles = acceptedFiles.split(","); + mimeType = file.type; + baseMimeType = mimeType.replace(/\/.*$/, ""); + for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) { + validType = acceptedFiles[_i]; + validType = validType.trim(); + if (validType.charAt(0) === ".") { + if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) { + return true; + } + } else if (/\/\*$/.test(validType)) { + if (baseMimeType === validType.replace(/\/.*$/, "")) { + return true; + } + } else { + if (mimeType === validType) { + return true; + } + } + } + return false; + }; + + if (typeof jQuery !== "undefined" && jQuery !== null) { + jQuery.fn.dropzone = function(options) { + return this.each(function() { + return new Dropzone(this, options); + }); + }; + } + + if (typeof module !== "undefined" && module !== null) { + module.exports = Dropzone; + } else { + window.Dropzone = Dropzone; + } + + Dropzone.ADDED = "added"; + + Dropzone.QUEUED = "queued"; + + Dropzone.ACCEPTED = Dropzone.QUEUED; + + Dropzone.UPLOADING = "uploading"; + + Dropzone.PROCESSING = Dropzone.UPLOADING; + + Dropzone.CANCELED = "canceled"; + + Dropzone.ERROR = "error"; + + Dropzone.SUCCESS = "success"; + + + /* + + Bugfix for iOS 6 and 7 + Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios + based on the work of https://github.com/stomita/ios-imagefile-megapixel + */ + + detectVerticalSquash = function(img) { + var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy; + iw = img.naturalWidth; + ih = img.naturalHeight; + canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = ih; + ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + data = ctx.getImageData(0, 0, 1, ih).data; + sy = 0; + ey = ih; + py = ih; + while (py > sy) { + alpha = data[(py - 1) * 4 + 3]; + if (alpha === 0) { + ey = py; + } else { + sy = py; + } + py = (ey + sy) >> 1; + } + ratio = py / ih; + if (ratio === 0) { + return 1; + } else { + return ratio; + } + }; + + drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { + var vertSquashRatio; + vertSquashRatio = detectVerticalSquash(img); + return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); + }; + + + /* + * contentloaded.js + * + * Author: Diego Perini (diego.perini at gmail.com) + * Summary: cross-browser wrapper for DOMContentLoaded + * Updated: 20101020 + * License: MIT + * Version: 1.2 + * + * URL: + * http://javascript.nwbox.com/ContentLoaded/ + * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE + */ + + contentLoaded = function(win, fn) { + var add, doc, done, init, poll, pre, rem, root, top; + done = false; + top = true; + doc = win.document; + root = doc.documentElement; + add = (doc.addEventListener ? "addEventListener" : "attachEvent"); + rem = (doc.addEventListener ? "removeEventListener" : "detachEvent"); + pre = (doc.addEventListener ? "" : "on"); + init = function(e) { + if (e.type === "readystatechange" && doc.readyState !== "complete") { + return; + } + (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); + if (!done && (done = true)) { + return fn.call(win, e.type || e); + } + }; + poll = function() { + var e; + try { + root.doScroll("left"); + } catch (_error) { + e = _error; + setTimeout(poll, 50); + return; + } + return init("poll"); + }; + if (doc.readyState !== "complete") { + if (doc.createEventObject && root.doScroll) { + try { + top = !win.frameElement; + } catch (_error) {} + if (top) { + poll(); + } + } + doc[add](pre + "DOMContentLoaded", init, false); + doc[add](pre + "readystatechange", init, false); + return win[add](pre + "load", init, false); + } + }; + + Dropzone._autoDiscoverFunction = function() { + if (Dropzone.autoDiscover) { + return Dropzone.discover(); + } + }; + + contentLoaded(window, Dropzone._autoDiscoverFunction); + +}).call(this); + + return module.exports; +})); \ No newline at end of file diff --git a/public/js/dropzone/dropzone.css b/public/js/dropzone/dropzone.css new file mode 100755 index 00000000..0494d1cc --- /dev/null +++ b/public/js/dropzone/dropzone.css @@ -0,0 +1,388 @@ +/* + * The MIT License + * Copyright (c) 2012 Matias Meno + */ +@-webkit-keyframes passing-through { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30%, 70% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } + 100% { + opacity: 0; + -webkit-transform: translateY(-40px); + -moz-transform: translateY(-40px); + -ms-transform: translateY(-40px); + -o-transform: translateY(-40px); + transform: translateY(-40px); } } +@-moz-keyframes passing-through { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30%, 70% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } + 100% { + opacity: 0; + -webkit-transform: translateY(-40px); + -moz-transform: translateY(-40px); + -ms-transform: translateY(-40px); + -o-transform: translateY(-40px); + transform: translateY(-40px); } } +@keyframes passing-through { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30%, 70% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } + 100% { + opacity: 0; + -webkit-transform: translateY(-40px); + -moz-transform: translateY(-40px); + -ms-transform: translateY(-40px); + -o-transform: translateY(-40px); + transform: translateY(-40px); } } +@-webkit-keyframes slide-in { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } } +@-moz-keyframes slide-in { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } } +@keyframes slide-in { + 0% { + opacity: 0; + -webkit-transform: translateY(40px); + -moz-transform: translateY(40px); + -ms-transform: translateY(40px); + -o-transform: translateY(40px); + transform: translateY(40px); } + 30% { + opacity: 1; + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); } } +@-webkit-keyframes pulse { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } + 10% { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); } + 20% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } } +@-moz-keyframes pulse { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } + 10% { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); } + 20% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } } +@keyframes pulse { + 0% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } + 10% { + -webkit-transform: scale(1.1); + -moz-transform: scale(1.1); + -ms-transform: scale(1.1); + -o-transform: scale(1.1); + transform: scale(1.1); } + 20% { + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); } } +.dropzone, .dropzone * { + box-sizing: border-box; } + +.dropzone { + min-height: 150px; + border: 2px solid rgba(0, 0, 0, 0.3); + background: white; + padding: 20px 20px; } + .dropzone.dz-clickable { + cursor: pointer; } + .dropzone.dz-clickable * { + cursor: default; } + .dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * { + cursor: pointer; } + .dropzone.dz-started .dz-message { + display: none; } + .dropzone.dz-drag-hover { + border-style: solid; } + .dropzone.dz-drag-hover .dz-message { + opacity: 0.5; } + .dropzone .dz-message { + text-align: center; + margin: 2em 0; } + .dropzone .dz-preview { + position: relative; + display: inline-block; + vertical-align: top; + margin: 16px; + min-height: 100px; } + .dropzone .dz-preview:hover { + z-index: 1000; } + .dropzone .dz-preview:hover .dz-details { + opacity: 1; } + .dropzone .dz-preview.dz-file-preview .dz-image { + border-radius: 20px; + background: #999; + background: linear-gradient(to bottom, #eee, #ddd); } + .dropzone .dz-preview.dz-file-preview .dz-details { + opacity: 1; } + .dropzone .dz-preview.dz-image-preview { + background: white; } + .dropzone .dz-preview.dz-image-preview .dz-details { + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -ms-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; } + .dropzone .dz-preview .dz-remove { + font-size: 14px; + text-align: center; + display: block; + cursor: pointer; + border: none; } + .dropzone .dz-preview .dz-remove:hover { + text-decoration: underline; } + .dropzone .dz-preview:hover .dz-details { + opacity: 1; } + .dropzone .dz-preview .dz-details { + z-index: 20; + position: absolute; + top: 0; + left: 0; + opacity: 0; + font-size: 13px; + min-width: 100%; + max-width: 100%; + padding: 2em 1em; + text-align: center; + color: rgba(0, 0, 0, 0.9); + line-height: 150%; } + .dropzone .dz-preview .dz-details .dz-size { + margin-bottom: 1em; + font-size: 16px; } + .dropzone .dz-preview .dz-details .dz-filename { + white-space: nowrap; } + .dropzone .dz-preview .dz-details .dz-filename:hover span { + border: 1px solid rgba(200, 200, 200, 0.8); + background-color: rgba(255, 255, 255, 0.8); } + .dropzone .dz-preview .dz-details .dz-filename:not(:hover) { + overflow: hidden; + text-overflow: ellipsis; } + .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span { + border: 1px solid transparent; } + .dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span { + background-color: rgba(255, 255, 255, 0.4); + padding: 0 0.4em; + border-radius: 3px; } + .dropzone .dz-preview:hover .dz-image img { + -webkit-transform: scale(1.05, 1.05); + -moz-transform: scale(1.05, 1.05); + -ms-transform: scale(1.05, 1.05); + -o-transform: scale(1.05, 1.05); + transform: scale(1.05, 1.05); + -webkit-filter: blur(8px); + filter: blur(8px); } + .dropzone .dz-preview .dz-image { + border-radius: 20px; + overflow: hidden; + width: 120px; + height: 120px; + position: relative; + display: block; + z-index: 10; } + .dropzone .dz-preview .dz-image img { + display: block; } + .dropzone .dz-preview.dz-success .dz-success-mark { + -webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + -moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + -ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + -o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); + animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); } + .dropzone .dz-preview.dz-error .dz-error-mark { + opacity: 1; + -webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + -moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + -ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + -o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); + animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); } + .dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark { + pointer-events: none; + opacity: 0; + z-index: 500; + position: absolute; + display: block; + top: 50%; + left: 50%; + margin-left: -27px; + margin-top: -27px; } + .dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg { + display: block; + width: 54px; + height: 54px; } + .dropzone .dz-preview.dz-processing .dz-progress { + opacity: 1; + -webkit-transition: all 0.2s linear; + -moz-transition: all 0.2s linear; + -ms-transition: all 0.2s linear; + -o-transition: all 0.2s linear; + transition: all 0.2s linear; } + .dropzone .dz-preview.dz-complete .dz-progress { + opacity: 0; + -webkit-transition: opacity 0.4s ease-in; + -moz-transition: opacity 0.4s ease-in; + -ms-transition: opacity 0.4s ease-in; + -o-transition: opacity 0.4s ease-in; + transition: opacity 0.4s ease-in; } + .dropzone .dz-preview:not(.dz-processing) .dz-progress { + -webkit-animation: pulse 6s ease infinite; + -moz-animation: pulse 6s ease infinite; + -ms-animation: pulse 6s ease infinite; + -o-animation: pulse 6s ease infinite; + animation: pulse 6s ease infinite; } + .dropzone .dz-preview .dz-progress { + opacity: 1; + z-index: 1000; + pointer-events: none; + position: absolute; + height: 16px; + left: 50%; + top: 50%; + margin-top: -8px; + width: 80px; + margin-left: -40px; + background: rgba(255, 255, 255, 0.9); + -webkit-transform: scale(1); + border-radius: 8px; + overflow: hidden; } + .dropzone .dz-preview .dz-progress .dz-upload { + background: #333; + background: linear-gradient(to bottom, #666, #444); + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 0; + -webkit-transition: width 300ms ease-in-out; + -moz-transition: width 300ms ease-in-out; + -ms-transition: width 300ms ease-in-out; + -o-transition: width 300ms ease-in-out; + transition: width 300ms ease-in-out; } + .dropzone .dz-preview.dz-error .dz-error-message { + display: block; } + .dropzone .dz-preview.dz-error:hover .dz-error-message { + opacity: 1; + pointer-events: auto; } + .dropzone .dz-preview .dz-error-message { + pointer-events: none; + z-index: 1000; + position: absolute; + display: block; + display: none; + opacity: 0; + -webkit-transition: opacity 0.3s ease; + -moz-transition: opacity 0.3s ease; + -ms-transition: opacity 0.3s ease; + -o-transition: opacity 0.3s ease; + transition: opacity 0.3s ease; + border-radius: 8px; + font-size: 13px; + top: 130px; + left: -10px; + width: 140px; + background: #be2626; + background: linear-gradient(to bottom, #be2626, #a92222); + padding: 0.5em 1.2em; + color: white; } + .dropzone .dz-preview .dz-error-message:after { + content: ''; + position: absolute; + top: -6px; + left: 64px; + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #be2626; } diff --git a/public/js/dropzone/dropzone.js b/public/js/dropzone/dropzone.js new file mode 100755 index 00000000..babbdd45 --- /dev/null +++ b/public/js/dropzone/dropzone.js @@ -0,0 +1,1728 @@ + +/* + * + * More info at [www.dropzonejs.com](http://www.dropzonejs.com) + * + * Copyright (c) 2012, Matias Meno + * + * 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. + * + */ + +(function() { + var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, + __slice = [].slice, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + noop = function() {}; + + Emitter = (function() { + function Emitter() {} + + Emitter.prototype.addEventListener = Emitter.prototype.on; + + Emitter.prototype.on = function(event, fn) { + this._callbacks = this._callbacks || {}; + if (!this._callbacks[event]) { + this._callbacks[event] = []; + } + this._callbacks[event].push(fn); + return this; + }; + + Emitter.prototype.emit = function() { + var args, callback, callbacks, event, _i, _len; + event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + this._callbacks = this._callbacks || {}; + callbacks = this._callbacks[event]; + if (callbacks) { + for (_i = 0, _len = callbacks.length; _i < _len; _i++) { + callback = callbacks[_i]; + callback.apply(this, args); + } + } + return this; + }; + + Emitter.prototype.removeListener = Emitter.prototype.off; + + Emitter.prototype.removeAllListeners = Emitter.prototype.off; + + Emitter.prototype.removeEventListener = Emitter.prototype.off; + + Emitter.prototype.off = function(event, fn) { + var callback, callbacks, i, _i, _len; + if (!this._callbacks || arguments.length === 0) { + this._callbacks = {}; + return this; + } + callbacks = this._callbacks[event]; + if (!callbacks) { + return this; + } + if (arguments.length === 1) { + delete this._callbacks[event]; + return this; + } + for (i = _i = 0, _len = callbacks.length; _i < _len; i = ++_i) { + callback = callbacks[i]; + if (callback === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; + }; + + return Emitter; + + })(); + + Dropzone = (function(_super) { + var extend, resolveOption; + + __extends(Dropzone, _super); + + Dropzone.prototype.Emitter = Emitter; + + + /* + This is a list of all available events you can register on a dropzone object. + + You can register an event handler like this: + + dropzone.on("dragEnter", function() { }); + */ + + Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"]; + + Dropzone.prototype.defaultOptions = { + url: null, + method: "post", + withCredentials: false, + parallelUploads: 2, + uploadMultiple: false, + maxFilesize: 256, + paramName: "file", + createImageThumbnails: true, + maxThumbnailFilesize: 10, + thumbnailWidth: 120, + thumbnailHeight: 120, + filesizeBase: 1000, + maxFiles: null, + filesizeBase: 1000, + params: {}, + clickable: true, + ignoreHiddenFiles: true, + acceptedFiles: null, + acceptedMimeTypes: null, + autoProcessQueue: true, + autoQueue: true, + addRemoveLinks: false, + previewsContainer: null, + capture: null, + dictDefaultMessage: "Drop files here to upload", + dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.", + dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.", + dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", + dictInvalidFileType: "You can't upload files of this type.", + dictResponseError: "Server responded with {{statusCode}} code.", + dictCancelUpload: "Cancel upload", + dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", + dictRemoveFile: "Remove file", + dictRemoveFileConfirmation: null, + dictMaxFilesExceeded: "You can not upload any more files.", + accept: function(file, done) { + return done(); + }, + init: function() { + return noop; + }, + forceFallback: false, + fallback: function() { + var child, messageElement, span, _i, _len, _ref; + this.element.className = "" + this.element.className + " dz-browser-not-supported"; + _ref = this.element.getElementsByTagName("div"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + if (/(^| )dz-message($| )/.test(child.className)) { + messageElement = child; + child.className = "dz-message"; + continue; + } + } + if (!messageElement) { + messageElement = Dropzone.createElement("
"); + this.element.appendChild(messageElement); + } + span = messageElement.getElementsByTagName("span")[0]; + if (span) { + span.textContent = this.options.dictFallbackMessage; + } + return this.element.appendChild(this.getFallbackForm()); + }, + resize: function(file) { + var info, srcRatio, trgRatio; + info = { + srcX: 0, + srcY: 0, + srcWidth: file.width, + srcHeight: file.height + }; + srcRatio = file.width / file.height; + info.optWidth = this.options.thumbnailWidth; + info.optHeight = this.options.thumbnailHeight; + if ((info.optWidth == null) && (info.optHeight == null)) { + info.optWidth = info.srcWidth; + info.optHeight = info.srcHeight; + } else if (info.optWidth == null) { + info.optWidth = srcRatio * info.optHeight; + } else if (info.optHeight == null) { + info.optHeight = (1 / srcRatio) * info.optWidth; + } + trgRatio = info.optWidth / info.optHeight; + if (file.height < info.optHeight || file.width < info.optWidth) { + info.trgHeight = info.srcHeight; + info.trgWidth = info.srcWidth; + } else { + if (srcRatio > trgRatio) { + info.srcHeight = file.height; + info.srcWidth = info.srcHeight * trgRatio; + } else { + info.srcWidth = file.width; + info.srcHeight = info.srcWidth / trgRatio; + } + } + info.srcX = (file.width - info.srcWidth) / 2; + info.srcY = (file.height - info.srcHeight) / 2; + return info; + }, + + /* + Those functions register themselves to the events on init and handle all + the user interface specific stuff. Overwriting them won't break the upload + but can break the way it's displayed. + You can overwrite them if you don't like the default behavior. If you just + want to add an additional event handler, register it on the dropzone object + and don't overwrite those options. + */ + drop: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragstart: noop, + dragend: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragenter: function(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragover: function(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragleave: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + paste: noop, + reset: function() { + return this.element.classList.remove("dz-started"); + }, + addedfile: function(file) { + var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results; + if (this.element === this.previewsContainer) { + this.element.classList.add("dz-started"); + } + if (this.previewsContainer) { + file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim()); + file.previewTemplate = file.previewElement; + this.previewsContainer.appendChild(file.previewElement); + _ref = file.previewElement.querySelectorAll("[data-dz-name]"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + node.textContent = file.name; + } + _ref1 = file.previewElement.querySelectorAll("[data-dz-size]"); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + node = _ref1[_j]; + node.innerHTML = this.filesize(file.size); + } + if (this.options.addRemoveLinks) { + file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + ""); + file.previewElement.appendChild(file._removeLink); + } + removeFileEvent = (function(_this) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + if (file.status === Dropzone.UPLOADING) { + return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() { + return _this.removeFile(file); + }); + } else { + if (_this.options.dictRemoveFileConfirmation) { + return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() { + return _this.removeFile(file); + }); + } else { + return _this.removeFile(file); + } + } + }; + })(this); + _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]"); + _results = []; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + removeLink = _ref2[_k]; + _results.push(removeLink.addEventListener("click", removeFileEvent)); + } + return _results; + } + }, + removedfile: function(file) { + var _ref; + if (file.previewElement) { + if ((_ref = file.previewElement) != null) { + _ref.parentNode.removeChild(file.previewElement); + } + } + return this._updateMaxFilesReachedClass(); + }, + thumbnail: function(file, dataUrl) { + var thumbnailElement, _i, _len, _ref; + if (file.previewElement) { + file.previewElement.classList.remove("dz-file-preview"); + _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + thumbnailElement = _ref[_i]; + thumbnailElement.alt = file.name; + thumbnailElement.src = dataUrl; + } + return setTimeout(((function(_this) { + return function() { + return file.previewElement.classList.add("dz-image-preview"); + }; + })(this)), 1); + } + }, + error: function(file, message) { + var node, _i, _len, _ref, _results; + if (file.previewElement) { + file.previewElement.classList.add("dz-error"); + if (typeof message !== "String" && message.error) { + message = message.error; + } + _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]"); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + _results.push(node.textContent = message); + } + return _results; + } + }, + errormultiple: noop, + processing: function(file) { + if (file.previewElement) { + file.previewElement.classList.add("dz-processing"); + if (file._removeLink) { + return file._removeLink.textContent = this.options.dictCancelUpload; + } + } + }, + processingmultiple: noop, + uploadprogress: function(file, progress, bytesSent) { + var node, _i, _len, _ref, _results; + if (file.previewElement) { + _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]"); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + if (node.nodeName === 'PROGRESS') { + _results.push(node.value = progress); + } else { + _results.push(node.style.width = "" + progress + "%"); + } + } + return _results; + } + }, + totaluploadprogress: noop, + sending: noop, + sendingmultiple: noop, + success: function(file) { + if (file.previewElement) { + return file.previewElement.classList.add("dz-success"); + } + }, + successmultiple: noop, + canceled: function(file) { + return this.emit("error", file, "Upload canceled."); + }, + canceledmultiple: noop, + complete: function(file) { + if (file._removeLink) { + file._removeLink.textContent = this.options.dictRemoveFile; + } + if (file.previewElement) { + return file.previewElement.classList.add("dz-complete"); + } + }, + completemultiple: noop, + maxfilesexceeded: noop, + maxfilesreached: noop, + queuecomplete: noop, + previewTemplate: "
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
" + }; + + extend = function() { + var key, object, objects, target, val, _i, _len; + target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + for (_i = 0, _len = objects.length; _i < _len; _i++) { + object = objects[_i]; + for (key in object) { + val = object[key]; + target[key] = val; + } + } + return target; + }; + + function Dropzone(element, options) { + var elementOptions, fallback, _ref; + this.element = element; + this.version = Dropzone.version; + this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, ""); + this.clickableElements = []; + this.listeners = []; + this.files = []; + if (typeof this.element === "string") { + this.element = document.querySelector(this.element); + } + if (!(this.element && (this.element.nodeType != null))) { + throw new Error("Invalid dropzone element."); + } + if (this.element.dropzone) { + throw new Error("Dropzone already attached."); + } + Dropzone.instances.push(this); + this.element.dropzone = this; + elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {}; + this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {}); + if (this.options.forceFallback || !Dropzone.isBrowserSupported()) { + return this.options.fallback.call(this); + } + if (this.options.url == null) { + this.options.url = this.element.getAttribute("action"); + } + if (!this.options.url) { + throw new Error("No URL provided."); + } + if (this.options.acceptedFiles && this.options.acceptedMimeTypes) { + throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); + } + if (this.options.acceptedMimeTypes) { + this.options.acceptedFiles = this.options.acceptedMimeTypes; + delete this.options.acceptedMimeTypes; + } + this.options.method = this.options.method.toUpperCase(); + if ((fallback = this.getExistingFallback()) && fallback.parentNode) { + fallback.parentNode.removeChild(fallback); + } + if (this.options.previewsContainer !== false) { + if (this.options.previewsContainer) { + this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer"); + } else { + this.previewsContainer = this.element; + } + } + if (this.options.clickable) { + if (this.options.clickable === true) { + this.clickableElements = [this.element]; + } else { + this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable"); + } + } + this.init(); + } + + Dropzone.prototype.getAcceptedFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.accepted) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getRejectedFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (!file.accepted) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getFilesWithStatus = function(status) { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status === status) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getQueuedFiles = function() { + return this.getFilesWithStatus(Dropzone.QUEUED); + }; + + Dropzone.prototype.getUploadingFiles = function() { + return this.getFilesWithStatus(Dropzone.UPLOADING); + }; + + Dropzone.prototype.getActiveFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.init = function() { + var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1; + if (this.element.tagName === "form") { + this.element.setAttribute("enctype", "multipart/form-data"); + } + if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) { + this.element.appendChild(Dropzone.createElement("
" + this.options.dictDefaultMessage + "
")); + } + if (this.clickableElements.length) { + setupHiddenFileInput = (function(_this) { + return function() { + if (_this.hiddenFileInput) { + document.body.removeChild(_this.hiddenFileInput); + } + _this.hiddenFileInput = document.createElement("input"); + _this.hiddenFileInput.setAttribute("type", "file"); + if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) { + _this.hiddenFileInput.setAttribute("multiple", "multiple"); + } + _this.hiddenFileInput.className = "dz-hidden-input"; + if (_this.options.acceptedFiles != null) { + _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles); + } + if (_this.options.capture != null) { + _this.hiddenFileInput.setAttribute("capture", _this.options.capture); + } + _this.hiddenFileInput.style.visibility = "hidden"; + _this.hiddenFileInput.style.position = "absolute"; + _this.hiddenFileInput.style.top = "0"; + _this.hiddenFileInput.style.left = "0"; + _this.hiddenFileInput.style.height = "0"; + _this.hiddenFileInput.style.width = "0"; + document.body.appendChild(_this.hiddenFileInput); + return _this.hiddenFileInput.addEventListener("change", function() { + var file, files, _i, _len; + files = _this.hiddenFileInput.files; + if (files.length) { + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + _this.addFile(file); + } + } + return setupHiddenFileInput(); + }); + }; + })(this); + setupHiddenFileInput(); + } + this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL; + _ref1 = this.events; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + eventName = _ref1[_i]; + this.on(eventName, this.options[eventName]); + } + this.on("uploadprogress", (function(_this) { + return function() { + return _this.updateTotalUploadProgress(); + }; + })(this)); + this.on("removedfile", (function(_this) { + return function() { + return _this.updateTotalUploadProgress(); + }; + })(this)); + this.on("canceled", (function(_this) { + return function(file) { + return _this.emit("complete", file); + }; + })(this)); + this.on("complete", (function(_this) { + return function(file) { + if (_this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) { + return setTimeout((function() { + return _this.emit("queuecomplete"); + }), 0); + } + }; + })(this)); + noPropagation = function(e) { + e.stopPropagation(); + if (e.preventDefault) { + return e.preventDefault(); + } else { + return e.returnValue = false; + } + }; + this.listeners = [ + { + element: this.element, + events: { + "dragstart": (function(_this) { + return function(e) { + return _this.emit("dragstart", e); + }; + })(this), + "dragenter": (function(_this) { + return function(e) { + noPropagation(e); + return _this.emit("dragenter", e); + }; + })(this), + "dragover": (function(_this) { + return function(e) { + var efct; + try { + efct = e.dataTransfer.effectAllowed; + } catch (_error) {} + e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy'; + noPropagation(e); + return _this.emit("dragover", e); + }; + })(this), + "dragleave": (function(_this) { + return function(e) { + return _this.emit("dragleave", e); + }; + })(this), + "drop": (function(_this) { + return function(e) { + noPropagation(e); + return _this.drop(e); + }; + })(this), + "dragend": (function(_this) { + return function(e) { + return _this.emit("dragend", e); + }; + })(this) + } + } + ]; + this.clickableElements.forEach((function(_this) { + return function(clickableElement) { + return _this.listeners.push({ + element: clickableElement, + events: { + "click": function(evt) { + if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) { + return _this.hiddenFileInput.click(); + } + } + } + }); + }; + })(this)); + this.enable(); + return this.options.init.call(this); + }; + + Dropzone.prototype.destroy = function() { + var _ref; + this.disable(); + this.removeAllFiles(true); + if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) { + this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); + this.hiddenFileInput = null; + } + delete this.element.dropzone; + return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); + }; + + Dropzone.prototype.updateTotalUploadProgress = function() { + var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref; + totalBytesSent = 0; + totalBytes = 0; + activeFiles = this.getActiveFiles(); + if (activeFiles.length) { + _ref = this.getActiveFiles(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + totalBytesSent += file.upload.bytesSent; + totalBytes += file.upload.total; + } + totalUploadProgress = 100 * totalBytesSent / totalBytes; + } else { + totalUploadProgress = 100; + } + return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent); + }; + + Dropzone.prototype._getParamName = function(n) { + if (typeof this.options.paramName === "function") { + return this.options.paramName(n); + } else { + return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : ""); + } + }; + + Dropzone.prototype.getFallbackForm = function() { + var existingFallback, fields, fieldsString, form; + if (existingFallback = this.getExistingFallback()) { + return existingFallback; + } + fieldsString = "
"; + if (this.options.dictFallbackText) { + fieldsString += "

" + this.options.dictFallbackText + "

"; + } + fieldsString += "
"; + fields = Dropzone.createElement(fieldsString); + if (this.element.tagName !== "FORM") { + form = Dropzone.createElement("
"); + form.appendChild(fields); + } else { + this.element.setAttribute("enctype", "multipart/form-data"); + this.element.setAttribute("method", this.options.method); + } + return form != null ? form : fields; + }; + + Dropzone.prototype.getExistingFallback = function() { + var fallback, getFallback, tagName, _i, _len, _ref; + getFallback = function(elements) { + var el, _i, _len; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + el = elements[_i]; + if (/(^| )fallback($| )/.test(el.className)) { + return el; + } + } + }; + _ref = ["div", "form"]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + tagName = _ref[_i]; + if (fallback = getFallback(this.element.getElementsByTagName(tagName))) { + return fallback; + } + } + }; + + Dropzone.prototype.setupEventListeners = function() { + var elementListeners, event, listener, _i, _len, _ref, _results; + _ref = this.listeners; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elementListeners = _ref[_i]; + _results.push((function() { + var _ref1, _results1; + _ref1 = elementListeners.events; + _results1 = []; + for (event in _ref1) { + listener = _ref1[event]; + _results1.push(elementListeners.element.addEventListener(event, listener, false)); + } + return _results1; + })()); + } + return _results; + }; + + Dropzone.prototype.removeEventListeners = function() { + var elementListeners, event, listener, _i, _len, _ref, _results; + _ref = this.listeners; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elementListeners = _ref[_i]; + _results.push((function() { + var _ref1, _results1; + _ref1 = elementListeners.events; + _results1 = []; + for (event in _ref1) { + listener = _ref1[event]; + _results1.push(elementListeners.element.removeEventListener(event, listener, false)); + } + return _results1; + })()); + } + return _results; + }; + + Dropzone.prototype.disable = function() { + var file, _i, _len, _ref, _results; + this.clickableElements.forEach(function(element) { + return element.classList.remove("dz-clickable"); + }); + this.removeEventListeners(); + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + _results.push(this.cancelUpload(file)); + } + return _results; + }; + + Dropzone.prototype.enable = function() { + this.clickableElements.forEach(function(element) { + return element.classList.add("dz-clickable"); + }); + return this.setupEventListeners(); + }; + + Dropzone.prototype.filesize = function(size) { + var cutoff, i, selectedSize, selectedUnit, unit, units, _i, _len; + units = ['TB', 'GB', 'MB', 'KB', 'b']; + selectedSize = selectedUnit = null; + for (i = _i = 0, _len = units.length; _i < _len; i = ++_i) { + unit = units[i]; + cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; + if (size >= cutoff) { + selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); + selectedUnit = unit; + break; + } + } + selectedSize = Math.round(10 * selectedSize) / 10; + return "" + selectedSize + " " + selectedUnit; + }; + + Dropzone.prototype._updateMaxFilesReachedClass = function() { + if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + if (this.getAcceptedFiles().length === this.options.maxFiles) { + this.emit('maxfilesreached', this.files); + } + return this.element.classList.add("dz-max-files-reached"); + } else { + return this.element.classList.remove("dz-max-files-reached"); + } + }; + + Dropzone.prototype.drop = function(e) { + var files, items; + if (!e.dataTransfer) { + return; + } + this.emit("drop", e); + files = e.dataTransfer.files; + if (files.length) { + items = e.dataTransfer.items; + if (items && items.length && (items[0].webkitGetAsEntry != null)) { + this._addFilesFromItems(items); + } else { + this.handleFiles(files); + } + } + }; + + Dropzone.prototype.paste = function(e) { + var items, _ref; + if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) { + return; + } + this.emit("paste", e); + items = e.clipboardData.items; + if (items.length) { + return this._addFilesFromItems(items); + } + }; + + Dropzone.prototype.handleFiles = function(files) { + var file, _i, _len, _results; + _results = []; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + _results.push(this.addFile(file)); + } + return _results; + }; + + Dropzone.prototype._addFilesFromItems = function(items) { + var entry, item, _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) { + if (entry.isFile) { + _results.push(this.addFile(item.getAsFile())); + } else if (entry.isDirectory) { + _results.push(this._addFilesFromDirectory(entry, entry.name)); + } else { + _results.push(void 0); + } + } else if (item.getAsFile != null) { + if ((item.kind == null) || item.kind === "file") { + _results.push(this.addFile(item.getAsFile())); + } else { + _results.push(void 0); + } + } else { + _results.push(void 0); + } + } + return _results; + }; + + Dropzone.prototype._addFilesFromDirectory = function(directory, path) { + var dirReader, entriesReader; + dirReader = directory.createReader(); + entriesReader = (function(_this) { + return function(entries) { + var entry, _i, _len; + for (_i = 0, _len = entries.length; _i < _len; _i++) { + entry = entries[_i]; + if (entry.isFile) { + entry.file(function(file) { + if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') { + return; + } + file.fullPath = "" + path + "/" + file.name; + return _this.addFile(file); + }); + } else if (entry.isDirectory) { + _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name); + } + } + }; + })(this); + return dirReader.readEntries(entriesReader, function(error) { + return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0; + }); + }; + + Dropzone.prototype.accept = function(file, done) { + if (file.size > this.options.maxFilesize * 1024 * 1024) { + return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize)); + } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) { + return done(this.options.dictInvalidFileType); + } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles)); + return this.emit("maxfilesexceeded", file); + } else { + return this.options.accept.call(this, file, done); + } + }; + + Dropzone.prototype.addFile = function(file) { + file.upload = { + progress: 0, + total: file.size, + bytesSent: 0 + }; + this.files.push(file); + file.status = Dropzone.ADDED; + this.emit("addedfile", file); + this._enqueueThumbnail(file); + return this.accept(file, (function(_this) { + return function(error) { + if (error) { + file.accepted = false; + _this._errorProcessing([file], error); + } else { + file.accepted = true; + if (_this.options.autoQueue) { + _this.enqueueFile(file); + } + } + return _this._updateMaxFilesReachedClass(); + }; + })(this)); + }; + + Dropzone.prototype.enqueueFiles = function(files) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + this.enqueueFile(file); + } + return null; + }; + + Dropzone.prototype.enqueueFile = function(file) { + if (file.status === Dropzone.ADDED && file.accepted === true) { + file.status = Dropzone.QUEUED; + if (this.options.autoProcessQueue) { + return setTimeout(((function(_this) { + return function() { + return _this.processQueue(); + }; + })(this)), 0); + } + } else { + throw new Error("This file can't be queued because it has already been processed or was rejected."); + } + }; + + Dropzone.prototype._thumbnailQueue = []; + + Dropzone.prototype._processingThumbnail = false; + + Dropzone.prototype._enqueueThumbnail = function(file) { + if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) { + this._thumbnailQueue.push(file); + return setTimeout(((function(_this) { + return function() { + return _this._processThumbnailQueue(); + }; + })(this)), 0); + } + }; + + Dropzone.prototype._processThumbnailQueue = function() { + if (this._processingThumbnail || this._thumbnailQueue.length === 0) { + return; + } + this._processingThumbnail = true; + return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) { + return function() { + _this._processingThumbnail = false; + return _this._processThumbnailQueue(); + }; + })(this)); + }; + + Dropzone.prototype.removeFile = function(file) { + if (file.status === Dropzone.UPLOADING) { + this.cancelUpload(file); + } + this.files = without(this.files, file); + this.emit("removedfile", file); + if (this.files.length === 0) { + return this.emit("reset"); + } + }; + + Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) { + var file, _i, _len, _ref; + if (cancelIfNecessary == null) { + cancelIfNecessary = false; + } + _ref = this.files.slice(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) { + this.removeFile(file); + } + } + return null; + }; + + Dropzone.prototype.createThumbnail = function(file, callback) { + var fileReader; + fileReader = new FileReader; + fileReader.onload = (function(_this) { + return function() { + if (file.type === "image/svg+xml") { + _this.emit("thumbnail", file, fileReader.result); + if (callback != null) { + callback(); + } + return; + } + return _this.createThumbnailFromUrl(file, fileReader.result, callback); + }; + })(this); + return fileReader.readAsDataURL(file); + }; + + Dropzone.prototype.createThumbnailFromUrl = function(file, imageUrl, callback) { + var img; + img = document.createElement("img"); + img.onload = (function(_this) { + return function() { + var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3; + file.width = img.width; + file.height = img.height; + resizeInfo = _this.options.resize.call(_this, file); + if (resizeInfo.trgWidth == null) { + resizeInfo.trgWidth = resizeInfo.optWidth; + } + if (resizeInfo.trgHeight == null) { + resizeInfo.trgHeight = resizeInfo.optHeight; + } + canvas = document.createElement("canvas"); + ctx = canvas.getContext("2d"); + canvas.width = resizeInfo.trgWidth; + canvas.height = resizeInfo.trgHeight; + drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); + thumbnail = canvas.toDataURL("image/png"); + _this.emit("thumbnail", file, thumbnail); + if (callback != null) { + return callback(); + } + }; + })(this); + if (callback != null) { + img.onerror = callback; + } + return img.src = imageUrl; + }; + + Dropzone.prototype.processQueue = function() { + var i, parallelUploads, processingLength, queuedFiles; + parallelUploads = this.options.parallelUploads; + processingLength = this.getUploadingFiles().length; + i = processingLength; + if (processingLength >= parallelUploads) { + return; + } + queuedFiles = this.getQueuedFiles(); + if (!(queuedFiles.length > 0)) { + return; + } + if (this.options.uploadMultiple) { + return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength)); + } else { + while (i < parallelUploads) { + if (!queuedFiles.length) { + return; + } + this.processFile(queuedFiles.shift()); + i++; + } + } + }; + + Dropzone.prototype.processFile = function(file) { + return this.processFiles([file]); + }; + + Dropzone.prototype.processFiles = function(files) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.processing = true; + file.status = Dropzone.UPLOADING; + this.emit("processing", file); + } + if (this.options.uploadMultiple) { + this.emit("processingmultiple", files); + } + return this.uploadFiles(files); + }; + + Dropzone.prototype._getFilesWithXhr = function(xhr) { + var file, files; + return files = (function() { + var _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.xhr === xhr) { + _results.push(file); + } + } + return _results; + }).call(this); + }; + + Dropzone.prototype.cancelUpload = function(file) { + var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref; + if (file.status === Dropzone.UPLOADING) { + groupedFiles = this._getFilesWithXhr(file.xhr); + for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) { + groupedFile = groupedFiles[_i]; + groupedFile.status = Dropzone.CANCELED; + } + file.xhr.abort(); + for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) { + groupedFile = groupedFiles[_j]; + this.emit("canceled", groupedFile); + } + if (this.options.uploadMultiple) { + this.emit("canceledmultiple", groupedFiles); + } + } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) { + file.status = Dropzone.CANCELED; + this.emit("canceled", file); + if (this.options.uploadMultiple) { + this.emit("canceledmultiple", [file]); + } + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + resolveOption = function() { + var args, option; + option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (typeof option === 'function') { + return option.apply(this, args); + } + return option; + }; + + Dropzone.prototype.uploadFile = function(file) { + return this.uploadFiles([file]); + }; + + Dropzone.prototype.uploadFiles = function(files) { + var file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, key, method, option, progressObj, response, updateProgress, url, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; + xhr = new XMLHttpRequest(); + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.xhr = xhr; + } + method = resolveOption(this.options.method, files); + url = resolveOption(this.options.url, files); + xhr.open(method, url, true); + xhr.withCredentials = !!this.options.withCredentials; + response = null; + handleError = (function(_this) { + return function() { + var _j, _len1, _results; + _results = []; + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr)); + } + return _results; + }; + })(this); + updateProgress = (function(_this) { + return function(e) { + var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results; + if (e != null) { + progress = 100 * e.loaded / e.total; + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + file.upload = { + progress: progress, + total: e.total, + bytesSent: e.loaded + }; + } + } else { + allFilesFinished = true; + progress = 100; + for (_k = 0, _len2 = files.length; _k < _len2; _k++) { + file = files[_k]; + if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) { + allFilesFinished = false; + } + file.upload.progress = progress; + file.upload.bytesSent = file.upload.total; + } + if (allFilesFinished) { + return; + } + } + _results = []; + for (_l = 0, _len3 = files.length; _l < _len3; _l++) { + file = files[_l]; + _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent)); + } + return _results; + }; + })(this); + xhr.onload = (function(_this) { + return function(e) { + var _ref; + if (files[0].status === Dropzone.CANCELED) { + return; + } + if (xhr.readyState !== 4) { + return; + } + response = xhr.responseText; + if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) { + try { + response = JSON.parse(response); + } catch (_error) { + e = _error; + response = "Invalid JSON response from server."; + } + } + updateProgress(); + if (!((200 <= (_ref = xhr.status) && _ref < 300))) { + return handleError(); + } else { + return _this._finished(files, response, e); + } + }; + })(this); + xhr.onerror = (function(_this) { + return function() { + if (files[0].status === Dropzone.CANCELED) { + return; + } + return handleError(); + }; + })(this); + progressObj = (_ref = xhr.upload) != null ? _ref : xhr; + progressObj.onprogress = updateProgress; + headers = { + "Accept": "application/json", + "Cache-Control": "no-cache", + "X-Requested-With": "XMLHttpRequest" + }; + if (this.options.headers) { + extend(headers, this.options.headers); + } + for (headerName in headers) { + headerValue = headers[headerName]; + xhr.setRequestHeader(headerName, headerValue); + } + formData = new FormData(); + if (this.options.params) { + _ref1 = this.options.params; + for (key in _ref1) { + value = _ref1[key]; + formData.append(key, value); + } + } + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + this.emit("sending", file, xhr, formData); + } + if (this.options.uploadMultiple) { + this.emit("sendingmultiple", files, xhr, formData); + } + if (this.element.tagName === "FORM") { + _ref2 = this.element.querySelectorAll("input, textarea, select, button"); + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + input = _ref2[_k]; + inputName = input.getAttribute("name"); + inputType = input.getAttribute("type"); + if (input.tagName === "SELECT" && input.hasAttribute("multiple")) { + _ref3 = input.options; + for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { + option = _ref3[_l]; + if (option.selected) { + formData.append(inputName, option.value); + } + } + } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) { + formData.append(inputName, input.value); + } + } + } + for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) { + formData.append(this._getParamName(i), files[i], files[i].name); + } + return xhr.send(formData); + }; + + Dropzone.prototype._finished = function(files, responseText, e) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.status = Dropzone.SUCCESS; + this.emit("success", file, responseText, e); + this.emit("complete", file); + } + if (this.options.uploadMultiple) { + this.emit("successmultiple", files, responseText, e); + this.emit("completemultiple", files); + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + Dropzone.prototype._errorProcessing = function(files, message, xhr) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.status = Dropzone.ERROR; + this.emit("error", file, message, xhr); + this.emit("complete", file); + } + if (this.options.uploadMultiple) { + this.emit("errormultiple", files, message, xhr); + this.emit("completemultiple", files); + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + return Dropzone; + + })(Emitter); + + Dropzone.version = "4.0.1"; + + Dropzone.options = {}; + + Dropzone.optionsForElement = function(element) { + if (element.getAttribute("id")) { + return Dropzone.options[camelize(element.getAttribute("id"))]; + } else { + return void 0; + } + }; + + Dropzone.instances = []; + + Dropzone.forElement = function(element) { + if (typeof element === "string") { + element = document.querySelector(element); + } + if ((element != null ? element.dropzone : void 0) == null) { + throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."); + } + return element.dropzone; + }; + + Dropzone.autoDiscover = true; + + Dropzone.discover = function() { + var checkElements, dropzone, dropzones, _i, _len, _results; + if (document.querySelectorAll) { + dropzones = document.querySelectorAll(".dropzone"); + } else { + dropzones = []; + checkElements = function(elements) { + var el, _i, _len, _results; + _results = []; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + el = elements[_i]; + if (/(^| )dropzone($| )/.test(el.className)) { + _results.push(dropzones.push(el)); + } else { + _results.push(void 0); + } + } + return _results; + }; + checkElements(document.getElementsByTagName("div")); + checkElements(document.getElementsByTagName("form")); + } + _results = []; + for (_i = 0, _len = dropzones.length; _i < _len; _i++) { + dropzone = dropzones[_i]; + if (Dropzone.optionsForElement(dropzone) !== false) { + _results.push(new Dropzone(dropzone)); + } else { + _results.push(void 0); + } + } + return _results; + }; + + Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i]; + + Dropzone.isBrowserSupported = function() { + var capableBrowser, regex, _i, _len, _ref; + capableBrowser = true; + if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) { + if (!("classList" in document.createElement("a"))) { + capableBrowser = false; + } else { + _ref = Dropzone.blacklistedBrowsers; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + regex = _ref[_i]; + if (regex.test(navigator.userAgent)) { + capableBrowser = false; + continue; + } + } + } + } else { + capableBrowser = false; + } + return capableBrowser; + }; + + without = function(list, rejectedItem) { + var item, _i, _len, _results; + _results = []; + for (_i = 0, _len = list.length; _i < _len; _i++) { + item = list[_i]; + if (item !== rejectedItem) { + _results.push(item); + } + } + return _results; + }; + + camelize = function(str) { + return str.replace(/[\-_](\w)/g, function(match) { + return match.charAt(1).toUpperCase(); + }); + }; + + Dropzone.createElement = function(string) { + var div; + div = document.createElement("div"); + div.innerHTML = string; + return div.childNodes[0]; + }; + + Dropzone.elementInside = function(element, container) { + if (element === container) { + return true; + } + while (element = element.parentNode) { + if (element === container) { + return true; + } + } + return false; + }; + + Dropzone.getElement = function(el, name) { + var element; + if (typeof el === "string") { + element = document.querySelector(el); + } else if (el.nodeType != null) { + element = el; + } + if (element == null) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element."); + } + return element; + }; + + Dropzone.getElements = function(els, name) { + var e, el, elements, _i, _j, _len, _len1, _ref; + if (els instanceof Array) { + elements = []; + try { + for (_i = 0, _len = els.length; _i < _len; _i++) { + el = els[_i]; + elements.push(this.getElement(el, name)); + } + } catch (_error) { + e = _error; + elements = null; + } + } else if (typeof els === "string") { + elements = []; + _ref = document.querySelectorAll(els); + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + el = _ref[_j]; + elements.push(el); + } + } else if (els.nodeType != null) { + elements = [els]; + } + if (!((elements != null) && elements.length)) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."); + } + return elements; + }; + + Dropzone.confirm = function(question, accepted, rejected) { + if (window.confirm(question)) { + return accepted(); + } else if (rejected != null) { + return rejected(); + } + }; + + Dropzone.isValidFile = function(file, acceptedFiles) { + var baseMimeType, mimeType, validType, _i, _len; + if (!acceptedFiles) { + return true; + } + acceptedFiles = acceptedFiles.split(","); + mimeType = file.type; + baseMimeType = mimeType.replace(/\/.*$/, ""); + for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) { + validType = acceptedFiles[_i]; + validType = validType.trim(); + if (validType.charAt(0) === ".") { + if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) { + return true; + } + } else if (/\/\*$/.test(validType)) { + if (baseMimeType === validType.replace(/\/.*$/, "")) { + return true; + } + } else { + if (mimeType === validType) { + return true; + } + } + } + return false; + }; + + if (typeof jQuery !== "undefined" && jQuery !== null) { + jQuery.fn.dropzone = function(options) { + return this.each(function() { + return new Dropzone(this, options); + }); + }; + } + + if (typeof module !== "undefined" && module !== null) { + module.exports = Dropzone; + } else { + window.Dropzone = Dropzone; + } + + Dropzone.ADDED = "added"; + + Dropzone.QUEUED = "queued"; + + Dropzone.ACCEPTED = Dropzone.QUEUED; + + Dropzone.UPLOADING = "uploading"; + + Dropzone.PROCESSING = Dropzone.UPLOADING; + + Dropzone.CANCELED = "canceled"; + + Dropzone.ERROR = "error"; + + Dropzone.SUCCESS = "success"; + + + /* + + Bugfix for iOS 6 and 7 + Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios + based on the work of https://github.com/stomita/ios-imagefile-megapixel + */ + + detectVerticalSquash = function(img) { + var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy; + iw = img.naturalWidth; + ih = img.naturalHeight; + canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = ih; + ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + data = ctx.getImageData(0, 0, 1, ih).data; + sy = 0; + ey = ih; + py = ih; + while (py > sy) { + alpha = data[(py - 1) * 4 + 3]; + if (alpha === 0) { + ey = py; + } else { + sy = py; + } + py = (ey + sy) >> 1; + } + ratio = py / ih; + if (ratio === 0) { + return 1; + } else { + return ratio; + } + }; + + drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { + var vertSquashRatio; + vertSquashRatio = detectVerticalSquash(img); + return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); + }; + + + /* + * contentloaded.js + * + * Author: Diego Perini (diego.perini at gmail.com) + * Summary: cross-browser wrapper for DOMContentLoaded + * Updated: 20101020 + * License: MIT + * Version: 1.2 + * + * URL: + * http://javascript.nwbox.com/ContentLoaded/ + * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE + */ + + contentLoaded = function(win, fn) { + var add, doc, done, init, poll, pre, rem, root, top; + done = false; + top = true; + doc = win.document; + root = doc.documentElement; + add = (doc.addEventListener ? "addEventListener" : "attachEvent"); + rem = (doc.addEventListener ? "removeEventListener" : "detachEvent"); + pre = (doc.addEventListener ? "" : "on"); + init = function(e) { + if (e.type === "readystatechange" && doc.readyState !== "complete") { + return; + } + (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); + if (!done && (done = true)) { + return fn.call(win, e.type || e); + } + }; + poll = function() { + var e; + try { + root.doScroll("left"); + } catch (_error) { + e = _error; + setTimeout(poll, 50); + return; + } + return init("poll"); + }; + if (doc.readyState !== "complete") { + if (doc.createEventObject && root.doScroll) { + try { + top = !win.frameElement; + } catch (_error) {} + if (top) { + poll(); + } + } + doc[add](pre + "DOMContentLoaded", init, false); + doc[add](pre + "readystatechange", init, false); + return win[add](pre + "load", init, false); + } + }; + + Dropzone._autoDiscoverFunction = function() { + if (Dropzone.autoDiscover) { + return Dropzone.discover(); + } + }; + + contentLoaded(window, Dropzone._autoDiscoverFunction); + +}).call(this); diff --git a/public/js/dropzone/min/basic.min.css b/public/js/dropzone/min/basic.min.css new file mode 100755 index 00000000..5238d5ca --- /dev/null +++ b/public/js/dropzone/min/basic.min.css @@ -0,0 +1 @@ +.dropzone,.dropzone *{box-sizing:border-box}.dropzone{position:relative}.dropzone .dz-preview{position:relative;display:inline-block;width:120px;margin:0.5em}.dropzone .dz-preview .dz-progress{display:block;height:15px;border:1px solid #aaa}.dropzone .dz-preview .dz-progress .dz-upload{display:block;height:100%;width:0;background:green}.dropzone .dz-preview .dz-error-message{color:red;display:none}.dropzone .dz-preview.dz-error .dz-error-message,.dropzone .dz-preview.dz-error .dz-error-mark{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{display:block}.dropzone .dz-preview .dz-error-mark,.dropzone .dz-preview .dz-success-mark{position:absolute;display:none;left:30px;top:30px;width:54px;height:58px;left:50%;margin-left:-27px} diff --git a/public/js/dropzone/min/dropzone-amd-module.min.js b/public/js/dropzone/min/dropzone-amd-module.min.js new file mode 100755 index 00000000..bfb1afef --- /dev/null +++ b/public/js/dropzone/min/dropzone-amd-module.min.js @@ -0,0 +1,2 @@ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){var b={exports:{}};return function(){var c,d,e,f,g,h,i,j,k=[].slice,l={}.hasOwnProperty,m=function(a,b){function c(){this.constructor=a}for(var d in b)l.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a};i=function(){},d=function(){function a(){}return a.prototype.addEventListener=a.prototype.on,a.prototype.on=function(a,b){return this._callbacks=this._callbacks||{},this._callbacks[a]||(this._callbacks[a]=[]),this._callbacks[a].push(b),this},a.prototype.emit=function(){var a,b,c,d,e,f;if(d=arguments[0],a=2<=arguments.length?k.call(arguments,1):[],this._callbacks=this._callbacks||{},c=this._callbacks[d])for(e=0,f=c.length;f>e;e++)b=c[e],b.apply(this,a);return this},a.prototype.removeListener=a.prototype.off,a.prototype.removeAllListeners=a.prototype.off,a.prototype.removeEventListener=a.prototype.off,a.prototype.off=function(a,b){var c,d,e,f,g;if(!this._callbacks||0===arguments.length)return this._callbacks={},this;if(d=this._callbacks[a],!d)return this;if(1===arguments.length)return delete this._callbacks[a],this;for(e=f=0,g=d.length;g>f;e=++f)if(c=d[e],c===b){d.splice(e,1);break}return this},a}(),c=function(a){function b(a,d){var e,f,g;if(this.element=a,this.version=b.version,this.defaultOptions.previewTemplate=this.defaultOptions.previewTemplate.replace(/\n*/g,""),this.clickableElements=[],this.listeners=[],this.files=[],"string"==typeof this.element&&(this.element=document.querySelector(this.element)),!this.element||null==this.element.nodeType)throw new Error("Invalid dropzone element.");if(this.element.dropzone)throw new Error("Dropzone already attached.");if(b.instances.push(this),this.element.dropzone=this,e=null!=(g=b.optionsForElement(this.element))?g:{},this.options=c({},this.defaultOptions,e,null!=d?d:{}),this.options.forceFallback||!b.isBrowserSupported())return this.options.fallback.call(this);if(null==this.options.url&&(this.options.url=this.element.getAttribute("action")),!this.options.url)throw new Error("No URL provided.");if(this.options.acceptedFiles&&this.options.acceptedMimeTypes)throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");this.options.acceptedMimeTypes&&(this.options.acceptedFiles=this.options.acceptedMimeTypes,delete this.options.acceptedMimeTypes),this.options.method=this.options.method.toUpperCase(),(f=this.getExistingFallback())&&f.parentNode&&f.parentNode.removeChild(f),this.options.previewsContainer!==!1&&(this.previewsContainer=this.options.previewsContainer?b.getElement(this.options.previewsContainer,"previewsContainer"):this.element),this.options.clickable&&(this.clickableElements=this.options.clickable===!0?[this.element]:b.getElements(this.options.clickable,"clickable")),this.init()}var c,e;return m(b,a),b.prototype.Emitter=d,b.prototype.events=["drop","dragstart","dragend","dragenter","dragover","dragleave","addedfile","removedfile","thumbnail","error","errormultiple","processing","processingmultiple","uploadprogress","totaluploadprogress","sending","sendingmultiple","success","successmultiple","canceled","canceledmultiple","complete","completemultiple","reset","maxfilesexceeded","maxfilesreached","queuecomplete"],b.prototype.defaultOptions={url:null,method:"post",withCredentials:!1,parallelUploads:2,uploadMultiple:!1,maxFilesize:256,paramName:"file",createImageThumbnails:!0,maxThumbnailFilesize:10,thumbnailWidth:120,thumbnailHeight:120,filesizeBase:1e3,maxFiles:null,filesizeBase:1e3,params:{},clickable:!0,ignoreHiddenFiles:!0,acceptedFiles:null,acceptedMimeTypes:null,autoProcessQueue:!0,autoQueue:!0,addRemoveLinks:!1,previewsContainer:null,capture:null,dictDefaultMessage:"Drop files here to upload",dictFallbackMessage:"Your browser does not support drag'n'drop file uploads.",dictFallbackText:"Please use the fallback form below to upload your files like in the olden days.",dictFileTooBig:"File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",dictInvalidFileType:"You can't upload files of this type.",dictResponseError:"Server responded with {{statusCode}} code.",dictCancelUpload:"Cancel upload",dictCancelUploadConfirmation:"Are you sure you want to cancel this upload?",dictRemoveFile:"Remove file",dictRemoveFileConfirmation:null,dictMaxFilesExceeded:"You can not upload any more files.",accept:function(a,b){return b()},init:function(){return i},forceFallback:!1,fallback:function(){var a,c,d,e,f,g;for(this.element.className=""+this.element.className+" dz-browser-not-supported",g=this.element.getElementsByTagName("div"),e=0,f=g.length;f>e;e++)a=g[e],/(^| )dz-message($| )/.test(a.className)&&(c=a,a.className="dz-message");return c||(c=b.createElement('
'),this.element.appendChild(c)),d=c.getElementsByTagName("span")[0],d&&(d.textContent=this.options.dictFallbackMessage),this.element.appendChild(this.getFallbackForm())},resize:function(a){var b,c,d;return b={srcX:0,srcY:0,srcWidth:a.width,srcHeight:a.height},c=a.width/a.height,b.optWidth=this.options.thumbnailWidth,b.optHeight=this.options.thumbnailHeight,null==b.optWidth&&null==b.optHeight?(b.optWidth=b.srcWidth,b.optHeight=b.srcHeight):null==b.optWidth?b.optWidth=c*b.optHeight:null==b.optHeight&&(b.optHeight=1/c*b.optWidth),d=b.optWidth/b.optHeight,a.heightd?(b.srcHeight=a.height,b.srcWidth=b.srcHeight*d):(b.srcWidth=a.width,b.srcHeight=b.srcWidth/d),b.srcX=(a.width-b.srcWidth)/2,b.srcY=(a.height-b.srcHeight)/2,b},drop:function(){return this.element.classList.remove("dz-drag-hover")},dragstart:i,dragend:function(){return this.element.classList.remove("dz-drag-hover")},dragenter:function(){return this.element.classList.add("dz-drag-hover")},dragover:function(){return this.element.classList.add("dz-drag-hover")},dragleave:function(){return this.element.classList.remove("dz-drag-hover")},paste:i,reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(a){var c,d,e,f,g,h,i,j,k,l,m,n,o;if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer){for(a.previewElement=b.createElement(this.options.previewTemplate.trim()),a.previewTemplate=a.previewElement,this.previewsContainer.appendChild(a.previewElement),l=a.previewElement.querySelectorAll("[data-dz-name]"),f=0,i=l.length;i>f;f++)c=l[f],c.textContent=a.name;for(m=a.previewElement.querySelectorAll("[data-dz-size]"),g=0,j=m.length;j>g;g++)c=m[g],c.innerHTML=this.filesize(a.size);for(this.options.addRemoveLinks&&(a._removeLink=b.createElement(''+this.options.dictRemoveFile+""),a.previewElement.appendChild(a._removeLink)),d=function(c){return function(d){return d.preventDefault(),d.stopPropagation(),a.status===b.UPLOADING?b.confirm(c.options.dictCancelUploadConfirmation,function(){return c.removeFile(a)}):c.options.dictRemoveFileConfirmation?b.confirm(c.options.dictRemoveFileConfirmation,function(){return c.removeFile(a)}):c.removeFile(a)}}(this),n=a.previewElement.querySelectorAll("[data-dz-remove]"),o=[],h=0,k=n.length;k>h;h++)e=n[h],o.push(e.addEventListener("click",d));return o}},removedfile:function(a){var b;return a.previewElement&&null!=(b=a.previewElement)&&b.parentNode.removeChild(a.previewElement),this._updateMaxFilesReachedClass()},thumbnail:function(a,b){var c,d,e,f;if(a.previewElement){for(a.previewElement.classList.remove("dz-file-preview"),f=a.previewElement.querySelectorAll("[data-dz-thumbnail]"),d=0,e=f.length;e>d;d++)c=f[d],c.alt=a.name,c.src=b;return setTimeout(function(){return function(){return a.previewElement.classList.add("dz-image-preview")}}(this),1)}},error:function(a,b){var c,d,e,f,g;if(a.previewElement){for(a.previewElement.classList.add("dz-error"),"String"!=typeof b&&b.error&&(b=b.error),f=a.previewElement.querySelectorAll("[data-dz-errormessage]"),g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(c.textContent=b);return g}},errormultiple:i,processing:function(a){return a.previewElement&&(a.previewElement.classList.add("dz-processing"),a._removeLink)?a._removeLink.textContent=this.options.dictCancelUpload:void 0},processingmultiple:i,uploadprogress:function(a,b){var c,d,e,f,g;if(a.previewElement){for(f=a.previewElement.querySelectorAll("[data-dz-uploadprogress]"),g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push("PROGRESS"===c.nodeName?c.value=b:c.style.width=""+b+"%");return g}},totaluploadprogress:i,sending:i,sendingmultiple:i,success:function(a){return a.previewElement?a.previewElement.classList.add("dz-success"):void 0},successmultiple:i,canceled:function(a){return this.emit("error",a,"Upload canceled.")},canceledmultiple:i,complete:function(a){return a._removeLink&&(a._removeLink.textContent=this.options.dictRemoveFile),a.previewElement?a.previewElement.classList.add("dz-complete"):void 0},completemultiple:i,maxfilesexceeded:i,maxfilesreached:i,queuecomplete:i,previewTemplate:'
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
'},c=function(){var a,b,c,d,e,f,g;for(d=arguments[0],c=2<=arguments.length?k.call(arguments,1):[],f=0,g=c.length;g>f;f++){b=c[f];for(a in b)e=b[a],d[a]=e}return d},b.prototype.getAcceptedFiles=function(){var a,b,c,d,e;for(d=this.files,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.accepted&&e.push(a);return e},b.prototype.getRejectedFiles=function(){var a,b,c,d,e;for(d=this.files,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.accepted||e.push(a);return e},b.prototype.getFilesWithStatus=function(a){var b,c,d,e,f;for(e=this.files,f=[],c=0,d=e.length;d>c;c++)b=e[c],b.status===a&&f.push(b);return f},b.prototype.getQueuedFiles=function(){return this.getFilesWithStatus(b.QUEUED)},b.prototype.getUploadingFiles=function(){return this.getFilesWithStatus(b.UPLOADING)},b.prototype.getActiveFiles=function(){var a,c,d,e,f;for(e=this.files,f=[],c=0,d=e.length;d>c;c++)a=e[c],(a.status===b.UPLOADING||a.status===b.QUEUED)&&f.push(a);return f},b.prototype.init=function(){var a,c,d,e,f,g,h;for("form"===this.element.tagName&&this.element.setAttribute("enctype","multipart/form-data"),this.element.classList.contains("dropzone")&&!this.element.querySelector(".dz-message")&&this.element.appendChild(b.createElement('
'+this.options.dictDefaultMessage+"
")),this.clickableElements.length&&(d=function(a){return function(){return a.hiddenFileInput&&document.body.removeChild(a.hiddenFileInput),a.hiddenFileInput=document.createElement("input"),a.hiddenFileInput.setAttribute("type","file"),(null==a.options.maxFiles||a.options.maxFiles>1)&&a.hiddenFileInput.setAttribute("multiple","multiple"),a.hiddenFileInput.className="dz-hidden-input",null!=a.options.acceptedFiles&&a.hiddenFileInput.setAttribute("accept",a.options.acceptedFiles),null!=a.options.capture&&a.hiddenFileInput.setAttribute("capture",a.options.capture),a.hiddenFileInput.style.visibility="hidden",a.hiddenFileInput.style.position="absolute",a.hiddenFileInput.style.top="0",a.hiddenFileInput.style.left="0",a.hiddenFileInput.style.height="0",a.hiddenFileInput.style.width="0",document.body.appendChild(a.hiddenFileInput),a.hiddenFileInput.addEventListener("change",function(){var b,c,e,f;if(c=a.hiddenFileInput.files,c.length)for(e=0,f=c.length;f>e;e++)b=c[e],a.addFile(b);return d()})}}(this))(),this.URL=null!=(g=window.URL)?g:window.webkitURL,h=this.events,e=0,f=h.length;f>e;e++)a=h[e],this.on(a,this.options[a]);return this.on("uploadprogress",function(a){return function(){return a.updateTotalUploadProgress()}}(this)),this.on("removedfile",function(a){return function(){return a.updateTotalUploadProgress()}}(this)),this.on("canceled",function(a){return function(b){return a.emit("complete",b)}}(this)),this.on("complete",function(a){return function(){return 0===a.getUploadingFiles().length&&0===a.getQueuedFiles().length?setTimeout(function(){return a.emit("queuecomplete")},0):void 0}}(this)),c=function(a){return a.stopPropagation(),a.preventDefault?a.preventDefault():a.returnValue=!1},this.listeners=[{element:this.element,events:{dragstart:function(a){return function(b){return a.emit("dragstart",b)}}(this),dragenter:function(a){return function(b){return c(b),a.emit("dragenter",b)}}(this),dragover:function(a){return function(b){var d;try{d=b.dataTransfer.effectAllowed}catch(e){}return b.dataTransfer.dropEffect="move"===d||"linkMove"===d?"move":"copy",c(b),a.emit("dragover",b)}}(this),dragleave:function(a){return function(b){return a.emit("dragleave",b)}}(this),drop:function(a){return function(b){return c(b),a.drop(b)}}(this),dragend:function(a){return function(b){return a.emit("dragend",b)}}(this)}}],this.clickableElements.forEach(function(a){return function(c){return a.listeners.push({element:c,events:{click:function(d){return c!==a.element||d.target===a.element||b.elementInside(d.target,a.element.querySelector(".dz-message"))?a.hiddenFileInput.click():void 0}}})}}(this)),this.enable(),this.options.init.call(this)},b.prototype.destroy=function(){var a;return this.disable(),this.removeAllFiles(!0),(null!=(a=this.hiddenFileInput)?a.parentNode:void 0)&&(this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput),this.hiddenFileInput=null),delete this.element.dropzone,b.instances.splice(b.instances.indexOf(this),1)},b.prototype.updateTotalUploadProgress=function(){var a,b,c,d,e,f,g,h;if(d=0,c=0,a=this.getActiveFiles(),a.length){for(h=this.getActiveFiles(),f=0,g=h.length;g>f;f++)b=h[f],d+=b.upload.bytesSent,c+=b.upload.total;e=100*d/c}else e=100;return this.emit("totaluploadprogress",e,c,d)},b.prototype._getParamName=function(a){return"function"==typeof this.options.paramName?this.options.paramName(a):""+this.options.paramName+(this.options.uploadMultiple?"["+a+"]":"")},b.prototype.getFallbackForm=function(){var a,c,d,e;return(a=this.getExistingFallback())?a:(d='
',this.options.dictFallbackText&&(d+="

"+this.options.dictFallbackText+"

"),d+='
',c=b.createElement(d),"FORM"!==this.element.tagName?(e=b.createElement('
'),e.appendChild(c)):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=e?e:c)},b.prototype.getExistingFallback=function(){var a,b,c,d,e,f;for(b=function(a){var b,c,d;for(c=0,d=a.length;d>c;c++)if(b=a[c],/(^| )fallback($| )/.test(b.className))return b},f=["div","form"],d=0,e=f.length;e>d;d++)if(c=f[d],a=b(this.element.getElementsByTagName(c)))return a},b.prototype.setupEventListeners=function(){var a,b,c,d,e,f,g;for(f=this.listeners,g=[],d=0,e=f.length;e>d;d++)a=f[d],g.push(function(){var d,e;d=a.events,e=[];for(b in d)c=d[b],e.push(a.element.addEventListener(b,c,!1));return e}());return g},b.prototype.removeEventListeners=function(){var a,b,c,d,e,f,g;for(f=this.listeners,g=[],d=0,e=f.length;e>d;d++)a=f[d],g.push(function(){var d,e;d=a.events,e=[];for(b in d)c=d[b],e.push(a.element.removeEventListener(b,c,!1));return e}());return g},b.prototype.disable=function(){var a,b,c,d,e;for(this.clickableElements.forEach(function(a){return a.classList.remove("dz-clickable")}),this.removeEventListeners(),d=this.files,e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(this.cancelUpload(a));return e},b.prototype.enable=function(){return this.clickableElements.forEach(function(a){return a.classList.add("dz-clickable")}),this.setupEventListeners()},b.prototype.filesize=function(a){var b,c,d,e,f,g,h,i;for(g=["TB","GB","MB","KB","b"],d=e=null,c=h=0,i=g.length;i>h;c=++h)if(f=g[c],b=Math.pow(this.options.filesizeBase,4-c)/10,a>=b){d=a/Math.pow(this.options.filesizeBase,4-c),e=f;break}return d=Math.round(10*d)/10,""+d+" "+e},b.prototype._updateMaxFilesReachedClass=function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")},b.prototype.drop=function(a){var b,c;a.dataTransfer&&(this.emit("drop",a),b=a.dataTransfer.files,b.length&&(c=a.dataTransfer.items,c&&c.length&&null!=c[0].webkitGetAsEntry?this._addFilesFromItems(c):this.handleFiles(b)))},b.prototype.paste=function(a){var b,c;if(null!=(null!=a&&null!=(c=a.clipboardData)?c.items:void 0))return this.emit("paste",a),b=a.clipboardData.items,b.length?this._addFilesFromItems(b):void 0},b.prototype.handleFiles=function(a){var b,c,d,e;for(e=[],c=0,d=a.length;d>c;c++)b=a[c],e.push(this.addFile(b));return e},b.prototype._addFilesFromItems=function(a){var b,c,d,e,f;for(f=[],d=0,e=a.length;e>d;d++)c=a[d],f.push(null!=c.webkitGetAsEntry&&(b=c.webkitGetAsEntry())?b.isFile?this.addFile(c.getAsFile()):b.isDirectory?this._addFilesFromDirectory(b,b.name):void 0:null!=c.getAsFile?null==c.kind||"file"===c.kind?this.addFile(c.getAsFile()):void 0:void 0);return f},b.prototype._addFilesFromDirectory=function(a,b){var c,d;return c=a.createReader(),d=function(a){return function(c){var d,e,f;for(e=0,f=c.length;f>e;e++)d=c[e],d.isFile?d.file(function(c){return a.options.ignoreHiddenFiles&&"."===c.name.substring(0,1)?void 0:(c.fullPath=""+b+"/"+c.name,a.addFile(c))}):d.isDirectory&&a._addFilesFromDirectory(d,""+b+"/"+d.name)}}(this),c.readEntries(d,function(a){return"undefined"!=typeof console&&null!==console&&"function"==typeof console.log?console.log(a):void 0})},b.prototype.accept=function(a,c){return a.size>1024*this.options.maxFilesize*1024?c(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(a.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):b.isValidFile(a,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(c(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",a)):this.options.accept.call(this,a,c):c(this.options.dictInvalidFileType)},b.prototype.addFile=function(a){return a.upload={progress:0,total:a.size,bytesSent:0},this.files.push(a),a.status=b.ADDED,this.emit("addedfile",a),this._enqueueThumbnail(a),this.accept(a,function(b){return function(c){return c?(a.accepted=!1,b._errorProcessing([a],c)):(a.accepted=!0,b.options.autoQueue&&b.enqueueFile(a)),b._updateMaxFilesReachedClass()}}(this))},b.prototype.enqueueFiles=function(a){var b,c,d;for(c=0,d=a.length;d>c;c++)b=a[c],this.enqueueFile(b);return null},b.prototype.enqueueFile=function(a){if(a.status!==b.ADDED||a.accepted!==!0)throw new Error("This file can't be queued because it has already been processed or was rejected.");return a.status=b.QUEUED,this.options.autoProcessQueue?setTimeout(function(a){return function(){return a.processQueue()}}(this),0):void 0},b.prototype._thumbnailQueue=[],b.prototype._processingThumbnail=!1,b.prototype._enqueueThumbnail=function(a){return this.options.createImageThumbnails&&a.type.match(/image.*/)&&a.size<=1024*this.options.maxThumbnailFilesize*1024?(this._thumbnailQueue.push(a),setTimeout(function(a){return function(){return a._processThumbnailQueue()}}(this),0)):void 0},b.prototype._processThumbnailQueue=function(){return this._processingThumbnail||0===this._thumbnailQueue.length?void 0:(this._processingThumbnail=!0,this.createThumbnail(this._thumbnailQueue.shift(),function(a){return function(){return a._processingThumbnail=!1,a._processThumbnailQueue()}}(this)))},b.prototype.removeFile=function(a){return a.status===b.UPLOADING&&this.cancelUpload(a),this.files=j(this.files,a),this.emit("removedfile",a),0===this.files.length?this.emit("reset"):void 0},b.prototype.removeAllFiles=function(a){var c,d,e,f;for(null==a&&(a=!1),f=this.files.slice(),d=0,e=f.length;e>d;d++)c=f[d],(c.status!==b.UPLOADING||a)&&this.removeFile(c);return null},b.prototype.createThumbnail=function(a,b){var c;return c=new FileReader,c.onload=function(d){return function(){var e;return"image/svg+xml"===a.type?(d.emit("thumbnail",a,c.result),void(null!=b&&b())):(e=document.createElement("img"),e.onload=function(){var c,f,g,i,j,k,l,m;return a.width=e.width,a.height=e.height,g=d.options.resize.call(d,a),null==g.trgWidth&&(g.trgWidth=g.optWidth),null==g.trgHeight&&(g.trgHeight=g.optHeight),c=document.createElement("canvas"),f=c.getContext("2d"),c.width=g.trgWidth,c.height=g.trgHeight,h(f,e,null!=(j=g.srcX)?j:0,null!=(k=g.srcY)?k:0,g.srcWidth,g.srcHeight,null!=(l=g.trgX)?l:0,null!=(m=g.trgY)?m:0,g.trgWidth,g.trgHeight),i=c.toDataURL("image/png"),d.emit("thumbnail",a,i),null!=b?b():void 0},e.onerror=b,e.src=c.result)}}(this),c.readAsDataURL(a)},b.prototype.processQueue=function(){var a,b,c,d;if(b=this.options.parallelUploads,c=this.getUploadingFiles().length,a=c,!(c>=b)&&(d=this.getQueuedFiles(),d.length>0)){if(this.options.uploadMultiple)return this.processFiles(d.slice(0,b-c));for(;b>a;){if(!d.length)return;this.processFile(d.shift()),a++}}},b.prototype.processFile=function(a){return this.processFiles([a])},b.prototype.processFiles=function(a){var c,d,e;for(d=0,e=a.length;e>d;d++)c=a[d],c.processing=!0,c.status=b.UPLOADING,this.emit("processing",c);return this.options.uploadMultiple&&this.emit("processingmultiple",a),this.uploadFiles(a)},b.prototype._getFilesWithXhr=function(a){var b,c;return c=function(){var c,d,e,f;for(e=this.files,f=[],c=0,d=e.length;d>c;c++)b=e[c],b.xhr===a&&f.push(b);return f}.call(this)},b.prototype.cancelUpload=function(a){var c,d,e,f,g,h,i;if(a.status===b.UPLOADING){for(d=this._getFilesWithXhr(a.xhr),e=0,g=d.length;g>e;e++)c=d[e],c.status=b.CANCELED;for(a.xhr.abort(),f=0,h=d.length;h>f;f++)c=d[f],this.emit("canceled",c);this.options.uploadMultiple&&this.emit("canceledmultiple",d)}else((i=a.status)===b.ADDED||i===b.QUEUED)&&(a.status=b.CANCELED,this.emit("canceled",a),this.options.uploadMultiple&&this.emit("canceledmultiple",[a]));return this.options.autoProcessQueue?this.processQueue():void 0},e=function(){var a,b;return b=arguments[0],a=2<=arguments.length?k.call(arguments,1):[],"function"==typeof b?b.apply(this,a):b},b.prototype.uploadFile=function(a){return this.uploadFiles([a])},b.prototype.uploadFiles=function(a){var d,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L;for(w=new XMLHttpRequest,x=0,B=a.length;B>x;x++)d=a[x],d.xhr=w;p=e(this.options.method,a),u=e(this.options.url,a),w.open(p,u,!0),w.withCredentials=!!this.options.withCredentials,s=null,g=function(b){return function(){var c,e,f;for(f=[],c=0,e=a.length;e>c;c++)d=a[c],f.push(b._errorProcessing(a,s||b.options.dictResponseError.replace("{{statusCode}}",w.status),w));return f}}(this),t=function(b){return function(c){var e,f,g,h,i,j,k,l,m;if(null!=c)for(f=100*c.loaded/c.total,g=0,j=a.length;j>g;g++)d=a[g],d.upload={progress:f,total:c.total,bytesSent:c.loaded};else{for(e=!0,f=100,h=0,k=a.length;k>h;h++)d=a[h],(100!==d.upload.progress||d.upload.bytesSent!==d.upload.total)&&(e=!1),d.upload.progress=f,d.upload.bytesSent=d.upload.total;if(e)return}for(m=[],i=0,l=a.length;l>i;i++)d=a[i],m.push(b.emit("uploadprogress",d,f,d.upload.bytesSent));return m}}(this),w.onload=function(c){return function(d){var e;if(a[0].status!==b.CANCELED&&4===w.readyState){if(s=w.responseText,w.getResponseHeader("content-type")&&~w.getResponseHeader("content-type").indexOf("application/json"))try{s=JSON.parse(s)}catch(f){d=f,s="Invalid JSON response from server."}return t(),200<=(e=w.status)&&300>e?c._finished(a,s,d):g()}}}(this),w.onerror=function(){return function(){return a[0].status!==b.CANCELED?g():void 0}}(this),r=null!=(G=w.upload)?G:w,r.onprogress=t,j={Accept:"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"},this.options.headers&&c(j,this.options.headers);for(h in j)i=j[h],w.setRequestHeader(h,i);if(f=new FormData,this.options.params){H=this.options.params;for(o in H)v=H[o],f.append(o,v)}for(y=0,C=a.length;C>y;y++)d=a[y],this.emit("sending",d,w,f);if(this.options.uploadMultiple&&this.emit("sendingmultiple",a,w,f),"FORM"===this.element.tagName)for(I=this.element.querySelectorAll("input, textarea, select, button"),z=0,D=I.length;D>z;z++)if(l=I[z],m=l.getAttribute("name"),n=l.getAttribute("type"),"SELECT"===l.tagName&&l.hasAttribute("multiple"))for(J=l.options,A=0,E=J.length;E>A;A++)q=J[A],q.selected&&f.append(m,q.value);else(!n||"checkbox"!==(K=n.toLowerCase())&&"radio"!==K||l.checked)&&f.append(m,l.value);for(k=F=0,L=a.length-1;L>=0?L>=F:F>=L;k=L>=0?++F:--F)f.append(this._getParamName(k),a[k],a[k].name);return w.send(f)},b.prototype._finished=function(a,c,d){var e,f,g;for(f=0,g=a.length;g>f;f++)e=a[f],e.status=b.SUCCESS,this.emit("success",e,c,d),this.emit("complete",e);return this.options.uploadMultiple&&(this.emit("successmultiple",a,c,d),this.emit("completemultiple",a)),this.options.autoProcessQueue?this.processQueue():void 0},b.prototype._errorProcessing=function(a,c,d){var e,f,g;for(f=0,g=a.length;g>f;f++)e=a[f],e.status=b.ERROR,this.emit("error",e,c,d),this.emit("complete",e);return this.options.uploadMultiple&&(this.emit("errormultiple",a,c,d),this.emit("completemultiple",a)),this.options.autoProcessQueue?this.processQueue():void 0},b}(d),c.version="4.0.1",c.options={},c.optionsForElement=function(a){return a.getAttribute("id")?c.options[e(a.getAttribute("id"))]:void 0},c.instances=[],c.forElement=function(a){if("string"==typeof a&&(a=document.querySelector(a)),null==(null!=a?a.dropzone:void 0))throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");return a.dropzone},c.autoDiscover=!0,c.discover=function(){var a,b,d,e,f,g;for(document.querySelectorAll?d=document.querySelectorAll(".dropzone"):(d=[],a=function(a){var b,c,e,f;for(f=[],c=0,e=a.length;e>c;c++)b=a[c],f.push(/(^| )dropzone($| )/.test(b.className)?d.push(b):void 0);return f},a(document.getElementsByTagName("div")),a(document.getElementsByTagName("form"))),g=[],e=0,f=d.length;f>e;e++)b=d[e],g.push(c.optionsForElement(b)!==!1?new c(b):void 0);return g},c.blacklistedBrowsers=[/opera.*Macintosh.*version\/12/i],c.isBrowserSupported=function(){var a,b,d,e,f;if(a=!0,window.File&&window.FileReader&&window.FileList&&window.Blob&&window.FormData&&document.querySelector)if("classList"in document.createElement("a"))for(f=c.blacklistedBrowsers,d=0,e=f.length;e>d;d++)b=f[d],b.test(navigator.userAgent)&&(a=!1);else a=!1;else a=!1;return a},j=function(a,b){var c,d,e,f;for(f=[],d=0,e=a.length;e>d;d++)c=a[d],c!==b&&f.push(c);return f},e=function(a){return a.replace(/[\-_](\w)/g,function(a){return a.charAt(1).toUpperCase()})},c.createElement=function(a){var b;return b=document.createElement("div"),b.innerHTML=a,b.childNodes[0]},c.elementInside=function(a,b){if(a===b)return!0;for(;a=a.parentNode;)if(a===b)return!0;return!1},c.getElement=function(a,b){var c;if("string"==typeof a?c=document.querySelector(a):null!=a.nodeType&&(c=a),null==c)throw new Error("Invalid `"+b+"` option provided. Please provide a CSS selector or a plain HTML element.");return c},c.getElements=function(a,b){var c,d,e,f,g,h,i,j;if(a instanceof Array){e=[];try{for(f=0,h=a.length;h>f;f++)d=a[f],e.push(this.getElement(d,b))}catch(k){c=k,e=null}}else if("string"==typeof a)for(e=[],j=document.querySelectorAll(a),g=0,i=j.length;i>g;g++)d=j[g],e.push(d);else null!=a.nodeType&&(e=[a]);if(null==e||!e.length)throw new Error("Invalid `"+b+"` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");return e},c.confirm=function(a,b,c){return window.confirm(a)?b():null!=c?c():void 0},c.isValidFile=function(a,b){var c,d,e,f,g;if(!b)return!0;for(b=b.split(","),d=a.type,c=d.replace(/\/.*$/,""),f=0,g=b.length;g>f;f++)if(e=b[f],e=e.trim(),"."===e.charAt(0)){if(-1!==a.name.toLowerCase().indexOf(e.toLowerCase(),a.name.length-e.length))return!0}else if(/\/\*$/.test(e)){if(c===e.replace(/\/.*$/,""))return!0}else if(d===e)return!0;return!1},"undefined"!=typeof a&&null!==a&&(a.fn.dropzone=function(a){return this.each(function(){return new c(this,a)})}),"undefined"!=typeof b&&null!==b?b.exports=c:window.Dropzone=c,c.ADDED="added",c.QUEUED="queued",c.ACCEPTED=c.QUEUED,c.UPLOADING="uploading",c.PROCESSING=c.UPLOADING,c.CANCELED="canceled",c.ERROR="error",c.SUCCESS="success",g=function(a){var b,c,d,e,f,g,h,i,j,k;for(h=a.naturalWidth,g=a.naturalHeight,c=document.createElement("canvas"),c.width=1,c.height=g,d=c.getContext("2d"),d.drawImage(a,0,0),e=d.getImageData(0,0,1,g).data,k=0,f=g,i=g;i>k;)b=e[4*(i-1)+3],0===b?f=i:k=i,i=f+k>>1;return j=i/g,0===j?1:j},h=function(a,b,c,d,e,f,h,i,j,k){var l; +return l=g(b),a.drawImage(b,c,d,e,f,h,i,j,k/l)},f=function(a,b){var c,d,e,f,g,h,i,j,k;if(e=!1,k=!0,d=a.document,j=d.documentElement,c=d.addEventListener?"addEventListener":"attachEvent",i=d.addEventListener?"removeEventListener":"detachEvent",h=d.addEventListener?"":"on",f=function(c){return"readystatechange"!==c.type||"complete"===d.readyState?(("load"===c.type?a:d)[i](h+c.type,f,!1),!e&&(e=!0)?b.call(a,c.type||c):void 0):void 0},g=function(){var a;try{j.doScroll("left")}catch(b){return a=b,void setTimeout(g,50)}return f("poll")},"complete"!==d.readyState){if(d.createEventObject&&j.doScroll){try{k=!a.frameElement}catch(l){}k&&g()}return d[c](h+"DOMContentLoaded",f,!1),d[c](h+"readystatechange",f,!1),a[c](h+"load",f,!1)}},c._autoDiscoverFunction=function(){return c.autoDiscover?c.discover():void 0},f(window,c._autoDiscoverFunction)}.call(this),b.exports}); \ No newline at end of file diff --git a/public/js/dropzone/min/dropzone.min.css b/public/js/dropzone/min/dropzone.min.css new file mode 100755 index 00000000..d04515e2 --- /dev/null +++ b/public/js/dropzone/min/dropzone.min.css @@ -0,0 +1 @@ +@-webkit-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-moz-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%, 70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-webkit-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-moz-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-webkit-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@-moz-keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}@keyframes pulse{0%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-ms-transform:scale(1.1);-o-transform:scale(1.1);transform:scale(1.1)}20%{-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:2px solid rgba(0,0,0,0.3);background:white;padding:20px 20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:0.5}.dropzone .dz-message{text-align:center;margin:2em 0}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom, #eee, #ddd)}.dropzone .dz-preview.dz-file-preview .dz-details{opacity:1}.dropzone .dz-preview.dz-image-preview{background:white}.dropzone .dz-preview.dz-image-preview .dz-details{-webkit-transition:opacity 0.2s linear;-moz-transition:opacity 0.2s linear;-ms-transition:opacity 0.2s linear;-o-transition:opacity 0.2s linear;transition:opacity 0.2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,0.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,0.8);background-color:rgba(255,255,255,0.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid transparent}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:rgba(255,255,255,0.4);padding:0 0.4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{-webkit-transform:scale(1.05, 1.05);-moz-transform:scale(1.05, 1.05);-ms-transform:scale(1.05, 1.05);-o-transform:scale(1.05, 1.05);transform:scale(1.05, 1.05);-webkit-filter:blur(8px);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{-webkit-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;-webkit-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-moz-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-ms-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);-o-animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px}.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{display:block;width:54px;height:54px}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;-webkit-transition:all 0.2s linear;-moz-transition:all 0.2s linear;-ms-transition:all 0.2s linear;-o-transition:all 0.2s linear;transition:all 0.2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;-webkit-transition:opacity 0.4s ease-in;-moz-transition:opacity 0.4s ease-in;-ms-transition:opacity 0.4s ease-in;-o-transition:opacity 0.4s ease-in;transition:opacity 0.4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{-webkit-animation:pulse 6s ease infinite;-moz-animation:pulse 6s ease infinite;-ms-animation:pulse 6s ease infinite;-o-animation:pulse 6s ease infinite;animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:16px;left:50%;top:50%;margin-top:-8px;width:80px;margin-left:-40px;background:rgba(255,255,255,0.9);-webkit-transform:scale(1);border-radius:8px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#333;background:linear-gradient(to bottom, #666, #444);position:absolute;top:0;left:0;bottom:0;width:0;-webkit-transition:width 300ms ease-in-out;-moz-transition:width 300ms ease-in-out;-ms-transition:width 300ms ease-in-out;-o-transition:width 300ms ease-in-out;transition:width 300ms ease-in-out}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;-webkit-transition:opacity 0.3s ease;-moz-transition:opacity 0.3s ease;-ms-transition:opacity 0.3s ease;-o-transition:opacity 0.3s ease;transition:opacity 0.3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#be2626;background:linear-gradient(to bottom, #be2626, #a92222);padding:0.5em 1.2em;color:white}.dropzone .dz-preview .dz-error-message:after{content:'';position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #be2626} diff --git a/public/js/dropzone/min/dropzone.min.js b/public/js/dropzone/min/dropzone.min.js new file mode 100755 index 00000000..56f76959 --- /dev/null +++ b/public/js/dropzone/min/dropzone.min.js @@ -0,0 +1,2 @@ +(function(){var a,b,c,d,e,f,g,h,i=[].slice,j={}.hasOwnProperty,k=function(a,b){function c(){this.constructor=a}for(var d in b)j.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a};g=function(){},b=function(){function a(){}return a.prototype.addEventListener=a.prototype.on,a.prototype.on=function(a,b){return this._callbacks=this._callbacks||{},this._callbacks[a]||(this._callbacks[a]=[]),this._callbacks[a].push(b),this},a.prototype.emit=function(){var a,b,c,d,e,f;if(d=arguments[0],a=2<=arguments.length?i.call(arguments,1):[],this._callbacks=this._callbacks||{},c=this._callbacks[d])for(e=0,f=c.length;f>e;e++)b=c[e],b.apply(this,a);return this},a.prototype.removeListener=a.prototype.off,a.prototype.removeAllListeners=a.prototype.off,a.prototype.removeEventListener=a.prototype.off,a.prototype.off=function(a,b){var c,d,e,f,g;if(!this._callbacks||0===arguments.length)return this._callbacks={},this;if(d=this._callbacks[a],!d)return this;if(1===arguments.length)return delete this._callbacks[a],this;for(e=f=0,g=d.length;g>f;e=++f)if(c=d[e],c===b){d.splice(e,1);break}return this},a}(),a=function(a){function c(a,b){var e,f,g;if(this.element=a,this.version=c.version,this.defaultOptions.previewTemplate=this.defaultOptions.previewTemplate.replace(/\n*/g,""),this.clickableElements=[],this.listeners=[],this.files=[],"string"==typeof this.element&&(this.element=document.querySelector(this.element)),!this.element||null==this.element.nodeType)throw new Error("Invalid dropzone element.");if(this.element.dropzone)throw new Error("Dropzone already attached.");if(c.instances.push(this),this.element.dropzone=this,e=null!=(g=c.optionsForElement(this.element))?g:{},this.options=d({},this.defaultOptions,e,null!=b?b:{}),this.options.forceFallback||!c.isBrowserSupported())return this.options.fallback.call(this);if(null==this.options.url&&(this.options.url=this.element.getAttribute("action")),!this.options.url)throw new Error("No URL provided.");if(this.options.acceptedFiles&&this.options.acceptedMimeTypes)throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");this.options.acceptedMimeTypes&&(this.options.acceptedFiles=this.options.acceptedMimeTypes,delete this.options.acceptedMimeTypes),this.options.method=this.options.method.toUpperCase(),(f=this.getExistingFallback())&&f.parentNode&&f.parentNode.removeChild(f),this.options.previewsContainer!==!1&&(this.previewsContainer=this.options.previewsContainer?c.getElement(this.options.previewsContainer,"previewsContainer"):this.element),this.options.clickable&&(this.clickableElements=this.options.clickable===!0?[this.element]:c.getElements(this.options.clickable,"clickable")),this.init()}var d,e;return k(c,a),c.prototype.Emitter=b,c.prototype.events=["drop","dragstart","dragend","dragenter","dragover","dragleave","addedfile","removedfile","thumbnail","error","errormultiple","processing","processingmultiple","uploadprogress","totaluploadprogress","sending","sendingmultiple","success","successmultiple","canceled","canceledmultiple","complete","completemultiple","reset","maxfilesexceeded","maxfilesreached","queuecomplete"],c.prototype.defaultOptions={url:null,method:"post",withCredentials:!1,parallelUploads:2,uploadMultiple:!1,maxFilesize:256,paramName:"file",createImageThumbnails:!0,maxThumbnailFilesize:10,thumbnailWidth:120,thumbnailHeight:120,filesizeBase:1e3,maxFiles:null,filesizeBase:1e3,params:{},clickable:!0,ignoreHiddenFiles:!0,acceptedFiles:null,acceptedMimeTypes:null,autoProcessQueue:!0,autoQueue:!0,addRemoveLinks:!1,previewsContainer:null,capture:null,dictDefaultMessage:"Drop files here to upload",dictFallbackMessage:"Your browser does not support drag'n'drop file uploads.",dictFallbackText:"Please use the fallback form below to upload your files like in the olden days.",dictFileTooBig:"File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",dictInvalidFileType:"You can't upload files of this type.",dictResponseError:"Server responded with {{statusCode}} code.",dictCancelUpload:"Cancel upload",dictCancelUploadConfirmation:"Are you sure you want to cancel this upload?",dictRemoveFile:"Remove file",dictRemoveFileConfirmation:null,dictMaxFilesExceeded:"You can not upload any more files.",accept:function(a,b){return b()},init:function(){return g},forceFallback:!1,fallback:function(){var a,b,d,e,f,g;for(this.element.className=""+this.element.className+" dz-browser-not-supported",g=this.element.getElementsByTagName("div"),e=0,f=g.length;f>e;e++)a=g[e],/(^| )dz-message($| )/.test(a.className)&&(b=a,a.className="dz-message");return b||(b=c.createElement('
'),this.element.appendChild(b)),d=b.getElementsByTagName("span")[0],d&&(d.textContent=this.options.dictFallbackMessage),this.element.appendChild(this.getFallbackForm())},resize:function(a){var b,c,d;return b={srcX:0,srcY:0,srcWidth:a.width,srcHeight:a.height},c=a.width/a.height,b.optWidth=this.options.thumbnailWidth,b.optHeight=this.options.thumbnailHeight,null==b.optWidth&&null==b.optHeight?(b.optWidth=b.srcWidth,b.optHeight=b.srcHeight):null==b.optWidth?b.optWidth=c*b.optHeight:null==b.optHeight&&(b.optHeight=1/c*b.optWidth),d=b.optWidth/b.optHeight,a.heightd?(b.srcHeight=a.height,b.srcWidth=b.srcHeight*d):(b.srcWidth=a.width,b.srcHeight=b.srcWidth/d),b.srcX=(a.width-b.srcWidth)/2,b.srcY=(a.height-b.srcHeight)/2,b},drop:function(){return this.element.classList.remove("dz-drag-hover")},dragstart:g,dragend:function(){return this.element.classList.remove("dz-drag-hover")},dragenter:function(){return this.element.classList.add("dz-drag-hover")},dragover:function(){return this.element.classList.add("dz-drag-hover")},dragleave:function(){return this.element.classList.remove("dz-drag-hover")},paste:g,reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(a){var b,d,e,f,g,h,i,j,k,l,m,n,o;if(this.element===this.previewsContainer&&this.element.classList.add("dz-started"),this.previewsContainer){for(a.previewElement=c.createElement(this.options.previewTemplate.trim()),a.previewTemplate=a.previewElement,this.previewsContainer.appendChild(a.previewElement),l=a.previewElement.querySelectorAll("[data-dz-name]"),f=0,i=l.length;i>f;f++)b=l[f],b.textContent=a.name;for(m=a.previewElement.querySelectorAll("[data-dz-size]"),g=0,j=m.length;j>g;g++)b=m[g],b.innerHTML=this.filesize(a.size);for(this.options.addRemoveLinks&&(a._removeLink=c.createElement(''+this.options.dictRemoveFile+""),a.previewElement.appendChild(a._removeLink)),d=function(b){return function(d){return d.preventDefault(),d.stopPropagation(),a.status===c.UPLOADING?c.confirm(b.options.dictCancelUploadConfirmation,function(){return b.removeFile(a)}):b.options.dictRemoveFileConfirmation?c.confirm(b.options.dictRemoveFileConfirmation,function(){return b.removeFile(a)}):b.removeFile(a)}}(this),n=a.previewElement.querySelectorAll("[data-dz-remove]"),o=[],h=0,k=n.length;k>h;h++)e=n[h],o.push(e.addEventListener("click",d));return o}},removedfile:function(a){var b;return a.previewElement&&null!=(b=a.previewElement)&&b.parentNode.removeChild(a.previewElement),this._updateMaxFilesReachedClass()},thumbnail:function(a,b){var c,d,e,f;if(a.previewElement){for(a.previewElement.classList.remove("dz-file-preview"),f=a.previewElement.querySelectorAll("[data-dz-thumbnail]"),d=0,e=f.length;e>d;d++)c=f[d],c.alt=a.name,c.src=b;return setTimeout(function(){return function(){return a.previewElement.classList.add("dz-image-preview")}}(this),1)}},error:function(a,b){var c,d,e,f,g;if(a.previewElement){for(a.previewElement.classList.add("dz-error"),"String"!=typeof b&&b.error&&(b=b.error),f=a.previewElement.querySelectorAll("[data-dz-errormessage]"),g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(c.textContent=b);return g}},errormultiple:g,processing:function(a){return a.previewElement&&(a.previewElement.classList.add("dz-processing"),a._removeLink)?a._removeLink.textContent=this.options.dictCancelUpload:void 0},processingmultiple:g,uploadprogress:function(a,b){var c,d,e,f,g;if(a.previewElement){for(f=a.previewElement.querySelectorAll("[data-dz-uploadprogress]"),g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push("PROGRESS"===c.nodeName?c.value=b:c.style.width=""+b+"%");return g}},totaluploadprogress:g,sending:g,sendingmultiple:g,success:function(a){return a.previewElement?a.previewElement.classList.add("dz-success"):void 0},successmultiple:g,canceled:function(a){return this.emit("error",a,"Upload canceled.")},canceledmultiple:g,complete:function(a){return a._removeLink&&(a._removeLink.textContent=this.options.dictRemoveFile),a.previewElement?a.previewElement.classList.add("dz-complete"):void 0},completemultiple:g,maxfilesexceeded:g,maxfilesreached:g,queuecomplete:g,previewTemplate:'
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
'},d=function(){var a,b,c,d,e,f,g;for(d=arguments[0],c=2<=arguments.length?i.call(arguments,1):[],f=0,g=c.length;g>f;f++){b=c[f];for(a in b)e=b[a],d[a]=e}return d},c.prototype.getAcceptedFiles=function(){var a,b,c,d,e;for(d=this.files,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.accepted&&e.push(a);return e},c.prototype.getRejectedFiles=function(){var a,b,c,d,e;for(d=this.files,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.accepted||e.push(a);return e},c.prototype.getFilesWithStatus=function(a){var b,c,d,e,f;for(e=this.files,f=[],c=0,d=e.length;d>c;c++)b=e[c],b.status===a&&f.push(b);return f},c.prototype.getQueuedFiles=function(){return this.getFilesWithStatus(c.QUEUED)},c.prototype.getUploadingFiles=function(){return this.getFilesWithStatus(c.UPLOADING)},c.prototype.getActiveFiles=function(){var a,b,d,e,f;for(e=this.files,f=[],b=0,d=e.length;d>b;b++)a=e[b],(a.status===c.UPLOADING||a.status===c.QUEUED)&&f.push(a);return f},c.prototype.init=function(){var a,b,d,e,f,g,h;for("form"===this.element.tagName&&this.element.setAttribute("enctype","multipart/form-data"),this.element.classList.contains("dropzone")&&!this.element.querySelector(".dz-message")&&this.element.appendChild(c.createElement('
'+this.options.dictDefaultMessage+"
")),this.clickableElements.length&&(d=function(a){return function(){return a.hiddenFileInput&&document.body.removeChild(a.hiddenFileInput),a.hiddenFileInput=document.createElement("input"),a.hiddenFileInput.setAttribute("type","file"),(null==a.options.maxFiles||a.options.maxFiles>1)&&a.hiddenFileInput.setAttribute("multiple","multiple"),a.hiddenFileInput.className="dz-hidden-input",null!=a.options.acceptedFiles&&a.hiddenFileInput.setAttribute("accept",a.options.acceptedFiles),null!=a.options.capture&&a.hiddenFileInput.setAttribute("capture",a.options.capture),a.hiddenFileInput.style.visibility="hidden",a.hiddenFileInput.style.position="absolute",a.hiddenFileInput.style.top="0",a.hiddenFileInput.style.left="0",a.hiddenFileInput.style.height="0",a.hiddenFileInput.style.width="0",document.body.appendChild(a.hiddenFileInput),a.hiddenFileInput.addEventListener("change",function(){var b,c,e,f;if(c=a.hiddenFileInput.files,c.length)for(e=0,f=c.length;f>e;e++)b=c[e],a.addFile(b);return d()})}}(this))(),this.URL=null!=(g=window.URL)?g:window.webkitURL,h=this.events,e=0,f=h.length;f>e;e++)a=h[e],this.on(a,this.options[a]);return this.on("uploadprogress",function(a){return function(){return a.updateTotalUploadProgress()}}(this)),this.on("removedfile",function(a){return function(){return a.updateTotalUploadProgress()}}(this)),this.on("canceled",function(a){return function(b){return a.emit("complete",b)}}(this)),this.on("complete",function(a){return function(){return 0===a.getUploadingFiles().length&&0===a.getQueuedFiles().length?setTimeout(function(){return a.emit("queuecomplete")},0):void 0}}(this)),b=function(a){return a.stopPropagation(),a.preventDefault?a.preventDefault():a.returnValue=!1},this.listeners=[{element:this.element,events:{dragstart:function(a){return function(b){return a.emit("dragstart",b)}}(this),dragenter:function(a){return function(c){return b(c),a.emit("dragenter",c)}}(this),dragover:function(a){return function(c){var d;try{d=c.dataTransfer.effectAllowed}catch(e){}return c.dataTransfer.dropEffect="move"===d||"linkMove"===d?"move":"copy",b(c),a.emit("dragover",c)}}(this),dragleave:function(a){return function(b){return a.emit("dragleave",b)}}(this),drop:function(a){return function(c){return b(c),a.drop(c)}}(this),dragend:function(a){return function(b){return a.emit("dragend",b)}}(this)}}],this.clickableElements.forEach(function(a){return function(b){return a.listeners.push({element:b,events:{click:function(d){return b!==a.element||d.target===a.element||c.elementInside(d.target,a.element.querySelector(".dz-message"))?a.hiddenFileInput.click():void 0}}})}}(this)),this.enable(),this.options.init.call(this)},c.prototype.destroy=function(){var a;return this.disable(),this.removeAllFiles(!0),(null!=(a=this.hiddenFileInput)?a.parentNode:void 0)&&(this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput),this.hiddenFileInput=null),delete this.element.dropzone,c.instances.splice(c.instances.indexOf(this),1)},c.prototype.updateTotalUploadProgress=function(){var a,b,c,d,e,f,g,h;if(d=0,c=0,a=this.getActiveFiles(),a.length){for(h=this.getActiveFiles(),f=0,g=h.length;g>f;f++)b=h[f],d+=b.upload.bytesSent,c+=b.upload.total;e=100*d/c}else e=100;return this.emit("totaluploadprogress",e,c,d)},c.prototype._getParamName=function(a){return"function"==typeof this.options.paramName?this.options.paramName(a):""+this.options.paramName+(this.options.uploadMultiple?"["+a+"]":"")},c.prototype.getFallbackForm=function(){var a,b,d,e;return(a=this.getExistingFallback())?a:(d='
',this.options.dictFallbackText&&(d+="

"+this.options.dictFallbackText+"

"),d+='
',b=c.createElement(d),"FORM"!==this.element.tagName?(e=c.createElement('
'),e.appendChild(b)):(this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method)),null!=e?e:b)},c.prototype.getExistingFallback=function(){var a,b,c,d,e,f;for(b=function(a){var b,c,d;for(c=0,d=a.length;d>c;c++)if(b=a[c],/(^| )fallback($| )/.test(b.className))return b},f=["div","form"],d=0,e=f.length;e>d;d++)if(c=f[d],a=b(this.element.getElementsByTagName(c)))return a},c.prototype.setupEventListeners=function(){var a,b,c,d,e,f,g;for(f=this.listeners,g=[],d=0,e=f.length;e>d;d++)a=f[d],g.push(function(){var d,e;d=a.events,e=[];for(b in d)c=d[b],e.push(a.element.addEventListener(b,c,!1));return e}());return g},c.prototype.removeEventListeners=function(){var a,b,c,d,e,f,g;for(f=this.listeners,g=[],d=0,e=f.length;e>d;d++)a=f[d],g.push(function(){var d,e;d=a.events,e=[];for(b in d)c=d[b],e.push(a.element.removeEventListener(b,c,!1));return e}());return g},c.prototype.disable=function(){var a,b,c,d,e;for(this.clickableElements.forEach(function(a){return a.classList.remove("dz-clickable")}),this.removeEventListeners(),d=this.files,e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(this.cancelUpload(a));return e},c.prototype.enable=function(){return this.clickableElements.forEach(function(a){return a.classList.add("dz-clickable")}),this.setupEventListeners()},c.prototype.filesize=function(a){var b,c,d,e,f,g,h,i;for(g=["TB","GB","MB","KB","b"],d=e=null,c=h=0,i=g.length;i>h;c=++h)if(f=g[c],b=Math.pow(this.options.filesizeBase,4-c)/10,a>=b){d=a/Math.pow(this.options.filesizeBase,4-c),e=f;break}return d=Math.round(10*d)/10,""+d+" "+e},c.prototype._updateMaxFilesReachedClass=function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")},c.prototype.drop=function(a){var b,c;a.dataTransfer&&(this.emit("drop",a),b=a.dataTransfer.files,b.length&&(c=a.dataTransfer.items,c&&c.length&&null!=c[0].webkitGetAsEntry?this._addFilesFromItems(c):this.handleFiles(b)))},c.prototype.paste=function(a){var b,c;if(null!=(null!=a&&null!=(c=a.clipboardData)?c.items:void 0))return this.emit("paste",a),b=a.clipboardData.items,b.length?this._addFilesFromItems(b):void 0},c.prototype.handleFiles=function(a){var b,c,d,e;for(e=[],c=0,d=a.length;d>c;c++)b=a[c],e.push(this.addFile(b));return e},c.prototype._addFilesFromItems=function(a){var b,c,d,e,f;for(f=[],d=0,e=a.length;e>d;d++)c=a[d],f.push(null!=c.webkitGetAsEntry&&(b=c.webkitGetAsEntry())?b.isFile?this.addFile(c.getAsFile()):b.isDirectory?this._addFilesFromDirectory(b,b.name):void 0:null!=c.getAsFile?null==c.kind||"file"===c.kind?this.addFile(c.getAsFile()):void 0:void 0);return f},c.prototype._addFilesFromDirectory=function(a,b){var c,d;return c=a.createReader(),d=function(a){return function(c){var d,e,f;for(e=0,f=c.length;f>e;e++)d=c[e],d.isFile?d.file(function(c){return a.options.ignoreHiddenFiles&&"."===c.name.substring(0,1)?void 0:(c.fullPath=""+b+"/"+c.name,a.addFile(c))}):d.isDirectory&&a._addFilesFromDirectory(d,""+b+"/"+d.name)}}(this),c.readEntries(d,function(a){return"undefined"!=typeof console&&null!==console&&"function"==typeof console.log?console.log(a):void 0})},c.prototype.accept=function(a,b){return a.size>1024*this.options.maxFilesize*1024?b(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(a.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):c.isValidFile(a,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles?(b(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",a)):this.options.accept.call(this,a,b):b(this.options.dictInvalidFileType)},c.prototype.addFile=function(a){return a.upload={progress:0,total:a.size,bytesSent:0},this.files.push(a),a.status=c.ADDED,this.emit("addedfile",a),this._enqueueThumbnail(a),this.accept(a,function(b){return function(c){return c?(a.accepted=!1,b._errorProcessing([a],c)):(a.accepted=!0,b.options.autoQueue&&b.enqueueFile(a)),b._updateMaxFilesReachedClass()}}(this))},c.prototype.enqueueFiles=function(a){var b,c,d;for(c=0,d=a.length;d>c;c++)b=a[c],this.enqueueFile(b);return null},c.prototype.enqueueFile=function(a){if(a.status!==c.ADDED||a.accepted!==!0)throw new Error("This file can't be queued because it has already been processed or was rejected.");return a.status=c.QUEUED,this.options.autoProcessQueue?setTimeout(function(a){return function(){return a.processQueue()}}(this),0):void 0},c.prototype._thumbnailQueue=[],c.prototype._processingThumbnail=!1,c.prototype._enqueueThumbnail=function(a){return this.options.createImageThumbnails&&a.type.match(/image.*/)&&a.size<=1024*this.options.maxThumbnailFilesize*1024?(this._thumbnailQueue.push(a),setTimeout(function(a){return function(){return a._processThumbnailQueue()}}(this),0)):void 0},c.prototype._processThumbnailQueue=function(){return this._processingThumbnail||0===this._thumbnailQueue.length?void 0:(this._processingThumbnail=!0,this.createThumbnail(this._thumbnailQueue.shift(),function(a){return function(){return a._processingThumbnail=!1,a._processThumbnailQueue()}}(this)))},c.prototype.removeFile=function(a){return a.status===c.UPLOADING&&this.cancelUpload(a),this.files=h(this.files,a),this.emit("removedfile",a),0===this.files.length?this.emit("reset"):void 0},c.prototype.removeAllFiles=function(a){var b,d,e,f;for(null==a&&(a=!1),f=this.files.slice(),d=0,e=f.length;e>d;d++)b=f[d],(b.status!==c.UPLOADING||a)&&this.removeFile(b);return null},c.prototype.createThumbnail=function(a,b){var c;return c=new FileReader,c.onload=function(d){return function(){var e;return"image/svg+xml"===a.type?(d.emit("thumbnail",a,c.result),void(null!=b&&b())):(e=document.createElement("img"),e.onload=function(){var c,g,h,i,j,k,l,m;return a.width=e.width,a.height=e.height,h=d.options.resize.call(d,a),null==h.trgWidth&&(h.trgWidth=h.optWidth),null==h.trgHeight&&(h.trgHeight=h.optHeight),c=document.createElement("canvas"),g=c.getContext("2d"),c.width=h.trgWidth,c.height=h.trgHeight,f(g,e,null!=(j=h.srcX)?j:0,null!=(k=h.srcY)?k:0,h.srcWidth,h.srcHeight,null!=(l=h.trgX)?l:0,null!=(m=h.trgY)?m:0,h.trgWidth,h.trgHeight),i=c.toDataURL("image/png"),d.emit("thumbnail",a,i),null!=b?b():void 0},e.onerror=b,e.src=c.result)}}(this),c.readAsDataURL(a)},c.prototype.processQueue=function(){var a,b,c,d;if(b=this.options.parallelUploads,c=this.getUploadingFiles().length,a=c,!(c>=b)&&(d=this.getQueuedFiles(),d.length>0)){if(this.options.uploadMultiple)return this.processFiles(d.slice(0,b-c));for(;b>a;){if(!d.length)return;this.processFile(d.shift()),a++}}},c.prototype.processFile=function(a){return this.processFiles([a])},c.prototype.processFiles=function(a){var b,d,e;for(d=0,e=a.length;e>d;d++)b=a[d],b.processing=!0,b.status=c.UPLOADING,this.emit("processing",b);return this.options.uploadMultiple&&this.emit("processingmultiple",a),this.uploadFiles(a)},c.prototype._getFilesWithXhr=function(a){var b,c;return c=function(){var c,d,e,f;for(e=this.files,f=[],c=0,d=e.length;d>c;c++)b=e[c],b.xhr===a&&f.push(b);return f}.call(this)},c.prototype.cancelUpload=function(a){var b,d,e,f,g,h,i;if(a.status===c.UPLOADING){for(d=this._getFilesWithXhr(a.xhr),e=0,g=d.length;g>e;e++)b=d[e],b.status=c.CANCELED;for(a.xhr.abort(),f=0,h=d.length;h>f;f++)b=d[f],this.emit("canceled",b);this.options.uploadMultiple&&this.emit("canceledmultiple",d)}else((i=a.status)===c.ADDED||i===c.QUEUED)&&(a.status=c.CANCELED,this.emit("canceled",a),this.options.uploadMultiple&&this.emit("canceledmultiple",[a]));return this.options.autoProcessQueue?this.processQueue():void 0},e=function(){var a,b;return b=arguments[0],a=2<=arguments.length?i.call(arguments,1):[],"function"==typeof b?b.apply(this,a):b},c.prototype.uploadFile=function(a){return this.uploadFiles([a])},c.prototype.uploadFiles=function(a){var b,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L;for(w=new XMLHttpRequest,x=0,B=a.length;B>x;x++)b=a[x],b.xhr=w;p=e(this.options.method,a),u=e(this.options.url,a),w.open(p,u,!0),w.withCredentials=!!this.options.withCredentials,s=null,g=function(c){return function(){var d,e,f;for(f=[],d=0,e=a.length;e>d;d++)b=a[d],f.push(c._errorProcessing(a,s||c.options.dictResponseError.replace("{{statusCode}}",w.status),w));return f}}(this),t=function(c){return function(d){var e,f,g,h,i,j,k,l,m;if(null!=d)for(f=100*d.loaded/d.total,g=0,j=a.length;j>g;g++)b=a[g],b.upload={progress:f,total:d.total,bytesSent:d.loaded};else{for(e=!0,f=100,h=0,k=a.length;k>h;h++)b=a[h],(100!==b.upload.progress||b.upload.bytesSent!==b.upload.total)&&(e=!1),b.upload.progress=f,b.upload.bytesSent=b.upload.total;if(e)return}for(m=[],i=0,l=a.length;l>i;i++)b=a[i],m.push(c.emit("uploadprogress",b,f,b.upload.bytesSent));return m}}(this),w.onload=function(b){return function(d){var e;if(a[0].status!==c.CANCELED&&4===w.readyState){if(s=w.responseText,w.getResponseHeader("content-type")&&~w.getResponseHeader("content-type").indexOf("application/json"))try{s=JSON.parse(s)}catch(f){d=f,s="Invalid JSON response from server."}return t(),200<=(e=w.status)&&300>e?b._finished(a,s,d):g()}}}(this),w.onerror=function(){return function(){return a[0].status!==c.CANCELED?g():void 0}}(this),r=null!=(G=w.upload)?G:w,r.onprogress=t,j={Accept:"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"},this.options.headers&&d(j,this.options.headers);for(h in j)i=j[h],w.setRequestHeader(h,i);if(f=new FormData,this.options.params){H=this.options.params;for(o in H)v=H[o],f.append(o,v)}for(y=0,C=a.length;C>y;y++)b=a[y],this.emit("sending",b,w,f);if(this.options.uploadMultiple&&this.emit("sendingmultiple",a,w,f),"FORM"===this.element.tagName)for(I=this.element.querySelectorAll("input, textarea, select, button"),z=0,D=I.length;D>z;z++)if(l=I[z],m=l.getAttribute("name"),n=l.getAttribute("type"),"SELECT"===l.tagName&&l.hasAttribute("multiple"))for(J=l.options,A=0,E=J.length;E>A;A++)q=J[A],q.selected&&f.append(m,q.value);else(!n||"checkbox"!==(K=n.toLowerCase())&&"radio"!==K||l.checked)&&f.append(m,l.value);for(k=F=0,L=a.length-1;L>=0?L>=F:F>=L;k=L>=0?++F:--F)f.append(this._getParamName(k),a[k],a[k].name);return w.send(f)},c.prototype._finished=function(a,b,d){var e,f,g;for(f=0,g=a.length;g>f;f++)e=a[f],e.status=c.SUCCESS,this.emit("success",e,b,d),this.emit("complete",e);return this.options.uploadMultiple&&(this.emit("successmultiple",a,b,d),this.emit("completemultiple",a)),this.options.autoProcessQueue?this.processQueue():void 0},c.prototype._errorProcessing=function(a,b,d){var e,f,g;for(f=0,g=a.length;g>f;f++)e=a[f],e.status=c.ERROR,this.emit("error",e,b,d),this.emit("complete",e);return this.options.uploadMultiple&&(this.emit("errormultiple",a,b,d),this.emit("completemultiple",a)),this.options.autoProcessQueue?this.processQueue():void 0},c}(b),a.version="4.0.1",a.options={},a.optionsForElement=function(b){return b.getAttribute("id")?a.options[c(b.getAttribute("id"))]:void 0},a.instances=[],a.forElement=function(a){if("string"==typeof a&&(a=document.querySelector(a)),null==(null!=a?a.dropzone:void 0))throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");return a.dropzone},a.autoDiscover=!0,a.discover=function(){var b,c,d,e,f,g;for(document.querySelectorAll?d=document.querySelectorAll(".dropzone"):(d=[],b=function(a){var b,c,e,f;for(f=[],c=0,e=a.length;e>c;c++)b=a[c],f.push(/(^| )dropzone($| )/.test(b.className)?d.push(b):void 0);return f},b(document.getElementsByTagName("div")),b(document.getElementsByTagName("form"))),g=[],e=0,f=d.length;f>e;e++)c=d[e],g.push(a.optionsForElement(c)!==!1?new a(c):void 0);return g},a.blacklistedBrowsers=[/opera.*Macintosh.*version\/12/i],a.isBrowserSupported=function(){var b,c,d,e,f;if(b=!0,window.File&&window.FileReader&&window.FileList&&window.Blob&&window.FormData&&document.querySelector)if("classList"in document.createElement("a"))for(f=a.blacklistedBrowsers,d=0,e=f.length;e>d;d++)c=f[d],c.test(navigator.userAgent)&&(b=!1);else b=!1;else b=!1;return b},h=function(a,b){var c,d,e,f;for(f=[],d=0,e=a.length;e>d;d++)c=a[d],c!==b&&f.push(c);return f},c=function(a){return a.replace(/[\-_](\w)/g,function(a){return a.charAt(1).toUpperCase()})},a.createElement=function(a){var b;return b=document.createElement("div"),b.innerHTML=a,b.childNodes[0]},a.elementInside=function(a,b){if(a===b)return!0;for(;a=a.parentNode;)if(a===b)return!0;return!1},a.getElement=function(a,b){var c;if("string"==typeof a?c=document.querySelector(a):null!=a.nodeType&&(c=a),null==c)throw new Error("Invalid `"+b+"` option provided. Please provide a CSS selector or a plain HTML element.");return c},a.getElements=function(a,b){var c,d,e,f,g,h,i,j;if(a instanceof Array){e=[];try{for(f=0,h=a.length;h>f;f++)d=a[f],e.push(this.getElement(d,b))}catch(k){c=k,e=null}}else if("string"==typeof a)for(e=[],j=document.querySelectorAll(a),g=0,i=j.length;i>g;g++)d=j[g],e.push(d);else null!=a.nodeType&&(e=[a]);if(null==e||!e.length)throw new Error("Invalid `"+b+"` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");return e},a.confirm=function(a,b,c){return window.confirm(a)?b():null!=c?c():void 0},a.isValidFile=function(a,b){var c,d,e,f,g;if(!b)return!0;for(b=b.split(","),d=a.type,c=d.replace(/\/.*$/,""),f=0,g=b.length;g>f;f++)if(e=b[f],e=e.trim(),"."===e.charAt(0)){if(-1!==a.name.toLowerCase().indexOf(e.toLowerCase(),a.name.length-e.length))return!0}else if(/\/\*$/.test(e)){if(c===e.replace(/\/.*$/,""))return!0}else if(d===e)return!0;return!1},"undefined"!=typeof jQuery&&null!==jQuery&&(jQuery.fn.dropzone=function(b){return this.each(function(){return new a(this,b)})}),"undefined"!=typeof module&&null!==module?module.exports=a:window.Dropzone=a,a.ADDED="added",a.QUEUED="queued",a.ACCEPTED=a.QUEUED,a.UPLOADING="uploading",a.PROCESSING=a.UPLOADING,a.CANCELED="canceled",a.ERROR="error",a.SUCCESS="success",e=function(a){var b,c,d,e,f,g,h,i,j,k;for(h=a.naturalWidth,g=a.naturalHeight,c=document.createElement("canvas"),c.width=1,c.height=g,d=c.getContext("2d"),d.drawImage(a,0,0),e=d.getImageData(0,0,1,g).data,k=0,f=g,i=g;i>k;)b=e[4*(i-1)+3],0===b?f=i:k=i,i=f+k>>1;return j=i/g,0===j?1:j},f=function(a,b,c,d,f,g,h,i,j,k){var l;return l=e(b),a.drawImage(b,c,d,f,g,h,i,j,k/l)},d=function(a,b){var c,d,e,f,g,h,i,j,k;if(e=!1,k=!0,d=a.document,j=d.documentElement,c=d.addEventListener?"addEventListener":"attachEvent",i=d.addEventListener?"removeEventListener":"detachEvent",h=d.addEventListener?"":"on",f=function(c){return"readystatechange"!==c.type||"complete"===d.readyState?(("load"===c.type?a:d)[i](h+c.type,f,!1),!e&&(e=!0)?b.call(a,c.type||c):void 0):void 0 +},g=function(){var a;try{j.doScroll("left")}catch(b){return a=b,void setTimeout(g,50)}return f("poll")},"complete"!==d.readyState){if(d.createEventObject&&j.doScroll){try{k=!a.frameElement}catch(l){}k&&g()}return d[c](h+"DOMContentLoaded",f,!1),d[c](h+"readystatechange",f,!1),a[c](h+"load",f,!1)}},a._autoDiscoverFunction=function(){return a.autoDiscover?a.discover():void 0},d(window,a._autoDiscoverFunction)}).call(this); \ No newline at end of file diff --git a/public/js/dropzone/readme.md b/public/js/dropzone/readme.md new file mode 100755 index 00000000..f281e046 --- /dev/null +++ b/public/js/dropzone/readme.md @@ -0,0 +1,10 @@ +# Warning! + +You shouldn't pull these files from the github master branch directly! + +They might be outdated or not working at all since I normally only push them +when I create a version release. + +To be sure to get a proper release, please go to the +[dropzone releases section on github](https://github.com/enyo/dropzone/releases/latest). + diff --git a/public/js/gist-async.min.js b/public/js/gist-async.min.js new file mode 100644 index 00000000..16a9e6d7 --- /dev/null +++ b/public/js/gist-async.min.js @@ -0,0 +1 @@ +/*! gist-async - v1.0.1 - 2015-05-15 - https://github.com/razor-x/gist-async */(function(){"use strict";var a;(a=jQuery)(function(){var b,c,d,e,f,g;return b="https://gist.github.com",d=a("div[data-gist]"),e={},c=[],g=[],f=function(a){var b;b=document.createElement("link"),b.type="text/css",b.rel="stylesheet",b.href=a,document.getElementsByTagName("head")[0].appendChild(b)},d.addClass("loading"),d.each(function(b,c){var d;return c=a(c),d=c.data("gist"),null==e[d]&&(e[d]={targets:[]}),e[d].targets.push(c)}),a.each(e,function(c,d){return a.getJSON(""+b+"/"+c+".json?callback=?",function(b){var d,h,i;return h=e[c],h.data=b,i=h.data.stylesheet,g.indexOf(i)<0&&(g.push(i),f(i)),d=h.data.div,h.files=a(d).find(".gist-file"),h.outer=a(d).first().html(""),a(h.targets).each(function(b,c){var e,f,g;return e=c.data("gist-file"),e?(g=h.outer.clone(),f='
'+a(h.files.get(h.data.files.indexOf(e))).html()+"
",g.html(f)):g=a(d),g.hide(),c.fadeOut("fast",function(){return a(this).replaceWith(g),g.fadeIn()})})})})})}).call(this); \ No newline at end of file diff --git a/public/js/javascript.js b/public/js/javascript.js new file mode 100644 index 00000000..c86f49e1 --- /dev/null +++ b/public/js/javascript.js @@ -0,0 +1,704 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// TODO actually recognize syntax of TypeScript constructs + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("javascript", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var statementIndent = parserConfig.statementIndent; + var jsonldMode = parserConfig.jsonld; + var jsonMode = parserConfig.json || jsonldMode; + var isTS = parserConfig.typescript; + var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; + + // Tokenizer + + var keywords = function(){ + function kw(type) {return {type: type, style: "keyword"};} + var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); + var operator = kw("operator"), atom = {type: "atom", style: "atom"}; + + var jsKeywords = { + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C, + "var": kw("var"), "const": kw("var"), "let": kw("var"), + "function": kw("function"), "catch": kw("catch"), + "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), + "in": operator, "typeof": operator, "instanceof": operator, + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"), + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C + }; + + // Extend the 'normal' keywords with the TypeScript language extensions + if (isTS) { + var type = {type: "variable", style: "variable-3"}; + var tsKeywords = { + // object-like things + "interface": kw("interface"), + "extends": kw("extends"), + "constructor": kw("constructor"), + + // scope modifiers + "public": kw("public"), + "private": kw("private"), + "protected": kw("protected"), + "static": kw("static"), + + // types + "string": type, "number": type, "bool": type, "any": type + }; + + for (var attr in tsKeywords) { + jsKeywords[attr] = tsKeywords[attr]; + } + } + + return jsKeywords; + }(); + + var isOperatorChar = /[+\-*&%=<>!?|~^]/; + var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; + + function readRegexp(stream) { + var escaped = false, next, inSet = false; + while ((next = stream.next()) != null) { + if (!escaped) { + if (next == "/" && !inSet) return; + if (next == "[") inSet = true; + else if (inSet && next == "]") inSet = false; + } + escaped = !escaped && next == "\\"; + } + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + function ret(tp, style, cont) { + type = tp; content = cont; + return style; + } + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { + return ret("number", "number"); + } else if (ch == "." && stream.match("..")) { + return ret("spread", "meta"); + } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + return ret(ch); + } else if (ch == "=" && stream.eat(">")) { + return ret("=>", "operator"); + } else if (ch == "0" && stream.eat(/x/i)) { + stream.eatWhile(/[\da-f]/i); + return ret("number", "number"); + } else if (/\d/.test(ch)) { + stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); + return ret("number", "number"); + } else if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } else if (stream.eat("/")) { + stream.skipToEnd(); + return ret("comment", "comment"); + } else if (state.lastType == "operator" || state.lastType == "keyword c" || + state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) { + readRegexp(stream); + stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); + return ret("regexp", "string-2"); + } else { + stream.eatWhile(isOperatorChar); + return ret("operator", "operator", stream.current()); + } + } else if (ch == "`") { + state.tokenize = tokenQuasi; + return tokenQuasi(stream, state); + } else if (ch == "#") { + stream.skipToEnd(); + return ret("error", "error"); + } else if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return ret("operator", "operator", stream.current()); + } else if (wordRE.test(ch)) { + stream.eatWhile(wordRE); + var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; + return (known && state.lastType != ".") ? ret(known.type, known.style, word) : + ret("variable", "variable", word); + } + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next; + if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ + state.tokenize = tokenBase; + return ret("jsonld-keyword", "meta"); + } + while ((next = stream.next()) != null) { + if (next == quote && !escaped) break; + escaped = !escaped && next == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenQuasi(stream, state) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && next == "\\"; + } + return ret("quasi", "string-2", stream.current()); + } + + var brackets = "([{}])"; + // This is a crude lookahead trick to try and notice that we're + // parsing the argument patterns for a fat-arrow function before we + // actually hit the arrow token. It only works if the arrow is on + // the same line as the arguments and there's no strange noise + // (comments) in between. Fallback is to only notice when we hit the + // arrow, and not declare the arguments as locals for the arrow + // body. + function findFatArrow(stream, state) { + if (state.fatArrowAt) state.fatArrowAt = null; + var arrow = stream.string.indexOf("=>", stream.start); + if (arrow < 0) return; + + var depth = 0, sawSomething = false; + for (var pos = arrow - 1; pos >= 0; --pos) { + var ch = stream.string.charAt(pos); + var bracket = brackets.indexOf(ch); + if (bracket >= 0 && bracket < 3) { + if (!depth) { ++pos; break; } + if (--depth == 0) break; + } else if (bracket >= 3 && bracket < 6) { + ++depth; + } else if (wordRE.test(ch)) { + sawSomething = true; + } else if (/["'\/]/.test(ch)) { + return; + } else if (sawSomething && !depth) { + ++pos; + break; + } + } + if (sawSomething && !depth) state.fatArrowAt = pos; + } + + // Parser + + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; + + function JSLexical(indented, column, type, align, prev, info) { + this.indented = indented; + this.column = column; + this.type = type; + this.prev = prev; + this.info = info; + if (align != null) this.align = align; + } + + function inScope(state, varname) { + for (var v = state.localVars; v; v = v.next) + if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } + } + + function parseJS(state, style, type, content, stream) { + var cc = state.cc; + // Communicate our context to the combinators. + // (Less wasteful than consing up a hundred closures on every call.) + cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; + + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = true; + + while(true) { + var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (combinator(type, content)) { + while(cc.length && cc[cc.length - 1].lex) + cc.pop()(); + if (cx.marked) return cx.marked; + if (type == "variable" && inScope(state, content)) return "variable-2"; + return style; + } + } + } + + // Combinator utils + + var cx = {state: null, column: null, marked: null, cc: null}; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + function register(varname) { + function inList(list) { + for (var v = list; v; v = v.next) + if (v.name == varname) return true; + return false; + } + var state = cx.state; + if (state.context) { + cx.marked = "def"; + if (inList(state.localVars)) return; + state.localVars = {name: varname, next: state.localVars}; + } else { + if (inList(state.globalVars)) return; + if (parserConfig.globalVars) + state.globalVars = {name: varname, next: state.globalVars}; + } + } + + // Combinators + + var defaultVars = {name: "this", next: {name: "arguments"}}; + function pushcontext() { + cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; + cx.state.localVars = defaultVars; + } + function popcontext() { + cx.state.localVars = cx.state.context.vars; + cx.state.context = cx.state.context.prev; + } + function pushlex(type, info) { + var result = function() { + var state = cx.state, indent = state.indented; + if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; + state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); + }; + result.lex = true; + return result; + } + function poplex() { + var state = cx.state; + if (state.lexical.prev) { + if (state.lexical.type == ")") + state.indented = state.lexical.indented; + state.lexical = state.lexical.prev; + } + } + poplex.lex = true; + + function expect(wanted) { + function exp(type) { + if (type == wanted) return cont(); + else if (wanted == ";") return pass(); + else return cont(exp); + }; + return exp; + } + + function statement(type, value) { + if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); + if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); + if (type == "keyword b") return cont(pushlex("form"), statement, poplex); + if (type == "{") return cont(pushlex("}"), block, poplex); + if (type == ";") return cont(); + if (type == "if") { + if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) + cx.state.cc.pop()(); + return cont(pushlex("form"), expression, statement, poplex, maybeelse); + } + if (type == "function") return cont(functiondef); + if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); + if (type == "variable") return cont(pushlex("stat"), maybelabel); + if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), + block, poplex, poplex); + if (type == "case") return cont(expression, expect(":")); + if (type == "default") return cont(expect(":")); + if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), + statement, poplex, popcontext); + if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex); + if (type == "class") return cont(pushlex("form"), className, poplex); + if (type == "export") return cont(pushlex("form"), afterExport, poplex); + if (type == "import") return cont(pushlex("form"), afterImport, poplex); + return pass(pushlex("stat"), expression, expect(";"), poplex); + } + function expression(type) { + return expressionInner(type, false); + } + function expressionNoComma(type) { + return expressionInner(type, true); + } + function expressionInner(type, noComma) { + if (cx.state.fatArrowAt == cx.stream.start) { + var body = noComma ? arrowBodyNoComma : arrowBody; + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); + else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); + } + + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); + if (type == "function") return cont(functiondef, maybeop); + if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression); + if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop); + if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); + if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); + if (type == "{") return contCommasep(objprop, "}", null, maybeop); + if (type == "quasi") { return pass(quasi, maybeop); } + return cont(); + } + function maybeexpression(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expression); + } + function maybeexpressionNoComma(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expressionNoComma); + } + + function maybeoperatorComma(type, value) { + if (type == ",") return cont(expression); + return maybeoperatorNoComma(type, value, false); + } + function maybeoperatorNoComma(type, value, noComma) { + var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; + var expr = noComma == false ? expression : expressionNoComma; + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "operator") { + if (/\+\+|--/.test(value)) return cont(me); + if (value == "?") return cont(expression, expect(":"), expr); + return cont(expr); + } + if (type == "quasi") { return pass(quasi, me); } + if (type == ";") return; + if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); + } + function quasi(type, value) { + if (type != "quasi") return pass(); + if (value.slice(value.length - 2) != "${") return cont(quasi); + return cont(expression, continueQuasi); + } + function continueQuasi(type) { + if (type == "}") { + cx.marked = "string-2"; + cx.state.tokenize = tokenQuasi; + return cont(quasi); + } + } + function arrowBody(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expression); + } + function arrowBodyNoComma(type) { + findFatArrow(cx.stream, cx.state); + return pass(type == "{" ? statement : expressionNoComma); + } + function maybelabel(type) { + if (type == ":") return cont(poplex, statement); + return pass(maybeoperatorComma, expect(";"), poplex); + } + function property(type) { + if (type == "variable") {cx.marked = "property"; return cont();} + } + function objprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property"; + if (value == "get" || value == "set") return cont(getterSetter); + return cont(afterprop); + } else if (type == "number" || type == "string") { + cx.marked = jsonldMode ? "property" : (cx.style + " property"); + return cont(afterprop); + } else if (type == "jsonld-keyword") { + return cont(afterprop); + } else if (type == "[") { + return cont(expression, expect("]"), afterprop); + } + } + function getterSetter(type) { + if (type != "variable") return pass(afterprop); + cx.marked = "property"; + return cont(functiondef); + } + function afterprop(type) { + if (type == ":") return cont(expressionNoComma); + if (type == "(") return pass(functiondef); + } + function commasep(what, end) { + function proceed(type) { + if (type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(what, proceed); + } + if (type == end) return cont(); + return cont(expect(end)); + } + return function(type) { + if (type == end) return cont(); + return pass(what, proceed); + }; + } + function contCommasep(what, end, info) { + for (var i = 3; i < arguments.length; i++) + cx.cc.push(arguments[i]); + return cont(pushlex(end, info), commasep(what, end), poplex); + } + function block(type) { + if (type == "}") return cont(); + return pass(statement, block); + } + function maybetype(type) { + if (isTS && type == ":") return cont(typedef); + } + function maybedefault(_, value) { + if (value == "=") return cont(expressionNoComma); + } + function typedef(type) { + if (type == "variable") {cx.marked = "variable-3"; return cont();} + } + function vardef() { + return pass(pattern, maybetype, maybeAssign, vardefCont); + } + function pattern(type, value) { + if (type == "variable") { register(value); return cont(); } + if (type == "[") return contCommasep(pattern, "]"); + if (type == "{") return contCommasep(proppattern, "}"); + } + function proppattern(type, value) { + if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { + register(value); + return cont(maybeAssign); + } + if (type == "variable") cx.marked = "property"; + return cont(expect(":"), pattern, maybeAssign); + } + function maybeAssign(_type, value) { + if (value == "=") return cont(expressionNoComma); + } + function vardefCont(type) { + if (type == ",") return cont(vardef); + } + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); + } + function forspec(type) { + if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); + } + function forspec1(type) { + if (type == "var") return cont(vardef, expect(";"), forspec2); + if (type == ";") return cont(forspec2); + if (type == "variable") return cont(formaybeinof); + return pass(expression, expect(";"), forspec2); + } + function formaybeinof(_type, value) { + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } + return cont(maybeoperatorComma, forspec2); + } + function forspec2(type, value) { + if (type == ";") return cont(forspec3); + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } + return pass(expression, expect(";"), forspec3); + } + function forspec3(type) { + if (type != ")") cont(expression); + } + function functiondef(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} + if (type == "variable") {register(value); return cont(functiondef);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext); + } + function funarg(type) { + if (type == "spread") return cont(funarg); + return pass(pattern, maybetype, maybedefault); + } + function className(type, value) { + if (type == "variable") {register(value); return cont(classNameAfter);} + } + function classNameAfter(type, value) { + if (value == "extends") return cont(expression, classNameAfter); + if (type == "{") return cont(pushlex("}"), classBody, poplex); + } + function classBody(type, value) { + if (type == "variable" || cx.style == "keyword") { + if (value == "static") { + cx.marked = "keyword"; + return cont(classBody); + } + cx.marked = "property"; + if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody); + return cont(functiondef, classBody); + } + if (value == "*") { + cx.marked = "keyword"; + return cont(classBody); + } + if (type == ";") return cont(classBody); + if (type == "}") return cont(); + } + function classGetterSetter(type) { + if (type != "variable") return pass(); + cx.marked = "property"; + return cont(); + } + function afterModule(type, value) { + if (type == "string") return cont(statement); + if (type == "variable") { register(value); return cont(maybeFrom); } + } + function afterExport(_type, value) { + if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } + if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + return pass(statement); + } + function afterImport(type) { + if (type == "string") return cont(); + return pass(importSpec, maybeFrom); + } + function importSpec(type, value) { + if (type == "{") return contCommasep(importSpec, "}"); + if (type == "variable") register(value); + if (value == "*") cx.marked = "keyword"; + return cont(maybeAs); + } + function maybeAs(_type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } + } + function maybeFrom(_type, value) { + if (value == "from") { cx.marked = "keyword"; return cont(expression); } + } + function arrayLiteral(type) { + if (type == "]") return cont(); + return pass(expressionNoComma, maybeArrayComprehension); + } + function maybeArrayComprehension(type) { + if (type == "for") return pass(comprehension, expect("]")); + if (type == ",") return cont(commasep(maybeexpressionNoComma, "]")); + return pass(commasep(expressionNoComma, "]")); + } + function comprehension(type) { + if (type == "for") return cont(forspec, comprehension); + if (type == "if") return cont(expression, comprehension); + } + + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + + // Interface + + return { + startState: function(basecolumn) { + var state = { + tokenize: tokenBase, + lastType: "sof", + cc: [], + lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), + localVars: parserConfig.localVars, + context: parserConfig.localVars && {vars: parserConfig.localVars}, + indented: 0 + }; + if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") + state.globalVars = parserConfig.globalVars; + return state; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (!state.lexical.hasOwnProperty("align")) + state.lexical.align = false; + state.indented = stream.indentation(); + findFatArrow(stream, state); + } + if (state.tokenize != tokenComment && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (type == "comment") return style; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; + return parseJS(state, style, type, content, stream); + }, + + indent: function(state, textAfter) { + if (state.tokenize == tokenComment) return CodeMirror.Pass; + if (state.tokenize != tokenBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; + // Kludge to prevent 'maybelse' from blocking lexical scope pops + if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse) break; + } + if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; + if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") + lexical = lexical.prev; + var type = lexical.type, closing = firstChar == type; + + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); + else if (type == "form" && firstChar == "{") return lexical.indented; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); + else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) + return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); + else if (lexical.align) return lexical.column + (closing ? 0 : 1); + else return lexical.indented + (closing ? 0 : indentUnit); + }, + + electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + lineComment: jsonMode ? null : "//", + fold: "brace", + closeBrackets: "()[]{}''\"\"``", + + helperType: jsonMode ? "json" : "javascript", + jsonldMode: jsonldMode, + jsonMode: jsonMode + }; +}); + +CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); + +CodeMirror.defineMIME("text/javascript", "javascript"); +CodeMirror.defineMIME("text/ecmascript", "javascript"); +CodeMirror.defineMIME("application/javascript", "javascript"); +CodeMirror.defineMIME("application/x-javascript", "javascript"); +CodeMirror.defineMIME("application/ecmascript", "javascript"); +CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); +CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); + +}); diff --git a/public/js/jquery.autocomplete.min.js b/public/js/jquery.autocomplete.min.js new file mode 100644 index 00000000..24fcd1a9 --- /dev/null +++ b/public/js/jquery.autocomplete.min.js @@ -0,0 +1,25 @@ +/** +* Ajax Autocomplete for jQuery, version 1.2.7 +* (c) 2013 Tomas Kirda +* +* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. +* For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/ +* +*/ +(function(e){"function"===typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function g(a,b){var c=function(){},c={autoSelectFirst:!1,appendTo:"body",serviceUrl:null,lookup:null,onSelect:null,width:"auto",minChars:1,maxHeight:300,deferRequestBy:0,params:{},formatResult:g.formatResult,delimiter:null,zIndex:9999,type:"GET",noCache:!1,onSearchStart:c,onSearchComplete:c,containerClass:"autocomplete-suggestions",tabDisabled:!1,dataType:"text",lookupFilter:function(a,b,c){return-1!== +a.value.toLowerCase().indexOf(c)},paramName:"query",transformResult:function(a){return"string"===typeof a?e.parseJSON(a):a}};this.element=a;this.el=e(a);this.suggestions=[];this.badQueries=[];this.selectedIndex=-1;this.currentValue=this.element.value;this.intervalId=0;this.cachedResponse=[];this.onChange=this.onChangeInterval=null;this.isLocal=this.ignoreValueChange=!1;this.suggestionsContainer=null;this.options=e.extend({},c,b);this.classes={selected:"autocomplete-selected",suggestion:"autocomplete-suggestion"}; +this.initialize();this.setOptions(b)}var h={extend:function(a,b){return e.extend(a,b)},createNode:function(a){var b=document.createElement("div");b.innerHTML=a;return b.firstChild}};g.utils=h;e.Autocomplete=g;g.formatResult=function(a,b){var c="("+b.replace(RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\)","g"),"\\$1")+")";return a.value.replace(RegExp(c,"gi"),"$1")};g.prototype={killerFn:null,initialize:function(){var a=this,b="."+a.classes.suggestion,c=a.classes.selected, +d=a.options,f;a.element.setAttribute("autocomplete","off");a.killerFn=function(b){0===e(b.target).closest("."+a.options.containerClass).length&&(a.killSuggestions(),a.disableKillerFn())};if(!d.width||"auto"===d.width)d.width=a.el.outerWidth();a.suggestionsContainer=g.utils.createNode('');f=e(a.suggestionsContainer);f.appendTo(d.appendTo).width(d.width);f.on("mouseover.autocomplete",b,function(){a.activate(e(this).data("index"))}); +f.on("mouseout.autocomplete",function(){a.selectedIndex=-1;f.children("."+c).removeClass(c)});f.on("click.autocomplete",b,function(){a.select(e(this).data("index"),!1)});a.fixPosition();if(window.opera)a.el.on("keypress.autocomplete",function(b){a.onKeyPress(b)});else a.el.on("keydown.autocomplete",function(b){a.onKeyPress(b)});a.el.on("keyup.autocomplete",function(b){a.onKeyUp(b)});a.el.on("blur.autocomplete",function(){a.onBlur()});a.el.on("focus.autocomplete",function(){a.fixPosition()})},onBlur:function(){this.enableKillerFn()}, +setOptions:function(a){var b=this.options;h.extend(b,a);if(this.isLocal=e.isArray(b.lookup))b.lookup=this.verifySuggestionsFormat(b.lookup);e(this.suggestionsContainer).css({"max-height":b.maxHeight+"px",width:b.width+"px","z-index":b.zIndex})},clearCache:function(){this.cachedResponse=[];this.badQueries=[]},clear:function(){this.clearCache();this.currentValue=null;this.suggestions=[]},disable:function(){this.disabled=!0},enable:function(){this.disabled=!1},fixPosition:function(){var a;"body"===this.options.appendTo&& +(a=this.el.offset(),e(this.suggestionsContainer).css({top:a.top+this.el.outerHeight()+"px",left:a.left+"px"}))},enableKillerFn:function(){e(document).on("click.autocomplete",this.killerFn)},disableKillerFn:function(){e(document).off("click.autocomplete",this.killerFn)},killSuggestions:function(){var a=this;a.stopKillSuggestions();a.intervalId=window.setInterval(function(){a.hide();a.stopKillSuggestions()},300)},stopKillSuggestions:function(){window.clearInterval(this.intervalId)},onKeyPress:function(a){if(!this.disabled&& +!this.visible&&40===a.keyCode&&this.currentValue)this.suggest();else if(!this.disabled&&this.visible){switch(a.keyCode){case 27:this.el.val(this.currentValue);this.hide();break;case 9:case 13:if(-1===this.selectedIndex){this.hide();return}this.select(this.selectedIndex,13===a.keyCode);if(9===a.keyCode&&!1===this.options.tabDisabled)return;break;case 38:this.moveUp();break;case 40:this.moveDown();break;default:return}a.stopImmediatePropagation();a.preventDefault()}},onKeyUp:function(a){var b=this; +if(!b.disabled){switch(a.keyCode){case 38:case 40:return}clearInterval(b.onChangeInterval);if(b.currentValue!==b.el.val())if(0'+a(e,b)+""});f.html(g).show();this.visible=!0;this.options.autoSelectFirst&&(this.selectedIndex=0,f.children().first().addClass(d))}},verifySuggestionsFormat:function(a){return a.length&&"string"=== +typeof a[0]?e.map(a,function(a){return{value:a,data:null}}):a},processResponse:function(a,b){var c=this.options,d=c.transformResult(a,b);d.suggestions=this.verifySuggestionsFormat(d.suggestions);c.noCache||(this.cachedResponse[d[c.paramName]]=d,0===d.suggestions.length&&this.badQueries.push(d[c.paramName]));b===this.getQuery(this.currentValue)&&(this.suggestions=d.suggestions,this.suggest())},activate:function(a){var b=this.classes.selected,c=e(this.suggestionsContainer),d=c.children();c.children("."+ +b).removeClass(b);this.selectedIndex=a;return-1!==this.selectedIndex&&d.length>this.selectedIndex?(a=d.get(this.selectedIndex),e(a).addClass(b),a):null},select:function(a,b){var c=this.suggestions[a];c&&(this.el.val(c),this.ignoreValueChange=b,this.hide(),this.onSelect(a))},moveUp:function(){-1!==this.selectedIndex&&(0===this.selectedIndex?(e(this.suggestionsContainer).children().first().removeClass(this.classes.selected),this.selectedIndex=-1,this.el.val(this.currentValue)):this.adjustScroll(this.selectedIndex- +1))},moveDown:function(){this.selectedIndex!==this.suggestions.length-1&&this.adjustScroll(this.selectedIndex+1)},adjustScroll:function(a){var b=this.activate(a),c,d;b&&(b=b.offsetTop,c=e(this.suggestionsContainer).scrollTop(),d=c+this.options.maxHeight-25,bd&&e(this.suggestionsContainer).scrollTop(b-this.options.maxHeight+25),this.el.val(this.getValue(this.suggestions[a].value)))},onSelect:function(a){var b=this.options.onSelect;a=this.suggestions[a]; +this.el.val(this.getValue(a.value));e.isFunction(b)&&b.call(this.element,a)},getValue:function(a){var b=this.options.delimiter,c;if(!b)return a;c=this.currentValue;b=c.split(b);return 1===b.length?a:c.substr(0,c.length-b[b.length-1].length)+a},dispose:function(){this.el.off(".autocomplete").removeData("autocomplete");this.disableKillerFn();e(this.suggestionsContainer).remove()}};e.fn.autocomplete=function(a,b){return 0===arguments.length?this.first().data("autocomplete"):this.each(function(){var c= +e(this),d=c.data("autocomplete");if("string"===typeof a){if(d&&"function"===typeof d[a])d[a](b)}else d&&d.dispose&&d.dispose(),d=new g(this,a),c.data("autocomplete",d)})}}); \ No newline at end of file diff --git a/public/js/jquery.countdown.min.js b/public/js/jquery.countdown.min.js new file mode 100755 index 00000000..69ac42a5 --- /dev/null +++ b/public/js/jquery.countdown.min.js @@ -0,0 +1,22 @@ +/*! + * The Final Countdown for jQuery v2.0.4 (http://hilios.github.io/jQuery.countdown/) + * Copyright (c) 2014 Edson Hilios + * + * 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. + */ +!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){"use strict";function b(a){if(a instanceof Date)return a;if(String(a).match(g))return String(a).match(/^[0-9]*$/)&&(a=Number(a)),String(a).match(/\-/)&&(a=String(a).replace(/\-/g,"/")),new Date(a);throw new Error("Couldn't cast `"+a+"` to a date object.")}function c(a){return function(b){var c=b.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);if(c)for(var e=0,f=c.length;f>e;++e){var g=c[e].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/),i=new RegExp(g[0]),j=g[1]||"",k=g[3]||"",l=null;g=g[2],h.hasOwnProperty(g)&&(l=h[g],l=Number(a[l])),null!==l&&("!"===j&&(l=d(k,l)),""===j&&10>l&&(l="0"+l.toString()),b=b.replace(i,l.toString()))}return b=b.replace(/%%/,"%")}}function d(a,b){var c="s",d="";return a&&(a=a.replace(/(:|;|\s)/gi,"").split(/\,/),1===a.length?c=a[0]:(d=a[0],c=a[1])),1===Math.abs(b)?d:c}var e=100,f=[],g=[];g.push(/^[0-9]*$/.source),g.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source),g.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source),g=new RegExp(g.join("|"));var h={Y:"years",m:"months",w:"weeks",d:"days",D:"totalDays",H:"hours",M:"minutes",S:"seconds"},i=function(b,c,d){this.el=b,this.$el=a(b),this.interval=null,this.offset={},this.instanceNumber=f.length,f.push(this),this.$el.data("countdown-instance",this.instanceNumber),d&&(this.$el.on("update.countdown",d),this.$el.on("stoped.countdown",d),this.$el.on("finish.countdown",d)),this.setFinalDate(c),this.start()};a.extend(i.prototype,{start:function(){null!==this.interval&&clearInterval(this.interval);var a=this;this.update(),this.interval=setInterval(function(){a.update.call(a)},e)},stop:function(){clearInterval(this.interval),this.interval=null,this.dispatchEvent("stoped")},pause:function(){this.stop.call(this)},resume:function(){this.start.call(this)},remove:function(){this.stop(),f[this.instanceNumber]=null,delete this.$el.data().countdownInstance},setFinalDate:function(a){this.finalDate=b(a)},update:function(){return 0===this.$el.closest("html").length?void this.remove():(this.totalSecsLeft=this.finalDate.getTime()-(new Date).getTime(),this.totalSecsLeft=Math.ceil(this.totalSecsLeft/1e3),this.totalSecsLeft=this.totalSecsLeft<0?0:this.totalSecsLeft,this.offset={seconds:this.totalSecsLeft%60,minutes:Math.floor(this.totalSecsLeft/60)%60,hours:Math.floor(this.totalSecsLeft/60/60)%24,days:Math.floor(this.totalSecsLeft/60/60/24)%7,totalDays:Math.floor(this.totalSecsLeft/60/60/24),weeks:Math.floor(this.totalSecsLeft/60/60/24/7),months:Math.floor(this.totalSecsLeft/60/60/24/30),years:Math.floor(this.totalSecsLeft/60/60/24/365)},void(0===this.totalSecsLeft?(this.stop(),this.dispatchEvent("finish")):this.dispatchEvent("update")))},dispatchEvent:function(b){var d=a.Event(b+".countdown");d.finalDate=this.finalDate,d.offset=a.extend({},this.offset),d.strftime=c(this.offset),this.$el.trigger(d)}}),a.fn.countdown=function(){var b=Array.prototype.slice.call(arguments,0);return this.each(function(){var c=a(this).data("countdown-instance");if(void 0!==c){var d=f[c],e=b[0];i.prototype.hasOwnProperty(e)?d[e].apply(d,b.slice(1)):null===String(e).match(/^[$A-Z_][0-9A-Z_$]*$/i)?(d.setFinalDate.call(d,e),d.start()):a.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi,e))}else new i(this,b[0],b[1])})}}); \ No newline at end of file diff --git a/public/js/jquery.counterup.min.js b/public/js/jquery.counterup.min.js new file mode 100755 index 00000000..cddf5a10 --- /dev/null +++ b/public/js/jquery.counterup.min.js @@ -0,0 +1,8 @@ +/*! +* jquery.counterup.js 1.0 +* +* Copyright 2013, Benjamin Intal http://gambit.ph @bfintal +* Released under the GPL v2 License +* +* Date: Nov 26, 2013 +*/(function(e){"use strict";e.fn.counterUp=function(t){var n=e.extend({time:400,delay:10},t);return this.each(function(){var t=e(this),r=n,i=function(){var e=[],n=r.time/r.delay,i=t.text(),s=/[0-9]+,[0-9]+/.test(i);i=i.replace(/,/g,"");var o=/^[0-9]+$/.test(i),u=/^[0-9]+\.[0-9]+$/.test(i),a=u?(i.split(".")[1]||[]).length:0;for(var f=n;f>=1;f--){var l=parseInt(i/n*f);u&&(l=parseFloat(i/n*f).toFixed(a));if(s)while(/(\d+)(\d{3})/.test(l.toString()))l=l.toString().replace(/(\d+)(\d{3})/,"$1,$2");e.unshift(l)}t.data("counterup-nums",e);t.text("0");var c=function(){t.text(t.data("counterup-nums").shift());if(t.data("counterup-nums").length)setTimeout(t.data("counterup-func"),r.delay);else{delete t.data("counterup-nums");t.data("counterup-nums",null);t.data("counterup-func",null)}};t.data("counterup-func",c);setTimeout(t.data("counterup-func"),r.delay)};t.waypoint(i,{offset:"100%",triggerOnce:!0})})}})(jQuery); \ No newline at end of file diff --git a/public/js/jquery.croneditor.js b/public/js/jquery.croneditor.js index 63c6c079..ebdbce57 100644 --- a/public/js/jquery.croneditor.js +++ b/public/js/jquery.croneditor.js @@ -1,33 +1,32 @@ +// Custom fork of https://github.com/marak/cron-editor that disables seconds, +// since hook.io currently has a minimum 60 second resolution on crons + $.fn.croneditor = function(opts) { + opts = opts || {}; var el = this; // Write the HTML template to the document $(el).html(tmpl); - var cronArr = ["*", "*", "*", "*", "*", "*"]; + var cronArr = ["*", "*", "*", "*", "*"]; + if (typeof opts.value === "string") { + cronArr = opts.value.split(' '); + } $( ".tabs" ).tabs({ activate: function( event, ui ) { switch ($(ui.newTab).attr('id')) { - // Seconds - case 'button-second-every': - cronArr[0] = "*/1"; - break; - case 'button-second-n': - cronArr[0] = "*/" + $( "#tabs-second .slider" ).slider("value"); - break; - // Minutes case 'button-minute-every': - cronArr[1] = "*/1"; + cronArr[0] = "*"; break; case 'button-minute-n': - cronArr[1] = "*/" + $( "#tabs-minute .slider" ).slider("value"); + cronArr[0] = "*/" + $( "#tabs-minute .slider" ).slider("value"); break; case 'button-minute-each': - cronArr[1] = "*"; + cronArr[0] = "*"; // TODO: toggle off selected minutes on load //$('.tabs-minute-format input[checked="checked"]').click() $('.tabs-minute-format').html(''); @@ -36,43 +35,43 @@ $.fn.croneditor = function(opts) { // Hours case 'button-hour-every': - cronArr[2] = "*/1"; + cronArr[1] = "*"; break; case 'button-hour-n': - cronArr[2] = "*/" + $( "#tabs-hour .slider" ).slider("value"); + cronArr[1] = "*/" + $( "#tabs-hour .slider" ).slider("value"); break; case 'button-hour-each': - cronArr[2] = "*"; + cronArr[1] = "*"; $('.tabs-hour-format').html(''); drawEachHours(); break; // Days case 'button-day-every': - cronArr[3] = "*"; + cronArr[2] = "*"; break; case 'button-day-each': - cronArr[3] = "*"; + cronArr[2] = "*"; $('.tabs-day-format').html(''); drawEachDays(); break; // Months case 'button-month-every': - cronArr[4] = "*"; + cronArr[3] = "*"; break; case 'button-month-each': - cronArr[4] = "*"; + cronArr[3] = "*"; $('.tabs-month-format').html(''); drawEachMonths(); break; // Weeks case 'button-week-every': - cronArr[5] = "*"; + cronArr[4] = "*"; break; case 'button-week-each': - cronArr[5] = "*"; + cronArr[4] = "*"; $('.tabs-week-format').html(''); drawEachWeek(); break; @@ -86,7 +85,6 @@ $.fn.croneditor = function(opts) { function drawCron () { var newCron = cronArr.join(' '); - newCron = newCron.substr(2, newCron.length); $('#cronString').val(newCron); // TODO: add back next estimated cron time /* @@ -111,25 +109,15 @@ $.fn.croneditor = function(opts) { $('#clear').click(function(){ $('#cronString').val('* * * * *'); - cronArr = ["*","*","*","*","*", "*"]; + cronArr = ["*","*","*","*","*"]; }); - $( "#tabs-second .slider" ).slider({ - min: 1, - max: 59, - slide: function( event, ui ) { - cronArr[0] = "*/" + ui.value; - $('#tabs-second-n .preview').html('Every ' + ui.value + ' seconds'); - drawCron(); - } - }); - $( "#tabs-minute .slider" ).slider({ min: 1, max: 59, slide: function( event, ui ) { - cronArr[1] = "*/" + ui.value; + cronArr[0] = "*/" + ui.value; $('#tabs-minute-n .preview').html('Every ' + ui.value + ' minutes'); drawCron(); } @@ -139,7 +127,7 @@ $.fn.croneditor = function(opts) { min: 1, max: 23, slide: function( event, ui ) { - cronArr[2] = "*/" + ui.value; + cronArr[1] = "*/" + ui.value; $('#tabs-hour-n .preview').html('Every ' + ui.value + ' Hours'); drawCron(); } @@ -164,21 +152,21 @@ $.fn.croneditor = function(opts) { $('.tabs-minute-format input[type="checkbox"]').click(function(){ var newItem = $(this).attr('id').replace('minute-check', ''); - if(cronArr[1] === "*") { - cronArr[1] = $(this).attr('id').replace('minute-check', ''); + if(cronArr[0] === "*") { + cronArr[0] = $(this).attr('id').replace('minute-check', ''); } else { // if value already in list, toggle it off - var list = cronArr[1].split(','); + var list = cronArr[0].split(','); if (list.indexOf(newItem) !== -1) { list.splice(list.indexOf(newItem), 1); - cronArr[1] = list.join(','); + cronArr[0] = list.join(','); } else { // else toggle it on - cronArr[1] = cronArr[1] + "," + newItem; + cronArr[0] = cronArr[0] + "," + newItem; } - if(cronArr[1] === "") { - cronArr[1] = "*"; + if(cronArr[0] === "") { + cronArr[0] = "*"; } } drawCron(); @@ -206,21 +194,21 @@ $.fn.croneditor = function(opts) { $('.tabs-hour-format input[type="checkbox"]').click(function(){ var newItem = $(this).attr('id').replace('hour-check', ''); - if(cronArr[2] === "*") { - cronArr[2] = $(this).attr('id').replace('hour-check', ''); + if(cronArr[1] === "*") { + cronArr[1] = $(this).attr('id').replace('hour-check', ''); } else { // if value already in list, toggle it off - var list = cronArr[2].split(','); + var list = cronArr[1].split(','); if (list.indexOf(newItem) !== -1) { list.splice(list.indexOf(newItem), 1); - cronArr[2] = list.join(','); + cronArr[1] = list.join(','); } else { // else toggle it on - cronArr[2] = cronArr[2] + "," + newItem; + cronArr[1] = cronArr[1] + "," + newItem; } - if(cronArr[2] === "") { - cronArr[2] = "*"; + if(cronArr[1] === "") { + cronArr[1] = "*"; } } drawCron(); @@ -247,21 +235,21 @@ $.fn.croneditor = function(opts) { $('.tabs-day-format input[type="checkbox"]').click(function(){ var newItem = $(this).attr('id').replace('day-check', ''); - if(cronArr[3] === "*") { - cronArr[3] = $(this).attr('id').replace('day-check', ''); + if(cronArr[2] === "*") { + cronArr[2] = $(this).attr('id').replace('day-check', ''); } else { // if value already in list, toggle it off - var list = cronArr[3].split(','); + var list = cronArr[2].split(','); if (list.indexOf(newItem) !== -1) { list.splice(list.indexOf(newItem), 1); - cronArr[3] = list.join(','); + cronArr[2] = list.join(','); } else { // else toggle it on - cronArr[3] = cronArr[3] + "," + newItem; + cronArr[2] = cronArr[2] + "," + newItem; } - if(cronArr[3] === "") { - cronArr[3] = "*"; + if(cronArr[2] === "") { + cronArr[2] = "*"; } } @@ -289,21 +277,21 @@ $.fn.croneditor = function(opts) { $('.tabs-month-format input[type="checkbox"]').click(function(){ var newItem = $(this).attr('id').replace('month-check', ''); - if(cronArr[4] === "*") { - cronArr[4] = $(this).attr('id').replace('month-check', ''); + if(cronArr[3] === "*") { + cronArr[3] = $(this).attr('id').replace('month-check', ''); } else { // if value already in list, toggle it off - var list = cronArr[4].split(','); + var list = cronArr[3].split(','); if (list.indexOf(newItem) !== -1) { list.splice(list.indexOf(newItem), 1); - cronArr[4] = list.join(','); + cronArr[3] = list.join(','); } else { // else toggle it on - cronArr[4] = cronArr[4] + "," + newItem; + cronArr[3] = cronArr[3] + "," + newItem; } - if(cronArr[4] === "") { - cronArr[4] = "*"; + if(cronArr[3] === "") { + cronArr[3] = "*"; } } @@ -329,21 +317,21 @@ $.fn.croneditor = function(opts) { $('.tabs-week-format input[type="checkbox"]').click(function(){ var newItem = $(this).attr('id').replace('week-check', ''); - if(cronArr[5] === "*") { - cronArr[5] = $(this).attr('id').replace('week-check', ''); + if(cronArr[4] === "*") { + cronArr[4] = $(this).attr('id').replace('week-check', ''); } else { // if value already in list, toggle it off - var list = cronArr[5].split(','); + var list = cronArr[4].split(','); if (list.indexOf(newItem) !== -1) { list.splice(list.indexOf(newItem), 1); - cronArr[5] = list.join(','); + cronArr[4] = list.join(','); } else { // else toggle it on - cronArr[5] = cronArr[5] + "," + newItem; + cronArr[4] = cronArr[4] + "," + newItem; } - if(cronArr[5] === "") { - cronArr[5] = "*"; + if(cronArr[4] === "") { + cronArr[4] = "*"; } } @@ -357,13 +345,13 @@ $.fn.croneditor = function(opts) { drawEachHours(); drawEachDays(); drawEachMonths(); - + drawCron(); }; // HTML Template for plugin var tmpl = '\
\ -\ +\
\ \ \ diff --git a/public/js/jquery.fancybox.pack.js b/public/js/jquery.fancybox.pack.js new file mode 100755 index 00000000..2db12808 --- /dev/null +++ b/public/js/jquery.fancybox.pack.js @@ -0,0 +1,46 @@ +/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */ +(function(s,H,f,w){var K=f("html"),q=f(s),p=f(H),b=f.fancybox=function(){b.open.apply(this,arguments)},J=navigator.userAgent.match(/msie/i),C=null,t=H.createTouch!==w,u=function(a){return a&&a.hasOwnProperty&&a instanceof f},r=function(a){return a&&"string"===f.type(a)},F=function(a){return r(a)&&0
',image:'',iframe:'",error:'

The requested content cannot be loaded.
Please try again later.

',closeBtn:'',next:'',prev:''},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0, +openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1, +isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=u(a)?f(a).get():[a]),f.each(a,function(e,c){var l={},g,h,k,n,m;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),u(c)?(l={href:c.data("fancybox-href")||c.attr("href"),title:f("
").text(c.data("fancybox-title")||c.attr("title")).html(),isDom:!0,element:c}, +f.metadata&&f.extend(!0,l,c.metadata())):l=c);g=d.href||l.href||(r(c)?c:null);h=d.title!==w?d.title:l.title||"";n=(k=d.content||l.content)?"html":d.type||l.type;!n&&l.isDom&&(n=c.data("fancybox-type"),n||(n=(n=c.prop("class").match(/fancybox\.(\w+)/))?n[1]:null));r(g)&&(n||(b.isImage(g)?n="image":b.isSWF(g)?n="swf":"#"===g.charAt(0)?n="inline":r(c)&&(n="html",k=c)),"ajax"===n&&(m=g.split(/\s+/,2),g=m.shift(),m=m.shift()));k||("inline"===n?g?k=f(r(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):l.isDom&&(k=c): +"html"===n?k=g:n||g||!l.isDom||(n="inline",k=c));f.extend(l,{href:g,type:n,content:k,title:h,selector:m});a[e]=l}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==w&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1===b.trigger("onCancel")||(b.hideLoading(),a&&(b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(), +b.coming=null,b.current||b._afterZoomOut(a)))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(b.isOpen&&!0!==a?(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]()):(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&& +(b.player.timer=setTimeout(b.next,b.current.playSpeed))},c=function(){d();p.unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};!0===a||!b.player.isActive&&!1!==a?b.current&&(b.current.loop||b.current.index=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==w&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,l;c&&(l=b._getPosition(d),a&&"scroll"===a.type?(delete l.position,c.stop(!0,!0).animate(l,200)):(c.css(l),e.pos=f.extend({},e.dim,l)))}, +update:function(a){var d=a&&a.originalEvent&&a.originalEvent.type,e=!d||"orientationchange"===d;e&&(clearTimeout(C),C=null);b.isOpen&&!C&&(C=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),C=null)},e&&!t?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,t&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"), +b.trigger("onUpdate")),b.update())},hideLoading:function(){p.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('
').click(b.cancel).appendTo("body");p.bind("keydown.loading",function(a){27===(a.which||a.keyCode)&&(a.preventDefault(),b.cancel())});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}));b.trigger("onLoading")},getViewport:function(){var a=b.current&& +b.current.locked||!1,d={x:q.scrollLeft(),y:q.scrollTop()};a&&a.length?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=t&&s.innerWidth?s.innerWidth:q.width(),d.h=t&&s.innerHeight?s.innerHeight:q.height());return d},unbindEvents:function(){b.wrap&&u(b.wrap)&&b.wrap.unbind(".fb");p.unbind(".fb");q.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(q.bind("orientationchange.fb"+(t?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&p.bind("keydown.fb",function(e){var c= +e.which||e.keyCode,l=e.target||e.srcElement;if(27===c&&b.coming)return!1;e.ctrlKey||e.altKey||e.shiftKey||e.metaKey||l&&(l.type||f(l).is("[contenteditable]"))||f.each(d,function(d,l){if(1h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();0!==c&&!k&&1g||0>l)&&b.next(0>g?"up":"right"),d.preventDefault())}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d,e){if(e&& +b.helpers[d]&&f.isFunction(b.helpers[d][a]))b.helpers[d][a](f.extend(!0,{},b.helpers[d].defaults,e),c)})}p.trigger(a)},isImage:function(a){return r(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i)},isSWF:function(a){return r(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=m(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&&(d.padding=[c,c, +c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive=!0;if("image"=== +c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=!0);"iframe"===c&&t&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(t?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,x(d.padding[a]))});b.trigger("onReady"); +if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width= +this.width/b.opts.pixelRatio;b.coming.height=this.height/b.opts.pixelRatio;b._afterLoad()};a.onerror=function(){this.onload=this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming, +d=f(a.tpl.iframe.replace(/\{rnd\}/g,(new Date).getTime())).attr("scrolling",t?"auto":a.iframe.scrolling).attr("src",a.href);f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);t||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload|| +b._afterLoad()},_preloadImages:function(){var a=b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,l,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove()); +b.unbindEvents();e=a.content;c=a.type;l=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("
").html(e).find(a.selector):u(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('
').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder", +!1)}));break;case "image":e=a.tpl.image.replace(/\{href\}/g,g);break;case "swf":e='',h="",f.each(a.swf,function(a,b){e+='';h+=" "+a+'="'+b+'"'}),e+='"}u(e)&&e.parent().is(a.inner)||a.inner.append(e);b.trigger("beforeShow"); +a.inner.css("overflow","yes"===l?"scroll":"no"===l?"hidden":l);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(!b.isOpened)f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();else if(d.prevMethod)b.transitions[d.prevMethod]();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,l=b.skin,g=b.inner,h=b.current,c=h.width,k=h.height,n=h.minWidth,v=h.minHeight,p=h.maxWidth, +q=h.maxHeight,t=h.scrolling,r=h.scrollOutside?h.scrollbarWidth:0,y=h.margin,z=m(y[1]+y[3]),s=m(y[0]+y[2]),w,A,u,D,B,G,C,E,I;e.add(l).add(g).width("auto").height("auto").removeClass("fancybox-tmp");y=m(l.outerWidth(!0)-l.width());w=m(l.outerHeight(!0)-l.height());A=z+y;u=s+w;D=F(c)?(a.w-A)*m(c)/100:c;B=F(k)?(a.h-u)*m(k)/100:k;if("iframe"===h.type){if(I=h.content,h.autoHeight&&1===I.data("ready"))try{I[0].contentWindow.document.location&&(g.width(D).height(9999),G=I.contents().find("body"),r&&G.css("overflow-x", +"hidden"),B=G.outerHeight(!0))}catch(H){}}else if(h.autoWidth||h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(D),h.autoHeight||g.height(B),h.autoWidth&&(D=g.width()),h.autoHeight&&(B=g.height()),g.removeClass("fancybox-tmp");c=m(D);k=m(B);E=D/B;n=m(F(n)?m(n,"w")-A:n);p=m(F(p)?m(p,"w")-A:p);v=m(F(v)?m(v,"h")-u:v);q=m(F(q)?m(q,"h")-u:q);G=p;C=q;h.fitToView&&(p=Math.min(a.w-A,p),q=Math.min(a.h-u,q));A=a.w-z;s=a.h-s;h.aspectRatio?(c>p&&(c=p,k=m(c/E)),k>q&&(k=q,c=m(k*E)),cA||z>s)&&c>n&&k>v&&!(19p&&(c=p,k=m(c/E)),g.width(c).height(k),e.width(c+y),a=e.width(),z=e.height();else c=Math.max(n,Math.min(c,c-(a-A))),k=Math.max(v,Math.min(k,k-(z-s)));r&&"auto"===t&&kA||z>s)&&c>n&&k>v;c=h.aspectRatio?cv&&k
').appendTo(d&&d.lenth?d:"body");this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(q.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay", +function(a){if(f(a.target).hasClass("fancybox-overlay"))return b.isActive?b.close():d.close(),!1});this.overlay.css(a.css).show()},close:function(){q.unbind("resize.overlay");this.el.hasClass("fancybox-lock")&&(f(".fancybox-margin").removeClass("fancybox-margin"),this.el.removeClass("fancybox-lock"),q.scrollTop(this.scrollV).scrollLeft(this.scrollH));f(".fancybox-overlay").remove().hide();f.extend(this,{overlay:null,fixed:!1})},update:function(){var a="100%",b;this.overlay.width(a).height("100%"); +J?(b=Math.max(H.documentElement.offsetWidth,H.body.offsetWidth),p.width()>b&&(a=p.width())):p.width()>q.width()&&(a=p.width());this.overlay.width(a).height(p.height())},onReady:function(a,b){var e=this.overlay;f(".fancybox-overlay").stop(!0,!0);e||this.create(a);a.locked&&this.fixed&&b.fixed&&(b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){b.locked&&!this.el.hasClass("fancybox-lock")&&(!1!==this.fixPosition&&f("*").filter(function(){return"fixed"=== +f(this).css("position")&&!f(this).hasClass("fancybox-overlay")&&!f(this).hasClass("fancybox-wrap")}).addClass("fancybox-margin"),this.el.addClass("fancybox-margin"),this.scrollV=q.scrollTop(),this.scrollH=q.scrollLeft(),this.el.addClass("fancybox-lock"),q.scrollTop(this.scrollV).scrollLeft(this.scrollH));this.open(a)},onUpdate:function(){this.fixed||this.update()},afterClose:function(a){this.overlay&&!b.coming&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float", +position:"bottom"},beforeShow:function(a){var d=b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(r(e)&&""!==f.trim(e)){d=f('
'+e+"
");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"),J&&d.width(d.width()),d.wrapInner(''),b.current.margin[2]+=Math.abs(m(d.css("margin-bottom")))}d["top"===a.position?"prependTo": +"appendTo"](c)}}};f.fn.fancybox=function(a){var d,e=f(this),c=this.selector||"",l=function(g){var h=f(this).blur(),k=d,l,m;g.ctrlKey||g.altKey||g.shiftKey||g.metaKey||h.is(".fancybox-wrap")||(l=a.groupAttr||"data-fancybox-group",m=h.attr(l),m||(l="rel",m=h.get(0)[l]),m&&""!==m&&"nofollow"!==m&&(h=c.length?f(c):e,h=h.filter("["+l+'="'+m+'"]'),k=h.index(this)),a.index=k,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;c&&!1!==a.live?p.undelegate(c,"click.fb-start").delegate(c+":not('.fancybox-item, .fancybox-nav')", +"click.fb-start",l):e.unbind("click.fb-start").bind("click.fb-start",l);this.filter("[data-fancybox-start=1]").trigger("click");return this};p.ready(function(){var a,d;f.scrollbarWidth===w&&(f.scrollbarWidth=function(){var a=f('
').appendTo("body"),b=a.children(),b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});f.support.fixedPosition===w&&(f.support.fixedPosition=function(){var a=f('
').appendTo("body"), +b=20===a[0].offsetTop||15===a[0].offsetTop;a.remove();return b}());f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")});a=f(s).width();K.addClass("fancybox-lock-test");d=f(s).width();K.removeClass("fancybox-lock-test");f("").appendTo("head")})})(window,document,jQuery); \ No newline at end of file diff --git a/public/js/jquery.growl.js b/public/js/jquery.growl.js new file mode 100644 index 00000000..d5c25511 --- /dev/null +++ b/public/js/jquery.growl.js @@ -0,0 +1,272 @@ +// Generated by CoffeeScript 1.10.0 + +/* +jQuery Growl +Copyright 2015 Kevin Sylvestre +1.3.2 + */ + +(function() { + "use strict"; + var $, Animation, Growl, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + $ = jQuery; + + Animation = (function() { + function Animation() {} + + Animation.transitions = { + "webkitTransition": "webkitTransitionEnd", + "mozTransition": "mozTransitionEnd", + "oTransition": "oTransitionEnd", + "transition": "transitionend" + }; + + Animation.transition = function($el) { + var el, ref, result, type; + el = $el[0]; + ref = this.transitions; + for (type in ref) { + result = ref[type]; + if (el.style[type] != null) { + return result; + } + } + }; + + return Animation; + + })(); + + Growl = (function() { + Growl.settings = { + namespace: 'growl', + duration: 3200, + close: "×", + location: "default", + style: "default", + size: "medium", + delayOnHover: true + }; + + Growl.growl = function(settings) { + if (settings == null) { + settings = {}; + } + this.initialize(); + return new Growl(settings); + }; + + Growl.initialize = function() { + return $("body:not(:has(#growls))").append('
'); + }; + + function Growl(settings) { + if (settings == null) { + settings = {}; + } + this.container = bind(this.container, this); + this.content = bind(this.content, this); + this.html = bind(this.html, this); + this.$growl = bind(this.$growl, this); + this.$growls = bind(this.$growls, this); + this.animate = bind(this.animate, this); + this.remove = bind(this.remove, this); + this.dismiss = bind(this.dismiss, this); + this.present = bind(this.present, this); + this.waitAndDismiss = bind(this.waitAndDismiss, this); + this.cycle = bind(this.cycle, this); + this.close = bind(this.close, this); + this.click = bind(this.click, this); + this.mouseLeave = bind(this.mouseLeave, this); + this.mouseEnter = bind(this.mouseEnter, this); + this.unbind = bind(this.unbind, this); + this.bind = bind(this.bind, this); + this.render = bind(this.render, this); + this.settings = $.extend({}, Growl.settings, settings); + this.$growls().attr('class', this.settings.location); + this.render(); + } + + Growl.prototype.render = function() { + var $growl; + $growl = this.$growl(); + this.$growls().append($growl); + if (this.settings.fixed) { + this.present(); + } else { + this.cycle(); + } + }; + + Growl.prototype.bind = function($growl) { + if ($growl == null) { + $growl = this.$growl(); + } + $growl.on("click", this.click); + if (this.settings.delayOnHover) { + $growl.on("mouseenter", this.mouseEnter); + $growl.on("mouseleave", this.mouseLeave); + } + return $growl.on("contextmenu", this.close).find("." + this.settings.namespace + "-close").on("click", this.close); + }; + + Growl.prototype.unbind = function($growl) { + if ($growl == null) { + $growl = this.$growl(); + } + $growl.off("click", this.click); + if (this.settings.delayOnHover) { + $growl.off("mouseenter", this.mouseEnter); + $growl.off("mouseleave", this.mouseLeave); + } + return $growl.off("contextmenu", this.close).find("." + this.settings.namespace + "-close").off("click", this.close); + }; + + Growl.prototype.mouseEnter = function(event) { + var $growl; + $growl = this.$growl(); + return $growl.stop(true, true); + }; + + Growl.prototype.mouseLeave = function(event) { + return this.waitAndDismiss(); + }; + + Growl.prototype.click = function(event) { + if (this.settings.url != null) { + event.preventDefault(); + event.stopPropagation(); + return window.open(this.settings.url); + } + }; + + Growl.prototype.close = function(event) { + var $growl; + event.preventDefault(); + event.stopPropagation(); + $growl = this.$growl(); + return $growl.stop().queue(this.dismiss).queue(this.remove); + }; + + Growl.prototype.cycle = function() { + var $growl; + $growl = this.$growl(); + return $growl.queue(this.present).queue(this.waitAndDismiss()); + }; + + Growl.prototype.waitAndDismiss = function() { + var $growl; + $growl = this.$growl(); + return $growl.delay(this.settings.duration).queue(this.dismiss).queue(this.remove); + }; + + Growl.prototype.present = function(callback) { + var $growl; + $growl = this.$growl(); + this.bind($growl); + return this.animate($growl, this.settings.namespace + "-incoming", 'out', callback); + }; + + Growl.prototype.dismiss = function(callback) { + var $growl; + $growl = this.$growl(); + this.unbind($growl); + return this.animate($growl, this.settings.namespace + "-outgoing", 'in', callback); + }; + + Growl.prototype.remove = function(callback) { + this.$growl().remove(); + return typeof callback === "function" ? callback() : void 0; + }; + + Growl.prototype.animate = function($element, name, direction, callback) { + var transition; + if (direction == null) { + direction = 'in'; + } + transition = Animation.transition($element); + $element[direction === 'in' ? 'removeClass' : 'addClass'](name); + $element.offset().position; + $element[direction === 'in' ? 'addClass' : 'removeClass'](name); + if (callback == null) { + return; + } + if (transition != null) { + $element.one(transition, callback); + } else { + callback(); + } + }; + + Growl.prototype.$growls = function() { + return this.$_growls != null ? this.$_growls : this.$_growls = $('#growls'); + }; + + Growl.prototype.$growl = function() { + return this.$_growl != null ? this.$_growl : this.$_growl = $(this.html()); + }; + + Growl.prototype.html = function() { + return this.container(this.content()); + }; + + Growl.prototype.content = function() { + return "
" + this.settings.close + "
\n
" + this.settings.title + "
\n
" + this.settings.message + "
"; + }; + + Growl.prototype.container = function(content) { + return "
\n " + content + "\n
"; + }; + + return Growl; + + })(); + + this.Growl = Growl; + + $.growl = function(options) { + if (options == null) { + options = {}; + } + return Growl.growl(options); + }; + + $.growl.error = function(options) { + var settings; + if (options == null) { + options = {}; + } + settings = { + title: "Error!", + style: "error" + }; + return $.growl($.extend(settings, options)); + }; + + $.growl.notice = function(options) { + var settings; + if (options == null) { + options = {}; + } + settings = { + title: "Notice!", + style: "notice" + }; + return $.growl($.extend(settings, options)); + }; + + $.growl.warning = function(options) { + var settings; + if (options == null) { + options = {}; + } + settings = { + title: "Warning!", + style: "warning" + }; + return $.growl($.extend(settings, options)); + }; + +}).call(this); \ No newline at end of file diff --git a/public/js/jquery.hookTheme.js b/public/js/jquery.hookTheme.js new file mode 100644 index 00000000..50210c2c --- /dev/null +++ b/public/js/jquery.hookTheme.js @@ -0,0 +1,113 @@ +$.fn.croneditor = function(opts) { + + var el = this; + +}; + +// HTML Template for plugin +var tmpl = '\ +
\ +\ +
\ +\ +\ +\ +
\ + \ +
\ +
\ + \ +
\ +
*
\ +
Every minute.
\ +
\ +
\ +
Every 1 minutes
\ +
\ +
\ +
\ +
Each selected minute

\ +
\ +
\ +
\ +
\ +
\ +
\ + \ +
\ +
*
\ +
Every hour
\ +
\ +
\ +
Every 1 hours
\ +
\ +
\ +
\ +
Each selected hour

\ +
\ +
\ +
\ +
\ +
\ +
\ + \ +
\ +
*
\ +
Every Day
\ +
\ +
\ +
Each selected Day

\ +
\ +
\ +
\ +
\ +
\ +
\ + \ +
\ +
*
\ +
Every month
\ +
\ +
\ +
Each selected month

\ +
\ +
\ +
\ +
\ +
\ +
\ + \ +
\ +
*
\ +
Every Day
\ +
\ +
\ +
Each selected Day

\ +
\ +
\ +
\ +
\ +
'; \ No newline at end of file diff --git a/public/js/jquery.meanmenu.min.js b/public/js/jquery.meanmenu.min.js new file mode 100755 index 00000000..f684a5c9 --- /dev/null +++ b/public/js/jquery.meanmenu.min.js @@ -0,0 +1 @@ +!function($){"use strict";$.fn.meanmenu=function(e){var n={meanMenuTarget:jQuery(this),meanMenuContainer:"body",meanMenuClose:"X",meanMenuCloseSize:"18px",meanMenuOpen:"",meanRevealPosition:"right",meanRevealPositionDistance:"0",meanRevealColour:"",meanScreenWidth:"480",meanNavPush:"",meanShowChildren:!0,meanExpandableChildren:!0,meanExpand:"+",meanContract:"-",meanRemoveAttrs:!1,onePage:!1,meanDisplay:"block",removeElements:""};e=$.extend(n,e);var a=window.innerWidth||document.documentElement.clientWidth;return this.each(function(){var n=e.meanMenuTarget,t=e.meanMenuContainer,r=e.meanMenuClose,i=e.meanMenuCloseSize,s=e.meanMenuOpen,u=e.meanRevealPosition,m=e.meanRevealPositionDistance,l=e.meanRevealColour,o=e.meanScreenWidth,c=e.meanNavPush,v=".meanmenu-reveal",h=e.meanShowChildren,d=e.meanExpandableChildren,y=e.meanExpand,j=e.meanContract,Q=e.meanRemoveAttrs,f=e.onePage,g=e.meanDisplay,p=e.removeElements,C=!1;(navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i)||navigator.userAgent.match(/iPad/i)||navigator.userAgent.match(/Android/i)||navigator.userAgent.match(/Blackberry/i)||navigator.userAgent.match(/Windows Phone/i))&&(C=!0),(navigator.userAgent.match(/MSIE 8/i)||navigator.userAgent.match(/MSIE 7/i))&&jQuery("html").css("overflow-y","scroll");var w="",x=function(){if("center"===u){var e=window.innerWidth||document.documentElement.clientWidth,n=e/2-22+"px";w="left:"+n+";right:auto;",C?jQuery(".meanmenu-reveal").animate({left:n}):jQuery(".meanmenu-reveal").css("left",n)}},A=!1,E=!1;"right"===u&&(w="right:"+m+";left:auto;"),"left"===u&&(w="left:"+m+";right:auto;"),x();var M="",P=function(){M.html(jQuery(M).is(".meanmenu-reveal.meanclose")?r:s)},W=function(){jQuery(".mean-bar,.mean-push").remove(),jQuery(t).removeClass("mean-container"),jQuery(n).css("display",g),A=!1,E=!1,jQuery(p).removeClass("mean-remove")},b=function(){var e="background:"+l+";color:"+l+";"+w;if(o>=a){jQuery(p).addClass("mean-remove"),E=!0,jQuery(t).addClass("mean-container"),jQuery(".mean-container").prepend('');var r=jQuery(n).html();jQuery(".mean-nav").html(r),Q&&jQuery("nav.mean-nav ul, nav.mean-nav ul *").each(function(){jQuery(this).is(".mean-remove")?jQuery(this).attr("class","mean-remove"):jQuery(this).removeAttr("class"),jQuery(this).removeAttr("id")}),jQuery(n).before('
'),jQuery(".mean-push").css("margin-top",c),jQuery(n).hide(),jQuery(".meanmenu-reveal").show(),jQuery(v).html(s),M=jQuery(v),jQuery(".mean-nav ul").hide(),h?d?(jQuery(".mean-nav ul ul").each(function(){jQuery(this).children().length&&jQuery(this,"li:first").parent().append(''+y+"")}),jQuery(".mean-expand").on("click",function(e){e.preventDefault(),jQuery(this).hasClass("mean-clicked")?(jQuery(this).text(y),jQuery(this).prev("ul").slideUp(300,function(){})):(jQuery(this).text(j),jQuery(this).prev("ul").slideDown(300,function(){})),jQuery(this).toggleClass("mean-clicked")})):jQuery(".mean-nav ul ul").show():jQuery(".mean-nav ul ul").hide(),jQuery(".mean-nav ul li").last().addClass("mean-last"),M.removeClass("meanclose"),jQuery(M).click(function(e){e.preventDefault(),A===!1?(M.css("text-align","center"),M.css("text-indent","0"),M.css("font-size",i),jQuery(".mean-nav ul:first").slideDown(),A=!0):(jQuery(".mean-nav ul:first").slideUp(),A=!1),M.toggleClass("meanclose"),P(),jQuery(p).addClass("mean-remove")}),f&&jQuery(".mean-nav ul > li > a:first-child").on("click",function(){jQuery(".mean-nav ul:first").slideUp(),A=!1,jQuery(M).toggleClass("meanclose").html(s)})}else W()};C||jQuery(window).resize(function(){a=window.innerWidth||document.documentElement.clientWidth,a>o,W(),o>=a?(b(),x()):W()}),jQuery(window).resize(function(){a=window.innerWidth||document.documentElement.clientWidth,C?(x(),o>=a?E===!1&&b():W()):(W(),o>=a&&(b(),x()))}),b()})}}(jQuery); \ No newline at end of file diff --git a/public/js/jquery.nivo.slider.pack.js b/public/js/jquery.nivo.slider.pack.js new file mode 100755 index 00000000..f18e2f99 --- /dev/null +++ b/public/js/jquery.nivo.slider.pack.js @@ -0,0 +1,10 @@ +/* + * jQuery Nivo Slider v3.2 + * http://nivo.dev7studios.com + * + * Copyright 2012, Dev7studios + * Free to use and abuse under the MIT license. + * http://www.opensource.org/licenses/mit-license.php + */ + +(function(e){var t=function(t,n){var r=e.extend({},e.fn.nivoSlider.defaults,n);var i={currentSlide:0,currentImage:"",totalSlides:0,running:false,paused:false,stop:false,controlNavEl:false};var s=e(t);s.data("nivo:vars",i).addClass("nivoSlider");var o=s.children();o.each(function(){var t=e(this);var n="";if(!t.is("img")){if(t.is("a")){t.addClass("nivo-imageLink");n=t}t=t.find("img:first")}var r=r===0?t.attr("width"):t.width(),s=s===0?t.attr("height"):t.height();if(n!==""){n.css("display","none")}t.css("display","none");i.totalSlides++});if(r.randomStart){r.startSlide=Math.floor(Math.random()*i.totalSlides)}if(r.startSlide>0){if(r.startSlide>=i.totalSlides){r.startSlide=i.totalSlides-1}i.currentSlide=r.startSlide}if(e(o[i.currentSlide]).is("img")){i.currentImage=e(o[i.currentSlide])}else{i.currentImage=e(o[i.currentSlide]).find("img:first")}if(e(o[i.currentSlide]).is("a")){e(o[i.currentSlide]).css("display","block")}var u=e("").addClass("nivo-main-image");u.attr("src",i.currentImage.attr("src")).show();s.append(u);e(window).resize(function(){s.children("img").width(s.width());u.attr("src",i.currentImage.attr("src"));u.stop().height("auto");e(".nivo-slice").remove();e(".nivo-box").remove()});s.append(e('
'));var a=function(t){var n=e(".nivo-caption",s);if(i.currentImage.attr("title")!=""&&i.currentImage.attr("title")!=undefined){var r=i.currentImage.attr("title");if(r.substr(0,1)=="#")r=e(r).html();if(n.css("display")=="block"){setTimeout(function(){n.html(r)},t.animSpeed)}else{n.html(r);n.stop().fadeIn(t.animSpeed)}}else{n.stop().fadeOut(t.animSpeed)}};a(r);var f=0;if(!r.manualAdvance&&o.length>1){f=setInterval(function(){d(s,o,r,false)},r.pauseTime)}if(r.directionNav){s.append('");e(s).on("click","a.nivo-prevNav",function(){if(i.running){return false}clearInterval(f);f="";i.currentSlide-=2;d(s,o,r,"prev")});e(s).on("click","a.nivo-nextNav",function(){if(i.running){return false}clearInterval(f);f="";d(s,o,r,"next")})}if(r.controlNav){i.controlNavEl=e('
');s.after(i.controlNavEl);for(var l=0;l')}else{i.controlNavEl.append(''+(l+1)+"")}}e("a:eq("+i.currentSlide+")",i.controlNavEl).addClass("active");e("a",i.controlNavEl).bind("click",function(){if(i.running)return false;if(e(this).hasClass("active"))return false;clearInterval(f);f="";u.attr("src",i.currentImage.attr("src"));i.currentSlide=e(this).attr("rel")-1;d(s,o,r,"control")})}if(r.pauseOnHover){s.hover(function(){i.paused=true;clearInterval(f);f=""},function(){i.paused=false;if(f===""&&!r.manualAdvance){f=setInterval(function(){d(s,o,r,false)},r.pauseTime)}})}s.bind("nivo:animFinished",function(){u.attr("src",i.currentImage.attr("src"));i.running=false;e(o).each(function(){if(e(this).is("a")){e(this).css("display","none")}});if(e(o[i.currentSlide]).is("a")){e(o[i.currentSlide]).css("display","block")}if(f===""&&!i.paused&&!r.manualAdvance){f=setInterval(function(){d(s,o,r,false)},r.pauseTime)}r.afterChange.call(this)});var h=function(t,n,r){if(e(r.currentImage).parent().is("a"))e(r.currentImage).parent().css("display","block");e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").width(t.width()).css("visibility","hidden").show();var i=e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").parent().is("a")?e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").parent().height():e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").height();for(var s=0;s
').css({left:o*s+"px",width:t.width()-o*s+"px",height:i+"px",opacity:"0",overflow:"hidden"}))}else{t.append(e('
').css({left:o*s+"px",width:o+"px",height:i+"px",opacity:"0",overflow:"hidden"}))}}e(".nivo-slice",t).height(i);u.stop().animate({height:e(r.currentImage).height()},n.animSpeed)};var p=function(t,n,r){if(e(r.currentImage).parent().is("a"))e(r.currentImage).parent().css("display","block");e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").width(t.width()).css("visibility","hidden").show();var i=Math.round(t.width()/n.boxCols),s=Math.round(e('img[src="'+r.currentImage.attr("src")+'"]',t).not(".nivo-main-image,.nivo-control img").height()/n.boxRows);for(var o=0;o
').css({opacity:0,left:i*a+"px",top:s*o+"px",width:t.width()-i*a+"px"}));e('.nivo-box[name="'+a+'"]',t).height(e('.nivo-box[name="'+a+'"] img',t).height()+"px")}else{t.append(e('
').css({opacity:0,left:i*a+"px",top:s*o+"px",width:i+"px"}));e('.nivo-box[name="'+a+'"]',t).height(e('.nivo-box[name="'+a+'"] img',t).height()+"px")}}}u.stop().animate({height:e(r.currentImage).height()},n.animSpeed)};var d=function(t,n,r,i){var s=t.data("nivo:vars");if(s&&s.currentSlide===s.totalSlides-1){r.lastSlide.call(this)}if((!s||s.stop)&&!i){return false}r.beforeChange.call(this);if(!i){u.attr("src",s.currentImage.attr("src"))}else{if(i==="prev"){u.attr("src",s.currentImage.attr("src"))}if(i==="next"){u.attr("src",s.currentImage.attr("src"))}}s.currentSlide++;if(s.currentSlide===s.totalSlides){s.currentSlide=0;r.slideshowEnd.call(this)}if(s.currentSlide<0){s.currentSlide=s.totalSlides-1}if(e(n[s.currentSlide]).is("img")){s.currentImage=e(n[s.currentSlide])}else{s.currentImage=e(n[s.currentSlide]).find("img:first")}if(r.controlNav){e("a",s.controlNavEl).removeClass("active");e("a:eq("+s.currentSlide+")",s.controlNavEl).addClass("active")}a(r);e(".nivo-slice",t).remove();e(".nivo-box",t).remove();var o=r.effect,f="";if(r.effect==="random"){f=new Array("sliceDownRight","sliceDownLeft","sliceUpRight","sliceUpLeft","sliceUpDown","sliceUpDownLeft","fold","fade","boxRandom","boxRain","boxRainReverse","boxRainGrow","boxRainGrowReverse");o=f[Math.floor(Math.random()*(f.length+1))];if(o===undefined){o="fade"}}if(r.effect.indexOf(",")!==-1){f=r.effect.split(",");o=f[Math.floor(Math.random()*f.length)];if(o===undefined){o="fade"}}if(s.currentImage.attr("data-transition")){o=s.currentImage.attr("data-transition")}s.running=true;var l=0,c=0,d="",m="",g="",y="";if(o==="sliceDown"||o==="sliceDownRight"||o==="sliceDownLeft"){h(t,r,s);l=0;c=0;d=e(".nivo-slice",t);if(o==="sliceDownLeft"){d=e(".nivo-slice",t)._reverse()}d.each(function(){var n=e(this);n.css({top:"0px"});if(c===r.slices-1){setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed)},100+l)}l+=50;c++})}else if(o==="sliceUp"||o==="sliceUpRight"||o==="sliceUpLeft"){h(t,r,s);l=0;c=0;d=e(".nivo-slice",t);if(o==="sliceUpLeft"){d=e(".nivo-slice",t)._reverse()}d.each(function(){var n=e(this);n.css({bottom:"0px"});if(c===r.slices-1){setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed)},100+l)}l+=50;c++})}else if(o==="sliceUpDown"||o==="sliceUpDownRight"||o==="sliceUpDownLeft"){h(t,r,s);l=0;c=0;var b=0;d=e(".nivo-slice",t);if(o==="sliceUpDownLeft"){d=e(".nivo-slice",t)._reverse()}d.each(function(){var n=e(this);if(c===0){n.css("top","0px");c++}else{n.css("bottom","0px");c=0}if(b===r.slices-1){setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1.0"},r.animSpeed)},100+l)}l+=50;b++})}else if(o==="fold"){h(t,r,s);l=0;c=0;e(".nivo-slice",t).each(function(){var n=e(this);var i=n.width();n.css({top:"0px",width:"0px"});if(c===r.slices-1){setTimeout(function(){n.animate({width:i,opacity:"1.0"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({width:i,opacity:"1.0"},r.animSpeed)},100+l)}l+=50;c++})}else if(o==="fade"){h(t,r,s);m=e(".nivo-slice:first",t);m.css({width:t.width()+"px"});m.animate({opacity:"1.0"},r.animSpeed*2,"",function(){t.trigger("nivo:animFinished")})}else if(o==="slideInRight"){h(t,r,s);m=e(".nivo-slice:first",t);m.css({width:"0px",opacity:"1"});m.animate({width:t.width()+"px"},r.animSpeed*2,"",function(){t.trigger("nivo:animFinished")})}else if(o==="slideInLeft"){h(t,r,s);m=e(".nivo-slice:first",t);m.css({width:"0px",opacity:"1",left:"",right:"0px"});m.animate({width:t.width()+"px"},r.animSpeed*2,"",function(){m.css({left:"0px",right:""});t.trigger("nivo:animFinished")})}else if(o==="boxRandom"){p(t,r,s);g=r.boxCols*r.boxRows;c=0;l=0;y=v(e(".nivo-box",t));y.each(function(){var n=e(this);if(c===g-1){setTimeout(function(){n.animate({opacity:"1"},r.animSpeed,"",function(){t.trigger("nivo:animFinished")})},100+l)}else{setTimeout(function(){n.animate({opacity:"1"},r.animSpeed)},100+l)}l+=20;c++})}else if(o==="boxRain"||o==="boxRainReverse"||o==="boxRainGrow"||o==="boxRainGrowReverse"){p(t,r,s);g=r.boxCols*r.boxRows;c=0;l=0;var w=0;var E=0;var S=[];S[w]=[];y=e(".nivo-box",t);if(o==="boxRainReverse"||o==="boxRainGrowReverse"){y=e(".nivo-box",t)._reverse()}y.each(function(){S[w][E]=e(this);E++;if(E===r.boxCols){w++;E=0;S[w]=[]}});for(var x=0;x=0&&T",{id:p.scrollName,href:"#top"}),p.scrollTitle&&d.attr("title",p.scrollTitle),d.appendTo("body"),p.scrollImg||p.scrollTrigger||d.html(p.scrollText),d.css({display:"none",position:"fixed",zIndex:p.zIndex}),p.activeOverlay&&l("
",{id:p.scrollName+"-active"}).css({position:"absolute",top:p.scrollDistance+"px",width:"100%",borderTop:"1px dotted"+p.activeOverlay,zIndex:p.zIndex}).appendTo("body"),p.animation){case"fade":s="fadeIn",t="fadeOut",c=p.animationSpeed;break;case"slide":s="slideDown",t="slideUp",c=p.animationSpeed;break;default:s="show",t="hide",c=0}i="top"===p.scrollFrom?p.scrollDistance:l(e).height()-l(o).height()-p.scrollDistance,n=l(o).scroll(function(){l(o).scrollTop()>i?f||(d[s](c),f=!0):f&&(d[t](c),f=!1)}),p.scrollTarget?"number"==typeof p.scrollTarget?a=p.scrollTarget:"string"==typeof p.scrollTarget&&(a=Math.floor(l(p.scrollTarget).offset().top)):a=0,d.click(function(o){o.preventDefault(),l("html, body").animate({scrollTop:a},p.scrollSpeed,p.easingType)})},l.fn.scrollUp.defaults={scrollName:"scrollUp",scrollDistance:300,scrollFrom:"top",scrollSpeed:300,easingType:"linear",animation:"fade",animationSpeed:200,scrollTrigger:!1,scrollTarget:!1,scrollText:"Scroll to top",scrollTitle:!1,scrollImg:!1,activeOverlay:!1,zIndex:2147483647},l.fn.scrollUp.destroy=function(r){l.removeData(e.body,"scrollUp"),l("#"+l.fn.scrollUp.settings.scrollName).remove(),l("#"+l.fn.scrollUp.settings.scrollName+"-active").remove(),l.fn.jquery.split(".")[1]>=7?l(o).off("scroll",r):l(o).unbind("scroll",r)},l.scrollUp=l.fn.scrollUp}(jQuery,window,document); \ No newline at end of file diff --git a/public/js/jquery.sortable.js b/public/js/jquery.sortable.js new file mode 100644 index 00000000..f4098a83 --- /dev/null +++ b/public/js/jquery.sortable.js @@ -0,0 +1,2 @@ +!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.sortable=t()}(this,function(){"use strict";function e(e,t,n){var r=null;return 0===t?e:function(){var a=n||this,o=arguments;clearTimeout(r),r=setTimeout(function(){e.apply(a,o)},t)}}var t,n,r,a=[],o=[],i=function(e,t,n){return void 0===n?e&&e.h5s&&e.h5s.data&&e.h5s.data[t]:(e.h5s=e.h5s||{},e.h5s.data=e.h5s.data||{},e.h5s.data[t]=n,void 0)},s=function(e){e.h5s&&delete e.h5s.data};switch(!0){case"matches"in window.Element.prototype:r="matches";break;case"mozMatchesSelector"in window.Element.prototype:r="mozMatchesSelector";break;case"msMatchesSelector"in window.Element.prototype:r="msMatchesSelector";break;case"webkitMatchesSelector"in window.Element.prototype:r="webkitMatchesSelector"}var l=function(e,t){if(!t)return Array.prototype.slice.call(e);for(var n=[],a=0;an){var c=o-n,f=p(e).top;if(id&&r>f+o-c)return}void 0===t.oldDisplay&&(t.oldDisplay=t.style.display),t.style.display="none",i {id: handle} + + function effectStarted(element, id, handle) {} + function effectStopped(element, id) {} + + this.add = function (effect) { + + if (!effects[effect.id]) { + effects[effect.id] = effect; + } + return this; + }; + + this.remove = function (effect) { + + if (effects[effect]) { + delete effects[effect]; + } else if (effect.id && effects[effect.id]) { + delete effects[effect.id]; + } + return this; + }; + + this.twinkle = function (event, options) { + + var settings = $.extend({}, defaults, options); + var effect = effects[settings.effect]; + + if (effect) { + event.element = event.element || 'body'; + effect.run(event, settings.effectOptions, function () { + + if ($.isFunction(settings.callback)) { + settings.callback(); + } + }); + } + return this; + }; + + this.stop = function (event, options) { + + var settings = $.extend({}, stopDefaults, options); + var effect = effects[settings.effect]; + + if (effect) { + event.element = event.element || 'body'; + effect.stop(event, settings.effectOptions, settings.callback); + } + return this; + }; + + this.twinkleAtElement = function (htmlElement, options) { + + var settings = $.extend({}, defaults, options); + var $htmlElement = $(htmlElement); + var offset = $htmlElement.offset(); + var position = $htmlElement.position(); + var width = $htmlElement.outerWidth(true); + var height = $htmlElement.outerHeight(true); + var offX = offset.left + width * settings.widthRatio; + var offY = offset.top + height * settings.heightRatio; + var posX = position.left + width * settings.widthRatio; + var posY = position.top + height * settings.heightRatio; + + return this.twinkle(new TwinkleEvent(offX, offY, htmlElement, posX, posY), options); + }; + + this.twinkleAtElements = function (htmlElements, options) { + + var self = this; + var settings = $.extend({}, defaults, options); + var delay = settings.delay; + var $htmlElements = $(htmlElements); + var size = $htmlElements.size(); + + $htmlElements.each(function (idx) { + + var htmlElement = this; + var opts = $.extend({}, options); + + if (idx !== size - 1) { + delete opts.callback; + } + + setTimeout(function () { + self.twinkleAtElement(htmlElement, opts); + }, delay); + + delay += settings.gap; + }); + return this; + }; + + this.stopAtElement = function (htmlElement, options) { + + var settings = $.extend({}, defaults, options); + var $htmlElement = $(htmlElement); + var offset = $htmlElement.offset(); + var position = $htmlElement.position(); + var width = $htmlElement.outerWidth(true); + var height = $htmlElement.outerHeight(true); + var offX = offset.left + width * settings.widthRatio; + var offY = offset.top + height * settings.heightRatio; + var posX = position.left + width * settings.widthRatio; + var posY = position.top + height * settings.heightRatio; + + return this.twinkle(new TwinkleEvent(offX, offY, htmlElement, posX, posY), options); + }; + + this.stopAtElements = function (htmlElements, options) { + + var self = this; + var settings = $.extend({}, stopDefaults, options); + var delay = settings.delay; + var $htmlElements = $(htmlElements); + var size = $htmlElements.size(); + + $htmlElements.each(function (idx) { + + var htmlElement = this, + opts = $.extend({}, options); + + if (idx !== size - 1) { + delete opts.callback; + } + + self.stopAtElement(htmlElement, opts); + }); + return this; + }; +} + + + +/*! +modplug 1.0 +http://larsjung.de/modplug +MIT License +*/ + +// This function is ment to be copied into your plugin file as a local +// variable. +// +// `modplug` expects a string `namespace` and a configuration object +// `options`. +// +// options = { +// statics: hash of functions, +// methods: hash of functions, +// defaultStatic: String/function, +// defaultMethod: String/function +// } +// +// For more details see . +var modplug = function (namespace, options) { + 'use strict'; + /*global jQuery: true */ + + // Some references to enhance minification. + var slice = [].slice, + $ = jQuery, + extend = $.extend, + isFn = $.isFunction, + + // Save the initial settings. + settings = extend({}, options), + + // Helper function to apply default methods. + applyMethod = function (obj, args, methodName, methods) { + + // If `methodName` is a function apply it to get the actual + // method name. + methodName = isFn(methodName) ? methodName.apply(obj, args) : methodName; + + // If method exists then apply it and return the result ... + if (isFn(methods[methodName])) { + return methods[methodName].apply(obj, args); + } + + // ... otherwise raise an error. + $.error('Method "' + methodName + '" does not exist on jQuery.' + namespace); + }, + + // This function gets exposed as `$.`. + statics = function () { + + // Try to apply a default method. + return applyMethod(this, slice.call(arguments), settings.defaultStatic, statics); + }, + + // This function gets exposed as `$(selector).`. + methods = function (method) { + + // If `method` exists then apply it ... + if (isFn(methods[method])) { + return methods[method].apply(this, slice.call(arguments, 1)); + } + + // ... otherwise try to apply a default method. + return applyMethod(this, slice.call(arguments), settings.defaultMethod, methods); + }, + + // Adds/overwrites plugin methods. This function gets exposed as + // `$..modplug` to make the plugin extendable. + plug = function (options) { + + if (options) { + extend(statics, options.statics); + extend(methods, options.methods); + } + + // Make sure that `$..modplug` points to this function + // after adding new methods. + statics.modplug = plug; + }; + + // Save objects or methods previously registered to the desired namespace. + // They are available via `$..modplug.prev`. + plug.prev = { + statics: $[namespace], + methods: $.fn[namespace] + }; + + // Init the plugin by adding the specified statics and methods. + plug(options); + + // Register the plugin. + $[namespace] = statics; + $.fn[namespace] = methods; +}; + + +var twinkler = new Twinkler(); +modplug('twinkle', { + statics: { + twinkle: function (element, left, top, options) { + + twinkler.twinkle(new TwinkleEvent(0, 0, element, left, top), options); + return this; + }, + add: function (effect) { + + twinkler.add(effect); + return this; + }, + remove: function (effect) { + + twinkler.remove(effect); + return this; + } + }, + methods: { + twinkle: function (options) { + + twinkler.twinkleAtElements(this, options); + return this; + }, + stop: function (options) { + + twinkler.stopAtElements(this, options); + return this; + } + }, + defaultStatic: 'twinkle', + defaultMethod: 'twinkle' +}); + +}()); + +(function () { +'use strict'; +/* CSS Effects */ + +var $ = jQuery; + +function blockEvents(event) { + + event.stopImmediatePropagation(); + event.preventDefault(); + return false; +} + +function animation(css, event, settings, callback) { + + var $dot; + + function cleanUp() { + + $dot.remove(); + if (callback instanceof Function) { + callback(); + } + } + + function fadeOut() { + + $dot.animate( + { + left: event.position.left - settings.radius, + top: event.position.top - settings.radius, + width: settings.radius * 2, + height: settings.radius * 2, + opacity: 0 + }, + settings.duration * 0.5, + 'linear', + cleanUp + ); + } + + function fadeIn() { + + $dot = $('
') + .css(css) + .bind('click dblclick mousedown mouseenter mouseover mousemove', blockEvents); + $(event.element).after($dot); + $dot.animate( + { + left: event.position.left - settings.radius * 0.5, + top: event.position.top - settings.radius * 0.5, + width: settings.radius, + height: settings.radius, + opacity: 1 + }, + settings.duration * 0.5, + 'linear', + fadeOut + ); + } + + function stop() {} + + fadeIn(); + + return { + stop: stop + }; +} + +var splashDefaults = { + color: 'rgba(255,0,0,0.5)', + radius: 300, + duration: 1000 + }; + +function SplashEffect() { + + this.id = 'splash-css'; + + this.run = function (event, options, callback) { + + var settings = $.extend({}, splashDefaults, options); + var css = { + position: 'absolute', + zIndex: 1000, + display: 'block', + borderRadius: settings.radius, + backgroundColor: settings.color, + boxShadow: '0 0 30px ' + settings.color, + left: event.position.left, + top: event.position.top, + width: 0, + height: 0, + opacity: 0.4 + }; + + animation(css, event, settings, callback); + }; +} + +var dropsDefaults = { + color: 'rgba(255,0,0,0.5)', + radius: 300, + duration: 1000, + width: 2, + count: 3, + delay: 300 + }; + +function DropsEffect() { + + this.id = 'drops-css'; + + this.run = function (event, options, callback) { + + var settings = $.extend({}, dropsDefaults, options); + var css = { + position: 'absolute', + zIndex: 1000, + display: 'block', + borderRadius: settings.radius, + border: settings.width + 'px solid ' + settings.color, + left: event.position.left, + top: event.position.top, + width: 0, + height: 0, + opacity: 0.4 + }; + + function setTimer(delay, callback) { + setTimeout(function () { + animation(css, event, settings, callback); + }, delay); + } + + var delay = 0; + var i; + + for (i = 0; i < settings.count; i += 1) { + setTimer(delay, i === settings.count - 1 ? callback : undefined); + delay += settings.delay; + } + }; +} + +function DropEffect() { + + var drops = new DropsEffect(); + + this.id = 'drop-css'; + + this.run = function (event, options, callback) { + + drops.run(event, $.extend(options, { count: 1 }), callback); + }; +} + +$.twinkle.add(new SplashEffect()).add(new DropEffect()).add(new DropsEffect()); + +}()); + +(function () { +'use strict'; +/* Canvas Effects */ + +var $ = jQuery; +var Objects = {}; + +(function () { + +function Interpolator(values) { + + var points; + + function equiDist(values) { + + var dist = 1 / (values.length - 1); + var points = []; + var i; + + for (i = 0; i < values.length; i += 1) { + points.push({ x: dist * i , y: values[i] }); + } + return points; + } + + function interpolate(p1, p2, x) { + + var m = (p2.y - p1.y) / (p2.x - p1.x); + var y = p1.y + m * (x - p1.x); + + return y; + } + + function findSection(x) { + + var i, prev, current; + + for (i = 1; i < points.length; i += 1) { + prev = points[i-1]; + current = points[i]; + if (x >= prev.x && x <= current.x) { + return [ prev, current ]; + } + } + + return undefined; + } + + points = equiDist(values); + + this.get = function (x) { + + var secPts; + + x = Math.max(0, Math.min(1, x)); + secPts = findSection(x); + return interpolate(secPts[0], secPts[1], x); + }; +} + +function scaleit(x, scale, offset) { + + scale = scale || 1; + offset = offset || 0; + x = (x - offset) / scale; + return x >= 0 && x <= 1 ? x : undefined; +} + +Objects.Interpolator = Interpolator; +Objects.Interpolator.scale = scaleit; + +}()); + +(function () { + +var $ = jQuery; + +function Path(ctx) { + + var context = ctx.getContext(); + + context.beginPath(); + + this.fill = function (fillStyle) { + + context.fillStyle = fillStyle; + context.fill(); + return ctx; + }; + + this.stroke = function (lineWidth, strokeStyle) { + + context.lineWidth = lineWidth; + context.strokeStyle = strokeStyle; + context.stroke(); + return ctx; + }; + + this.draw = function (lineWidth, strokeStyle, fillStyle) { + + this.fill(fillStyle); + this.stroke(lineWidth, strokeStyle); + return ctx; + }; + + this.circle = function (x, y, radius) { + + context.arc(x, y, radius, 0, 2 * Math.PI, false); + return this; + }; +} + +function Ctx(context) { + + if (!context || !context.canvas) { + return undefined; + } else if (!(this instanceof Ctx)) { + return new Ctx(context); + } + + var width = $(context.canvas).width(); + var height = $(context.canvas).height(); + + this.getContext = function () { + + return context; + }; + + this.getWidth = function () { + + return width; + }; + + this.getHeight = function () { + + return height; + }; + + this.clear = function () { + + this.resetTransform(); + context.clearRect(0, 0, width, height); + return this; + }; + + this.resetTransform = function () { + + context.setTransform(1, 0, 0, 1, 0, 0); + return this; + }; + + this.translate = function (x, y) { + + context.translate(x, y); + return this; + }; + + this.rotate = function (alpha) { + + context.rotate(Math.PI * alpha / 180); + return this; + }; + + this.opacity = function (opacity) { + + context.globalAlpha = opacity; + return this; + }; + + this.path = function () { + + return new Path(this); + }; +} + +Objects.Ctx = Ctx; + +}()); + +(function () { + +function CanvasEffect(twinkleEvent, width, height, frame, callback) { + + if (!(this instanceof Objects.CanvasEffect)) { + return new Objects.CanvasEffect(twinkleEvent, width, height, frame, callback); + } + + var element = twinkleEvent.element; + var x = twinkleEvent.position.left; + var y = twinkleEvent.position.top; + var css = { + position: 'absolute', + zIndex: 1000, + display: 'block', + left: x - width * 0.5, + top: y - height * 0.5, + width: width, + height: height + }; + + this.run = function (duration, fps) { + + var $canvas, ctx, i; + var frameCount = duration / 1000 * fps; + var delta = 1 / frameCount; + + function setFrameTimer(fraction) { + + setTimeout(function () { + + if (ctx) { + frame({ + ctx: ctx, + frac: fraction, + millis: duration * fraction + }); + } + }, duration * fraction); + } + + function cleanUp() { + + $canvas.remove(); + $canvas = undefined; + ctx = undefined; + if (callback instanceof Function) { + callback(); + } + } + + function blockEvents(event) { + + event.stopImmediatePropagation(); + event.preventDefault(); + return false; + } + + $canvas = jQuery('').attr('width', width).attr('height', height).css(css); + jQuery(element).after($canvas); + $canvas.bind('click dblclick mousedown mouseenter mouseover mousemove', blockEvents); + ctx = new Objects.Ctx($canvas.get(0).getContext('2d')); + + for (i = 0; i <= frameCount; i += 1) { + setFrameTimer(i * delta); + } + + setTimeout(cleanUp, duration); + }; +} + +Objects.CanvasEffect = CanvasEffect; + +}()); + +(function () { + +var $ = jQuery; + +var defaults = { + color: 'rgba(255,0,0,0.5)', + radius: 300, + duration: 1000 + }; + +function SplashEffect() { + + this.id = 'splash'; + + this.run = function (twinkleEvent, options, callback) { + + var settings = $.extend({}, defaults, options); + var size = settings.radius * 2; + var opacityIpl = new Objects.Interpolator([ 0.4, 1, 0 ]); + var radiusIpl = new Objects.Interpolator([ 0, settings.radius ]); + + function frame(frameEvent) { + + var radius = radiusIpl.get(frameEvent.frac); + var opacity = opacityIpl.get(frameEvent.frac); + var ctx = frameEvent.ctx; + + ctx + .clear() + .opacity(opacity) + .path() + .circle(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5, radius) + .fill(settings.color); + } + + new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25); + }; +} + +$.twinkle.add(new SplashEffect()); + +}()); + +(function () { + +var $ = jQuery; + +var defaults = { + color: 'rgba(255,0,0,0.5)', + radius: 300, + duration: 1000, + width: 2 + }; + +function DropEffect() { + + this.id = 'drop'; + + this.run = function (twinkleEvent, options, callback) { + + var settings = $.extend({}, defaults, options); + var size = settings.radius * 2; + var opacityIpl = new Objects.Interpolator([ 0.4, 1, 0 ]); + var radiusIpl = new Objects.Interpolator([ 0, settings.radius ]); + + function frame(frameEvent) { + + var radius = radiusIpl.get(frameEvent.frac); + var opacity = opacityIpl.get(frameEvent.frac); + var ctx = frameEvent.ctx; + + ctx + .clear() + .opacity(opacity) + .path() + .circle(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5, radius) + .stroke(settings.width, settings.color); + } + + new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25); + }; +} + +$.twinkle.add(new DropEffect()); + +}()); + +(function () { + +var $ = jQuery; + +var defaults = { + color: 'rgba(255,0,0,0.5)', + radius: 300, + duration: 1000, + width: 2, + count: 3, + delay: 100 + }; + +function DropsEffect() { + + this.id = 'drops'; + + this.run = function (twinkleEvent, options, callback) { + + var settings = $.extend({}, defaults, options); + var size = settings.radius * 2; + var opacityIpl = new Objects.Interpolator([ 0.4, 1, 0 ]); + var radiusIpl = new Objects.Interpolator([ 0, settings.radius ]); + var scale = (settings.duration - (settings.count - 1) * settings.delay) / settings.duration; + var offset = settings.delay / settings.duration; + + function frame(frameEvent) { + + var i, frac, radius, opacity; + var ctx = frameEvent.ctx; + var width = ctx.getWidth(); + var height = ctx.getHeight(); + + ctx.clear(); + for (i = 0; i < settings.count; i += 1) { + frac = Objects.Interpolator.scale(frameEvent.frac, scale, offset * i); + + if (frac !== undefined) { + radius = radiusIpl.get(frac); + opacity = opacityIpl.get(frac); + ctx + .opacity(opacity) + .path() + .circle(width * 0.5, height * 0.5, radius) + .stroke(settings.width, settings.color); + } + } + } + + new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25); + }; +} + +$.twinkle.add(new DropsEffect()); + +}()); + +(function () { + +var $ = jQuery; + +var defaults = { + color: 'rgba(255,0,0,0.5)', + radius: 100, + duration: 3000 + }; + +function PulseEffect() { + + this.id = 'pulse'; + + this.run = function (twinkleEvent, options, callback) { + + var settings = $.extend({}, defaults, options); + var size = settings.radius * 2; + var opacityIpl = new Objects.Interpolator([ 0, 1, 0.6, 1, 0.6, 1, 0 ]); + var radiusIpl = new Objects.Interpolator([ 0, settings.radius, settings.radius * 0.6, settings.radius, settings.radius * 0.6, settings.radius, 0 ]); + + function frame(frameEvent) { + + var radius = radiusIpl.get(frameEvent.frac), + opacity = opacityIpl.get(frameEvent.frac), + ctx = frameEvent.ctx; + + ctx + .clear() + .opacity(opacity) + .path() + .circle(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5, radius) + .fill(settings.color); + } + + new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25); + }; +} + +$.twinkle.add(new PulseEffect()); + +}()); + +(function () { + +var $ = jQuery; + +var defaults = { + color: 'rgba(255,0,0,0.5)', + radius: 100, + duration: 3000, + satellites: 10, + satellitesRadius: 10, + circulations: 1.5 + }; + +function OrbitEffect() { + + this.id = 'orbit'; + + this.run = function (twinkleEvent, options, callback) { + + var settings = $.extend({}, defaults, options); + var size = settings.radius * 2; + var opacityIpl = new Objects.Interpolator([ 0.4, 1, 1, 0.4 ]); + var r = settings.radius - settings.satellitesRadius; + var radiusIpl = new Objects.Interpolator([ 0, r, r, 0 ]); + + function frame(frameEvent) { + + var radius = radiusIpl.get(frameEvent.frac); + var opacity = opacityIpl.get(frameEvent.frac); + var bog = Math.PI * 2 * settings.circulations * frameEvent.frac; + var ctx = frameEvent.ctx; + var path, i, x, y; + + ctx + .clear() + .opacity(opacity) + .translate(ctx.getWidth() * 0.5, ctx.getHeight() * 0.5); + + path = ctx.path(); + for (i = 0; i < settings.satellites; i += 1) { + bog += Math.PI * 2 / settings.satellites; + x = Math.cos(bog) * radius; + y = Math.sin(bog) * radius; + ctx.getContext().moveTo(x, y); + path.circle(x, y, settings.satellitesRadius); + } + path.fill(settings.color); + } + + new Objects.CanvasEffect(twinkleEvent, size, size, frame, callback).run(settings.duration, 25); + }; +} + +$.twinkle.add(new OrbitEffect()); + +}()); + +}()); + diff --git a/public/js/main.js b/public/js/main.js new file mode 100755 index 00000000..b98985d8 --- /dev/null +++ b/public/js/main.js @@ -0,0 +1,159 @@ +(function ($) { + "use strict"; + + $('.mobile_memu nav').meanmenu({ + meanScreenWidth: "991", + meanMenuContainer: ".mobile_memu", + }); + + /*--------------------- + upcoming-product-list + --------------------- */ + $(".model_owl").owlCarousel({ + + autoPlay: false, //Set AutoPlay to seconds here + items : 4, + slideSpeed:600, + rewindNav : false, + itemsDesktop : [1169,3], + itemsTablet: [991,2], + itemsTabletSmall: [767,1], + itemsMobile : [320,1], + pagination : false, + navigation : true, + navigationText : ["",""] + }); + /*--------------------- + upcoming-product-list + --------------------- */ + $(".newp_carsol").owlCarousel({ + + autoPlay: false, //Set AutoPlay to seconds here + items : 1, + slideSpeed:600, + itemsDesktop : [1169,1], + itemsTablet: [991,1], + itemsTabletSmall: [767,1], + itemsMobile : [320,1], + pagination : false, + navigation : false, + rewindNav : false, + navigationText : ["",""] + }); + /*--------------------- + upcoming-product-list + --------------------- */ + $(".stock_owl").owlCarousel({ + + autoPlay: false, //Set AutoPlay to seconds here + slideSpeed:600, + items : 1, + itemsDesktop : [1169,1], + itemsTablet: [991,1], + itemsTabletSmall: [767,1], + itemsMobile : [320,1], + pagination : false, + navigation : true, + rewindNav : false, + navigationText : ["",""], + }); + /*--------------------- + upcoming-product-list + --------------------- */ + $(".blog_carsol").owlCarousel({ + + autoPlay: false, //Set AutoPlay to seconds here + items : 2, + slideSpeed:600, + itemsDesktopSmall : [1169,2], + itemsTablet: [991,1], + itemsTabletSmall: [767,1], + itemsMobile : [320,1], + pagination : false, + navigation : false, + rewindNav : false, + navigationText : ["",""] + }); + /*--------------------- + upcoming-product-list + --------------------- */ + $(".client_owl").owlCarousel({ + + autoPlay: true, //Set AutoPlay to seconds here + items : 5, + slideSpeed:600, + //rewindNav : false, + itemsDesktop : [1169,4], + itemsTablet: [991,3], + itemsTabletSmall: [767,2], + itemsMobile : [479,1], + pagination : false, + navigation : false, + navigationText : ["",""] + }); + + /*--------------------- + countdown + --------------------- */ + $('[data-countdown]').each(function() { + var $this = $(this), finalDate = $(this).data('countdown'); + $this.countdown(finalDate, function(event) { + $this.html(event.strftime('%-D

Days

%-H

Hour

%M

Min

%S

Sec

')); + }); + }); + // scroolup + $.scrollUp({ + scrollText: '', + easingType: 'linear', + scrollSpeed: 900, + animation: 'fade' + }); + /*--------------------- + price slider + --------------------- */ + $( "#slider-range" ).slider({ + range: true, + min: 40, + max: 600, + values: [ 60, 570 ], + slide: function( event, ui ) { + $( "#amount" ).val( "$" + ui.values[ 0 ] + " - $" + ui.values[ 1 ] ); + } + }); + /* + $( "#amount" ).val( "$" + $( "#slider-range" ).slider( "values", 0 ) + + " - $" + $( "#slider-range" ).slider( "values", 1 ) ); + */ + + /*--------------------- + collapse + --------------------- */ + $('.panel_heading a').on('click', function(){ + $('.panel_heading a').removeClass('active'); + $(this).addClass('active'); + }) + /*--------------------- + fancybox + --------------------- */ + $('.fancybox').fancybox(); + /*--------------------- + counterUp + --------------------- */ + $('.about-counter').counterUp({ + delay: 10, + time: 1000 + }); +/*----- main slider -----*/ + $('#mainSlider').nivoSlider({ + directionNav: false, + animSpeed: 500, + slices: 18, + pauseTime: 111115000, + pauseOnHover: false, + controlNav: true, + prevText: '', + nextText: '' + }); + + +})(jQuery); \ No newline at end of file diff --git a/public/js/owl.carousel.min.js b/public/js/owl.carousel.min.js new file mode 100755 index 00000000..394505e0 --- /dev/null +++ b/public/js/owl.carousel.min.js @@ -0,0 +1,47 @@ +"function"!==typeof Object.create&&(Object.create=function(f){function g(){}g.prototype=f;return new g}); +(function(f,g,k){var l={init:function(a,b){this.$elem=f(b);this.options=f.extend({},f.fn.owlCarousel.options,this.$elem.data(),a);this.userOptions=a;this.loadContent()},loadContent:function(){function a(a){var d,e="";if("function"===typeof b.options.jsonSuccess)b.options.jsonSuccess.apply(this,[a]);else{for(d in a.owl)a.owl.hasOwnProperty(d)&&(e+=a.owl[d].item);b.$elem.html(e)}b.logIn()}var b=this,e;"function"===typeof b.options.beforeInit&&b.options.beforeInit.apply(this,[b.$elem]);"string"===typeof b.options.jsonPath? +(e=b.options.jsonPath,f.getJSON(e,a)):b.logIn()},logIn:function(){this.$elem.data("owl-originalStyles",this.$elem.attr("style"));this.$elem.data("owl-originalClasses",this.$elem.attr("class"));this.$elem.css({opacity:0});this.orignalItems=this.options.items;this.checkBrowser();this.wrapperWidth=0;this.checkVisible=null;this.setVars()},setVars:function(){if(0===this.$elem.children().length)return!1;this.baseClass();this.eventTypes();this.$userItems=this.$elem.children();this.itemsAmount=this.$userItems.length; +this.wrapItems();this.$owlItems=this.$elem.find(".owl-item");this.$owlWrapper=this.$elem.find(".owl-wrapper");this.playDirection="next";this.prevItem=0;this.prevArr=[0];this.currentItem=0;this.customEvents();this.onStartup()},onStartup:function(){this.updateItems();this.calculateAll();this.buildControls();this.updateControls();this.response();this.moveEvents();this.stopOnHover();this.owlStatus();!1!==this.options.transitionStyle&&this.transitionTypes(this.options.transitionStyle);!0===this.options.autoPlay&& +(this.options.autoPlay=5E3);this.play();this.$elem.find(".owl-wrapper").css("display","block");this.$elem.is(":visible")?this.$elem.css("opacity",1):this.watchVisibility();this.onstartup=!1;this.eachMoveUpdate();"function"===typeof this.options.afterInit&&this.options.afterInit.apply(this,[this.$elem])},eachMoveUpdate:function(){!0===this.options.lazyLoad&&this.lazyLoad();!0===this.options.autoHeight&&this.autoHeight();this.onVisibleItems();"function"===typeof this.options.afterAction&&this.options.afterAction.apply(this, +[this.$elem])},updateVars:function(){"function"===typeof this.options.beforeUpdate&&this.options.beforeUpdate.apply(this,[this.$elem]);this.watchVisibility();this.updateItems();this.calculateAll();this.updatePosition();this.updateControls();this.eachMoveUpdate();"function"===typeof this.options.afterUpdate&&this.options.afterUpdate.apply(this,[this.$elem])},reload:function(){var a=this;g.setTimeout(function(){a.updateVars()},0)},watchVisibility:function(){var a=this;if(!1===a.$elem.is(":visible"))a.$elem.css({opacity:0}), +g.clearInterval(a.autoPlayInterval),g.clearInterval(a.checkVisible);else return!1;a.checkVisible=g.setInterval(function(){a.$elem.is(":visible")&&(a.reload(),a.$elem.animate({opacity:1},200),g.clearInterval(a.checkVisible))},500)},wrapItems:function(){this.$userItems.wrapAll('
').wrap('
');this.$elem.find(".owl-wrapper").wrap('
');this.wrapperOuter=this.$elem.find(".owl-wrapper-outer");this.$elem.css("display","block")}, +baseClass:function(){var a=this.$elem.hasClass(this.options.baseClass),b=this.$elem.hasClass(this.options.theme);a||this.$elem.addClass(this.options.baseClass);b||this.$elem.addClass(this.options.theme)},updateItems:function(){var a,b;if(!1===this.options.responsive)return!1;if(!0===this.options.singleItem)return this.options.items=this.orignalItems=1,this.options.itemsCustom=!1,this.options.itemsDesktop=!1,this.options.itemsDesktopSmall=!1,this.options.itemsTablet=!1,this.options.itemsTabletSmall= +!1,this.options.itemsMobile=!1;a=f(this.options.responsiveBaseWidth).width();a>(this.options.itemsDesktop[0]||this.orignalItems)&&(this.options.items=this.orignalItems);if(!1!==this.options.itemsCustom)for(this.options.itemsCustom.sort(function(a,b){return a[0]-b[0]}),b=0;bthis.itemsAmount&& +!0===this.options.itemsScaleUp&&(this.options.items=this.itemsAmount)},response:function(){var a=this,b,e;if(!0!==a.options.responsive)return!1;e=f(g).width();a.resizer=function(){f(g).width()!==e&&(!1!==a.options.autoPlay&&g.clearInterval(a.autoPlayInterval),g.clearTimeout(b),b=g.setTimeout(function(){e=f(g).width();a.updateVars()},a.options.responsiveRefreshRate))};f(g).resize(a.resizer)},updatePosition:function(){this.jumpTo(this.currentItem);!1!==this.options.autoPlay&&this.checkAp()},appendItemsSizes:function(){var a= +this,b=0,e=a.itemsAmount-a.options.items;a.$owlItems.each(function(c){var d=f(this);d.css({width:a.itemWidth}).data("owl-item",Number(c));if(0===c%a.options.items||c===e)c>e||(b+=1);d.data("owl-roundPages",b)})},appendWrapperSizes:function(){this.$owlWrapper.css({width:this.$owlItems.length*this.itemWidth*2,left:0});this.appendItemsSizes()},calculateAll:function(){this.calculateWidth();this.appendWrapperSizes();this.loops();this.max()},calculateWidth:function(){this.itemWidth=Math.round(this.$elem.width()/ +this.options.items)},max:function(){var a=-1*(this.itemsAmount*this.itemWidth-this.options.items*this.itemWidth);this.options.items>this.itemsAmount?this.maximumPixels=a=this.maximumItem=0:(this.maximumItem=this.itemsAmount-this.options.items,this.maximumPixels=a);return a},min:function(){return 0},loops:function(){var a=0,b=0,e,c;this.positionsInArray=[0];this.pagesInArray=[];for(e=0;e').toggleClass("clickable",!this.browser.isTouch).appendTo(this.$elem);!0===this.options.pagination&&this.buildPagination();!0===this.options.navigation&&this.buildButtons()},buildButtons:function(){var a=this,b=f('
');a.owlControls.append(b);a.buttonPrev= +f("
",{"class":"owl-prev",html:a.options.navigationText[0]||""});a.buttonNext=f("
",{"class":"owl-next",html:a.options.navigationText[1]||""});b.append(a.buttonPrev).append(a.buttonNext);b.on("touchstart.owlControls mousedown.owlControls",'div[class^="owl"]',function(a){a.preventDefault()});b.on("touchend.owlControls mouseup.owlControls",'div[class^="owl"]',function(b){b.preventDefault();f(this).hasClass("owl-next")?a.next():a.prev()})},buildPagination:function(){var a=this;a.paginationWrapper= +f('
');a.owlControls.append(a.paginationWrapper);a.paginationWrapper.on("touchend.owlControls mouseup.owlControls",".owl-page",function(b){b.preventDefault();Number(f(this).data("owl-page"))!==a.currentItem&&a.goTo(Number(f(this).data("owl-page")),!0)})},updatePagination:function(){var a,b,e,c,d,g;if(!1===this.options.pagination)return!1;this.paginationWrapper.html("");a=0;b=this.itemsAmount-this.itemsAmount%this.options.items;for(c=0;c",{"class":"owl-page"}),g=f("",{text:!0===this.options.paginationNumbers?a:"","class":!0===this.options.paginationNumbers?"owl-numbers":""}),d.append(g),d.data("owl-page",b===c?e:c),d.data("owl-roundPages",a),this.paginationWrapper.append(d));this.checkPagination()},checkPagination:function(){var a=this;if(!1===a.options.pagination)return!1;a.paginationWrapper.find(".owl-page").each(function(){f(this).data("owl-roundPages")=== +f(a.$owlItems[a.currentItem]).data("owl-roundPages")&&(a.paginationWrapper.find(".owl-page").removeClass("active"),f(this).addClass("active"))})},checkNavigation:function(){if(!1===this.options.navigation)return!1;!1===this.options.rewindNav&&(0===this.currentItem&&0===this.maximumItem?(this.buttonPrev.addClass("disabled"),this.buttonNext.addClass("disabled")):0===this.currentItem&&0!==this.maximumItem?(this.buttonPrev.addClass("disabled"),this.buttonNext.removeClass("disabled")):this.currentItem=== +this.maximumItem?(this.buttonPrev.removeClass("disabled"),this.buttonNext.addClass("disabled")):0!==this.currentItem&&this.currentItem!==this.maximumItem&&(this.buttonPrev.removeClass("disabled"),this.buttonNext.removeClass("disabled")))},updateControls:function(){this.updatePagination();this.checkNavigation();this.owlControls&&(this.options.items>=this.itemsAmount?this.owlControls.hide():this.owlControls.show())},destroyControls:function(){this.owlControls&&this.owlControls.remove()},next:function(a){if(this.isTransition)return!1; +this.currentItem+=!0===this.options.scrollPerPage?this.options.items:1;if(this.currentItem>this.maximumItem+(!0===this.options.scrollPerPage?this.options.items-1:0))if(!0===this.options.rewindNav)this.currentItem=0,a="rewind";else return this.currentItem=this.maximumItem,!1;this.goTo(this.currentItem,a)},prev:function(a){if(this.isTransition)return!1;this.currentItem=!0===this.options.scrollPerPage&&0this.currentItem)if(!0===this.options.rewindNav)this.currentItem=this.maximumItem,a="rewind";else return this.currentItem=0,!1;this.goTo(this.currentItem,a)},goTo:function(a,b,e){var c=this;if(c.isTransition)return!1;"function"===typeof c.options.beforeMove&&c.options.beforeMove.apply(this,[c.$elem]);a>=c.maximumItem?a=c.maximumItem:0>=a&&(a=0);c.currentItem=c.owl.currentItem=a;if(!1!==c.options.transitionStyle&&"drag"!==e&&1===c.options.items&&!0===c.browser.support3d)return c.swapSpeed(0), +!0===c.browser.support3d?c.transition3d(c.positionsInArray[a]):c.css2slide(c.positionsInArray[a],1),c.afterGo(),c.singleItemTransition(),!1;a=c.positionsInArray[a];!0===c.browser.support3d?(c.isCss3Finish=!1,!0===b?(c.swapSpeed("paginationSpeed"),g.setTimeout(function(){c.isCss3Finish=!0},c.options.paginationSpeed)):"rewind"===b?(c.swapSpeed(c.options.rewindSpeed),g.setTimeout(function(){c.isCss3Finish=!0},c.options.rewindSpeed)):(c.swapSpeed("slideSpeed"),g.setTimeout(function(){c.isCss3Finish=!0}, +c.options.slideSpeed)),c.transition3d(a)):!0===b?c.css2slide(a,c.options.paginationSpeed):"rewind"===b?c.css2slide(a,c.options.rewindSpeed):c.css2slide(a,c.options.slideSpeed);c.afterGo()},jumpTo:function(a){"function"===typeof this.options.beforeMove&&this.options.beforeMove.apply(this,[this.$elem]);a>=this.maximumItem||-1===a?a=this.maximumItem:0>=a&&(a=0);this.swapSpeed(0);!0===this.browser.support3d?this.transition3d(this.positionsInArray[a]):this.css2slide(this.positionsInArray[a],1);this.currentItem= +this.owl.currentItem=a;this.afterGo()},afterGo:function(){this.prevArr.push(this.currentItem);this.prevItem=this.owl.prevItem=this.prevArr[this.prevArr.length-2];this.prevArr.shift(0);this.prevItem!==this.currentItem&&(this.checkPagination(),this.checkNavigation(),this.eachMoveUpdate(),!1!==this.options.autoPlay&&this.checkAp());"function"===typeof this.options.afterMove&&this.prevItem!==this.currentItem&&this.options.afterMove.apply(this,[this.$elem])},stop:function(){this.apStatus="stop";g.clearInterval(this.autoPlayInterval)}, +checkAp:function(){"stop"!==this.apStatus&&this.play()},play:function(){var a=this;a.apStatus="play";if(!1===a.options.autoPlay)return!1;g.clearInterval(a.autoPlayInterval);a.autoPlayInterval=g.setInterval(function(){a.next(!0)},a.options.autoPlay)},swapSpeed:function(a){"slideSpeed"===a?this.$owlWrapper.css(this.addCssSpeed(this.options.slideSpeed)):"paginationSpeed"===a?this.$owlWrapper.css(this.addCssSpeed(this.options.paginationSpeed)):"string"!==typeof a&&this.$owlWrapper.css(this.addCssSpeed(a))}, +addCssSpeed:function(a){return{"-webkit-transition":"all "+a+"ms ease","-moz-transition":"all "+a+"ms ease","-o-transition":"all "+a+"ms ease",transition:"all "+a+"ms ease"}},removeTransition:function(){return{"-webkit-transition":"","-moz-transition":"","-o-transition":"",transition:""}},doTranslate:function(a){return{"-webkit-transform":"translate3d("+a+"px, 0px, 0px)","-moz-transform":"translate3d("+a+"px, 0px, 0px)","-o-transform":"translate3d("+a+"px, 0px, 0px)","-ms-transform":"translate3d("+ +a+"px, 0px, 0px)",transform:"translate3d("+a+"px, 0px,0px)"}},transition3d:function(a){this.$owlWrapper.css(this.doTranslate(a))},css2move:function(a){this.$owlWrapper.css({left:a})},css2slide:function(a,b){var e=this;e.isCssFinish=!1;e.$owlWrapper.stop(!0,!0).animate({left:a},{duration:b||e.options.slideSpeed,complete:function(){e.isCssFinish=!0}})},checkBrowser:function(){var a=k.createElement("div");a.style.cssText=" -moz-transform:translate3d(0px, 0px, 0px); -ms-transform:translate3d(0px, 0px, 0px); -o-transform:translate3d(0px, 0px, 0px); -webkit-transform:translate3d(0px, 0px, 0px); transform:translate3d(0px, 0px, 0px)"; +a=a.style.cssText.match(/translate3d\(0px, 0px, 0px\)/g);this.browser={support3d:null!==a&&1===a.length,isTouch:"ontouchstart"in g||g.navigator.msMaxTouchPoints}},moveEvents:function(){if(!1!==this.options.mouseDrag||!1!==this.options.touchDrag)this.gestures(),this.disabledEvents()},eventTypes:function(){var a=["s","e","x"];this.ev_types={};!0===this.options.mouseDrag&&!0===this.options.touchDrag?a=["touchstart.owl mousedown.owl","touchmove.owl mousemove.owl","touchend.owl touchcancel.owl mouseup.owl"]: +!1===this.options.mouseDrag&&!0===this.options.touchDrag?a=["touchstart.owl","touchmove.owl","touchend.owl touchcancel.owl"]:!0===this.options.mouseDrag&&!1===this.options.touchDrag&&(a=["mousedown.owl","mousemove.owl","mouseup.owl"]);this.ev_types.start=a[0];this.ev_types.move=a[1];this.ev_types.end=a[2]},disabledEvents:function(){this.$elem.on("dragstart.owl",function(a){a.preventDefault()});this.$elem.on("mousedown.disableTextSelect",function(a){return f(a.target).is("input, textarea, select, option")})}, +gestures:function(){function a(a){if(void 0!==a.touches)return{x:a.touches[0].pageX,y:a.touches[0].pageY};if(void 0===a.touches){if(void 0!==a.pageX)return{x:a.pageX,y:a.pageY};if(void 0===a.pageX)return{x:a.clientX,y:a.clientY}}}function b(a){"on"===a?(f(k).on(d.ev_types.move,e),f(k).on(d.ev_types.end,c)):"off"===a&&(f(k).off(d.ev_types.move),f(k).off(d.ev_types.end))}function e(b){b=b.originalEvent||b||g.event;d.newPosX=a(b).x-h.offsetX;d.newPosY=a(b).y-h.offsetY;d.newRelativeX=d.newPosX-h.relativePos; +"function"===typeof d.options.startDragging&&!0!==h.dragging&&0!==d.newRelativeX&&(h.dragging=!0,d.options.startDragging.apply(d,[d.$elem]));(8d.newRelativeX)&&!0===d.browser.isTouch&&(void 0!==b.preventDefault?b.preventDefault():b.returnValue=!1,h.sliding=!0);(10d.newPosY)&&!1===h.sliding&&f(k).off("touchmove.owl");d.newPosX=Math.max(Math.min(d.newPosX,d.newRelativeX/5),d.maximumPixels+d.newRelativeX/5);!0===d.browser.support3d?d.transition3d(d.newPosX):d.css2move(d.newPosX)} +function c(a){a=a.originalEvent||a||g.event;var c;a.target=a.target||a.srcElement;h.dragging=!1;!0!==d.browser.isTouch&&d.$owlWrapper.removeClass("grabbing");d.dragDirection=0>d.newRelativeX?d.owl.dragDirection="left":d.owl.dragDirection="right";0!==d.newRelativeX&&(c=d.getNewPosition(),d.goTo(c,!1,"drag"),h.targetElement===a.target&&!0!==d.browser.isTouch&&(f(a.target).on("click.disable",function(a){a.stopImmediatePropagation();a.stopPropagation();a.preventDefault();f(a.target).off("click.disable")}), +a=f._data(a.target,"events").click,c=a.pop(),a.splice(0,0,c)));b("off")}var d=this,h={offsetX:0,offsetY:0,baseElWidth:0,relativePos:0,position:null,minSwipe:null,maxSwipe:null,sliding:null,dargging:null,targetElement:null};d.isCssFinish=!0;d.$elem.on(d.ev_types.start,".owl-wrapper",function(c){c=c.originalEvent||c||g.event;var e;if(3===c.which)return!1;if(!(d.itemsAmount<=d.options.items)){if(!1===d.isCssFinish&&!d.options.dragBeforeAnimFinish||!1===d.isCss3Finish&&!d.options.dragBeforeAnimFinish)return!1; +!1!==d.options.autoPlay&&g.clearInterval(d.autoPlayInterval);!0===d.browser.isTouch||d.$owlWrapper.hasClass("grabbing")||d.$owlWrapper.addClass("grabbing");d.newPosX=0;d.newRelativeX=0;f(this).css(d.removeTransition());e=f(this).position();h.relativePos=e.left;h.offsetX=a(c).x-e.left;h.offsetY=a(c).y-e.top;b("on");h.sliding=!1;h.targetElement=c.target||c.srcElement}})},getNewPosition:function(){var a=this.closestItem();a>this.maximumItem?a=this.currentItem=this.maximumItem:0<=this.newPosX&&(this.currentItem= +a=0);return a},closestItem:function(){var a=this,b=!0===a.options.scrollPerPage?a.pagesInArray:a.positionsInArray,e=a.newPosX,c=null;f.each(b,function(d,g){e-a.itemWidth/20>b[d+1]&&e-a.itemWidth/20(b[d+1]||b[d]-a.itemWidth)&&"right"===a.moveDirection()&&(!0===a.options.scrollPerPage?(c=b[d+1]||b[b.length-1],a.currentItem=f.inArray(c,a.positionsInArray)): +(c=b[d+1],a.currentItem=d+1))});return a.currentItem},moveDirection:function(){var a;0>this.newRelativeX?(a="right",this.playDirection="next"):(a="left",this.playDirection="prev");return a},customEvents:function(){var a=this;a.$elem.on("owl.next",function(){a.next()});a.$elem.on("owl.prev",function(){a.prev()});a.$elem.on("owl.play",function(b,e){a.options.autoPlay=e;a.play();a.hoverStatus="play"});a.$elem.on("owl.stop",function(){a.stop();a.hoverStatus="stop"});a.$elem.on("owl.goTo",function(b,e){a.goTo(e)}); +a.$elem.on("owl.jumpTo",function(b,e){a.jumpTo(e)})},stopOnHover:function(){var a=this;!0===a.options.stopOnHover&&!0!==a.browser.isTouch&&!1!==a.options.autoPlay&&(a.$elem.on("mouseover",function(){a.stop()}),a.$elem.on("mouseout",function(){"stop"!==a.hoverStatus&&a.play()}))},lazyLoad:function(){var a,b,e,c,d;if(!1===this.options.lazyLoad)return!1;for(a=0;a=this.currentItem:!0)&&e=f?g.setTimeout(c,100):e()}var d=this,f=0,k;"DIV"===b.prop("tagName")?(b.css("background-image","url("+b.data("src")+")"),k=!0):b[0].src=b.data("src");c()},autoHeight:function(){function a(){var a=f(e.$owlItems[e.currentItem]).height();e.wrapperOuter.css("height",a+"px");e.wrapperOuter.hasClass("autoHeight")||g.setTimeout(function(){e.wrapperOuter.addClass("autoHeight")},0)}function b(){d+=1;e.completeImg(c.get(0))?a():100>=d?g.setTimeout(b, +100):e.wrapperOuter.css("height","")}var e=this,c=f(e.$owlItems[e.currentItem]).find("img"),d;void 0!==c.get(0)?(d=0,b()):a()},completeImg:function(a){return!a.complete||"undefined"!==typeof a.naturalWidth&&0===a.naturalWidth?!1:!0},onVisibleItems:function(){var a;!0===this.options.addClassActive&&this.$owlItems.removeClass("active");this.visibleItems=[];for(a=this.currentItem;a=this.$userItems.length||-1===e?this.$userItems.eq(-1).after(a):this.$userItems.eq(e).before(a);this.setVars()},removeItem:function(a){if(0===this.$elem.children().length)return!1;a=void 0===a||-1===a?-1:a;this.unWrap();this.$userItems.eq(a).remove();this.setVars()}};f.fn.owlCarousel=function(a){return this.each(function(){if(!0=== +f(this).data("owl-init"))return!1;f(this).data("owl-init",!0);var b=Object.create(l);b.init(a,this);f.data(this,"owlCarousel",b)})};f.fn.owlCarousel.options={items:5,itemsCustom:!1,itemsDesktop:[1199,4],itemsDesktopSmall:[979,3],itemsTablet:[768,2],itemsTabletSmall:!1,itemsMobile:[479,1],singleItem:!1,itemsScaleUp:!1,slideSpeed:200,paginationSpeed:800,rewindSpeed:1E3,autoPlay:!1,stopOnHover:!1,navigation:!1,navigationText:["prev","next"],rewindNav:!0,scrollPerPage:!1,pagination:!0,paginationNumbers:!1, +responsive:!0,responsiveRefreshRate:200,responsiveBaseWidth:g,baseClass:"owl-carousel",theme:"owl-theme",lazyLoad:!1,lazyFollow:!0,lazyEffect:"fade",autoHeight:!1,jsonPath:!1,jsonSuccess:!1,dragBeforeAnimFinish:!0,mouseDrag:!0,touchDrag:!0,addClassActive:!1,transitionStyle:!1,beforeUpdate:!1,afterUpdate:!1,beforeInit:!1,afterInit:!1,beforeMove:!1,afterMove:!1,afterAction:!1,startDragging:!1,afterLazyLoad:!1}})(jQuery,window,document); \ No newline at end of file diff --git a/public/js/plugins.js b/public/js/plugins.js new file mode 100755 index 00000000..f8874802 --- /dev/null +++ b/public/js/plugins.js @@ -0,0 +1,24 @@ +// Avoid `console` errors in browsers that lack a console. +(function() { + var method; + var noop = function () {}; + var methods = [ + 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', + 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', + 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', + 'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn' + ]; + var length = methods.length; + var console = (window.console = window.console || {}); + + while (length--) { + method = methods[length]; + + // Only stub undefined methods. + if (!console[method]) { + console[method] = noop; + } + } +}()); + +// Place any jQuery/helper plugins in here. diff --git a/public/js/price_slider.js b/public/js/price_slider.js new file mode 100755 index 00000000..ce8b2491 --- /dev/null +++ b/public/js/price_slider.js @@ -0,0 +1,16617 @@ +/*! jQuery UI - v1.11.4 - 2015-03-11 +* http://jqueryui.com +* Includes: core.js, widget.js, mouse.js, position.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, draggable.js, droppable.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, progressbar.js, resizable.js, selectable.js, selectmenu.js, slider.js, sortable.js, spinner.js, tabs.js, tooltip.js +* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ + +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + } else { + + // Browser globals + factory( jQuery ); + } +}(function( $ ) { +/*! + * jQuery UI Core 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/ui-core/ + */ + + +// $.ui might exist from components with no dependencies, e.g., $.ui.position +$.ui = $.ui || {}; + +$.extend( $.ui, { + version: "1.11.4", + + keyCode: { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 + } +}); + +// plugins +$.fn.extend({ + scrollParent: function( includeHidden ) { + var position = this.css( "position" ), + excludeStaticParent = position === "absolute", + overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/, + scrollParent = this.parents().filter( function() { + var parent = $( this ); + if ( excludeStaticParent && parent.css( "position" ) === "static" ) { + return false; + } + return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + parent.css( "overflow-x" ) ); + }).eq( 0 ); + + return position === "fixed" || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent; + }, + + uniqueId: (function() { + var uuid = 0; + + return function() { + return this.each(function() { + if ( !this.id ) { + this.id = "ui-id-" + ( ++uuid ); + } + }); + }; + })(), + + removeUniqueId: function() { + return this.each(function() { + if ( /^ui-id-\d+$/.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + }); + } +}); + +// selectors +function focusable( element, isTabIndexNotNaN ) { + var map, mapName, img, + nodeName = element.nodeName.toLowerCase(); + if ( "area" === nodeName ) { + map = element.parentNode; + mapName = map.name; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap='#" + mapName + "']" )[ 0 ]; + return !!img && visible( img ); + } + return ( /^(input|select|textarea|button|object)$/.test( nodeName ) ? + !element.disabled : + "a" === nodeName ? + element.href || isTabIndexNotNaN : + isTabIndexNotNaN) && + // the element and all of its ancestors must be visible + visible( element ); +} + +function visible( element ) { + return $.expr.filters.visible( element ) && + !$( element ).parents().addBack().filter(function() { + return $.css( this, "visibility" ) === "hidden"; + }).length; +} + +$.extend( $.expr[ ":" ], { + data: $.expr.createPseudo ? + $.expr.createPseudo(function( dataName ) { + return function( elem ) { + return !!$.data( elem, dataName ); + }; + }) : + // support: jQuery <1.8 + function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + }, + + focusable: function( element ) { + return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); + }, + + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ), + isTabIndexNaN = isNaN( tabIndex ); + return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); + } +}); + +// support: jQuery <1.8 +if ( !$( "" ).outerWidth( 1 ).jquery ) { + $.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; + if ( border ) { + size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; + }); +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) +if ( $( "" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { + $.fn.removeData = (function( removeData ) { + return function( key ) { + if ( arguments.length ) { + return removeData.call( this, $.camelCase( key ) ); + } else { + return removeData.call( this ); + } + }; + })( $.fn.removeData ); +} + +// deprecated +$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +$.fn.extend({ + focus: (function( orig ) { + return function( delay, fn ) { + return typeof delay === "number" ? + this.each(function() { + var elem = this; + setTimeout(function() { + $( elem ).focus(); + if ( fn ) { + fn.call( elem ); + } + }, delay ); + }) : + orig.apply( this, arguments ); + }; + })( $.fn.focus ), + + disableSelection: (function() { + var eventType = "onselectstart" in document.createElement( "div" ) ? + "selectstart" : + "mousedown"; + + return function() { + return this.bind( eventType + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }; + })(), + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + }, + + zIndex: function( zIndex ) { + if ( zIndex !== undefined ) { + return this.css( "zIndex", zIndex ); + } + + if ( this.length ) { + var elem = $( this[ 0 ] ), position, value; + while ( elem.length && elem[ 0 ] !== document ) { + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + //
+ value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + } +}); + +// $.ui.plugin is deprecated. Use $.widget() extensions instead. +$.ui.plugin = { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args, allowDisconnected ) { + var i, + set = instance.plugins[ name ]; + + if ( !set ) { + return; + } + + if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } +}; + + +/*! + * jQuery UI Widget 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/jQuery.widget/ + */ + + +var widget_uuid = 0, + widget_slice = Array.prototype.slice; + +$.cleanData = (function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; (elem = elems[i]) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // http://bugs.jquery.com/ticket/8235 + } catch ( e ) {} + } + orig( elems ); + }; +})( $.cleanData ); + +$.widget = function( name, base, prototype ) { + var fullName, existingConstructor, constructor, basePrototype, + // proxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + proxiedPrototype = {}, + namespace = name.split( "." )[ 0 ]; + + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + // extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + // copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + // track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }); + + basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); + }); + constructor.prototype = $.widget.extend( basePrototype, { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + }); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); + }); + // remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); + + return constructor; +}; + +$.widget.extend = function( target ) { + var input = widget_slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = widget_slice.call( arguments, 1 ), + returnValue = this; + + if ( isMethodCall ) { + this.each(function() { + var methodValue, + instance = $.data( this, fullName ); + if ( options === "instance" ) { + returnValue = instance; + return false; + } + if ( !instance ) { + return $.error( "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + " widget instance" ); + } + methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + }); + } else { + + // Allow multiple hashes to be passed on init + if ( args.length ) { + options = $.widget.extend.apply( null, [ options ].concat(args) ); + } + + this.each(function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } + } else { + $.data( this, fullName, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "
", + options: { + disabled: false, + + // callbacks + create: null + }, + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = widget_uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } + + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this._create(); + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, + + destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .unbind( this.eventNamespace ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); + this.widget() + .unbind( this.eventNamespace ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetFullName + "-disabled " + + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + }, + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + parts, + curOption, + i; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + .toggleClass( this.widgetFullName + "-disabled", !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } + } + + return this; + }, + + enable: function() { + return this._setOptions({ disabled: false }); + }, + disable: function() { + return this._setOptions({ disabled: true }); + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement, + instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^([\w:-]*)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $( this.bindings.not( element ).get() ); + this.focusable = $( this.focusable.not( element ).get() ); + this.hoverable = $( this.hoverable.not( element ).get() ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +var widget = $.widget; + + +/*! + * jQuery UI Mouse 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/mouse/ + */ + + +var mouseHandled = false; +$( document ).mouseup( function() { + mouseHandled = false; +}); + +var mouse = $.widget("ui.mouse", { + version: "1.11.4", + options: { + cancel: "input,textarea,button,select,option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .bind("mousedown." + this.widgetName, function(event) { + return that._mouseDown(event); + }) + .bind("click." + this.widgetName, function(event) { + if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { + $.removeData(event.target, that.widgetName + ".preventClickEvent"); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind("." + this.widgetName); + if ( this._mouseMoveDelegate ) { + this.document + .unbind("mousemove." + this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup." + this.widgetName, this._mouseUpDelegate); + } + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if ( mouseHandled ) { + return; + } + + this._mouseMoved = false; + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = (event.which === 1), + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + that.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { + $.removeData(event.target, this.widgetName + ".preventClickEvent"); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return that._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return that._mouseUp(event); + }; + + this.document + .bind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .bind( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // Only check for mouseups outside the document if you've moved inside the document + // at least once. This prevents the firing of mouseup in the case of IE<9, which will + // fire a mousemove event if content is placed under the cursor. See #7778 + // Support: IE <9 + if ( this._mouseMoved ) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { + return this._mouseUp(event); + + // Iframe mouseup check - mouseup occurred in another document + } else if ( !event.which ) { + return this._mouseUp( event ); + } + } + + if ( event.which || event.button ) { + this._mouseMoved = true; + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + this.document + .unbind( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .unbind( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target === this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + ".preventClickEvent", true); + } + + this._mouseStop(event); + } + + mouseHandled = false; + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(/* event */) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(/* event */) {}, + _mouseDrag: function(/* event */) {}, + _mouseStop: function(/* event */) {}, + _mouseCapture: function(/* event */) { return true; } +}); + + +/*! + * jQuery UI Position 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/position/ + */ + +(function() { + +$.ui = $.ui || {}; + +var cachedScrollbarWidth, supportsOffsetFractions, + max = Math.max, + abs = Math.abs, + round = Math.round, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +function getOffsets( offsets, width, height ) { + return [ + parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[0]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "
" ), + innerDiv = div.children()[0]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[0].clientWidth; + } + + div.remove(); + + return (cachedScrollbarWidth = w1 - w2); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-x" ), + overflowY = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[0].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[0].scrollHeight ); + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[0] ), + isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9; + return { + element: withinElement, + isWindow: isWindow, + isDocument: isDocument, + offset: withinElement.offset() || { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + + // support: jQuery 1.6.x + // jQuery 1.6 doesn't support .outerWidth/Height() on documents or windows + width: isWindow || isDocument ? withinElement.width() : withinElement.outerWidth(), + height: isWindow || isDocument ? withinElement.height() : withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[0].preventDefault ) { + // force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + // clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + }); + + // normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each(function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + // if the browser doesn't support fractions, then round for consistent results + if ( !supportsOffsetFractions ) { + position.left = round( position.left ); + position.top = round( position.top ); + } + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem: elem + }); + } + }); + + if ( options.using ) { + // adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + }); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // element is wider than within + if ( data.collisionWidth > outerWidth ) { + // element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; + position.left += overLeft - newOverRight; + // element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + // element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + // too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + // too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + // adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // element is taller than within + if ( data.collisionHeight > outerHeight ) { + // element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; + position.top += overTop - newOverBottom; + // element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + // element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + // too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + // too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + // adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; + if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { + position.top += myOffset + atOffset + offset; + } + } else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; + if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +// fraction support test +(function() { + var testElement, testElementParent, testElementStyle, offsetLeft, i, + body = document.getElementsByTagName( "body" )[ 0 ], + div = document.createElement( "div" ); + + //Create a "fake body" for testing based on method used in jQuery.support + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + background: "none" + }; + if ( body ) { + $.extend( testElementStyle, { + position: "absolute", + left: "-1000px", + top: "-1000px" + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || document.documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + div.style.cssText = "position: absolute; left: 10.7432222px;"; + + offsetLeft = $( div ).offset().left; + supportsOffsetFractions = offsetLeft > 10 && offsetLeft < 11; + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); +})(); + +})(); + +var position = $.ui.position; + + +/*! + * jQuery UI Accordion 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/accordion/ + */ + + +var accordion = $.widget( "ui.accordion", { + version: "1.11.4", + options: { + active: 0, + animate: {}, + collapsible: false, + event: "click", + header: "> li > :first-child,> :not(li):even", + heightStyle: "auto", + icons: { + activeHeader: "ui-icon-triangle-1-s", + header: "ui-icon-triangle-1-e" + }, + + // callbacks + activate: null, + beforeActivate: null + }, + + hideProps: { + borderTopWidth: "hide", + borderBottomWidth: "hide", + paddingTop: "hide", + paddingBottom: "hide", + height: "hide" + }, + + showProps: { + borderTopWidth: "show", + borderBottomWidth: "show", + paddingTop: "show", + paddingBottom: "show", + height: "show" + }, + + _create: function() { + var options = this.options; + this.prevShow = this.prevHide = $(); + this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ) + // ARIA + .attr( "role", "tablist" ); + + // don't allow collapsible: false and active: false / null + if ( !options.collapsible && (options.active === false || options.active == null) ) { + options.active = 0; + } + + this._processPanels(); + // handle negative values + if ( options.active < 0 ) { + options.active += this.headers.length; + } + this._refresh(); + }, + + _getCreateEventData: function() { + return { + header: this.active, + panel: !this.active.length ? $() : this.active.next() + }; + }, + + _createIcons: function() { + var icons = this.options.icons; + if ( icons ) { + $( "" ) + .addClass( "ui-accordion-header-icon ui-icon " + icons.header ) + .prependTo( this.headers ); + this.active.children( ".ui-accordion-header-icon" ) + .removeClass( icons.header ) + .addClass( icons.activeHeader ); + this.headers.addClass( "ui-accordion-icons" ); + } + }, + + _destroyIcons: function() { + this.headers + .removeClass( "ui-accordion-icons" ) + .children( ".ui-accordion-header-icon" ) + .remove(); + }, + + _destroy: function() { + var contents; + + // clean up main element + this.element + .removeClass( "ui-accordion ui-widget ui-helper-reset" ) + .removeAttr( "role" ); + + // clean up headers + this.headers + .removeClass( "ui-accordion-header ui-accordion-header-active ui-state-default " + + "ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) + .removeAttr( "role" ) + .removeAttr( "aria-expanded" ) + .removeAttr( "aria-selected" ) + .removeAttr( "aria-controls" ) + .removeAttr( "tabIndex" ) + .removeUniqueId(); + + this._destroyIcons(); + + // clean up content panels + contents = this.headers.next() + .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom " + + "ui-accordion-content ui-accordion-content-active ui-state-disabled" ) + .css( "display", "" ) + .removeAttr( "role" ) + .removeAttr( "aria-hidden" ) + .removeAttr( "aria-labelledby" ) + .removeUniqueId(); + + if ( this.options.heightStyle !== "content" ) { + contents.css( "height", "" ); + } + }, + + _setOption: function( key, value ) { + if ( key === "active" ) { + // _activate() will handle invalid values and update this.options + this._activate( value ); + return; + } + + if ( key === "event" ) { + if ( this.options.event ) { + this._off( this.headers, this.options.event ); + } + this._setupEvents( value ); + } + + this._super( key, value ); + + // setting collapsible: false while collapsed; open first panel + if ( key === "collapsible" && !value && this.options.active === false ) { + this._activate( 0 ); + } + + if ( key === "icons" ) { + this._destroyIcons(); + if ( value ) { + this._createIcons(); + } + } + + // #5332 - opacity doesn't cascade to positioned elements in IE + // so we need to add the disabled class to the headers and panels + if ( key === "disabled" ) { + this.element + .toggleClass( "ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + this.headers.add( this.headers.next() ) + .toggleClass( "ui-state-disabled", !!value ); + } + }, + + _keydown: function( event ) { + if ( event.altKey || event.ctrlKey ) { + return; + } + + var keyCode = $.ui.keyCode, + length = this.headers.length, + currentIndex = this.headers.index( event.target ), + toFocus = false; + + switch ( event.keyCode ) { + case keyCode.RIGHT: + case keyCode.DOWN: + toFocus = this.headers[ ( currentIndex + 1 ) % length ]; + break; + case keyCode.LEFT: + case keyCode.UP: + toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; + break; + case keyCode.SPACE: + case keyCode.ENTER: + this._eventHandler( event ); + break; + case keyCode.HOME: + toFocus = this.headers[ 0 ]; + break; + case keyCode.END: + toFocus = this.headers[ length - 1 ]; + break; + } + + if ( toFocus ) { + $( event.target ).attr( "tabIndex", -1 ); + $( toFocus ).attr( "tabIndex", 0 ); + toFocus.focus(); + event.preventDefault(); + } + }, + + _panelKeyDown: function( event ) { + if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) { + $( event.currentTarget ).prev().focus(); + } + }, + + refresh: function() { + var options = this.options; + this._processPanels(); + + // was collapsed or no panel + if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) { + options.active = false; + this.active = $(); + // active false only when collapsible is true + } else if ( options.active === false ) { + this._activate( 0 ); + // was active, but active panel is gone + } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + // all remaining panel are disabled + if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) { + options.active = false; + this.active = $(); + // activate previous panel + } else { + this._activate( Math.max( 0, options.active - 1 ) ); + } + // was active, active panel still exists + } else { + // make sure active index is correct + options.active = this.headers.index( this.active ); + } + + this._destroyIcons(); + + this._refresh(); + }, + + _processPanels: function() { + var prevHeaders = this.headers, + prevPanels = this.panels; + + this.headers = this.element.find( this.options.header ) + .addClass( "ui-accordion-header ui-state-default ui-corner-all" ); + + this.panels = this.headers.next() + .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ) + .filter( ":not(.ui-accordion-content-active)" ) + .hide(); + + // Avoid memory leaks (#10056) + if ( prevPanels ) { + this._off( prevHeaders.not( this.headers ) ); + this._off( prevPanels.not( this.panels ) ); + } + }, + + _refresh: function() { + var maxHeight, + options = this.options, + heightStyle = options.heightStyle, + parent = this.element.parent(); + + this.active = this._findActive( options.active ) + .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ) + .removeClass( "ui-corner-all" ); + this.active.next() + .addClass( "ui-accordion-content-active" ) + .show(); + + this.headers + .attr( "role", "tab" ) + .each(function() { + var header = $( this ), + headerId = header.uniqueId().attr( "id" ), + panel = header.next(), + panelId = panel.uniqueId().attr( "id" ); + header.attr( "aria-controls", panelId ); + panel.attr( "aria-labelledby", headerId ); + }) + .next() + .attr( "role", "tabpanel" ); + + this.headers + .not( this.active ) + .attr({ + "aria-selected": "false", + "aria-expanded": "false", + tabIndex: -1 + }) + .next() + .attr({ + "aria-hidden": "true" + }) + .hide(); + + // make sure at least one header is in the tab order + if ( !this.active.length ) { + this.headers.eq( 0 ).attr( "tabIndex", 0 ); + } else { + this.active.attr({ + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + }) + .next() + .attr({ + "aria-hidden": "false" + }); + } + + this._createIcons(); + + this._setupEvents( options.event ); + + if ( heightStyle === "fill" ) { + maxHeight = parent.height(); + this.element.siblings( ":visible" ).each(function() { + var elem = $( this ), + position = elem.css( "position" ); + + if ( position === "absolute" || position === "fixed" ) { + return; + } + maxHeight -= elem.outerHeight( true ); + }); + + this.headers.each(function() { + maxHeight -= $( this ).outerHeight( true ); + }); + + this.headers.next() + .each(function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + }) + .css( "overflow", "auto" ); + } else if ( heightStyle === "auto" ) { + maxHeight = 0; + this.headers.next() + .each(function() { + maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() ); + }) + .height( maxHeight ); + } + }, + + _activate: function( index ) { + var active = this._findActive( index )[ 0 ]; + + // trying to activate the already active panel + if ( active === this.active[ 0 ] ) { + return; + } + + // trying to collapse, simulate a click on the currently active header + active = active || this.active[ 0 ]; + + this._eventHandler({ + target: active, + currentTarget: active, + preventDefault: $.noop + }); + }, + + _findActive: function( selector ) { + return typeof selector === "number" ? this.headers.eq( selector ) : $(); + }, + + _setupEvents: function( event ) { + var events = { + keydown: "_keydown" + }; + if ( event ) { + $.each( event.split( " " ), function( index, eventName ) { + events[ eventName ] = "_eventHandler"; + }); + } + + this._off( this.headers.add( this.headers.next() ) ); + this._on( this.headers, events ); + this._on( this.headers.next(), { keydown: "_panelKeyDown" }); + this._hoverable( this.headers ); + this._focusable( this.headers ); + }, + + _eventHandler: function( event ) { + var options = this.options, + active = this.active, + clicked = $( event.currentTarget ), + clickedIsActive = clicked[ 0 ] === active[ 0 ], + collapsing = clickedIsActive && options.collapsible, + toShow = collapsing ? $() : clicked.next(), + toHide = active.next(), + eventData = { + oldHeader: active, + oldPanel: toHide, + newHeader: collapsing ? $() : clicked, + newPanel: toShow + }; + + event.preventDefault(); + + if ( + // click on active header, but not collapsible + ( clickedIsActive && !options.collapsible ) || + // allow canceling activation + ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { + return; + } + + options.active = collapsing ? false : this.headers.index( clicked ); + + // when the call to ._toggle() comes after the class changes + // it causes a very odd bug in IE 8 (see #6720) + this.active = clickedIsActive ? $() : clicked; + this._toggle( eventData ); + + // switch classes + // corner classes on the previously active header stay after the animation + active.removeClass( "ui-accordion-header-active ui-state-active" ); + if ( options.icons ) { + active.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.activeHeader ) + .addClass( options.icons.header ); + } + + if ( !clickedIsActive ) { + clicked + .removeClass( "ui-corner-all" ) + .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" ); + if ( options.icons ) { + clicked.children( ".ui-accordion-header-icon" ) + .removeClass( options.icons.header ) + .addClass( options.icons.activeHeader ); + } + + clicked + .next() + .addClass( "ui-accordion-content-active" ); + } + }, + + _toggle: function( data ) { + var toShow = data.newPanel, + toHide = this.prevShow.length ? this.prevShow : data.oldPanel; + + // handle activating a panel during the animation for another activation + this.prevShow.add( this.prevHide ).stop( true, true ); + this.prevShow = toShow; + this.prevHide = toHide; + + if ( this.options.animate ) { + this._animate( toShow, toHide, data ); + } else { + toHide.hide(); + toShow.show(); + this._toggleComplete( data ); + } + + toHide.attr({ + "aria-hidden": "true" + }); + toHide.prev().attr({ + "aria-selected": "false", + "aria-expanded": "false" + }); + // if we're switching panels, remove the old header from the tab order + // if we're opening from collapsed state, remove the previous header from the tab order + // if we're collapsing, then keep the collapsing header in the tab order + if ( toShow.length && toHide.length ) { + toHide.prev().attr({ + "tabIndex": -1, + "aria-expanded": "false" + }); + } else if ( toShow.length ) { + this.headers.filter(function() { + return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0; + }) + .attr( "tabIndex", -1 ); + } + + toShow + .attr( "aria-hidden", "false" ) + .prev() + .attr({ + "aria-selected": "true", + "aria-expanded": "true", + tabIndex: 0 + }); + }, + + _animate: function( toShow, toHide, data ) { + var total, easing, duration, + that = this, + adjust = 0, + boxSizing = toShow.css( "box-sizing" ), + down = toShow.length && + ( !toHide.length || ( toShow.index() < toHide.index() ) ), + animate = this.options.animate || {}, + options = down && animate.down || animate, + complete = function() { + that._toggleComplete( data ); + }; + + if ( typeof options === "number" ) { + duration = options; + } + if ( typeof options === "string" ) { + easing = options; + } + // fall back from options to animation in case of partial down settings + easing = easing || options.easing || animate.easing; + duration = duration || options.duration || animate.duration; + + if ( !toHide.length ) { + return toShow.animate( this.showProps, duration, easing, complete ); + } + if ( !toShow.length ) { + return toHide.animate( this.hideProps, duration, easing, complete ); + } + + total = toShow.show().outerHeight(); + toHide.animate( this.hideProps, { + duration: duration, + easing: easing, + step: function( now, fx ) { + fx.now = Math.round( now ); + } + }); + toShow + .hide() + .animate( this.showProps, { + duration: duration, + easing: easing, + complete: complete, + step: function( now, fx ) { + fx.now = Math.round( now ); + if ( fx.prop !== "height" ) { + if ( boxSizing === "content-box" ) { + adjust += fx.now; + } + } else if ( that.options.heightStyle !== "content" ) { + fx.now = Math.round( total - toHide.outerHeight() - adjust ); + adjust = 0; + } + } + }); + }, + + _toggleComplete: function( data ) { + var toHide = data.oldPanel; + + toHide + .removeClass( "ui-accordion-content-active" ) + .prev() + .removeClass( "ui-corner-top" ) + .addClass( "ui-corner-all" ); + + // Work around for rendering bug in IE (#5421) + if ( toHide.length ) { + toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className; + } + this._trigger( "activate", null, data ); + } +}); + + +/*! + * jQuery UI Menu 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/menu/ + */ + + +var menu = $.widget( "ui.menu", { + version: "1.11.4", + defaultElement: "
    ", + delay: 300, + options: { + icons: { + submenu: "ui-icon-carat-1-e" + }, + items: "> *", + menus: "ul", + position: { + my: "left-1 top", + at: "right top" + }, + role: "menu", + + // callbacks + blur: null, + focus: null, + select: null + }, + + _create: function() { + this.activeMenu = this.element; + + // Flag used to prevent firing of the click handler + // as the event bubbles up through nested menus + this.mouseHandled = false; + this.element + .uniqueId() + .addClass( "ui-menu ui-widget ui-widget-content" ) + .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ) + .attr({ + role: this.options.role, + tabIndex: 0 + }); + + if ( this.options.disabled ) { + this.element + .addClass( "ui-state-disabled" ) + .attr( "aria-disabled", "true" ); + } + + this._on({ + // Prevent focus from sticking to links inside menu after clicking + // them (focus should always stay on UL during navigation). + "mousedown .ui-menu-item": function( event ) { + event.preventDefault(); + }, + "click .ui-menu-item": function( event ) { + var target = $( event.target ); + if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) { + this.select( event ); + + // Only set the mouseHandled flag if the event will bubble, see #9469. + if ( !event.isPropagationStopped() ) { + this.mouseHandled = true; + } + + // Open submenu on click + if ( target.has( ".ui-menu" ).length ) { + this.expand( event ); + } else if ( !this.element.is( ":focus" ) && $( this.document[ 0 ].activeElement ).closest( ".ui-menu" ).length ) { + + // Redirect focus to the menu + this.element.trigger( "focus", [ true ] ); + + // If the active item is on the top level, let it stay active. + // Otherwise, blur the active item since it is no longer visible. + if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) { + clearTimeout( this.timer ); + } + } + } + }, + "mouseenter .ui-menu-item": function( event ) { + // Ignore mouse events while typeahead is active, see #10458. + // Prevents focusing the wrong item when typeahead causes a scroll while the mouse + // is over an item in the menu + if ( this.previousFilter ) { + return; + } + var target = $( event.currentTarget ); + // Remove ui-state-active class from siblings of the newly focused menu item + // to avoid a jump caused by adjacent elements both having a class with a border + target.siblings( ".ui-state-active" ).removeClass( "ui-state-active" ); + this.focus( event, target ); + }, + mouseleave: "collapseAll", + "mouseleave .ui-menu": "collapseAll", + focus: function( event, keepActiveItem ) { + // If there's already an active item, keep it active + // If not, activate the first item + var item = this.active || this.element.find( this.options.items ).eq( 0 ); + + if ( !keepActiveItem ) { + this.focus( event, item ); + } + }, + blur: function( event ) { + this._delay(function() { + if ( !$.contains( this.element[0], this.document[0].activeElement ) ) { + this.collapseAll( event ); + } + }); + }, + keydown: "_keydown" + }); + + this.refresh(); + + // Clicks outside of a menu collapse any open menus + this._on( this.document, { + click: function( event ) { + if ( this._closeOnDocumentClick( event ) ) { + this.collapseAll( event ); + } + + // Reset the mouseHandled flag + this.mouseHandled = false; + } + }); + }, + + _destroy: function() { + // Destroy (sub)menus + this.element + .removeAttr( "aria-activedescendant" ) + .find( ".ui-menu" ).addBack() + .removeClass( "ui-menu ui-widget ui-widget-content ui-menu-icons ui-front" ) + .removeAttr( "role" ) + .removeAttr( "tabIndex" ) + .removeAttr( "aria-labelledby" ) + .removeAttr( "aria-expanded" ) + .removeAttr( "aria-hidden" ) + .removeAttr( "aria-disabled" ) + .removeUniqueId() + .show(); + + // Destroy menu items + this.element.find( ".ui-menu-item" ) + .removeClass( "ui-menu-item" ) + .removeAttr( "role" ) + .removeAttr( "aria-disabled" ) + .removeUniqueId() + .removeClass( "ui-state-hover" ) + .removeAttr( "tabIndex" ) + .removeAttr( "role" ) + .removeAttr( "aria-haspopup" ) + .children().each( function() { + var elem = $( this ); + if ( elem.data( "ui-menu-submenu-carat" ) ) { + elem.remove(); + } + }); + + // Destroy menu dividers + this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" ); + }, + + _keydown: function( event ) { + var match, prev, character, skip, + preventDefault = true; + + switch ( event.keyCode ) { + case $.ui.keyCode.PAGE_UP: + this.previousPage( event ); + break; + case $.ui.keyCode.PAGE_DOWN: + this.nextPage( event ); + break; + case $.ui.keyCode.HOME: + this._move( "first", "first", event ); + break; + case $.ui.keyCode.END: + this._move( "last", "last", event ); + break; + case $.ui.keyCode.UP: + this.previous( event ); + break; + case $.ui.keyCode.DOWN: + this.next( event ); + break; + case $.ui.keyCode.LEFT: + this.collapse( event ); + break; + case $.ui.keyCode.RIGHT: + if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { + this.expand( event ); + } + break; + case $.ui.keyCode.ENTER: + case $.ui.keyCode.SPACE: + this._activate( event ); + break; + case $.ui.keyCode.ESCAPE: + this.collapse( event ); + break; + default: + preventDefault = false; + prev = this.previousFilter || ""; + character = String.fromCharCode( event.keyCode ); + skip = false; + + clearTimeout( this.filterTimer ); + + if ( character === prev ) { + skip = true; + } else { + character = prev + character; + } + + match = this._filterMenuItems( character ); + match = skip && match.index( this.active.next() ) !== -1 ? + this.active.nextAll( ".ui-menu-item" ) : + match; + + // If no matches on the current filter, reset to the last character pressed + // to move down the menu to the first item that starts with that character + if ( !match.length ) { + character = String.fromCharCode( event.keyCode ); + match = this._filterMenuItems( character ); + } + + if ( match.length ) { + this.focus( event, match ); + this.previousFilter = character; + this.filterTimer = this._delay(function() { + delete this.previousFilter; + }, 1000 ); + } else { + delete this.previousFilter; + } + } + + if ( preventDefault ) { + event.preventDefault(); + } + }, + + _activate: function( event ) { + if ( !this.active.is( ".ui-state-disabled" ) ) { + if ( this.active.is( "[aria-haspopup='true']" ) ) { + this.expand( event ); + } else { + this.select( event ); + } + } + }, + + refresh: function() { + var menus, items, + that = this, + icon = this.options.icons.submenu, + submenus = this.element.find( this.options.menus ); + + this.element.toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ); + + // Initialize nested menus + submenus.filter( ":not(.ui-menu)" ) + .addClass( "ui-menu ui-widget ui-widget-content ui-front" ) + .hide() + .attr({ + role: this.options.role, + "aria-hidden": "true", + "aria-expanded": "false" + }) + .each(function() { + var menu = $( this ), + item = menu.parent(), + submenuCarat = $( "" ) + .addClass( "ui-menu-icon ui-icon " + icon ) + .data( "ui-menu-submenu-carat", true ); + + item + .attr( "aria-haspopup", "true" ) + .prepend( submenuCarat ); + menu.attr( "aria-labelledby", item.attr( "id" ) ); + }); + + menus = submenus.add( this.element ); + items = menus.find( this.options.items ); + + // Initialize menu-items containing spaces and/or dashes only as dividers + items.not( ".ui-menu-item" ).each(function() { + var item = $( this ); + if ( that._isDivider( item ) ) { + item.addClass( "ui-widget-content ui-menu-divider" ); + } + }); + + // Don't refresh list items that are already adapted + items.not( ".ui-menu-item, .ui-menu-divider" ) + .addClass( "ui-menu-item" ) + .uniqueId() + .attr({ + tabIndex: -1, + role: this._itemRole() + }); + + // Add aria-disabled attribute to any disabled menu item + items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); + + // If the active item has been removed, blur the menu + if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + this.blur(); + } + }, + + _itemRole: function() { + return { + menu: "menuitem", + listbox: "option" + }[ this.options.role ]; + }, + + _setOption: function( key, value ) { + if ( key === "icons" ) { + this.element.find( ".ui-menu-icon" ) + .removeClass( this.options.icons.submenu ) + .addClass( value.submenu ); + } + if ( key === "disabled" ) { + this.element + .toggleClass( "ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + } + this._super( key, value ); + }, + + focus: function( event, item ) { + var nested, focused; + this.blur( event, event && event.type === "focus" ); + + this._scrollIntoView( item ); + + this.active = item.first(); + focused = this.active.addClass( "ui-state-focus" ).removeClass( "ui-state-active" ); + // Only update aria-activedescendant if there's a role + // otherwise we assume focus is managed elsewhere + if ( this.options.role ) { + this.element.attr( "aria-activedescendant", focused.attr( "id" ) ); + } + + // Highlight active parent menu item, if any + this.active + .parent() + .closest( ".ui-menu-item" ) + .addClass( "ui-state-active" ); + + if ( event && event.type === "keydown" ) { + this._close(); + } else { + this.timer = this._delay(function() { + this._close(); + }, this.delay ); + } + + nested = item.children( ".ui-menu" ); + if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) { + this._startOpening(nested); + } + this.activeMenu = item.parent(); + + this._trigger( "focus", event, { item: item } ); + }, + + _scrollIntoView: function( item ) { + var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight; + if ( this._hasScroll() ) { + borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0; + paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0; + offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop; + scroll = this.activeMenu.scrollTop(); + elementHeight = this.activeMenu.height(); + itemHeight = item.outerHeight(); + + if ( offset < 0 ) { + this.activeMenu.scrollTop( scroll + offset ); + } else if ( offset + itemHeight > elementHeight ) { + this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight ); + } + } + }, + + blur: function( event, fromFocus ) { + if ( !fromFocus ) { + clearTimeout( this.timer ); + } + + if ( !this.active ) { + return; + } + + this.active.removeClass( "ui-state-focus" ); + this.active = null; + + this._trigger( "blur", event, { item: this.active } ); + }, + + _startOpening: function( submenu ) { + clearTimeout( this.timer ); + + // Don't open if already open fixes a Firefox bug that caused a .5 pixel + // shift in the submenu position when mousing over the carat icon + if ( submenu.attr( "aria-hidden" ) !== "true" ) { + return; + } + + this.timer = this._delay(function() { + this._close(); + this._open( submenu ); + }, this.delay ); + }, + + _open: function( submenu ) { + var position = $.extend({ + of: this.active + }, this.options.position ); + + clearTimeout( this.timer ); + this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) ) + .hide() + .attr( "aria-hidden", "true" ); + + submenu + .show() + .removeAttr( "aria-hidden" ) + .attr( "aria-expanded", "true" ) + .position( position ); + }, + + collapseAll: function( event, all ) { + clearTimeout( this.timer ); + this.timer = this._delay(function() { + // If we were passed an event, look for the submenu that contains the event + var currentMenu = all ? this.element : + $( event && event.target ).closest( this.element.find( ".ui-menu" ) ); + + // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway + if ( !currentMenu.length ) { + currentMenu = this.element; + } + + this._close( currentMenu ); + + this.blur( event ); + this.activeMenu = currentMenu; + }, this.delay ); + }, + + // With no arguments, closes the currently active menu - if nothing is active + // it closes all menus. If passed an argument, it will search for menus BELOW + _close: function( startMenu ) { + if ( !startMenu ) { + startMenu = this.active ? this.active.parent() : this.element; + } + + startMenu + .find( ".ui-menu" ) + .hide() + .attr( "aria-hidden", "true" ) + .attr( "aria-expanded", "false" ) + .end() + .find( ".ui-state-active" ).not( ".ui-state-focus" ) + .removeClass( "ui-state-active" ); + }, + + _closeOnDocumentClick: function( event ) { + return !$( event.target ).closest( ".ui-menu" ).length; + }, + + _isDivider: function( item ) { + + // Match hyphen, em dash, en dash + return !/[^\-\u2014\u2013\s]/.test( item.text() ); + }, + + collapse: function( event ) { + var newItem = this.active && + this.active.parent().closest( ".ui-menu-item", this.element ); + if ( newItem && newItem.length ) { + this._close(); + this.focus( event, newItem ); + } + }, + + expand: function( event ) { + var newItem = this.active && + this.active + .children( ".ui-menu " ) + .find( this.options.items ) + .first(); + + if ( newItem && newItem.length ) { + this._open( newItem.parent() ); + + // Delay so Firefox will not hide activedescendant change in expanding submenu from AT + this._delay(function() { + this.focus( event, newItem ); + }); + } + }, + + next: function( event ) { + this._move( "next", "first", event ); + }, + + previous: function( event ) { + this._move( "prev", "last", event ); + }, + + isFirstItem: function() { + return this.active && !this.active.prevAll( ".ui-menu-item" ).length; + }, + + isLastItem: function() { + return this.active && !this.active.nextAll( ".ui-menu-item" ).length; + }, + + _move: function( direction, filter, event ) { + var next; + if ( this.active ) { + if ( direction === "first" || direction === "last" ) { + next = this.active + [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" ) + .eq( -1 ); + } else { + next = this.active + [ direction + "All" ]( ".ui-menu-item" ) + .eq( 0 ); + } + } + if ( !next || !next.length || !this.active ) { + next = this.activeMenu.find( this.options.items )[ filter ](); + } + + this.focus( event, next ); + }, + + nextPage: function( event ) { + var item, base, height; + + if ( !this.active ) { + this.next( event ); + return; + } + if ( this.isLastItem() ) { + return; + } + if ( this._hasScroll() ) { + base = this.active.offset().top; + height = this.element.height(); + this.active.nextAll( ".ui-menu-item" ).each(function() { + item = $( this ); + return item.offset().top - base - height < 0; + }); + + this.focus( event, item ); + } else { + this.focus( event, this.activeMenu.find( this.options.items ) + [ !this.active ? "first" : "last" ]() ); + } + }, + + previousPage: function( event ) { + var item, base, height; + if ( !this.active ) { + this.next( event ); + return; + } + if ( this.isFirstItem() ) { + return; + } + if ( this._hasScroll() ) { + base = this.active.offset().top; + height = this.element.height(); + this.active.prevAll( ".ui-menu-item" ).each(function() { + item = $( this ); + return item.offset().top - base + height > 0; + }); + + this.focus( event, item ); + } else { + this.focus( event, this.activeMenu.find( this.options.items ).first() ); + } + }, + + _hasScroll: function() { + return this.element.outerHeight() < this.element.prop( "scrollHeight" ); + }, + + select: function( event ) { + // TODO: It should never be possible to not have an active item at this + // point, but the tests don't trigger mouseenter before click. + this.active = this.active || $( event.target ).closest( ".ui-menu-item" ); + var ui = { item: this.active }; + if ( !this.active.has( ".ui-menu" ).length ) { + this.collapseAll( event, true ); + } + this._trigger( "select", event, ui ); + }, + + _filterMenuItems: function(character) { + var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ), + regex = new RegExp( "^" + escapedCharacter, "i" ); + + return this.activeMenu + .find( this.options.items ) + + // Only match on items, not dividers or other content (#10571) + .filter( ".ui-menu-item" ) + .filter(function() { + return regex.test( $.trim( $( this ).text() ) ); + }); + } +}); + + +/*! + * jQuery UI Autocomplete 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/autocomplete/ + */ + + +$.widget( "ui.autocomplete", { + version: "1.11.4", + defaultElement: "", + options: { + appendTo: null, + autoFocus: false, + delay: 300, + minLength: 1, + position: { + my: "left top", + at: "left bottom", + collision: "none" + }, + source: null, + + // callbacks + change: null, + close: null, + focus: null, + open: null, + response: null, + search: null, + select: null + }, + + requestIndex: 0, + pending: 0, + + _create: function() { + // Some browsers only repeat keydown events, not keypress events, + // so we use the suppressKeyPress flag to determine if we've already + // handled the keydown event. #7269 + // Unfortunately the code for & in keypress is the same as the up arrow, + // so we use the suppressKeyPressRepeat flag to avoid handling keypress + // events when we know the keydown event was used to modify the + // search term. #7799 + var suppressKeyPress, suppressKeyPressRepeat, suppressInput, + nodeName = this.element[ 0 ].nodeName.toLowerCase(), + isTextarea = nodeName === "textarea", + isInput = nodeName === "input"; + + this.isMultiLine = + // Textareas are always multi-line + isTextarea ? true : + // Inputs are always single-line, even if inside a contentEditable element + // IE also treats inputs as contentEditable + isInput ? false : + // All other element types are determined by whether or not they're contentEditable + this.element.prop( "isContentEditable" ); + + this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; + this.isNewMenu = true; + + this.element + .addClass( "ui-autocomplete-input" ) + .attr( "autocomplete", "off" ); + + this._on( this.element, { + keydown: function( event ) { + if ( this.element.prop( "readOnly" ) ) { + suppressKeyPress = true; + suppressInput = true; + suppressKeyPressRepeat = true; + return; + } + + suppressKeyPress = false; + suppressInput = false; + suppressKeyPressRepeat = false; + var keyCode = $.ui.keyCode; + switch ( event.keyCode ) { + case keyCode.PAGE_UP: + suppressKeyPress = true; + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + suppressKeyPress = true; + this._move( "nextPage", event ); + break; + case keyCode.UP: + suppressKeyPress = true; + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + suppressKeyPress = true; + this._keyEvent( "next", event ); + break; + case keyCode.ENTER: + // when menu is open and has focus + if ( this.menu.active ) { + // #6055 - Opera still allows the keypress to occur + // which causes forms to submit + suppressKeyPress = true; + event.preventDefault(); + this.menu.select( event ); + } + break; + case keyCode.TAB: + if ( this.menu.active ) { + this.menu.select( event ); + } + break; + case keyCode.ESCAPE: + if ( this.menu.element.is( ":visible" ) ) { + if ( !this.isMultiLine ) { + this._value( this.term ); + } + this.close( event ); + // Different browsers have different default behavior for escape + // Single press can mean undo or clear + // Double press in IE means clear the whole form + event.preventDefault(); + } + break; + default: + suppressKeyPressRepeat = true; + // search timeout should be triggered before the input value is changed + this._searchTimeout( event ); + break; + } + }, + keypress: function( event ) { + if ( suppressKeyPress ) { + suppressKeyPress = false; + if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { + event.preventDefault(); + } + return; + } + if ( suppressKeyPressRepeat ) { + return; + } + + // replicate some key handlers to allow them to repeat in Firefox and Opera + var keyCode = $.ui.keyCode; + switch ( event.keyCode ) { + case keyCode.PAGE_UP: + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + this._move( "nextPage", event ); + break; + case keyCode.UP: + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + this._keyEvent( "next", event ); + break; + } + }, + input: function( event ) { + if ( suppressInput ) { + suppressInput = false; + event.preventDefault(); + return; + } + this._searchTimeout( event ); + }, + focus: function() { + this.selectedItem = null; + this.previous = this._value(); + }, + blur: function( event ) { + if ( this.cancelBlur ) { + delete this.cancelBlur; + return; + } + + clearTimeout( this.searching ); + this.close( event ); + this._change( event ); + } + }); + + this._initSource(); + this.menu = $( "
      " ) + .addClass( "ui-autocomplete ui-front" ) + .appendTo( this._appendTo() ) + .menu({ + // disable ARIA support, the live region takes care of that + role: null + }) + .hide() + .menu( "instance" ); + + this._on( this.menu.element, { + mousedown: function( event ) { + // prevent moving focus out of the text field + event.preventDefault(); + + // IE doesn't prevent moving focus even with event.preventDefault() + // so we set a flag to know when we should ignore the blur event + this.cancelBlur = true; + this._delay(function() { + delete this.cancelBlur; + }); + + // clicking on the scrollbar causes focus to shift to the body + // but we can't detect a mouseup or a click immediately afterward + // so we have to track the next mousedown and close the menu if + // the user clicks somewhere outside of the autocomplete + var menuElement = this.menu.element[ 0 ]; + if ( !$( event.target ).closest( ".ui-menu-item" ).length ) { + this._delay(function() { + var that = this; + this.document.one( "mousedown", function( event ) { + if ( event.target !== that.element[ 0 ] && + event.target !== menuElement && + !$.contains( menuElement, event.target ) ) { + that.close(); + } + }); + }); + } + }, + menufocus: function( event, ui ) { + var label, item; + // support: Firefox + // Prevent accidental activation of menu items in Firefox (#7024 #9118) + if ( this.isNewMenu ) { + this.isNewMenu = false; + if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) { + this.menu.blur(); + + this.document.one( "mousemove", function() { + $( event.target ).trigger( event.originalEvent ); + }); + + return; + } + } + + item = ui.item.data( "ui-autocomplete-item" ); + if ( false !== this._trigger( "focus", event, { item: item } ) ) { + // use value to match what will end up in the input, if it was a key event + if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) { + this._value( item.value ); + } + } + + // Announce the value in the liveRegion + label = ui.item.attr( "aria-label" ) || item.value; + if ( label && $.trim( label ).length ) { + this.liveRegion.children().hide(); + $( "
      " ).text( label ).appendTo( this.liveRegion ); + } + }, + menuselect: function( event, ui ) { + var item = ui.item.data( "ui-autocomplete-item" ), + previous = this.previous; + + // only trigger when focus was lost (click on menu) + if ( this.element[ 0 ] !== this.document[ 0 ].activeElement ) { + this.element.focus(); + this.previous = previous; + // #6109 - IE triggers two focus events and the second + // is asynchronous, so we need to reset the previous + // term synchronously and asynchronously :-( + this._delay(function() { + this.previous = previous; + this.selectedItem = item; + }); + } + + if ( false !== this._trigger( "select", event, { item: item } ) ) { + this._value( item.value ); + } + // reset the term after the select event + // this allows custom select handling to work properly + this.term = this._value(); + + this.close( event ); + this.selectedItem = item; + } + }); + + this.liveRegion = $( "", { + role: "status", + "aria-live": "assertive", + "aria-relevant": "additions" + }) + .addClass( "ui-helper-hidden-accessible" ) + .appendTo( this.document[ 0 ].body ); + + // turning off autocomplete prevents the browser from remembering the + // value when navigating through history, so we re-enable autocomplete + // if the page is unloaded before the widget is destroyed. #7790 + this._on( this.window, { + beforeunload: function() { + this.element.removeAttr( "autocomplete" ); + } + }); + }, + + _destroy: function() { + clearTimeout( this.searching ); + this.element + .removeClass( "ui-autocomplete-input" ) + .removeAttr( "autocomplete" ); + this.menu.element.remove(); + this.liveRegion.remove(); + }, + + _setOption: function( key, value ) { + this._super( key, value ); + if ( key === "source" ) { + this._initSource(); + } + if ( key === "appendTo" ) { + this.menu.element.appendTo( this._appendTo() ); + } + if ( key === "disabled" && value && this.xhr ) { + this.xhr.abort(); + } + }, + + _appendTo: function() { + var element = this.options.appendTo; + + if ( element ) { + element = element.jquery || element.nodeType ? + $( element ) : + this.document.find( element ).eq( 0 ); + } + + if ( !element || !element[ 0 ] ) { + element = this.element.closest( ".ui-front" ); + } + + if ( !element.length ) { + element = this.document[ 0 ].body; + } + + return element; + }, + + _initSource: function() { + var array, url, + that = this; + if ( $.isArray( this.options.source ) ) { + array = this.options.source; + this.source = function( request, response ) { + response( $.ui.autocomplete.filter( array, request.term ) ); + }; + } else if ( typeof this.options.source === "string" ) { + url = this.options.source; + this.source = function( request, response ) { + if ( that.xhr ) { + that.xhr.abort(); + } + that.xhr = $.ajax({ + url: url, + data: request, + dataType: "json", + success: function( data ) { + response( data ); + }, + error: function() { + response([]); + } + }); + }; + } else { + this.source = this.options.source; + } + }, + + _searchTimeout: function( event ) { + clearTimeout( this.searching ); + this.searching = this._delay(function() { + + // Search if the value has changed, or if the user retypes the same value (see #7434) + var equalValues = this.term === this._value(), + menuVisible = this.menu.element.is( ":visible" ), + modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; + + if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) { + this.selectedItem = null; + this.search( null, event ); + } + }, this.options.delay ); + }, + + search: function( value, event ) { + value = value != null ? value : this._value(); + + // always save the actual value, not the one passed as an argument + this.term = this._value(); + + if ( value.length < this.options.minLength ) { + return this.close( event ); + } + + if ( this._trigger( "search", event ) === false ) { + return; + } + + return this._search( value ); + }, + + _search: function( value ) { + this.pending++; + this.element.addClass( "ui-autocomplete-loading" ); + this.cancelSearch = false; + + this.source( { term: value }, this._response() ); + }, + + _response: function() { + var index = ++this.requestIndex; + + return $.proxy(function( content ) { + if ( index === this.requestIndex ) { + this.__response( content ); + } + + this.pending--; + if ( !this.pending ) { + this.element.removeClass( "ui-autocomplete-loading" ); + } + }, this ); + }, + + __response: function( content ) { + if ( content ) { + content = this._normalize( content ); + } + this._trigger( "response", null, { content: content } ); + if ( !this.options.disabled && content && content.length && !this.cancelSearch ) { + this._suggest( content ); + this._trigger( "open" ); + } else { + // use ._close() instead of .close() so we don't cancel future searches + this._close(); + } + }, + + close: function( event ) { + this.cancelSearch = true; + this._close( event ); + }, + + _close: function( event ) { + if ( this.menu.element.is( ":visible" ) ) { + this.menu.element.hide(); + this.menu.blur(); + this.isNewMenu = true; + this._trigger( "close", event ); + } + }, + + _change: function( event ) { + if ( this.previous !== this._value() ) { + this._trigger( "change", event, { item: this.selectedItem } ); + } + }, + + _normalize: function( items ) { + // assume all items have the right format when the first item is complete + if ( items.length && items[ 0 ].label && items[ 0 ].value ) { + return items; + } + return $.map( items, function( item ) { + if ( typeof item === "string" ) { + return { + label: item, + value: item + }; + } + return $.extend( {}, item, { + label: item.label || item.value, + value: item.value || item.label + }); + }); + }, + + _suggest: function( items ) { + var ul = this.menu.element.empty(); + this._renderMenu( ul, items ); + this.isNewMenu = true; + this.menu.refresh(); + + // size and position menu + ul.show(); + this._resizeMenu(); + ul.position( $.extend({ + of: this.element + }, this.options.position ) ); + + if ( this.options.autoFocus ) { + this.menu.next(); + } + }, + + _resizeMenu: function() { + var ul = this.menu.element; + ul.outerWidth( Math.max( + // Firefox wraps long text (possibly a rounding bug) + // so we add 1px to avoid the wrapping (#7513) + ul.width( "" ).outerWidth() + 1, + this.element.outerWidth() + ) ); + }, + + _renderMenu: function( ul, items ) { + var that = this; + $.each( items, function( index, item ) { + that._renderItemData( ul, item ); + }); + }, + + _renderItemData: function( ul, item ) { + return this._renderItem( ul, item ).data( "ui-autocomplete-item", item ); + }, + + _renderItem: function( ul, item ) { + return $( "
    • " ).text( item.label ).appendTo( ul ); + }, + + _move: function( direction, event ) { + if ( !this.menu.element.is( ":visible" ) ) { + this.search( null, event ); + return; + } + if ( this.menu.isFirstItem() && /^previous/.test( direction ) || + this.menu.isLastItem() && /^next/.test( direction ) ) { + + if ( !this.isMultiLine ) { + this._value( this.term ); + } + + this.menu.blur(); + return; + } + this.menu[ direction ]( event ); + }, + + widget: function() { + return this.menu.element; + }, + + _value: function() { + return this.valueMethod.apply( this.element, arguments ); + }, + + _keyEvent: function( keyEvent, event ) { + if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { + this._move( keyEvent, event ); + + // prevents moving cursor to beginning/end of the text field in some browsers + event.preventDefault(); + } + } +}); + +$.extend( $.ui.autocomplete, { + escapeRegex: function( value ) { + return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); + }, + filter: function( array, term ) { + var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" ); + return $.grep( array, function( value ) { + return matcher.test( value.label || value.value || value ); + }); + } +}); + +// live region extension, adding a `messages` option +// NOTE: This is an experimental API. We are still investigating +// a full solution for string manipulation and internationalization. +$.widget( "ui.autocomplete", $.ui.autocomplete, { + options: { + messages: { + noResults: "No search results.", + results: function( amount ) { + return amount + ( amount > 1 ? " results are" : " result is" ) + + " available, use up and down arrow keys to navigate."; + } + } + }, + + __response: function( content ) { + var message; + this._superApply( arguments ); + if ( this.options.disabled || this.cancelSearch ) { + return; + } + if ( content && content.length ) { + message = this.options.messages.results( content.length ); + } else { + message = this.options.messages.noResults; + } + this.liveRegion.children().hide(); + $( "
      " ).text( message ).appendTo( this.liveRegion ); + } +}); + +var autocomplete = $.ui.autocomplete; + + +/*! + * jQuery UI Button 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/button/ + */ + + +var lastActive, + baseClasses = "ui-button ui-widget ui-state-default ui-corner-all", + typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only", + formResetHandler = function() { + var form = $( this ); + setTimeout(function() { + form.find( ":ui-button" ).button( "refresh" ); + }, 1 ); + }, + radioGroup = function( radio ) { + var name = radio.name, + form = radio.form, + radios = $( [] ); + if ( name ) { + name = name.replace( /'/g, "\\'" ); + if ( form ) { + radios = $( form ).find( "[name='" + name + "'][type=radio]" ); + } else { + radios = $( "[name='" + name + "'][type=radio]", radio.ownerDocument ) + .filter(function() { + return !this.form; + }); + } + } + return radios; + }; + +$.widget( "ui.button", { + version: "1.11.4", + defaultElement: "").addClass(this._triggerClass). + html(!buttonImage ? buttonText : $("").attr( + { src:buttonImage, alt:buttonText, title:buttonText }))); + input[isRTL ? "before" : "after"](inst.trigger); + inst.trigger.click(function() { + if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) { + $.datepicker._hideDatepicker(); + } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) { + $.datepicker._hideDatepicker(); + $.datepicker._showDatepicker(input[0]); + } else { + $.datepicker._showDatepicker(input[0]); + } + return false; + }); + } + }, + + /* Apply the maximum length for the date format. */ + _autoSize: function(inst) { + if (this._get(inst, "autoSize") && !inst.inline) { + var findMax, max, maxI, i, + date = new Date(2009, 12 - 1, 20), // Ensure double digits + dateFormat = this._get(inst, "dateFormat"); + + if (dateFormat.match(/[DM]/)) { + findMax = function(names) { + max = 0; + maxI = 0; + for (i = 0; i < names.length; i++) { + if (names[i].length > max) { + max = names[i].length; + maxI = i; + } + } + return maxI; + }; + date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ? + "monthNames" : "monthNamesShort")))); + date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ? + "dayNames" : "dayNamesShort"))) + 20 - date.getDay()); + } + inst.input.attr("size", this._formatDate(inst, date).length); + } + }, + + /* Attach an inline date picker to a div. */ + _inlineDatepicker: function(target, inst) { + var divSpan = $(target); + if (divSpan.hasClass(this.markerClassName)) { + return; + } + divSpan.addClass(this.markerClassName).append(inst.dpDiv); + $.data(target, "datepicker", inst); + this._setDate(inst, this._getDefaultDate(inst), true); + this._updateDatepicker(inst); + this._updateAlternate(inst); + //If disabled option is true, disable the datepicker before showing it (see ticket #5665) + if( inst.settings.disabled ) { + this._disableDatepicker( target ); + } + // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements + // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height + inst.dpDiv.css( "display", "block" ); + }, + + /* Pop-up the date picker in a "dialog" box. + * @param input element - ignored + * @param date string or Date - the initial date to display + * @param onSelect function - the function to call when a date is selected + * @param settings object - update the dialog date picker instance's settings (anonymous object) + * @param pos int[2] - coordinates for the dialog's position within the screen or + * event - with x/y coordinates or + * leave empty for default (screen centre) + * @return the manager object + */ + _dialogDatepicker: function(input, date, onSelect, settings, pos) { + var id, browserWidth, browserHeight, scrollX, scrollY, + inst = this._dialogInst; // internal instance + + if (!inst) { + this.uuid += 1; + id = "dp" + this.uuid; + this._dialogInput = $(""); + this._dialogInput.keydown(this._doKeyDown); + $("body").append(this._dialogInput); + inst = this._dialogInst = this._newInst(this._dialogInput, false); + inst.settings = {}; + $.data(this._dialogInput[0], "datepicker", inst); + } + datepicker_extendRemove(inst.settings, settings || {}); + date = (date && date.constructor === Date ? this._formatDate(inst, date) : date); + this._dialogInput.val(date); + + this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); + if (!this._pos) { + browserWidth = document.documentElement.clientWidth; + browserHeight = document.documentElement.clientHeight; + scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + scrollY = document.documentElement.scrollTop || document.body.scrollTop; + this._pos = // should use actual width/height below + [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; + } + + // move input on screen for focus, but hidden behind dialog + this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px"); + inst.settings.onSelect = onSelect; + this._inDialog = true; + this.dpDiv.addClass(this._dialogClass); + this._showDatepicker(this._dialogInput[0]); + if ($.blockUI) { + $.blockUI(this.dpDiv); + } + $.data(this._dialogInput[0], "datepicker", inst); + return this; + }, + + /* Detach a datepicker from its control. + * @param target element - the target input field or division or span + */ + _destroyDatepicker: function(target) { + var nodeName, + $target = $(target), + inst = $.data(target, "datepicker"); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + $.removeData(target, "datepicker"); + if (nodeName === "input") { + inst.append.remove(); + inst.trigger.remove(); + $target.removeClass(this.markerClassName). + unbind("focus", this._showDatepicker). + unbind("keydown", this._doKeyDown). + unbind("keypress", this._doKeyPress). + unbind("keyup", this._doKeyUp); + } else if (nodeName === "div" || nodeName === "span") { + $target.removeClass(this.markerClassName).empty(); + } + + if ( datepicker_instActive === inst ) { + datepicker_instActive = null; + } + }, + + /* Enable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _enableDatepicker: function(target) { + var nodeName, inline, + $target = $(target), + inst = $.data(target, "datepicker"); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if (nodeName === "input") { + target.disabled = false; + inst.trigger.filter("button"). + each(function() { this.disabled = false; }).end(). + filter("img").css({opacity: "1.0", cursor: ""}); + } else if (nodeName === "div" || nodeName === "span") { + inline = $target.children("." + this._inlineClass); + inline.children().removeClass("ui-state-disabled"); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + prop("disabled", false); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === target ? null : value); }); // delete entry + }, + + /* Disable the date picker to a jQuery selection. + * @param target element - the target input field or division or span + */ + _disableDatepicker: function(target) { + var nodeName, inline, + $target = $(target), + inst = $.data(target, "datepicker"); + + if (!$target.hasClass(this.markerClassName)) { + return; + } + + nodeName = target.nodeName.toLowerCase(); + if (nodeName === "input") { + target.disabled = true; + inst.trigger.filter("button"). + each(function() { this.disabled = true; }).end(). + filter("img").css({opacity: "0.5", cursor: "default"}); + } else if (nodeName === "div" || nodeName === "span") { + inline = $target.children("." + this._inlineClass); + inline.children().addClass("ui-state-disabled"); + inline.find("select.ui-datepicker-month, select.ui-datepicker-year"). + prop("disabled", true); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === target ? null : value); }); // delete entry + this._disabledInputs[this._disabledInputs.length] = target; + }, + + /* Is the first field in a jQuery collection disabled as a datepicker? + * @param target element - the target input field or division or span + * @return boolean - true if disabled, false if enabled + */ + _isDisabledDatepicker: function(target) { + if (!target) { + return false; + } + for (var i = 0; i < this._disabledInputs.length; i++) { + if (this._disabledInputs[i] === target) { + return true; + } + } + return false; + }, + + /* Retrieve the instance data for the target control. + * @param target element - the target input field or division or span + * @return object - the associated instance data + * @throws error if a jQuery problem getting data + */ + _getInst: function(target) { + try { + return $.data(target, "datepicker"); + } + catch (err) { + throw "Missing instance data for this datepicker"; + } + }, + + /* Update or retrieve the settings for a date picker attached to an input field or division. + * @param target element - the target input field or division or span + * @param name object - the new settings to update or + * string - the name of the setting to change or retrieve, + * when retrieving also "all" for all instance settings or + * "defaults" for all global defaults + * @param value any - the new value for the setting + * (omit if above is an object or to retrieve a value) + */ + _optionDatepicker: function(target, name, value) { + var settings, date, minDate, maxDate, + inst = this._getInst(target); + + if (arguments.length === 2 && typeof name === "string") { + return (name === "defaults" ? $.extend({}, $.datepicker._defaults) : + (inst ? (name === "all" ? $.extend({}, inst.settings) : + this._get(inst, name)) : null)); + } + + settings = name || {}; + if (typeof name === "string") { + settings = {}; + settings[name] = value; + } + + if (inst) { + if (this._curInst === inst) { + this._hideDatepicker(); + } + + date = this._getDateDatepicker(target, true); + minDate = this._getMinMaxDate(inst, "min"); + maxDate = this._getMinMaxDate(inst, "max"); + datepicker_extendRemove(inst.settings, settings); + // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided + if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) { + inst.settings.minDate = this._formatDate(inst, minDate); + } + if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) { + inst.settings.maxDate = this._formatDate(inst, maxDate); + } + if ( "disabled" in settings ) { + if ( settings.disabled ) { + this._disableDatepicker(target); + } else { + this._enableDatepicker(target); + } + } + this._attachments($(target), inst); + this._autoSize(inst); + this._setDate(inst, date); + this._updateAlternate(inst); + this._updateDatepicker(inst); + } + }, + + // change method deprecated + _changeDatepicker: function(target, name, value) { + this._optionDatepicker(target, name, value); + }, + + /* Redraw the date picker attached to an input field or division. + * @param target element - the target input field or division or span + */ + _refreshDatepicker: function(target) { + var inst = this._getInst(target); + if (inst) { + this._updateDatepicker(inst); + } + }, + + /* Set the dates for a jQuery selection. + * @param target element - the target input field or division or span + * @param date Date - the new date + */ + _setDateDatepicker: function(target, date) { + var inst = this._getInst(target); + if (inst) { + this._setDate(inst, date); + this._updateDatepicker(inst); + this._updateAlternate(inst); + } + }, + + /* Get the date(s) for the first entry in a jQuery selection. + * @param target element - the target input field or division or span + * @param noDefault boolean - true if no default date is to be used + * @return Date - the current date + */ + _getDateDatepicker: function(target, noDefault) { + var inst = this._getInst(target); + if (inst && !inst.inline) { + this._setDateFromField(inst, noDefault); + } + return (inst ? this._getDate(inst) : null); + }, + + /* Handle keystrokes. */ + _doKeyDown: function(event) { + var onSelect, dateStr, sel, + inst = $.datepicker._getInst(event.target), + handled = true, + isRTL = inst.dpDiv.is(".ui-datepicker-rtl"); + + inst._keyEvent = true; + if ($.datepicker._datepickerShowing) { + switch (event.keyCode) { + case 9: $.datepicker._hideDatepicker(); + handled = false; + break; // hide on tab out + case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." + + $.datepicker._currentClass + ")", inst.dpDiv); + if (sel[0]) { + $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]); + } + + onSelect = $.datepicker._get(inst, "onSelect"); + if (onSelect) { + dateStr = $.datepicker._formatDate(inst); + + // trigger custom callback + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); + } else { + $.datepicker._hideDatepicker(); + } + + return false; // don't submit the form + case 27: $.datepicker._hideDatepicker(); + break; // hide on escape + case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, "stepBigMonths") : + -$.datepicker._get(inst, "stepMonths")), "M"); + break; // previous month/year on page up/+ ctrl + case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, "stepBigMonths") : + +$.datepicker._get(inst, "stepMonths")), "M"); + break; // next month/year on page down/+ ctrl + case 35: if (event.ctrlKey || event.metaKey) { + $.datepicker._clearDate(event.target); + } + handled = event.ctrlKey || event.metaKey; + break; // clear on ctrl or command +end + case 36: if (event.ctrlKey || event.metaKey) { + $.datepicker._gotoToday(event.target); + } + handled = event.ctrlKey || event.metaKey; + break; // current on ctrl or command +home + case 37: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D"); + } + handled = event.ctrlKey || event.metaKey; + // -1 day on ctrl or command +left + if (event.originalEvent.altKey) { + $.datepicker._adjustDate(event.target, (event.ctrlKey ? + -$.datepicker._get(inst, "stepBigMonths") : + -$.datepicker._get(inst, "stepMonths")), "M"); + } + // next month/year on alt +left on Mac + break; + case 38: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, -7, "D"); + } + handled = event.ctrlKey || event.metaKey; + break; // -1 week on ctrl or command +up + case 39: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D"); + } + handled = event.ctrlKey || event.metaKey; + // +1 day on ctrl or command +right + if (event.originalEvent.altKey) { + $.datepicker._adjustDate(event.target, (event.ctrlKey ? + +$.datepicker._get(inst, "stepBigMonths") : + +$.datepicker._get(inst, "stepMonths")), "M"); + } + // next month/year on alt +right + break; + case 40: if (event.ctrlKey || event.metaKey) { + $.datepicker._adjustDate(event.target, +7, "D"); + } + handled = event.ctrlKey || event.metaKey; + break; // +1 week on ctrl or command +down + default: handled = false; + } + } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home + $.datepicker._showDatepicker(this); + } else { + handled = false; + } + + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + }, + + /* Filter entered characters - based on date format. */ + _doKeyPress: function(event) { + var chars, chr, + inst = $.datepicker._getInst(event.target); + + if ($.datepicker._get(inst, "constrainInput")) { + chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat")); + chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode); + return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1); + } + }, + + /* Synchronise manual entry and field/alternate field. */ + _doKeyUp: function(event) { + var date, + inst = $.datepicker._getInst(event.target); + + if (inst.input.val() !== inst.lastVal) { + try { + date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), + (inst.input ? inst.input.val() : null), + $.datepicker._getFormatConfig(inst)); + + if (date) { // only if valid + $.datepicker._setDateFromField(inst); + $.datepicker._updateAlternate(inst); + $.datepicker._updateDatepicker(inst); + } + } + catch (err) { + } + } + return true; + }, + + /* Pop-up the date picker for a given input field. + * If false returned from beforeShow event handler do not show. + * @param input element - the input field attached to the date picker or + * event - if triggered by focus + */ + _showDatepicker: function(input) { + input = input.target || input; + if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger + input = $("input", input.parentNode)[0]; + } + + if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here + return; + } + + var inst, beforeShow, beforeShowSettings, isFixed, + offset, showAnim, duration; + + inst = $.datepicker._getInst(input); + if ($.datepicker._curInst && $.datepicker._curInst !== inst) { + $.datepicker._curInst.dpDiv.stop(true, true); + if ( inst && $.datepicker._datepickerShowing ) { + $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] ); + } + } + + beforeShow = $.datepicker._get(inst, "beforeShow"); + beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {}; + if(beforeShowSettings === false){ + return; + } + datepicker_extendRemove(inst.settings, beforeShowSettings); + + inst.lastVal = null; + $.datepicker._lastInput = input; + $.datepicker._setDateFromField(inst); + + if ($.datepicker._inDialog) { // hide cursor + input.value = ""; + } + if (!$.datepicker._pos) { // position below input + $.datepicker._pos = $.datepicker._findPos(input); + $.datepicker._pos[1] += input.offsetHeight; // add the height + } + + isFixed = false; + $(input).parents().each(function() { + isFixed |= $(this).css("position") === "fixed"; + return !isFixed; + }); + + offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]}; + $.datepicker._pos = null; + //to avoid flashes on Firefox + inst.dpDiv.empty(); + // determine sizing offscreen + inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"}); + $.datepicker._updateDatepicker(inst); + // fix width for dynamic number of date pickers + // and adjust position before showing + offset = $.datepicker._checkOffset(inst, offset, isFixed); + inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ? + "static" : (isFixed ? "fixed" : "absolute")), display: "none", + left: offset.left + "px", top: offset.top + "px"}); + + if (!inst.inline) { + showAnim = $.datepicker._get(inst, "showAnim"); + duration = $.datepicker._get(inst, "duration"); + inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 ); + $.datepicker._datepickerShowing = true; + + if ( $.effects && $.effects.effect[ showAnim ] ) { + inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration); + } else { + inst.dpDiv[showAnim || "show"](showAnim ? duration : null); + } + + if ( $.datepicker._shouldFocusInput( inst ) ) { + inst.input.focus(); + } + + $.datepicker._curInst = inst; + } + }, + + /* Generate the date picker content. */ + _updateDatepicker: function(inst) { + this.maxRows = 4; //Reset the max number of rows being displayed (see #7043) + datepicker_instActive = inst; // for delegate hover events + inst.dpDiv.empty().append(this._generateHTML(inst)); + this._attachHandlers(inst); + + var origyearshtml, + numMonths = this._getNumberOfMonths(inst), + cols = numMonths[1], + width = 17, + activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" ); + + if ( activeCell.length > 0 ) { + datepicker_handleMouseover.apply( activeCell.get( 0 ) ); + } + + inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""); + if (cols > 1) { + inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em"); + } + inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") + + "Class"]("ui-datepicker-multi"); + inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") + + "Class"]("ui-datepicker-rtl"); + + if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) { + inst.input.focus(); + } + + // deffered render of the years select (to avoid flashes on Firefox) + if( inst.yearshtml ){ + origyearshtml = inst.yearshtml; + setTimeout(function(){ + //assure that inst.yearshtml didn't change. + if( origyearshtml === inst.yearshtml && inst.yearshtml ){ + inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml); + } + origyearshtml = inst.yearshtml = null; + }, 0); + } + }, + + // #6694 - don't focus the input if it's already focused + // this breaks the change event in IE + // Support: IE and jQuery <1.9 + _shouldFocusInput: function( inst ) { + return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" ); + }, + + /* Check positioning to remain on screen. */ + _checkOffset: function(inst, offset, isFixed) { + var dpWidth = inst.dpDiv.outerWidth(), + dpHeight = inst.dpDiv.outerHeight(), + inputWidth = inst.input ? inst.input.outerWidth() : 0, + inputHeight = inst.input ? inst.input.outerHeight() : 0, + viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()), + viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop()); + + offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0); + offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0; + offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0; + + // now check if datepicker is showing outside window viewport - move to a better place if so. + offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offset.left + dpWidth - viewWidth) : 0); + offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? + Math.abs(dpHeight + inputHeight) : 0); + + return offset; + }, + + /* Find an object's position on the screen. */ + _findPos: function(obj) { + var position, + inst = this._getInst(obj), + isRTL = this._get(inst, "isRTL"); + + while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) { + obj = obj[isRTL ? "previousSibling" : "nextSibling"]; + } + + position = $(obj).offset(); + return [position.left, position.top]; + }, + + /* Hide the date picker from view. + * @param input element - the input field attached to the date picker + */ + _hideDatepicker: function(input) { + var showAnim, duration, postProcess, onClose, + inst = this._curInst; + + if (!inst || (input && inst !== $.data(input, "datepicker"))) { + return; + } + + if (this._datepickerShowing) { + showAnim = this._get(inst, "showAnim"); + duration = this._get(inst, "duration"); + postProcess = function() { + $.datepicker._tidyDialog(inst); + }; + + // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed + if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) { + inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess); + } else { + inst.dpDiv[(showAnim === "slideDown" ? "slideUp" : + (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess); + } + + if (!showAnim) { + postProcess(); + } + this._datepickerShowing = false; + + onClose = this._get(inst, "onClose"); + if (onClose) { + onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]); + } + + this._lastInput = null; + if (this._inDialog) { + this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" }); + if ($.blockUI) { + $.unblockUI(); + $("body").append(this.dpDiv); + } + } + this._inDialog = false; + } + }, + + /* Tidy up after a dialog display. */ + _tidyDialog: function(inst) { + inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar"); + }, + + /* Close date picker if clicked elsewhere. */ + _checkExternalClick: function(event) { + if (!$.datepicker._curInst) { + return; + } + + var $target = $(event.target), + inst = $.datepicker._getInst($target[0]); + + if ( ( ( $target[0].id !== $.datepicker._mainDivId && + $target.parents("#" + $.datepicker._mainDivId).length === 0 && + !$target.hasClass($.datepicker.markerClassName) && + !$target.closest("." + $.datepicker._triggerClass).length && + $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) || + ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) { + $.datepicker._hideDatepicker(); + } + }, + + /* Adjust one of the date sub-fields. */ + _adjustDate: function(id, offset, period) { + var target = $(id), + inst = this._getInst(target[0]); + + if (this._isDisabledDatepicker(target[0])) { + return; + } + this._adjustInstDate(inst, offset + + (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning + period); + this._updateDatepicker(inst); + }, + + /* Action for current link. */ + _gotoToday: function(id) { + var date, + target = $(id), + inst = this._getInst(target[0]); + + if (this._get(inst, "gotoCurrent") && inst.currentDay) { + inst.selectedDay = inst.currentDay; + inst.drawMonth = inst.selectedMonth = inst.currentMonth; + inst.drawYear = inst.selectedYear = inst.currentYear; + } else { + date = new Date(); + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + } + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Action for selecting a new month/year. */ + _selectMonthYear: function(id, select, period) { + var target = $(id), + inst = this._getInst(target[0]); + + inst["selected" + (period === "M" ? "Month" : "Year")] = + inst["draw" + (period === "M" ? "Month" : "Year")] = + parseInt(select.options[select.selectedIndex].value,10); + + this._notifyChange(inst); + this._adjustDate(target); + }, + + /* Action for selecting a day. */ + _selectDay: function(id, month, year, td) { + var inst, + target = $(id); + + if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) { + return; + } + + inst = this._getInst(target[0]); + inst.selectedDay = inst.currentDay = $("a", td).html(); + inst.selectedMonth = inst.currentMonth = month; + inst.selectedYear = inst.currentYear = year; + this._selectDate(id, this._formatDate(inst, + inst.currentDay, inst.currentMonth, inst.currentYear)); + }, + + /* Erase the input field and hide the date picker. */ + _clearDate: function(id) { + var target = $(id); + this._selectDate(target, ""); + }, + + /* Update the input field with the selected date. */ + _selectDate: function(id, dateStr) { + var onSelect, + target = $(id), + inst = this._getInst(target[0]); + + dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); + if (inst.input) { + inst.input.val(dateStr); + } + this._updateAlternate(inst); + + onSelect = this._get(inst, "onSelect"); + if (onSelect) { + onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback + } else if (inst.input) { + inst.input.trigger("change"); // fire the change event + } + + if (inst.inline){ + this._updateDatepicker(inst); + } else { + this._hideDatepicker(); + this._lastInput = inst.input[0]; + if (typeof(inst.input[0]) !== "object") { + inst.input.focus(); // restore focus + } + this._lastInput = null; + } + }, + + /* Update any alternate field to synchronise with the main field. */ + _updateAlternate: function(inst) { + var altFormat, date, dateStr, + altField = this._get(inst, "altField"); + + if (altField) { // update alternate field too + altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat"); + date = this._getDate(inst); + dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst)); + $(altField).each(function() { $(this).val(dateStr); }); + } + }, + + /* Set as beforeShowDay function to prevent selection of weekends. + * @param date Date - the date to customise + * @return [boolean, string] - is this date selectable?, what is its CSS class? + */ + noWeekends: function(date) { + var day = date.getDay(); + return [(day > 0 && day < 6), ""]; + }, + + /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. + * @param date Date - the date to get the week for + * @return number - the number of the week within the year that contains this date + */ + iso8601Week: function(date) { + var time, + checkDate = new Date(date.getTime()); + + // Find Thursday of this week starting on Monday + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); + + time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + }, + + /* Parse a string value into a date object. + * See formatDate below for the possible formats. + * + * @param format string - the expected format of the date + * @param value string - the date in the above format + * @param settings Object - attributes include: + * shortYearCutoff number - the cutoff year for determining the century (optional) + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return Date - the extracted date value or null if value is blank + */ + parseDate: function (format, value, settings) { + if (format == null || value == null) { + throw "Invalid arguments"; + } + + value = (typeof value === "object" ? value.toString() : value + ""); + if (value === "") { + return null; + } + + var iFormat, dim, extra, + iValue = 0, + shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff, + shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp : + new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)), + dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, + dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, + monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, + monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, + year = -1, + month = -1, + day = -1, + doy = -1, + literal = false, + date, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }, + // Extract a number from the string value + getNumber = function(match) { + var isDoubled = lookAhead(match), + size = (match === "@" ? 14 : (match === "!" ? 20 : + (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))), + minSize = (match === "y" ? size : 1), + digits = new RegExp("^\\d{" + minSize + "," + size + "}"), + num = value.substring(iValue).match(digits); + if (!num) { + throw "Missing number at position " + iValue; + } + iValue += num[0].length; + return parseInt(num[0], 10); + }, + // Extract a name from the string value and convert to an index + getName = function(match, shortNames, longNames) { + var index = -1, + names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) { + return [ [k, v] ]; + }).sort(function (a, b) { + return -(a[1].length - b[1].length); + }); + + $.each(names, function (i, pair) { + var name = pair[1]; + if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) { + index = pair[0]; + iValue += name.length; + return false; + } + }); + if (index !== -1) { + return index + 1; + } else { + throw "Unknown name at position " + iValue; + } + }, + // Confirm that a literal character matches the string value + checkLiteral = function() { + if (value.charAt(iValue) !== format.charAt(iFormat)) { + throw "Unexpected literal at position " + iValue; + } + iValue++; + }; + + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + checkLiteral(); + } + } else { + switch (format.charAt(iFormat)) { + case "d": + day = getNumber("d"); + break; + case "D": + getName("D", dayNamesShort, dayNames); + break; + case "o": + doy = getNumber("o"); + break; + case "m": + month = getNumber("m"); + break; + case "M": + month = getName("M", monthNamesShort, monthNames); + break; + case "y": + year = getNumber("y"); + break; + case "@": + date = new Date(getNumber("@")); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "!": + date = new Date((getNumber("!") - this._ticksTo1970) / 10000); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case "'": + if (lookAhead("'")){ + checkLiteral(); + } else { + literal = true; + } + break; + default: + checkLiteral(); + } + } + } + + if (iValue < value.length){ + extra = value.substr(iValue); + if (!/^\s+/.test(extra)) { + throw "Extra/unparsed characters found in date: " + extra; + } + } + + if (year === -1) { + year = new Date().getFullYear(); + } else if (year < 100) { + year += new Date().getFullYear() - new Date().getFullYear() % 100 + + (year <= shortYearCutoff ? 0 : -100); + } + + if (doy > -1) { + month = 1; + day = doy; + do { + dim = this._getDaysInMonth(year, month - 1); + if (day <= dim) { + break; + } + month++; + day -= dim; + } while (true); + } + + date = this._daylightSavingAdjust(new Date(year, month - 1, day)); + if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { + throw "Invalid date"; // E.g. 31/02/00 + } + return date; + }, + + /* Standard date formats. */ + ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601) + COOKIE: "D, dd M yy", + ISO_8601: "yy-mm-dd", + RFC_822: "D, d M y", + RFC_850: "DD, dd-M-y", + RFC_1036: "D, d M y", + RFC_1123: "D, d M yy", + RFC_2822: "D, d M yy", + RSS: "D, d M y", // RFC 822 + TICKS: "!", + TIMESTAMP: "@", + W3C: "yy-mm-dd", // ISO 8601 + + _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + + Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), + + /* Format a date object into a string value. + * The format can be combinations of the following: + * d - day of month (no leading zero) + * dd - day of month (two digit) + * o - day of year (no leading zeros) + * oo - day of year (three digit) + * D - day name short + * DD - day name long + * m - month of year (no leading zero) + * mm - month of year (two digit) + * M - month name short + * MM - month name long + * y - year (two digit) + * yy - year (four digit) + * @ - Unix timestamp (ms since 01/01/1970) + * ! - Windows ticks (100ns since 01/01/0001) + * "..." - literal text + * '' - single quote + * + * @param format string - the desired format of the date + * @param date Date - the date value to format + * @param settings Object - attributes include: + * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional) + * dayNames string[7] - names of the days from Sunday (optional) + * monthNamesShort string[12] - abbreviated names of the months (optional) + * monthNames string[12] - names of the months (optional) + * @return string - the date in the above format + */ + formatDate: function (format, date, settings) { + if (!date) { + return ""; + } + + var iFormat, + dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort, + dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames, + monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort, + monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }, + // Format a number, with leading zero if necessary + formatNumber = function(match, value, len) { + var num = "" + value; + if (lookAhead(match)) { + while (num.length < len) { + num = "0" + num; + } + } + return num; + }, + // Format a name, short or long as requested + formatName = function(match, value, shortNames, longNames) { + return (lookAhead(match) ? longNames[value] : shortNames[value]); + }, + output = "", + literal = false; + + if (date) { + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + output += format.charAt(iFormat); + } + } else { + switch (format.charAt(iFormat)) { + case "d": + output += formatNumber("d", date.getDate(), 2); + break; + case "D": + output += formatName("D", date.getDay(), dayNamesShort, dayNames); + break; + case "o": + output += formatNumber("o", + Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3); + break; + case "m": + output += formatNumber("m", date.getMonth() + 1, 2); + break; + case "M": + output += formatName("M", date.getMonth(), monthNamesShort, monthNames); + break; + case "y": + output += (lookAhead("y") ? date.getFullYear() : + (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100); + break; + case "@": + output += date.getTime(); + break; + case "!": + output += date.getTime() * 10000 + this._ticksTo1970; + break; + case "'": + if (lookAhead("'")) { + output += "'"; + } else { + literal = true; + } + break; + default: + output += format.charAt(iFormat); + } + } + } + } + return output; + }, + + /* Extract all possible characters from the date format. */ + _possibleChars: function (format) { + var iFormat, + chars = "", + literal = false, + // Check whether a format character is doubled + lookAhead = function(match) { + var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match); + if (matches) { + iFormat++; + } + return matches; + }; + + for (iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === "'" && !lookAhead("'")) { + literal = false; + } else { + chars += format.charAt(iFormat); + } + } else { + switch (format.charAt(iFormat)) { + case "d": case "m": case "y": case "@": + chars += "0123456789"; + break; + case "D": case "M": + return null; // Accept anything + case "'": + if (lookAhead("'")) { + chars += "'"; + } else { + literal = true; + } + break; + default: + chars += format.charAt(iFormat); + } + } + } + return chars; + }, + + /* Get a setting value, defaulting if necessary. */ + _get: function(inst, name) { + return inst.settings[name] !== undefined ? + inst.settings[name] : this._defaults[name]; + }, + + /* Parse existing date and initialise date picker. */ + _setDateFromField: function(inst, noDefault) { + if (inst.input.val() === inst.lastVal) { + return; + } + + var dateFormat = this._get(inst, "dateFormat"), + dates = inst.lastVal = inst.input ? inst.input.val() : null, + defaultDate = this._getDefaultDate(inst), + date = defaultDate, + settings = this._getFormatConfig(inst); + + try { + date = this.parseDate(dateFormat, dates, settings) || defaultDate; + } catch (event) { + dates = (noDefault ? "" : dates); + } + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + inst.currentDay = (dates ? date.getDate() : 0); + inst.currentMonth = (dates ? date.getMonth() : 0); + inst.currentYear = (dates ? date.getFullYear() : 0); + this._adjustInstDate(inst); + }, + + /* Retrieve the default date shown on opening. */ + _getDefaultDate: function(inst) { + return this._restrictMinMax(inst, + this._determineDate(inst, this._get(inst, "defaultDate"), new Date())); + }, + + /* A date may be specified as an exact value or a relative one. */ + _determineDate: function(inst, date, defaultDate) { + var offsetNumeric = function(offset) { + var date = new Date(); + date.setDate(date.getDate() + offset); + return date; + }, + offsetString = function(offset) { + try { + return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"), + offset, $.datepicker._getFormatConfig(inst)); + } + catch (e) { + // Ignore + } + + var date = (offset.toLowerCase().match(/^c/) ? + $.datepicker._getDate(inst) : null) || new Date(), + year = date.getFullYear(), + month = date.getMonth(), + day = date.getDate(), + pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, + matches = pattern.exec(offset); + + while (matches) { + switch (matches[2] || "d") { + case "d" : case "D" : + day += parseInt(matches[1],10); break; + case "w" : case "W" : + day += parseInt(matches[1],10) * 7; break; + case "m" : case "M" : + month += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + case "y": case "Y" : + year += parseInt(matches[1],10); + day = Math.min(day, $.datepicker._getDaysInMonth(year, month)); + break; + } + matches = pattern.exec(offset); + } + return new Date(year, month, day); + }, + newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) : + (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime())))); + + newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate); + if (newDate) { + newDate.setHours(0); + newDate.setMinutes(0); + newDate.setSeconds(0); + newDate.setMilliseconds(0); + } + return this._daylightSavingAdjust(newDate); + }, + + /* Handle switch to/from daylight saving. + * Hours may be non-zero on daylight saving cut-over: + * > 12 when midnight changeover, but then cannot generate + * midnight datetime, so jump to 1AM, otherwise reset. + * @param date (Date) the date to check + * @return (Date) the corrected date + */ + _daylightSavingAdjust: function(date) { + if (!date) { + return null; + } + date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0); + return date; + }, + + /* Set the date(s) directly. */ + _setDate: function(inst, date, noChange) { + var clear = !date, + origMonth = inst.selectedMonth, + origYear = inst.selectedYear, + newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date())); + + inst.selectedDay = inst.currentDay = newDate.getDate(); + inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth(); + inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear(); + if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) { + this._notifyChange(inst); + } + this._adjustInstDate(inst); + if (inst.input) { + inst.input.val(clear ? "" : this._formatDate(inst)); + } + }, + + /* Retrieve the date(s) directly. */ + _getDate: function(inst) { + var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null : + this._daylightSavingAdjust(new Date( + inst.currentYear, inst.currentMonth, inst.currentDay))); + return startDate; + }, + + /* Attach the onxxx handlers. These are declared statically so + * they work with static code transformers like Caja. + */ + _attachHandlers: function(inst) { + var stepMonths = this._get(inst, "stepMonths"), + id = "#" + inst.id.replace( /\\\\/g, "\\" ); + inst.dpDiv.find("[data-handler]").map(function () { + var handler = { + prev: function () { + $.datepicker._adjustDate(id, -stepMonths, "M"); + }, + next: function () { + $.datepicker._adjustDate(id, +stepMonths, "M"); + }, + hide: function () { + $.datepicker._hideDatepicker(); + }, + today: function () { + $.datepicker._gotoToday(id); + }, + selectDay: function () { + $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this); + return false; + }, + selectMonth: function () { + $.datepicker._selectMonthYear(id, this, "M"); + return false; + }, + selectYear: function () { + $.datepicker._selectMonthYear(id, this, "Y"); + return false; + } + }; + $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]); + }); + }, + + /* Generate the HTML for the current state of the date picker. */ + _generateHTML: function(inst) { + var maxDraw, prevText, prev, nextText, next, currentText, gotoDate, + controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin, + monthNames, monthNamesShort, beforeShowDay, showOtherMonths, + selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate, + cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows, + printDate, dRow, tbody, daySettings, otherMonth, unselectable, + tempDate = new Date(), + today = this._daylightSavingAdjust( + new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time + isRTL = this._get(inst, "isRTL"), + showButtonPanel = this._get(inst, "showButtonPanel"), + hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"), + navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"), + numMonths = this._getNumberOfMonths(inst), + showCurrentAtPos = this._get(inst, "showCurrentAtPos"), + stepMonths = this._get(inst, "stepMonths"), + isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1), + currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) : + new Date(inst.currentYear, inst.currentMonth, inst.currentDay))), + minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + drawMonth = inst.drawMonth - showCurrentAtPos, + drawYear = inst.drawYear; + + if (drawMonth < 0) { + drawMonth += 12; + drawYear--; + } + if (maxDate) { + maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(), + maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate())); + maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); + while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) { + drawMonth--; + if (drawMonth < 0) { + drawMonth = 11; + drawYear--; + } + } + } + inst.drawMonth = drawMonth; + inst.drawYear = drawYear; + + prevText = this._get(inst, "prevText"); + prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)), + this._getFormatConfig(inst))); + + prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ? + "" + prevText + "" : + (hideIfNoPrevNext ? "" : "" + prevText + "")); + + nextText = this._get(inst, "nextText"); + nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText, + this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)), + this._getFormatConfig(inst))); + + next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ? + "" + nextText + "" : + (hideIfNoPrevNext ? "" : "" + nextText + "")); + + currentText = this._get(inst, "currentText"); + gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today); + currentText = (!navigationAsDateFormat ? currentText : + this.formatDate(currentText, gotoDate, this._getFormatConfig(inst))); + + controls = (!inst.inline ? "" : ""); + + buttonPanel = (showButtonPanel) ? "
      " + (isRTL ? controls : "") + + (this._isInRange(inst, gotoDate) ? "" : "") + (isRTL ? "" : controls) + "
      " : ""; + + firstDay = parseInt(this._get(inst, "firstDay"),10); + firstDay = (isNaN(firstDay) ? 0 : firstDay); + + showWeek = this._get(inst, "showWeek"); + dayNames = this._get(inst, "dayNames"); + dayNamesMin = this._get(inst, "dayNamesMin"); + monthNames = this._get(inst, "monthNames"); + monthNamesShort = this._get(inst, "monthNamesShort"); + beforeShowDay = this._get(inst, "beforeShowDay"); + showOtherMonths = this._get(inst, "showOtherMonths"); + selectOtherMonths = this._get(inst, "selectOtherMonths"); + defaultDate = this._getDefaultDate(inst); + html = ""; + dow; + for (row = 0; row < numMonths[0]; row++) { + group = ""; + this.maxRows = 4; + for (col = 0; col < numMonths[1]; col++) { + selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay)); + cornerClass = " ui-corner-all"; + calender = ""; + if (isMultiMonth) { + calender += "
      "; + } + calender += "
      " + + (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") + + (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") + + this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate, + row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers + "
      " + + ""; + thead = (showWeek ? "" : ""); + for (dow = 0; dow < 7; dow++) { // days of the week + day = (dow + firstDay) % 7; + thead += ""; + } + calender += thead + ""; + daysInMonth = this._getDaysInMonth(drawYear, drawMonth); + if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) { + inst.selectedDay = Math.min(inst.selectedDay, daysInMonth); + } + leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; + curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate + numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043) + this.maxRows = numRows; + printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays)); + for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows + calender += ""; + tbody = (!showWeek ? "" : ""); + for (dow = 0; dow < 7; dow++) { // create date picker days + daySettings = (beforeShowDay ? + beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]); + otherMonth = (printDate.getMonth() !== drawMonth); + unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] || + (minDate && printDate < minDate) || (maxDate && printDate > maxDate); + tbody += ""; // display selectable date + printDate.setDate(printDate.getDate() + 1); + printDate = this._daylightSavingAdjust(printDate); + } + calender += tbody + ""; + } + drawMonth++; + if (drawMonth > 11) { + drawMonth = 0; + drawYear++; + } + calender += "
      " + this._get(inst, "weekHeader") + "= 5 ? " class='ui-datepicker-week-end'" : "") + ">" + + "" + dayNamesMin[day] + "
      " + + this._get(inst, "calculateWeek")(printDate) + "" + // actions + (otherMonth && !showOtherMonths ? " " : // display for other months + (unselectable ? "" + printDate.getDate() + "" : "" + printDate.getDate() + "")) + "
      " + (isMultiMonth ? "
      " + + ((numMonths[0] > 0 && col === numMonths[1]-1) ? "
      " : "") : ""); + group += calender; + } + html += group; + } + html += buttonPanel; + inst._keyEvent = false; + return html; + }, + + /* Generate the month and year header. */ + _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate, + secondary, monthNames, monthNamesShort) { + + var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear, + changeMonth = this._get(inst, "changeMonth"), + changeYear = this._get(inst, "changeYear"), + showMonthAfterYear = this._get(inst, "showMonthAfterYear"), + html = "
      ", + monthHtml = ""; + + // month selection + if (secondary || !changeMonth) { + monthHtml += "" + monthNames[drawMonth] + ""; + } else { + inMinYear = (minDate && minDate.getFullYear() === drawYear); + inMaxYear = (maxDate && maxDate.getFullYear() === drawYear); + monthHtml += ""; + } + + if (!showMonthAfterYear) { + html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : ""); + } + + // year selection + if ( !inst.yearshtml ) { + inst.yearshtml = ""; + if (secondary || !changeYear) { + html += "" + drawYear + ""; + } else { + // determine range of years to display + years = this._get(inst, "yearRange").split(":"); + thisYear = new Date().getFullYear(); + determineYear = function(value) { + var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) : + (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) : + parseInt(value, 10))); + return (isNaN(year) ? thisYear : year); + }; + year = determineYear(years[0]); + endYear = Math.max(year, determineYear(years[1] || "")); + year = (minDate ? Math.max(year, minDate.getFullYear()) : year); + endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); + inst.yearshtml += ""; + + html += inst.yearshtml; + inst.yearshtml = null; + } + } + + html += this._get(inst, "yearSuffix"); + if (showMonthAfterYear) { + html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml; + } + html += "
      "; // Close datepicker_header + return html; + }, + + /* Adjust one of the date sub-fields. */ + _adjustInstDate: function(inst, offset, period) { + var year = inst.drawYear + (period === "Y" ? offset : 0), + month = inst.drawMonth + (period === "M" ? offset : 0), + day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0), + date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day))); + + inst.selectedDay = date.getDate(); + inst.drawMonth = inst.selectedMonth = date.getMonth(); + inst.drawYear = inst.selectedYear = date.getFullYear(); + if (period === "M" || period === "Y") { + this._notifyChange(inst); + } + }, + + /* Ensure a date is within any min/max bounds. */ + _restrictMinMax: function(inst, date) { + var minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + newDate = (minDate && date < minDate ? minDate : date); + return (maxDate && newDate > maxDate ? maxDate : newDate); + }, + + /* Notify change of month/year. */ + _notifyChange: function(inst) { + var onChange = this._get(inst, "onChangeMonthYear"); + if (onChange) { + onChange.apply((inst.input ? inst.input[0] : null), + [inst.selectedYear, inst.selectedMonth + 1, inst]); + } + }, + + /* Determine the number of months to show. */ + _getNumberOfMonths: function(inst) { + var numMonths = this._get(inst, "numberOfMonths"); + return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths)); + }, + + /* Determine the current maximum date - ensure no time components are set. */ + _getMinMaxDate: function(inst, minMax) { + return this._determineDate(inst, this._get(inst, minMax + "Date"), null); + }, + + /* Find the number of days in a given month. */ + _getDaysInMonth: function(year, month) { + return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate(); + }, + + /* Find the day of the week of the first of a month. */ + _getFirstDayOfMonth: function(year, month) { + return new Date(year, month, 1).getDay(); + }, + + /* Determines if we should allow a "next/prev" month display change. */ + _canAdjustMonth: function(inst, offset, curYear, curMonth) { + var numMonths = this._getNumberOfMonths(inst), + date = this._daylightSavingAdjust(new Date(curYear, + curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1)); + + if (offset < 0) { + date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); + } + return this._isInRange(inst, date); + }, + + /* Is the given date in the accepted range? */ + _isInRange: function(inst, date) { + var yearSplit, currentYear, + minDate = this._getMinMaxDate(inst, "min"), + maxDate = this._getMinMaxDate(inst, "max"), + minYear = null, + maxYear = null, + years = this._get(inst, "yearRange"); + if (years){ + yearSplit = years.split(":"); + currentYear = new Date().getFullYear(); + minYear = parseInt(yearSplit[0], 10); + maxYear = parseInt(yearSplit[1], 10); + if ( yearSplit[0].match(/[+\-].*/) ) { + minYear += currentYear; + } + if ( yearSplit[1].match(/[+\-].*/) ) { + maxYear += currentYear; + } + } + + return ((!minDate || date.getTime() >= minDate.getTime()) && + (!maxDate || date.getTime() <= maxDate.getTime()) && + (!minYear || date.getFullYear() >= minYear) && + (!maxYear || date.getFullYear() <= maxYear)); + }, + + /* Provide the configuration settings for formatting/parsing. */ + _getFormatConfig: function(inst) { + var shortYearCutoff = this._get(inst, "shortYearCutoff"); + shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff : + new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); + return {shortYearCutoff: shortYearCutoff, + dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"), + monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")}; + }, + + /* Format the given date for display. */ + _formatDate: function(inst, day, month, year) { + if (!day) { + inst.currentDay = inst.selectedDay; + inst.currentMonth = inst.selectedMonth; + inst.currentYear = inst.selectedYear; + } + var date = (day ? (typeof day === "object" ? day : + this._daylightSavingAdjust(new Date(year, month, day))) : + this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay))); + return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst)); + } +}); + +/* + * Bind hover events for datepicker elements. + * Done via delegate so the binding only occurs once in the lifetime of the parent div. + * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker. + */ +function datepicker_bindHover(dpDiv) { + var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a"; + return dpDiv.delegate(selector, "mouseout", function() { + $(this).removeClass("ui-state-hover"); + if (this.className.indexOf("ui-datepicker-prev") !== -1) { + $(this).removeClass("ui-datepicker-prev-hover"); + } + if (this.className.indexOf("ui-datepicker-next") !== -1) { + $(this).removeClass("ui-datepicker-next-hover"); + } + }) + .delegate( selector, "mouseover", datepicker_handleMouseover ); +} + +function datepicker_handleMouseover() { + if (!$.datepicker._isDisabledDatepicker( datepicker_instActive.inline? datepicker_instActive.dpDiv.parent()[0] : datepicker_instActive.input[0])) { + $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); + $(this).addClass("ui-state-hover"); + if (this.className.indexOf("ui-datepicker-prev") !== -1) { + $(this).addClass("ui-datepicker-prev-hover"); + } + if (this.className.indexOf("ui-datepicker-next") !== -1) { + $(this).addClass("ui-datepicker-next-hover"); + } + } +} + +/* jQuery extend now ignores nulls! */ +function datepicker_extendRemove(target, props) { + $.extend(target, props); + for (var name in props) { + if (props[name] == null) { + target[name] = props[name]; + } + } + return target; +} + +/* Invoke the datepicker functionality. + @param options string - a command, optionally followed by additional parameters or + Object - settings for attaching new datepicker functionality + @return jQuery object */ +$.fn.datepicker = function(options){ + + /* Verify an empty collection wasn't passed - Fixes #6976 */ + if ( !this.length ) { + return this; + } + + /* Initialise the date picker. */ + if (!$.datepicker.initialized) { + $(document).mousedown($.datepicker._checkExternalClick); + $.datepicker.initialized = true; + } + + /* Append datepicker main container to body if not exist. */ + if ($("#"+$.datepicker._mainDivId).length === 0) { + $("body").append($.datepicker.dpDiv); + } + + var otherArgs = Array.prototype.slice.call(arguments, 1); + if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) { + return $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this[0]].concat(otherArgs)); + } + if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") { + return $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this[0]].concat(otherArgs)); + } + return this.each(function() { + typeof options === "string" ? + $.datepicker["_" + options + "Datepicker"]. + apply($.datepicker, [this].concat(otherArgs)) : + $.datepicker._attachDatepicker(this, options); + }); +}; + +$.datepicker = new Datepicker(); // singleton instance +$.datepicker.initialized = false; +$.datepicker.uuid = new Date().getTime(); +$.datepicker.version = "1.11.4"; + +var datepicker = $.datepicker; + + +/*! + * jQuery UI Draggable 1.11.4 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/draggable/ + */ + + +$.widget("ui.draggable", $.ui.mouse, { + version: "1.11.4", + widgetEventPrefix: "drag", + options: { + addClasses: true, + appendTo: "parent", + axis: false, + connectToSortable: false, + containment: false, + cursor: "auto", + cursorAt: false, + grid: false, + handle: false, + helper: "original", + iframeFix: false, + opacity: false, + refreshPositions: false, + revert: false, + revertDuration: 500, + scope: "default", + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: false, + snapMode: "both", + snapTolerance: 20, + stack: false, + zIndex: false, + + // callbacks + drag: null, + start: null, + stop: null + }, + _create: function() { + + if ( this.options.helper === "original" ) { + this._setPositionRelative(); + } + if (this.options.addClasses){ + this.element.addClass("ui-draggable"); + } + if (this.options.disabled){ + this.element.addClass("ui-draggable-disabled"); + } + this._setHandleClassName(); + + this._mouseInit(); + }, + + _setOption: function( key, value ) { + this._super( key, value ); + if ( key === "handle" ) { + this._removeHandleClassName(); + this._setHandleClassName(); + } + }, + + _destroy: function() { + if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) { + this.destroyOnClear = true; + return; + } + this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" ); + this._removeHandleClassName(); + this._mouseDestroy(); + }, + + _mouseCapture: function(event) { + var o = this.options; + + this._blurActiveElement( event ); + + // among others, prevent a drag on a resizable-handle + if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) { + return false; + } + + //Quit if we're not on a valid handle + this.handle = this._getHandle(event); + if (!this.handle) { + return false; + } + + this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix ); + + return true; + + }, + + _blockFrames: function( selector ) { + this.iframeBlocks = this.document.find( selector ).map(function() { + var iframe = $( this ); + + return $( "
      " ) + .css( "position", "absolute" ) + .appendTo( iframe.parent() ) + .outerWidth( iframe.outerWidth() ) + .outerHeight( iframe.outerHeight() ) + .offset( iframe.offset() )[ 0 ]; + }); + }, + + _unblockFrames: function() { + if ( this.iframeBlocks ) { + this.iframeBlocks.remove(); + delete this.iframeBlocks; + } + }, + + _blurActiveElement: function( event ) { + var document = this.document[ 0 ]; + + // Only need to blur if the event occurred on the draggable itself, see #10527 + if ( !this.handleElement.is( event.target ) ) { + return; + } + + // support: IE9 + // IE9 throws an "Unspecified error" accessing document.activeElement from an +
      +
      + + +
      +

      Plugins and Service Middlewares

      +

      Chain multiple services together as middlewares to create composite services. An example use-case would be adding the examples/basic-auth service (view source) as the first middleware step in the chain, adding a Basic Authentication layer.

      +

      Any existing hook.io service can be used as a middleware or you can create new custom services to be used as middlewares. Circular chains will halt during execution. This service will be the last executed service in the chain.

      +

      Inputs ( executed in-order, before the service )

      +

      Request Plugins

      + +
        +
      + +
      + + +
      + + + + +
      + + + + +
      + + + +
      + + + +
      + + + +
      + + + + +
      +

      + You may drag and drop services to change their order. +

      + + +
      +
      + + + +
      + +

      Response Plugins

      + +
      + + + +
      + + +
      + +
      +

      Schema for HTTP Input Parameters

      +
      + +
      +
      +

      Adding a schema to a service provides an automated layer of validation and default values for incoming HTTP request parameters.

      +

      Schemas are useful for validating incoming service parameters or setting default service values without having to write any additional validation code.

      +

      Schemas are implemented using the mschema library. Additional documentation and tests for schema usage can be found here. +

      + Example Schema: +

      +
      +
      {
      +  "name": {
      +    "type": "string",
      +    "required": true
      +  },
      +  "password": {
      +    "type": "string",
      +    "minLength": 8,
      +    "maxLength": 64
      +  },
      +  "age": {
      +    "type": "number",
      +    "required": true,
      +    "default": 42
      +  }
      +}
      +                
      +
      +
      + +
      + +
      +
      + + +
      + + +
      + +
      + + +
      +

      Security

      + + +
      +

      + Note: In order to enable private services, you must upgrade to a paid account. +

      +
      + +
      + +
      + + +   + + + +
      +

      + If a service is set to "Private", it will only be accessible when logged into the web app, or through a valid API Access Key with an associated role of hook::run. +

      +

      + Additionally, Private services restrict public access to potentially private information like your source code or logs. Read More. +

      + +
      +

      A public service will be publicly indexed and available for anyone to access at anytime.

      +

      A private service will be hidden and only allowed accessible through a role based key access credential.

      +
      +
      +
      + + +
      +

      Timeouts

      + +

      The timeout value is how long your service will run in Milliseconds until {{appName}} automatically exits the service and ends the response.

      +

      Some custom services may require longer timeouts, but most services should not. Read More about Timeouts

      +
      +

      + Note: In order increase the service timeout value enable, you must upgrade to a paid account. +

      +
      +
      + + +
      +
      + + + +
      +

      Metrics

      + +

      No metrics are available yet. Try running or re-running the Hook.

      +
      +
      + + + + + +
      MetricCount
      +

      {{appUrl}}/metrics/marak/echo/report

      +
      +
      +
      + + +
      +

      Endpoints

      +

      Your microservice contains the following unique URL endpoints. These are used for help managing the microservice.

      +

      Note: These endpoints are currently: Public. In order to protect these URLs set the microservice to Private.

      + +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + +
      EndpointURL
      Administration Page/admin
      View Source/_src
      View Logs/_log
      View Revisions/_rev
      Share Fork/_fork
      +
      +
      +
      + + +
      +

      Local Development

      +

      {{appName}} services may be cloned and run locally by installing the {{appName}} SDK and Microcule.

      +

      Quick SDK Install

      +
      
      +  sudo npm install -g hook.io-sdk
      +
      +
      +

      + Once the SDK is installed, you will have access to the hook-clone CLI command. Using hook-clone you can copy this service for local development running the following commands: +

      + +

      Clone Service to Local

      +
      +
      
      +  hook-clone examples/bash-schema
      +  cd bash-schema
      +  npm install
      +  npm start
      +
      +
      +
      +

      + This will start a local HTTP development server mounted with the cloned microservice. +

      +
      + + + + + +
      +
      + Danger Zone: + ||Delete Hook|| + +
      +
      + + +
      + +   +
      +
      + + +
      + + + + + +
      +
    • +
+ +
+ + +
+
+
+ + \ No newline at end of file diff --git a/view/admin.js b/view/admin.js new file mode 100644 index 00000000..55b21572 --- /dev/null +++ b/view/admin.js @@ -0,0 +1,564 @@ +// Make run count live + +var hook = require('../lib/resources/hook'); +var user = require('../lib/resources/user'); +var resource = require('resource'); +var hooks = require('microcule-examples'); +var cache = require('../lib/resources/cache'); +var billing = require('../lib/resources/billing'); +var metric = require('../lib/resources/metric'); +var request = require('hyperquest'); +var df = require('dateformat'); +var forms = require('mschema-forms'); +var themes = require('../lib/resources/themes'); +var web = require('../lib/web/web'); +var languages = require('../lib/resources/programmingLanguage').languages; +var checkRoleAccess = require('../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../config'); +var psr = require('parse-service-request'); + +function numberWithCommas(x) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); +} + +module['exports'] = function view (opts, callback) { + var req = opts.request, + res = opts.response + result = opts; + + var self = this, $ = self.$; + + var params; + + req.session.redirectTo = req.url; + + /* Remark: Removed in favor or role check + if (!req.isAuthenticated()) { + req.session.redirectTo = req.url; + return res.redirect('/login'); + } + */ + + $ = req.white($); + + psr(req, res, function(req, res, fields){ + + params = opts.request.resource.params; + + // params.owner = req.session.user; + checkRoleAccess({ req: req, res: res, role: "hook::update" }, function (err, hasPermission) { + // console.log('check for role access', err, hasPermission) + if (!hasPermission) { // don't allow anonymous hook update + if (req.jsonResponse !== true && typeof params.hook_private_key === "undefined") { + req.session.redirectTo = req.url; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::update")); + } else { + //user = req.resource.owner; + user = req.resource.params.owner; + boot = { + owner: user + }; + params.owner = user; + next(); + } + }); + + function next () { + if (typeof params.owner === 'undefined' || params.owner.length === 0) { + return res.redirect('/' + req.session.user); + } + + var name; + if (typeof params.previousName !== 'undefined') { + name = params.previousName.toLowerCase(); + } else { + name = params.name.toLowerCase(); + } + + if (typeof name === 'undefined' || name.length === 0) { + return res.redirect('/' + req.session.user); + } + + /* + if (req.session.user !== params.owner && req.session.user !== "marak") { + return res.end(req.session.user + ' does not have permission to manage ' + params.owner + "/" + params.name); + } + */ + + // console.log('finding', { owner: params.owner, name: name }); + // fetch the latest version of hook ( non-cached ) + hook.find({ owner: params.owner, name: name }, function (err, result) { + if (err) { + return res.end(err.message); + } + if (result.length === 0) { + return web.handle404(req, res); + } + req.hook = result[0]; + billing.find({ owner: req.session.user }, function (err, results) { + if (err) { + return callback(err, $.html()); + } + if (results.length > 0) { + // TODO: better billings check + req.billings = results[0]; + } + presentView(); + }); + }); + } + + }); + + function presentView () { + + if (params.save || req.method === "POST") { + // update the hook + // at this point, auth should have already taken place, so we can just call Hook.save + + // manually assign properties + var data = {}; + // strings + data.gist = params.gist; + data.language = params.language; + + if (params.hookSource === "code") { + delete params.gist; + params.source = params.codeEditor; + } else if (params.hookSource === "gist") { + delete params.source; + data.mainEntry = params.gistMainEntry; + } else if (params.hookSource === "githubRepo") { + data.githubRepo = params.repo; + data.githubBranch = params.branch; + data.mainEntry = params.githubMainEntry; + } + + data.sourceType = params.hookSource; + data.source = params.source; + data.name = params.name; + + // path is an optional argument. do not allow undefined or empty values to overwrite path + if (typeof params.path === "string" && params.path.length > 0) { + data.path = params.path; + } + + if (params.isPrivate === true || params.isPrivate === "true") { + data.isPrivate = true; + } else { + data.isPrivate = false; + } + + // make more API friendly + if (params.themeActive || params.themeStatus === "enabled") { + data.themeStatus = "enabled"; + } else { + data.themeStatus = "disabled"; + } + + if (params.schemaActive) { + data.mschema = JSON.parse(params.schema); + data.mschemaStatus = "enabled"; + } else { + data.mschemaStatus = "disabled"; + } + + // TODO: also add view as new property name for public API + if (params.themeActive) { + data.themeStatus = "enabled"; + data.themeSource = params.themeSource; + data.presenterSource = params.presenterSource; + } + + if (typeof params.view === "string" && params.view.length > 1) { + data.themeSource = params.view; + data.view = params.view; + // params.themeActive = true; + } + + data.themeName = params.themeSelect; + data.theme = params.theme; + data.presenter = params.presenter; + data.mode = params.mode; + + // We don't want to let users setup an infinite chain ( since it will never respond or timeout ) + // Remark: In most cases, this should already be covered server-side ( see: ./lib/resources/hook/run.js) + // TODO: better gaurds against circular / self referencing chains + // TODO: do not allow users to chain services to themself ( infinite chain loop currently can't be caught ) + // TODO: do not allow the same named service to appear in the chain twice ( not a performance risk, but seems like bad design ) + + data.inputs = []; + params.inputs = params.inputs || []; + if (params.inputs.length > 0) { + data.inputs = params.inputs.split(',') || []; + } + + // TODO: this should be part of the psr / mschema code path, not here + if (typeof params.customTimeout === "string") { + params.customTimeout = parseInt(params.customTimeout , 10); + if (params.customTimeout.toString() !== "NaN") { + + } else { + params.customTimeout = 10000; + } + // a non NaN number was parsed, assign it as validation value and to instance value + } + + // todo: only available for paid accounts + if ((req.user && req.user.paidStatus === "paid") || req.session.paidStatus === "paid") { + data.customTimeout = params.customTimeout || 10000; + } + + // TODO: check to see if index.html file matches up with known theme + data.status = params.status || req.hook.status; + + if (typeof params.isPublic !== 'undefined') { + data.isPublic = true; + } else { + data.isPublic = false; + } + + data.id = req.hook.id; + var key = '/hook/' + req.hook.owner + "/" + data.name; + data.owner = req.hook.owner; + + if (typeof data.language === "undefined") { + delete data.language; + } + + if (data.language === "bash") { + data.source = data.source.replace(/\r/g, '') + } + + return hook.update(data, function(err, result){ + if (err) { + // TODO: generic error handler + return res.end(err.message); + } + + resource.emit('hook::updated', { + ip: req.connection.remoteAddress, + owner: req.hook.owner, + name: data.name + }); + + cache.set(key, result, function(){ + if (req.jsonResponse) { + var rsp = { + "status": "updated", + "key": key, + "value": result + }; + return res.json({ status: 'updated', hook: result }); + } + return res.redirect('/admin?owner=' + req.hook.owner + "&name=" + data.name + "&status=saved"); + }); + }); + + } else { + + var owner = req.hook.owner, + service = req.hook.name; + // get latest metrics + var reportKey = '/' + req.hook.owner + '/' + req.hook.name + '/report'; + metric.hgetall(reportKey, function (err, report) { + + if (report === null) { + $('.hookMetrics').remove(); + return finish(req.hook); + } + $('.noMetrics').remove(); + $('.hookMetricReport').html(config.app.url + '/metrics' + reportKey); + $('.hookMetricReport').attr("href", config.app.url + '/metrics' + reportKey); + + var now = new Date(); + var monthlyHitsKey = 'monthlyHits - ' + (now.getMonth() + 1) + '/' + now.getFullYear(); + + // List each metric we want to show + + $('.hookMetrics table').append('\ + ' + 'running' + '\ + ' + (report['running'] || 0) + '\ + '); + + $('.hookMetrics table').append('\ + ' + monthlyHitsKey + '\ + ' + (report[monthlyHitsKey] || 0) + '\ + '); + + $('.hookMetrics table').append('\ + ' + 'total hits' + '\ + ' + (report['totalHits'] || 0) + '\ + '); + + $('.hookMetrics table').append('\ + ' + 'last started' + '\ + ' + (df(Number(report['lastStart']))) + '\ + '); + + $('.hookMetrics table').append('\ + ' + 'last status code' + '\ + ' + (report['statusCode']) + '\ + '); + + finish(req.hook); + }); + } + + function finish (h) { + + var services = hook.services; + if (req.jsonResponse) { + return res.json("OK") + } + + if (req.user.paidStatus === "paid" || req.session.paidStatus === "paid") { + $('.paidAccount').remove(); + } else { + $('.securityHolder input').attr('disabled', 'DISABLED') + // TODO: + $('.hookPrivate').attr('DISABLED', 'DISABLED'); + $('.hookPrivateLabel').css('color', '#aaa'); + $('.securityHints').remove(); + } + + for (var s in services) { + $('.services').append(services[s]); + } + + if (params.status === "forked") { + $('.message').html('Hook Forked!') + } + + if (params.status === "refreshed") { + $('.message').html('Refreshed any cached sources.') + } + + if (params.status === "created") { + $('.message').html('Created new service.') + } + + if (params.status === "saved") { + $('.message').html('Service saved.') + } + + $('#owner').attr('value', h.owner); + + $('.hookLink').attr('href', '/' + h.owner + '/' + h.name); + $('.hookLogs').attr('href', '/' + h.owner + '/' + h.name + "/logs"); + $('.clearLogs').attr('href', '/' + h.owner + '/' + h.name + "/logs?flush=true"); + $('.hookSource').attr('href', '/' + h.owner + '/' + h.name + "/_src"); + $('.hookResource').attr('href', '/' + h.owner + '/' + h.name + "/resource"); + $('.hookView').attr('href', '/' + h.owner + '/' + h.name + "/view"); + $('.hookPresenter').attr('href', '/' + h.owner + '/' + h.name + "/presenter"); + $('.hookRevisions').attr('href', '/' + h.owner + '/' + h.name + '/_rev'); + $('.hookAdmin').attr('href', '/' + h.owner + '/' + h.name + '/_admin'); + + if(h.isPrivate) { + $('.hookRun').attr('href', '/' + h.owner + '/' + h.name + '?hook_private_key=' + req.session.hookAccessKey); + } else { + $('.hookRun').attr('href', '/' + h.owner + '/' + h.name); + } + $('.hookFork').attr('href', '/' + h.owner + '/' + h.name + "/_fork"); + + $('#themeSource').html(h.themeSource); + $('#presenterSource').html(h.presenterSource); + + if (typeof h.mschema !== "undefined") { + $('.hookSchema').html(JSON.stringify(h.mschema, true, 2)); + } + + if (typeof h.customTimeout === "number") { + $('.customTimeout').attr('value', h.customTimeout.toString()); + } + + if (req.user.paidStatus === "paid" || req.session.paidStatus === "paid") { + // + } else { + $('.customTimeout').attr('disabled', 'DISABLED'); + } + + $('#name').attr('value', h.name); + $('.owner').attr('value', h.owner); + + $('.hookName').html(h.name); + $('.hookOwner').html(h.owner); + + if (h.isPrivate) { + $('.hookPrivate').attr('checked', 'CHECKED'); + } else { + $('.hookPublic').attr('checked', 'CHECKED'); + } + $('#path').attr('value', h.path); + $('.previousName').attr('value', h.name); + + $('.hookSource').attr('value', h.gist); + + if (typeof req.user.accessToken === "undefined") { + //$('.githubRepoSource').attr('DISABLED', 'DISABLED'); + //$('.gistSource').attr('DISABLED', 'DISABLED'); + //$('.gistRepoLabel').css('color', '#aaa'); + //$('.gistSourceLabel').css('color', '#aaa'); + } else { + $('.githubRequired').remove(); + } + + if (h.sourceType === "gist") { + $('#gist').attr('value', h.gist); + $('#gistSource').attr('checked', 'CHECKED'); + $('.gistUrlHolder').attr('style', 'display:block;'); + $('.codeEditorHolderHolder').attr('style', 'display:none;'); + $('.githubRepoHolder').attr('style', 'display:none;'); + } else if (h.sourceType === "githubRepo") { + $('#repo').attr('value', h.githubRepo); + $('#branch').attr('value', h.githubBranch); + $('#githubMainEntry').attr('value', h.mainEntry); + $('#githubRepoSource').attr('checked', 'CHECKED'); + $('.gistUrlHolder').attr('style', 'display:none;'); + $('.codeEditorHolder').attr('style', 'display:none;'); + $('.githubRepoHolder').attr('style', 'display:block;'); + } else { + $('#editorSource').attr('checked', 'CHECKED'); + $('.gistUrlHolder').attr('style', 'display:none;'); + $('.codeEditorHolder').attr('style', 'display:block;'); + $('.githubRepoHolder').attr('style', 'display:none;'); + } + /* + if (h.gist && h.gist.length > 5) { + // do nothing + } else { + } + */ + + /* + if (h.inputs) { + $('#inputs').val(h.inputs); + if (h.inputs.length > 0) { + // $('.inputsHolder').css('display', 'block'); + } + h.inputs.forEach(function(input){ + $('.plugins').append('
  • ' + input + '   X
  • '); + }) + } + */ + + $('.isPublic').attr('checked', 'CHECKED'); + $('.isPublic').attr('DISABLED', 'DISABLED'); + $('.isPublic').attr('title', 'Private Hooks require a paid account.'); + + if (h.isPublic === true) { + $('.isPublic').attr('checked', 'CHECKED'); + } + + if (typeof h.language !== 'undefined') { + $('#language').prepend('') + $('#gatewayLang').attr('value', h.language); + $('#gatewayForm').attr('action', '/gateway'); + } + + if (typeof h.status !== 'undefined') { + $('.status').prepend('') + } + + if (typeof h.mode !== 'undefined') { + $('.mode').prepend('') + } + + $('.deleteLink').attr('href', '/' + h.owner + "/" + h.name + "/delete"); + $('.deleteLink').attr('data-name', (h.owner + "/" + h.name)); + + self.parent.components.themeSelector.present({ + request: req, + response: res, + theme: h.theme, presenter: h.presenter, hook: h, themes: themes }, function(err, html){ + var el = $('.themeSelector') + el.html(html); + + $('#theme').attr('value', h.theme); + $('#presenter').attr('value', h.presenter); + + if (typeof h.themeName !== 'undefined' && h.themeName.length > 0) { + $('.themeSelect').prepend('') + } else { + $('.themeSelect').append('') + } + + if (h.themeStatus === "enabled") { + $('#themeActive').attr('checked', 'CHECKED'); + $('.themeRow').attr('style', 'display: block;'); + } + + if (h.mschemaStatus === "enabled") { + $('#schemaActive').attr('checked', 'CHECKED'); + $('.schemaRow').attr('style', 'display: block;'); + } + + var i18n = require('./helpers/i18n'); + var i = req.i18n; + i18n(i, $); + + var _source = h.source; + if (typeof req.session.tempSource !== "undefined") { + _source = req.session.tempSource; + delete req.session.tempSource; + } + + if (typeof req.session.tempLang === "string") { + // TODO: better default databinding ( instead of prepend ) in boot + $('#language').prepend(''); + delete req.session.tempLang; + } + + $('.customTheme').remove(); + var out = $.html(); + out = out.replace("{{themes}}", JSON.stringify(themes, true, 2)); + + var boot = { + baseUrl: config.app.url, + owner: req.session.user, + source: _source, + paidStatus: req.session.paidStatus, + // presenter: new Buffer(h.presenterSource || "").toString('base64'), + view: new Buffer(h.themeSource || "").toString('base64'), + themes: themes + }; + + var services = hooks.services; + var examples = {}; + + // pull out base examples for every langauge + + // don't show alpha langs ( for now ) + var alpha = ['gcc', 'go', 'ocaml', 'rust', 'r', 'java']; + Object.keys(hook.languages).forEach(function(l){ + // TODO: don't show alpha langs + if (alpha.indexOf(l) === -1) { + examples[l] = services['' + l + '']; + } + }); + + /* + for (var s in services) { + var e = services[s]; + var type = s.split('-')[0], + lang = s.split('-')[1]; + if (type === "examples" && lang === "javascript") { + $('.selectSnippet').prepend('') + } + } + */ + + boot.examples = examples; + out = out.replace('{{hook}}', JSON.stringify(boot, true, 2)); + var appAdminEmail = "hookmaster@hook.io"; + out = out.replace(/\{\{appAdminEmail\}\}/g, appAdminEmail); + return callback(null, out); + }); + + } + + } + +}; \ No newline at end of file diff --git a/view/affiliates.html b/view/affiliates.html new file mode 100644 index 00000000..13fb6d15 --- /dev/null +++ b/view/affiliates.html @@ -0,0 +1,35 @@ +
    +

    Affiliates Program

    +
    +

    Get paid for referring new developers to {{appName}}!

    +

    Automatic Enrollment

    + +

    Each time you refer a new developer to sign up for {{appName}} with your referral link, you are eligible to receive money back each month if the developer who signed up chooses to pay for their account.

    +
    + +

    Get paid each month for every developer you refer who pays for an account

    +

    If a developer you refer to {{appName}} decides to pay for their account, you will receive 5% of all profits made from that user. This will probably be a few cents a month, but it can quickly add up...

    +
    + +

    Get paid each month for every developer your referral signs up

    +

    If a developer you sign up refers another developer...you will receive 2.5% of the profit every month...

    +
    + +

    Get paid each month for every developer your referral's referral signs up...

    +

    ...and it keeps going all way the down. Referrals are hierarchical and multi-level! We strongly feel our affiliate program profits platform growth in a healthy way while rewarding loyal users who enjoy using {{appName}} and genuinely want to share the service with other developers

    +
    + +

    What is the best way to get new developers to sign up?

    +

    Great question! We suggest trying any of the following:

    +
      +
    • Writing a blog post about {{appName}}
    • +
    • Creating a useful Service and sharing it with others
    • +
    • Opening a Pull Request for your favorite npm module to include a live hosted demo on {{appName}}
    • +
    • Tell your co-workers about {{appName}}
    • +
    • Give a talk about {{appName}} at your local meetup
    • +
    +
    + +

    Note: Percentage points of profit paid to users may change at anytime. Affiliate program is still in beta testing. Currently, all referrals are tracked and we will retroactively apply affiliate payments to all existing users once we have released this system to the public.

    +

    +
    \ No newline at end of file diff --git a/view/affiliates.js b/view/affiliates.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/affiliates.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/all.js b/view/all.js index 8189467e..5b9b6836 100644 --- a/view/all.js +++ b/view/all.js @@ -8,39 +8,43 @@ module['exports'] = function view (opts, callback) { res = opts.response, params = req.resource.params; - if (typeof params.signedMeUp !== "undefined" || typeof params.s !== "undefined") { - req.session.referredBy = req.params.username; - return res.redirect("/"); + return res.end("Not implemented"); + + if (!req.isAuthenticated()) { + return res.redirect('/login'); + } + if (req.session.user.toLowerCase() !== "marak") { + return res.redirect('/' + req.session.user); } - + if (!opts.request.isAuthenticated()) { $('.manageEnv').remove() $('.referrals').remove() } - hook.all(function(err, hooks){ - console.log(err, hooks) - opts.hooks = hooks; - opts.hooks = opts.hooks.sort(function(a, b){ - console.log(a, b) - if (a.owner.toLowerCase() > b.owner.toLowerCase()) { - return 1; - } - if (a.owner.toLowerCase() < b.owner.toLowerCase()) { - return -1; - } - return 0; - }); - for(var h in opts.hooks) { - $('.hooks').append('' + opts.hooks[h].owner + "/" + opts.hooks[h].name + '') - } - - if (Object.keys(opts.hooks).length > 0) { - $('.noHooks').remove(); - } - - callback(null, $.html()); - - }) + hook.all(function(err, hooks){ + // console.log(err, hooks) + opts.hooks = hooks; + opts.hooks = opts.hooks.sort(function(a, b){ + // console.log(a, b) + if (a.owner.toLowerCase() > b.owner.toLowerCase()) { + return 1; + } + if (a.owner.toLowerCase() < b.owner.toLowerCase()) { + return -1; + } + return 0; + }); + for(var h in opts.hooks) { + $('.hooks').append('' + opts.hooks[h].owner + "/" + opts.hooks[h].name + '') + } + + if (Object.keys(opts.hooks).length > 0) { + $('.noHooks').remove(); + } + + callback(null, $.html()); + + }) }; \ No newline at end of file diff --git a/view/api.html b/view/api.html new file mode 100644 index 00000000..80deb3d2 --- /dev/null +++ b/view/api.html @@ -0,0 +1,85 @@ + + +
    +

    Developer API

    +
    +
    + + + + +
    + +
    +
    + +

    Hosted Microservices and Webhooks

    +

    Remember, you don't have to use the {{appName}} Developer API from an outside system!

    +

    {{appName}} provides free hosting for Microservices and Webhooks. These hosted services have full integrated access to the {{appName}} Developer API.

    +

    Deploying a new hosted Microservice is easy to do, and can be very useful if you intend to run a block of code in response to HTTP requests or as a scheduled task.

    + + +

    API Access Keys

    +

    Any authorized usage of the {{appName}} Developer API will require an API access key with roles.

    +

    Generating an API access key is simple. Just visit /keys and create a new key with the required roles you wish to use on the Developer API.

    +

    Tip: Generate new keys with granular roles per service in order to maximize your account's security.

    + + +

    REST HTTP API

    +

    All services and URLs on {{appName}} can be communicated with using simple HTTP requests.

    +

    Every actionable page on the {{appName}} website is also an API endpoint capable of receiving data from a JSON POST or Form POST.

    +

    The content of the rendered page result will be determined by the content-type header of the incoming HTTP request to the API. Read More.

    +

    We'll be releasing a nicely formatted API definition file soon. Until then, we recommend checking out the {{appName}}-sdk.

    + + +

    Client SDK

    +

    The easiest way to programmatically interact with the the {{appName}} HTTP API is by using the {{appName}}-sdk client.

    +

    This SDK provides a full mapping of all {{appName}}'s API methods in an easy to use interface

    +

    If you'd like to see additional API SDK clients added, you can open an issue here.

    + +
    +
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/view/api.js b/view/api.js new file mode 100644 index 00000000..686583ae --- /dev/null +++ b/view/api.js @@ -0,0 +1,6 @@ +module['exports'] = function view (opts, callback) { + var req = opts.request + $ = this.$; + $ = req.white($); + return callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/billing.html b/view/billing.html deleted file mode 100644 index bf8274d9..00000000 --- a/view/billing.html +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - - -
    - -
    -

    Select the amount you want to pay each month

    -
    - -
    - - - - - - -
    -
    -
    -
    -
    -

    We let our users choose how much to pay every month. The more you choose to pay, the better we can make the service in the future. We appreciate your business and respect your choice to pay whatever you want.

    -
    - - - - -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/view/billing.js b/view/billing.js deleted file mode 100644 index d17427bb..00000000 --- a/view/billing.js +++ /dev/null @@ -1,160 +0,0 @@ -// Stripe info -// Plan ID: BASIC_HOSTING_PLAN -// Cost: $5.00 per 30 days, no trial -// Statement: Hook.io Hosting Plan - -var hook = require('../lib/resources/hook'); -var user = require('../lib/resources/user'); -var bodyParser = require('body-parser'); -var mergeParams = require('./mergeParams'); -var request = require('request'); -var billing = require('../lib/resources/billing') -var stripe = require('stripe')('sk_test_ZXdJj4I3Db2iB9ZRm0gqyzDV'); - -var billingForm = require('./billingForm'); -var addPaymentOption = require('./addPaymentOption'); - -module['exports'] = function view (opts, callback) { - console.log('billing') - var $ = this.$; - var req = opts.request, res = opts.response; - - bodyParser()(req, res, function bodyParsed(){ - mergeParams(req, res, function(){}); - var params = req.resource.params; - console.log(params); - - if (!req.isAuthenticated()) { - req.session.redirectTo = "/billing"; - return res.redirect('/login'); - } - - // TODO: make it so billing information can be captured directly from the /pricing page - - function showBillings (results, callback) { - - var count = results.length; - - function finish () { - - var _billing = results[0]; - // TODO: data bind $('#slider').slider("value", (_billing.amount / 100)); - // console.log('databinding', _billing.amount) - $('#databind').text(' \ - var resource = {}; \ - resource.billing = { \ - amount: ' + _billing.amount + ' \ - };'); - if (count === 0) { - callback(null); - } - }; - - results.forEach(function(item){ - item.destroy(); - billingForm(item, function (err, re){ - $('.billingForm').append(re); - count--; - finish(); - }); - }); - - }; - - $('.addPaymentOption').html(addPaymentOption()); - // console.log('getting params', params); - // if new billing information was posted ( from account page ), add it - if (params.addCustomer) { - // console.log('adding new customer'); - // create a new customer based on email address - stripe.customers.create( - { email: params.stripeEmail }, - function (err, customer) { - if (err) { - $('.status').html(err.message); - } - // console.log('new customer created', err, customer); - $('.status').html('New billing informationed added!'); - - // select plan based on user-selected value - var _plan = "BASIC_HOSTING_PLAN"; - - if (params.amount > 500) { - _plan = _plan + "_" + (params.amount / 100); - } - - // console.log('attempting to use plan', _plan); - - stripe.customers.createSubscription(customer.id, { - plan: _plan, - source: params.stripeToken // source is the token created from checkout.js - }, function(err, charge){ - if (err) { - $('.status').addClass('error'); - $('.status').html(err.message); - return callback(err, $.html()); - } - - billing.create({ - owner: req.user.username, - stripeID: customer.id, - amount: params.amount, - plan: _plan - }, function (err, _billing) { - // console.log('new billing created', err, _billing); - if (err) { - $('.status').html(err.message); - return callback(null, err.message); - } - - // console.log('added to plan', err, charge); - $('.status').html('Billing Information Added! Thank you!'); - billing.find({ owner: req.user.username }, function (err, results) { - if (err) { - $('.status').html(err.message); - return callback(err, $.html()); - } - showBillings(results, function(){ - callback(err, $.html()); - }); - }); - }); - }); - } - ); - } else { - billing.find({ owner: req.user.username }, function (err, results) { - if (err) { - return callback(null, err.message); - } - // console.log('billing results', err, results) - if (results.length === 0) { - var checkOut = ' \ -
    \ - \ - \ -
    \ - '; - // $('.billingForm').html('

    No Billing Options Found!

    ' + checkOut); - callback(null, $.html()); - } else { - var _billing = results[0]; - showBillings(results, function(){ - callback(null, $.html()); - }); - // $('.billingForm').html(JSON.stringify(_billing, true, 2)); - } - }); - // callback(null, $.html()); - } - }); -}; diff --git a/view/blog/feed.html b/view/blog/feed.html new file mode 100644 index 00000000..e69de29b diff --git a/view/blog/feed.js b/view/blog/feed.js new file mode 100644 index 00000000..974e6fcf --- /dev/null +++ b/view/blog/feed.js @@ -0,0 +1,50 @@ +var RSS = require('rss'); + +module['exports'] = function view (opts, callback) { + + var self = this; + + var feedOptions = { + title: 'all things microservice, the hook.io microservice blog', + description: 'a blog about everything microservice related, maintained by the hook.io hosting platform', + feed_url: 'https://hook.io/blog/feed', + site_url: 'https://hook.io', + managingEditor: 'hook.io', + webMaster: 'hook.io', + copyright: '2016 hook.io', + language: 'en', + pubDate: 'Jul 29, 2015 15:38:30 PDT' + }; + + var feed = new RSS(feedOptions); + + function addItem (post) { + try { + var getData = new Function(post.$('.data').html() + ' return data;'); + var data = getData(); + feed.item(data); + } catch (err) { + console.log('blog rendering error', err); + } + }; + + var posts = [ + 'the-monolith-versus-the-microservice-a-tale-of-two-applications', + 'new-multi-language-support', + 'hook-in-your-language', + 'role-based-access-control', + 'websocket-hooks', + 'multi-cloud-virtual-file-system' + ]; + + posts.forEach(function(p){ + var post = self.parent[p]; + addItem(post); + }); + + var xml = feed.xml(); + callback(null, xml); +}; + +// cache this page indefintely +module['exports'].cache = 0; \ No newline at end of file diff --git a/view/blog/hook-in-your-language.html b/view/blog/hook-in-your-language.html new file mode 100644 index 00000000..05b892ee --- /dev/null +++ b/view/blog/hook-in-your-language.html @@ -0,0 +1,94 @@ + + + + +
    +

    Hook in your language

    + +
    + +

    Hello, Haló, Haloo, Allô, Hallo,こんにちは, Pronto, 여보세요, Hâlo, 喂, آلو, Алло, ¡Hola!, ฮัลโหล.

    + +

    + If you haven't heard yet, hook.io is a 100% open-source platform for Microservices. +

    +

    + Recently, we added support for 11+ software programming languages. +

    +

    + While it's a nice technical feature to support so many programming languages, it's equally important we support multiple written languages.

    +

    + +

    Bring on the i18n!

    + +

    Many open-source projects and businesses consider i18n internationalization an after-thought, a non-critical piece of functionality which takes away from your core focus.

    + +

    We believe that accessibility is key.

    + +

    Limiting your project to only one language excludes large segments of users. No one should be excluded. Everyone should have the right to use your project.

    + +

    + For these reasons, hook.io has released i18n internationalization support for our web interface. +

    +

    + This is the first step in a long process of ensuring hook.io is accessible to anyone with a web connected device. +

    + +

    A journey of a thousand miles begins with a small step.

    +

    + As of today, we have basic translations online for the German language. +

    + +

    + You can test this functionality by visiting hook.io with your browser agent set to de, or you can simply visit: https://hook.io?lang=de +

    + +

    Contributing to Language Support

    +

    + The neat thing about our i18n support, is that our locale translation files are located independently from any complicated source code. +

    +

    Anyone can make an addition or change to our language support simply by opening a Pull Request at: https://github.com/bigcompany/hook.io-i18n

    + +

    Our amazing i18n integration pulls from this repo and will automatically update the hook.io website with your new translation!

    + +

    Remember: Open-source counts on your contribution

    + +

    + We hope to hear from you in your language soon! +

    +
    +
    + +
    diff --git a/view/blog/index.html b/view/blog/index.html new file mode 100644 index 00000000..e3efe7c3 --- /dev/null +++ b/view/blog/index.html @@ -0,0 +1,17 @@ + +
    +
    +

    Recent Posts

    +
      +
    + +
    +
    +

    Subscribe:

    +
    +
    +
    \ No newline at end of file diff --git a/view/blog/index.js b/view/blog/index.js new file mode 100644 index 00000000..5df7a334 --- /dev/null +++ b/view/blog/index.js @@ -0,0 +1,36 @@ +var df = require('dateformat'); +var config = require('../../config'); + +module['exports'] = function view (opts, callback) { + + var self = this, $ = this.$; + + function appendPost (post) { + if(post.$) { + var getData = new Function(post.$('.data').html() + ' return data;'); + var data = getData(); + $('.posts').prepend('
  • ' + df(new Date(data.date), "mm/dd") + ' - ' + data.title + '
  • '); + } else { + console.log('not available', post.name) + } + }; + + var posts = [ + 'the-monolith-versus-the-microservice-a-tale-of-two-applications', + 'new-multi-language-support', + 'hook-in-your-language', + 'role-based-access-control', + 'websocket-hooks', + "multi-cloud-virtual-file-system" + ]; + + posts.forEach(function(p){ + var post = self.parent[p]; + appendPost(post); + }); + + callback(null, $.html()); +}; + +// cache this page indefintely +module['exports'].cache = 0; \ No newline at end of file diff --git a/view/blog/layout.html b/view/blog/layout.html new file mode 100644 index 00000000..7e737e64 --- /dev/null +++ b/view/blog/layout.html @@ -0,0 +1 @@ +
    \ No newline at end of file diff --git a/view/blog/layout.js b/view/blog/layout.js new file mode 100644 index 00000000..51fa4cca --- /dev/null +++ b/view/blog/layout.js @@ -0,0 +1,10 @@ +var config = require('../../config'); +module['exports'] = function view (opts, callback) { + var req = opts.req, + $ = this.$; + $('title').html('hook.io/blog - all things microservice, the hook.io microservice blog'); + + + //req.i18n.setLocale('de'); + return callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/blog/multi-cloud-virtual-file-system.html b/view/blog/multi-cloud-virtual-file-system.html new file mode 100644 index 00000000..5287586b --- /dev/null +++ b/view/blog/multi-cloud-virtual-file-system.html @@ -0,0 +1,97 @@ + + + +
    +

    Multi-cloud Virtual File System

    + +
    +
    + Posted by in Updates May 19, 2016 +
    +
    + +
    + +

    + hook.io is a 100% open-source hosting platform for webhooks and microservices. Our primary focus is delivering the best possible experience for developers looking to quickly and effortlessly host robust microservices.

    +

    + A highly requested feature from developers for our platform has been persistent file storage. In order to fulfill this requested functionality, we are happy to announce today our Multi-Cloud Virtual Filesystem +

    +

    This new Virtual File System is available through the HTTP REST API, the Client SDK, and directly inside of your Hook Services

    + +

    + What does this mean for hook.io users? You now have access to seamlessly manage files across any of these five cloud file storage adapters: + +

      +
    • Amazon S3
    • +
    • Google Cloud Files
    • +
    • Microsoft Azure
    • +
    • Rackspace Cloud Files
    • +
    • Any SSH/SFTP enabled server
    • +
    +

    + +

    To keep things very easy, we've created a common file-system API for communicating with any of these cloud providers. This one simple file-system API follows the same familiar API pattern as the Node.js core fs module. Here is an example using our zero-configuration built-in file storage:

    +

    + +

    +module['exports'] = function readFile (hook) {
    +  hook.fs.readFile('testing-file.txt', function(err, res){
    +    if (err){ 
    +      return res.end(err.message); 
    +    }
    +    hook.res.end(JSON.stringify(res, true, 2));
    +  });
    +};
    +
    + +

    +

    There are more forkable examples here. Using your own cloud provider credentials is as simple as passing in the right configuration options.

    + + +

    Start managing cloud files instantly

    +
    +

    + Interested in storing files instantly? We recommend starting at: https://hook.io/files +

    +

    From here you will be able to see live code examples for reading and writing to multiple cloud file providers. Simply pass in the correct configuration.

    +

    Remember, we also have support for any SFTP / SSH servers as well as a zero-configuration built-in file storage which requires no additional API access keys or registration with third party platforms.

    +

    Protip: You can store sensitive data as Environment Variables

    +

    + Stream all the multi-cloud files! +

    +
    + +
    diff --git a/view/blog/new-multi-language-support.html b/view/blog/new-multi-language-support.html new file mode 100644 index 00000000..f7772c3e --- /dev/null +++ b/view/blog/new-multi-language-support.html @@ -0,0 +1,128 @@ + + + + +
    +

    New multi-language support

    +
    +

    + hook.io is a 100% open-source platform for Microservices. +

    +

    +For the past year we have been working on making our JavaScript support full-featured.

    +

    While we love still love JavaScript, hook.io has now added support for ten programming languages outside of JavaScript.

    + +

    Newly Added Languages

    +
      +
    • Bash
    • +
    • Coffee-Script
    • +
    • Lua
    • +
    • PHP
    • +
    • Python
    • +
    • Perl
    • +
    • Ruby
    • +
    • Scheme
    • +
    • SmallTalk
    • +
    • Tcl
    • +
    + +

    To try this new functionality visit our Interactive Microservice Tutorial or simply select a new language when creating or editing a Hook service on hook.io

    +

    More blog posts will follow detailing the usage of each individual language.

    + + +

    Seeking Developer Community Managers

    + +

    + We are now seeking individual Developer Community Managers to help make the support for each new language full-featured. +

    +

    +Our idea is to have at least one domain expert contributing to hook.io for every language we support. +

    + +

    Why become a Developer Community Manager?

    + +
    +
      +
    • Get directly involved working with Microservices and the hook.io open-source project
    • +
    • See your favorite language have first-class support on hook.io
    • +
    • Add an impressive notch to your resume
    • +
    • Get free hosting from hook.io for all your Microservices
    • +
    • Be first in line for a paid position ( We love to hire contributors! )
    • +
    • The satisfaction of working on a good open-source project
    • +
    +
    +

    Responsibilities

    + +

    +Since the launch of our platform, the JavaScript language has been the primary target for Microservice features ( such as streaming http / logging / datastore / npm modules ) +

    +

    +It's imperative that every new language we add can offer the same feature-set, level of functionality, and experience of our JavaScript support. +

    +

    +Your primary responsibility as a Developer Community Manager will be working with the hook.io platform and user-base to ensure that your language has first-class support on hook.io +

    +

    Compensation

    + +

    Developer Community Managers are currently a volunteer based position. The amount of work you do depends on how well you wish to see your target language implemented.

    + +

    +A featured section will be created on the hook.io website for all Developer Community Managers to optionally list their profile / website. +

    +

    +hook.io is currently raising a seed investment. Once we have raised seed funding, paid positions will be available.

    We love to hire contributors! +

    + + +

    How to Apply

    +

    +We currently have several open issues on Github discussing which languages to add and how to implement them into the stack. +

    + +

    +We also have an irc room #hook.io on irc.freenode.net +

    + +

    +If you are interested in becoming a Developer Community Manager for a specific language, please feel free to open up a Github Issue or start a conversation in our irc channel. +

    +
    + +

    + We look forward to hearing from you soon! +

    +
    +
    + +
    \ No newline at end of file diff --git a/view/blog/role-based-access-control.html b/view/blog/role-based-access-control.html new file mode 100644 index 00000000..8e556442 --- /dev/null +++ b/view/blog/role-based-access-control.html @@ -0,0 +1,115 @@ + + + + +
    +

    Role Based Access Control

    + +
    + +

    + If you haven't heard yet, hook.io is the 100% open-source platform for microservices. +

    +

    + hook.io is a full-featured platform that supports 12+ software programming languages, even ECMAScript 7! +

    +

    + With over 32 million successful deployments, one of our most requested features in the past few months has been private Services and API access keys. +

    +

    + Today, we are happy to announce support for Role Base Access Control and API Access keys.

    +

    + +

    Private Services!

    + +

    It's now very easy to restrict public access to hook.io services.

    +

    To quickly get started, new security settings are now available when creating or editing hooks.

    +

    For more information on how this works, keep on reading. + +

    Easy to use Role Based Permissions!

    + +

    hook.io provides a very simple mechanism for creating API Access keys which are mapped to a custom list of roles. These roles represent hook.io system events, which are subject to role based permission checks.

    + +

    Role checks for API access are performed by sending a valid hook_private_key HTTP parameter in client request targeting hook.io

    +

    A hook_private_key can be provided by any of the following formats: query string, json, form, multipart-form, or http header value.

    + +

    Key Based API Access

    + +

    Multiple API Access Keys can be created with custom roles, allowing granular role based permission access for any number of users or third-party services accessing hook.io

    + +

    Third-Party Developer Support

    + +

    + In addition to implementing API Access Keys, all service endpoints on hook.io are now exposed as HTTP API endpoints with role based permission checks. +

    +

    + For example, you can now access the /logs or the /datastore for your account from a third-party service if you provide a valid hook_private_key parameter. +

    + +

    + The best way to see this in action is to visit the new Datastore HTTP API Endpoint. +

    + +

    Streaming System Events

    + +

    + All events and role checks on hook.io are now logged at the /events endpoint. +

    + +

    The System Events API endpoint provides a high level of visibility into when your services on hook.io are being accessed, and which client is accessing them. + +

    The events API endpoint is widely accessible and is capable of providing streaming responses.

    + +

    Business Grade Security and Service

    + +

    + With this new addition of Role Based Access Control and System Events, hook.io can now safely protect services which must remain private.

    +

    + +

    + This new seamlessly functionality provide secure services to third-party applications, allowing a whole new class of applications to be developed!

    +

    + +

    + To try out this new functionality, create a free account at hook.io and then visit /keys +

    + +

    + We hope to hear about the services you build soon! +

    +
    +
    + +
    diff --git a/view/blog/the-monolith-versus-the-microservice-a-tale-of-two-applications.html b/view/blog/the-monolith-versus-the-microservice-a-tale-of-two-applications.html new file mode 100644 index 00000000..bb2d24ea --- /dev/null +++ b/view/blog/the-monolith-versus-the-microservice-a-tale-of-two-applications.html @@ -0,0 +1,144 @@ +
    + + +
    + +

    The Monolith versus The Microservice

    +

    A Tale of Two Applications

    +

    Date: Wed Jul 29 2015 15:38:30 GMT-0700 (PDT)
    +Author: Marak +

    +

    The source code used in this post can be found here on Github

    +

    Preface

    +

    The current standard approach to building backend HTTP services is monolithic. An HTTP server process is responsible for any amount of "routes". A route represents a unique url pattern that is associated with a unique piece of functionality ( the route handler ).

    +

    The problem with this monolithic approach is that since every route is sharing the same process and server state, potential issues in one route can affect the state of the entire application or server. This is bad.

    +

    The Monolith approach to application design leads to having to worry about the global state of the entire application any time a single route is modified.

    +

    The Microservice approach to application design offers a clear path to improving the stability and scalability of an application by isolating it's services into distinct composable units.

    +

    A New Company Builds A Web Application

    +

    A brand new technology company emerges. They must build a new web application to power their business.

    +

    The application must contain the following sections:

    +
    /       - Index / Company Homepage
    +/about  - Information About the Company
    +/signup - Sign Up for Company Product
    +
    + +

    The new company quickly gets to work, but unfortunately for our new company, their development team is not so great. During development they introduce a few bugs into the application.

    +

    Server

    +

    First they build the server component. This actually looks pretty good for now. Good job team!

    +
    var express = require('express');
    +var app = express();
    +
    +app.get('/', require('../routes/index'));
    +app.get('/about', require('../routes/about'));
    +app.get('/signup', require('../routes/signup'));
    +
    +app.listen(9999);
    +
    +

    Index Page

    +

    Next, they move on to building the index section. This page looks good. No mistakes yet!

    +
    module['exports'] = function index (req, res) {
    +  res.write('<h1>Welcome to our awesome website!<br/>');
    +  res.write('<a href="{{appUrl}}/about">About</a><br/>');
    +  res.write('<a href="{{appUrl}}/signup">Signup</a><br/>');
    +  res.end();
    +};
    +
    +

    About Page

    +

    Moving on to the about section, the development team makes a minor mistake.

    +
    var someHTML = '';
    +module['exports'] = function about (req, res) {
    +  function buildHTML() {
    +    someHTML += '<h1>This is our awesome about page! Please buy our stuff!</h1>';
    +    return someHTML;
    +  }
    +  res.end(buildHTML());
    +};
    +
    +

    This is a contrived example, but the error should be clear.

    +

    The developer has declared the someHTML variable in the wrong place and has broken encapsulation. The someHTML variable will continue to append to itself until the server runs out of memory.

    +

    Introducing additional state outside the scope of the route handler breaks functional encapsulation and can cause bad things can happen.

    +

    Let's not worry about this error for now. We'll get to see it in action later.

    +

    Signup Page

    +

    Finally, after several weeks of arduous development the team finishes the last section, signup.

    +
    module['exports'] = function signup (req, res) {
    +  while(true);
    +};
    +
    +

    Looks like the development team really blew it on the signup page!

    +

    At least the office feels a bit warmer today.

    +

    The signup page is another contrived example, but it simulates what can happen with bad code. This route handler both fails to call res.end and puts the process into an infinite loop. Running this page will cause the server to hit 100% CPU ( and get quite hot ).

    +

    Deployment

    +

    Since the new company didn't have a budget for testing or quality assurance, the site was deployed that week on Friday afternoon.

    +

    Unfortunately for the new company, their site went down almost immediately.

    +

    Debugging the issues was complicated, partly because the server kept locking up due to lack of available RAM and CPU.

    +

    Ultimately, a key potential customer was unable to access any sections of the site on launch and choose to signup with a competitor. The company failed.

    +

    The Same Application built with Microservices

    +

    Now, the same development story with the same development team. The only difference this time is that instead of building their application as a monolithic service, the company choose to use microservices.

    +

    Server

    +

    First, they built the front-facing HTTP server. This acts a load balancer, sending incoming requests to the worker cloud with a round-robin strategy.

    +

    The team chooses to use the run-remote-service npm module, which is a thin component responsible for proxying HTTP requests. They could have just as easily used the request module instead.

    +
    var express = require('express');
    +var runRemoteService = require('run-remote-service')({ 
    +  pool: ["10000", "10001", "10002", "10003", "10004"] 
    +});
    +
    +var server = {};
    +
    +server.listen = function () {
    +  var app = express();
    +  app.get('/', runRemoteService);
    +  app.get('/about', runRemoteService);
    +  app.get('/signup', runRemoteService);
    +  app.listen(9999);
    +};
    +
    +module['exports'] = server;
    +
    +

    You may notice the worker pool is hard-coded in this example. With a bit of imagination you can imagine an elastic worker pool where workers can register themselves with the load balancer at run-time.

    +

    Worker

    +

    Second, they built the worker component, which is used to execute services. Workers receive incoming HTTP requests from the server and execute service source code in response to these requests.

    +

    The team chooses to use the run-service npm module which acts a thin layer of abstraction for executing untrusted source code in response to incoming HTTP requests.

    +

    The run-service module provides a level of control over service execution to ensure that any issues within the service remain isolated and always trapped and returned to the server in a meaningful way.

    +
    var argv = require('minimist')(process.argv.slice(2));
    +var port = argv.p || 10000;
    +var runService = require('run-service');
    +var express = require('express');
    +var worker = {};
    +worker.start = function (opts, cb) {
    +  var app = express();
    +  app.post('/', runService);
    +  app.post('/about', runService);
    +  app.post('/signup', runService);
    +  app.listen(port);
    +};
    +module['exports'] = worker;
    +
    +

    Building out the pages

    +

    After completing the server and worker, the development team builds the same routes for index, about, and signup in the exact same way as before.

    +

    The second application still contains the exact same bugs that brought down the first application.

    +

    Deployment

    +

    There is still no budget for testing or quality assurance, so the team again deploys the application on Friday afternoon.

    +

    Surprisingly, the site almost works! Great job team!

    +

    The index and about sections are working flawlessly. The out of scope someHTML variable in about causes no issues in this version of the application as every service is now stateless.

    +

    The signup page is displaying a timeout error. The error stated that the total amount of execution time for the service had exceeded. It suggested checking for infinite loops or that res.end() had not been called. The team was able to quickly identify the issue and had the site working within a few hours.

    +

    The team was also able to fix the signup page without ever having to take down the application, or the index and about sections. Since all routes are isolated, they were able to modify signup.js without taking down the front-facing server or any other pages.

    +

    On launch, the same key customer was able to access the site and retrieve information about the product. The customer was unable to signup for the service, but came back the next day and was able to sign up. This key customer was crucial to the early success of the business and the new company was able to succeed.

    +

    Conclusion

    +

    The tale of these two applications is not about the contrived examples of forgetting to call res.end, or accidentally putting the someHTML variable in the wrong place.

    +

    The tale of these two applications is about application design choice.

    +

    Instead of a programming error, the server could have just as easily ran out of resources. In high-traffic situations, you may find the need to isolate specific routes onto separate servers.

    +

    Do you really want to have your entire application bound to a single monolith where a minor issue in a single route can take down the entire application?

    +

    The Monolith is limited and brittle. The Microservice is scalable and robust.

    +

    Moving forward, I highly suggest you begin to migrate towards integrating microservices into your stack.

    +

    Looking for an easy way to get your microservices hosted online? Check out https://hook.io.

    +


    +
    \ No newline at end of file diff --git a/view/blog/websocket-hooks.html b/view/blog/websocket-hooks.html new file mode 100644 index 00000000..187fee77 --- /dev/null +++ b/view/blog/websocket-hooks.html @@ -0,0 +1,79 @@ + + + +
    +

    Websocket Hooks

    + +
    + +

    + We are happy to introduce a brand new way of sending data to any Hook service endpoint, Websockets! +

    +

    This new functionality enables a bridge between Websocket connections and Webhook microservices.

    +

    + Through a standard ws:// connection, you can now connect to any existing hook.io microservice though Websockets. +

    +

    + hook.io will hold the Websocket connection, attempting to parse any incoming messages as JSON and forward them to an existing hook service. +

    +

    If a string or non-JSON message is received, hook.io will simply pass the data to the hook service as hook.params.body +

    Any Websocket client will work, including the browser.

    + +

    Below is an example of using the wscat tool to connect to a hook.io service over Websockets to send and receive data.

    + +

    Example

    +

    wscat -c ws://hook.io/examples/echo

    + +
    +

    Simple as pie!

    + +

    Binary Data Support / Websocket Transform Streams

    + +

    + Currently, hook.io Websockets can only receive data as text or JSON messages. In the near future, we will most likely add support for streaming binary data over Websocket with the ability to perform transforms on the data stream. +

    +

    Remember, you can still perform streaming transforms of binary data using the regular HTTP API:

    +echo "foo" | curl --header "content-type: application/octet-stream" --data-binary @- http://hook.io/marak/transform
    +

    +

    + If you are interested in helping with this 100% open-source hook.io project, come join the discussion at: https://github.com/bigcompany/hook.io +

    + +

    + Go Websockets! +

    +
    + +
    diff --git a/view/chat.html b/view/chat.html new file mode 100644 index 00000000..0284abad --- /dev/null +++ b/view/chat.html @@ -0,0 +1,3 @@ +
    + --> + + + + + \ No newline at end of file diff --git a/view/components/themeSelector.js b/view/components/themeSelector.js index c3059f2f..10105f2c 100644 --- a/view/components/themeSelector.js +++ b/view/components/themeSelector.js @@ -1,3 +1,13 @@ module['exports'] = function view (opts, callback) { + var self = this, $ = self.$; + if (opts.theme) { + $('#theme').attr('value', opts.theme); + var themes = opts.themes; + } + if (opts.presenter) { + $('#presenter').attr('value', opts.presenter); + } callback(null, this.$.html()); -}; \ No newline at end of file +}; + +module['exports'].useLayout = false; \ No newline at end of file diff --git a/view/contact.html b/view/contact.html new file mode 100644 index 00000000..6847d59b --- /dev/null +++ b/view/contact.html @@ -0,0 +1,79 @@ + + +
    +

    Contact Us

    +
    +
    +
    +
    +
    + Contact Information +
    +
      +
    • +
      + +
      + +
      +
      + + +
    • + +
    • +
      + +
      + +
      +
      +
    • +
    • +
      + +
      + +
      +
      +
    • +
    +
    +
    +

    * Required Fields

    +
    + +
    +
    +
    + +
    + We have received your message. Thank you!
    + Someone will get back to you soon. +
    +
    +
    \ No newline at end of file diff --git a/view/contact.js b/view/contact.js new file mode 100644 index 00000000..3e1aa97c --- /dev/null +++ b/view/contact.js @@ -0,0 +1,52 @@ +var email = require('resource-email'); +var config = require('../config'); +var psr = require('parse-service-request'); + +module['exports'] = function view (opts, callback) { + var req = opts.req, + res = opts.res, + $ = this.$; + + // Only allow logged in users to send contact emails + // It could be better to have the contact form not require a login, but this is necessary to help prevent email spam from bots which auto submit the contact forms + if (!req.isAuthenticated()) { + req.session.redirectTo = req.url; + return res.redirect(302, '/login?restricted=true'); + } + + psr(req, res, function(){ + var params = req.resource.params; + + if ((typeof params.subject === "string" && typeof params.comment === "string") && (params.subject.length > 0 || params.comment.length > 0)) { + var _config = { + //provider: 'sendgrid', + provider: config.email.provider, + api_user: config.email.api_user, + api_key: config.email.api_key, + to: "marak@hook.io", + from: params.email, + subject: 'hook.io - contact - ' + params.accountName + ' ' + params.subject, + html: params.comment + }; + email.send(_config, function (err, result) { + if (err) { + return res.end(err.message); + } + $('#contactForm').remove(); + return callback(null, $.html()); + }); + } else { + + $('#email').attr('value', req.session.email); + $('#accountName').attr('value', req.session.user); + + if (typeof params.t === "string") { + $('#Subject').attr('value', params.t); + } + $('.sent').remove(); + return callback(null, $.html()); + } + + }); + +}; \ No newline at end of file diff --git a/view/cron.html b/view/cron.html deleted file mode 100644 index 1601c699..00000000 --- a/view/cron.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - -
    -

    Cron

    -

    All Hooks support the ability to schedule their execution based on a date time pattern.

    -

    We call this date time pattern a Cron.

    -

    By design, Crons will not pass any data into the Hook. If you need to pass data into the scheduled Hook, you can simply set default data in the Hook or chain two Hooks together to pipe the data.

    - -

    Create a New Hook Now

    - -

    Example Cron Patterns

    -
    - Additional Cron Documentation -

    -

    -
    \ No newline at end of file diff --git a/view/cron.js b/view/cron.js deleted file mode 100644 index c3059f2f..00000000 --- a/view/cron.js +++ /dev/null @@ -1,3 +0,0 @@ -module['exports'] = function view (opts, callback) { - callback(null, this.$.html()); -}; \ No newline at end of file diff --git a/view/cron/admin.html b/view/cron/admin.html new file mode 100644 index 00000000..b9359526 --- /dev/null +++ b/view/cron/admin.html @@ -0,0 +1,782 @@ + + +
    +

    Manage Cron Job

    +
    +
    +
    + +
    +
    + + + +
    + +
    + + +
    + +
    +
    +
    + + +
    +
    +
    + +
    +
    + +
    + +
    + +   + +
    + +
    +
    + +
    + +
    +
    +
    + + + Update +
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +

    + If you require a higher timeout value please Contact Support +

    +
    + +
    + + + +
    +
    +
    +
    + + +
    + Danger Zone: + ||Delete Cron|| +
    +
    + +
    + + + + +
    +
    + + + + + + + \ No newline at end of file diff --git a/view/cron/admin.js b/view/cron/admin.js new file mode 100644 index 00000000..52fee1cf --- /dev/null +++ b/view/cron/admin.js @@ -0,0 +1,145 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var mschema = require('mschema'); +var cron = require('../../lib/resources/cron/cron'); +var user = require('../../lib/resources/user'); +var servicePlan = require('../../lib/resources/servicePlan'); + +var resource = require('resource'); +var web = require('../../lib/web/web'); + +module.exports = function cronAdminPresenter (opts, cb) { + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + var self = this; + + $ = req.white($); + + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = "error"; + return res.json(validate); + } + + // params.owner = req.session.user; + checkRoleAccess({ req: req, res: res, role: 'cron::update' }, function (err, hasPermission) { + // console.log('check for role access', err, hasPermission) + if (!hasPermission) { + if (req.jsonResponse !== true && typeof params.hook_private_key === 'undefined') { + req.session.redirectTo = req.url; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, 'cron::update')); + } else { + var name; + if (typeof params.previousName !== 'undefined') { + name = params.previousName.toLowerCase(); + } else { + name = params.name.toLowerCase(); + } + if (typeof name === 'undefined' || name.length === 0) { + return res.redirect('/cron/' + req.session.user); + } + cron.findOne({ owner: params.owner, name: name }, async function (err, c) { + if (err) { + return web.handle404(req, res); + } + user.findOne({ name: params.owner }, function (err, u) { + if (err) { + return res.end(err.message); + } + req.session.servicePlan = u.servicePlan || 'free'; + req.session.timezone = u.timezone || 'America/New_York'; + req.session.serviceLimits = servicePlan[u.servicePlan]; + req.cron = c; + finish(); + }); + }); + } + }); + }); + + function finish () { + var data = req.resource.params; + + if (req.method === 'GET') { + if (!req.isAuthenticated()) { + $('.keys').remove(); + $('.myKeys').remove(); + req.session.redirectTo = '/cron/create'; + return res.redirect('/login'); + } else { + // load the cron and databind values + var c = req.cron; + $('.cronName').val(c.name); + $('.cronPreviousName').val(c.name); + $('.cronOwner').val(c.owner); + $('.cronExpression').val(c.cronExpression); + $('.cronURI').val(c.uri); + $('.deleteLink').attr('href', '/cron/' + c.owner + "/" + c.name + "/destroy"); + $('.deleteLink').attr('data-name', (c.owner + "/" + c.name)); + if (c.status === 'paused') { + $('input[name="status"][value="paused"]').attr('checked', 'CHECKED'); + } else { + $('input[name="status"][value="active"]').attr('checked', 'CHECKED'); + } + var boot = { + baseUrl: config.app.url, + owner: req.session.user, + name: req.resource.params.name + }; + var out = $.html(); + out = out.replace('{{cron}}', JSON.stringify(boot, true, 2)); + return cb(null, out); + } + return; + } + data.id = req.cron.id; + data.owner = req.cron.owner; + data.servicePlan = req.session.servicePlan; + return cron.update(data, function (err, result) { + if (err) { + // TODO: generic error handler + return res.end(err.message); + } + resource.emit('cron::updated', { + ip: req.connection.remoteAddress, + owner: req.cron.owner, + name: data.name + }); + var key = '/' + data.owner + '/' + data.name; + if (req.jsonResponse) { + var rsp = { + 'status': 'updated', + 'key': key, + 'value': result + }; + return res.json({ status: 'updated', cron: result }); + } + return res.redirect('/admin?owner=' + req.cron.owner + '&name=' + data.name + '&status=saved'); + }); + cb(null, 'updated'); + + } + +}; + +module['exports'].schema = { + "name": { + type: 'string' + }, + "owner": { + type: 'string', + required: true + } +}; \ No newline at end of file diff --git a/view/cron/all.html b/view/cron/all.html new file mode 100644 index 00000000..f0bd0b9c --- /dev/null +++ b/view/cron/all.html @@ -0,0 +1,74 @@ + + + + +
    +

    Cron Jobs

    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    It looks like you haven't created any Cron Jobs yet.

    + +
    + + + + + + + + + + +
    NameExpressionRanLast RunNext EstimatedStatus 
    +
    + +
    +
    +
    \ No newline at end of file diff --git a/view/cron/all.js b/view/cron/all.js new file mode 100644 index 00000000..eab674fe --- /dev/null +++ b/view/cron/all.js @@ -0,0 +1,111 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var mschema = require('mschema'); +var cron = require('../../lib/resources/cron/cron'); +var metric = require('../../lib/resources/metric'); + +var html = require('../helpers/html'); +var fnst = require('date-fns-timezone'); + +module['exports'] = function allCronPresenter (opts, cb) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + $ = req.white($); + + var self = this; + + psr(req, res, function (req, res, fields) { + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + checkRoleAccess({ req: req, res: res, role: 'cron::read' }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, 'cron::read')); + } else { + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = 'error'; + return res.json(validate); + } else { + return cron.find({ owner: req.resource.owner }, function (err, results) { + if (err) { + return res.end(err.message); + } + if (results.length === 0) { + $('.crons').remove(); + } else { + $('.nocrons').remove(); + } + var keys = [], metricKeys = []; + results.forEach(function (key){ + keys.push('/' + key.owner + '/' + key.name); + metricKeys.push('/cron/' + key.owner + '/' + key.name); + }); + + metric.batchGet(metricKeys, function (err, metrics) { + cron.batchGet(keys, function (err, crons) { + var arr = []; + results.forEach(function(r, i){ + if (r) { + var cached = crons['/' + r.owner + '/' + r.name] || { + lastExecutionDate: 'n/a', + nextExecutionDate: 'n/a' + }; + var hits = metrics['/cron/' + r.owner + '/' + r.name] || "0"; + var timezone = req.session.timezone || 'America/New_York'; + var lastExecutionDate = 'n/a'; + var nextExecutionDate = 'n/a'; + + if (cached.lastExecutionDate) { + lastExecutionDate = fnst.formatToTimeZone(cached.lastExecutionDate, 'MMMM DD, YYYY HH:mm:ss z', { timeZone: timezone }); + } + if (cached.nextExecutionDate) { + nextExecutionDate = fnst.formatToTimeZone(cached.nextExecutionDate, 'MMMM DD, YYYY HH:mm:ss z', { timeZone: timezone }); + } + if (lastExecutionDate === 'Invalid Date') { + lastExecutionDate = 'n/a'; + } + if (nextExecutionDate === 'Invalid Date') { + nextExecutionDate = 'n/a'; + } + arr.push({ + name: r.name, + owner: r.owner, + cronExpression: r.cronExpression, + hits: hits, + lastExecutionDate: lastExecutionDate, + nextExecutionDate: nextExecutionDate, + status: r.status + }) + $('.crons').append(html.rowToString([ + html.makeLink(config.app.url + '/cron/' + r.owner + '/' + r.name, r.name), + r.cronExpression, + hits, + lastExecutionDate, + nextExecutionDate, + r.status, + html.makeLink(config.app.url + '/cron/' + r.owner + '/' + r.name + '/admin', 'Edit'), + ])); + } + }); + if (req.jsonResponse) { + return res.json(arr); + } + return cb(null, $.html()); + }); + }); + }); + } + } + }); + } +}; diff --git a/view/cron/get.html b/view/cron/get.html new file mode 100644 index 00000000..b5e00830 --- /dev/null +++ b/view/cron/get.html @@ -0,0 +1,102 @@ + + + + +
    +

    Cron Jobs

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name:
    Status:Paused
    URI:123
    Method:GET
    Expression:n/a
    Next Execution Time:n/a
    Last Execution Time:n/a
    Total Executions:n/a
    Edit this Cron
    +
    +
    +
    +
    +
    diff --git a/view/cron/get.js b/view/cron/get.js new file mode 100644 index 00000000..82421654 --- /dev/null +++ b/view/cron/get.js @@ -0,0 +1,103 @@ +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var cron = require('../../lib/resources/cron/cron'); +var metric = require('../../lib/resources/metric'); +var cache = require('../../lib/resources/cache'); +var df = require('dateformat'); +var mschema = require('mschema'); +var psr = require('parse-service-request'); + +module.exports = function (opts, cb) { + var $ = this.$, + req = opts.req, + res = opts.res; + $ = req.white($); + var self = this; + psr(req, res, function(){ + + if (typeof req.params === 'object') { + Object.keys(req.params).forEach(function (p) { + req.resource.params[p] = req.params[p]; + }); + } + + checkRoleAccess({ req: req, res: res, role: 'cron::read' }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, 'cron::read')); + } else { + + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = "error"; + return res.json(validate); + } else { + finish(); + } + } + }); + }); + + function finish () { + cron.findOne({ + owner: req.params.owner, + name: req.params.name + }, function (err, c) { + if (err) { + return res.end(err.message); + } + $('.cronName').html(c.name); + cache.get('/cron/' + req.params.owner + '/' + req.params.name, function (err, cached) { + if (cached === null) { + return res.end('Something went wrong getting cached cron. Please contact support.'); + } + metric.get('/cron/' + req.params.owner + '/' + req.params.name, function (err, metrics) { + /* + $('.json').html(JSON.stringify(c, true, 2)); + $('.json').append(JSON.stringify(cached, true, 2)); + $('.json').append(JSON.stringify(metrics, true, 2)); + */ + var obj = { + name: c.name, + owner: c.owner, + method: c.method, + status: c.status, + cronExpression: c.cronExpression, + uri: c.uri, + hits: metrics, + lastExecutionDate: df(cached.lastExecutionDate, 'mm/dd/yyyy HH:MM:ss Z'), + nextExecutionDate: df(cached.nextExecutionDate, 'mm/dd/yyyy HH:MM:ss Z') + }; + if (req.jsonResponse) { + return res.json(obj); + } + $('.totalExecutions').html(metrics || 'n/a'); + if (cached.lastExecutionDate) { + $('.lastExecutionDate').html(obj.lastExecutionDate); + } + if (cached.nextExecutionDate) { + $('.nextExecutionDate').html(obj.nextExecutionDate); + } + $('.cronMethod').html(c.method); + $('.cronURI').html(c.uri); + $('.cronExpression').html(c.cronExpression); + $('.cronStatus').html(c.status); + $('.editLink').attr('href', config.app.url + '/cron/' + c.owner + '/' + c.name + '/admin'); + $('.testLink').attr('href', config.app.url + '/cron/' + c.owner + '/' + c.name + '/test'); + cb(null, $.html()); + }); + }); + }); + } + +}; + +module.exports.schema = { + 'owner': { + type: 'string', + required: true + }, + 'name': { + type: 'string', + required: true + } +}; \ No newline at end of file diff --git a/view/cron/index.html b/view/cron/index.html new file mode 100644 index 00000000..a1d4ae67 --- /dev/null +++ b/view/cron/index.html @@ -0,0 +1,65 @@ + + + + + + +
    +

    Cron

    +
    +
    +
    +
    + +
    +
    +
    +
    +

    A time-based job scheduler for making web requests. An alternate to Linux Cron

    + +
    +
    +
    +
    \ No newline at end of file diff --git a/view/cron/index.js b/view/cron/index.js new file mode 100644 index 00000000..c7b569a2 --- /dev/null +++ b/view/cron/index.js @@ -0,0 +1,82 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var mschema = require('mschema'); +var cron = require('../../lib/resources/cron/cron'); + +module['exports'] = function allCronPresenter (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + $ = req.white($); + + var self = this; + + if (!req.isAuthenticated()) { + $('.crons').remove(); + return callback(null, $.html()); + } else { + $('.loginLink').remove(); + } + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function rowToString (row) { + var str = ''; + for (var col in row) { + var val = row[col]; + if (!val) { + val = ' '; + } + str += '' + val + ''; + } + //str += '' + r.name + '' + r.cronPattern + ''; + str += ''; + return str; + } + + function makeLink (url, text) { + var str = '' + text + ''; + return str; + } + + function finish () { + checkRoleAccess({ req: req, res: res, role: 'cron::read' }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, 'cron::read')); + } else { + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = 'error'; + return res.json(validate); + } else { + return cron.find({ owner: req.resource.owner }, function (err, result) { + if (err) { + return res.end(err.message); + } + result.forEach(function(r){ + $('.crons').append(rowToString([ + makeLink(config.app.url + '/cron/' + r.owner + '/' + r.name, r.name), + r.cronExpression, + undefined, + undefined, + undefined + ])); + }); + return callback(null, $.html()); + }); + } + } + }); + + } + +}; \ No newline at end of file diff --git a/view/cron/new.html b/view/cron/new.html new file mode 100644 index 00000000..ed640a40 --- /dev/null +++ b/view/cron/new.html @@ -0,0 +1,253 @@ + + + + + + +
    +

    Create New Cron Job

    +
    +
    +
    +
    +

    Cron Job

    + + Submitting this form will register a new cron service. + +
    + +
    + + +
    +
    + + +
    +
    +
    + Note: The Cron will not execute until it has been further configured and activated. +
    +
    + +
    + +
    + + +
    + + diff --git a/view/cron/new.js b/view/cron/new.js new file mode 100644 index 00000000..d8bb4c39 --- /dev/null +++ b/view/cron/new.js @@ -0,0 +1,86 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var mschema = require('mschema'); +var cron = require('../../lib/resources/cron/cron'); +var resource = require('resource'); + +module['exports'] = function createCronPresenter (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + var self = this; + + $ = req.white($); + + if (req.method === 'GET') { + if (!req.isAuthenticated()) { + req.session.redirectTo = '/cron/new'; + return res.redirect('/login'); + } else { + return callback(null, $.html()); + } + } + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + checkRoleAccess({ req: req, res: res, role: 'cron::create' }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, 'cron::create')); + } else { + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = 'error'; + res.status(400); + return res.json(validate); + } else { + req.resource.params.owner = req.resource.owner; + req.resource.params; + return cron.create({ + name: req.resource.params.name, + owner: req.resource.params.owner, + uri: req.resource.params.uri, + cronExpression: req.resource.params.cronExpression, + status: req.resource.params.status + }, function (err, result) { + if (err) { + res.status(400); + return res.json({ error: true, message: err.message }); + } + resource.emit('cron::created', { + ip: req.connection.remoteAddress, + owner: req.resource.params.owner, + name: req.resource.params.name + }); + if (req.jsonResponse) { + return res.json(result); + } else { + return res.redirect(config.app.url + '/cron/' + result.owner + '/' + result.name + '/admin'); + } + }); + } + } + }); + } + +}; + +module['exports'].schema = { + name: { + type: 'string', + required: true + }, + uri: { + type: 'string', + required: true, + regex: /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/ + } +}; \ No newline at end of file diff --git a/view/cron/status.html b/view/cron/status.html new file mode 100644 index 00000000..66cf201f --- /dev/null +++ b/view/cron/status.html @@ -0,0 +1,70 @@ + + + + +
    +

    Cron Status

    +
    +
    +
    +
    + +
    +
    +
    +
    + + + + + + + + + +
    StatusOnline
    Last Checked
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/view/cron/status.js b/view/cron/status.js new file mode 100644 index 00000000..6af6f3cf --- /dev/null +++ b/view/cron/status.js @@ -0,0 +1,22 @@ +let cron = require('../../lib/resources/cron/cron'); +let dateformat = require('dateformat'); +let fnst = require('date-fns-timezone') +let fns = require('date-fns') + +module.exports = function (opts, cb) { + var $ = this.$, + req = opts.req, + res = opts.res; + $ = req.white($); + cron.getStatus(function (err, result) { + if (err) { + return res.end(err.message); + } + var date = result.lastCronBatch; + var timezone = req.session.timezone || 'America/New_York'; + const zonedDate = fnst.formatToTimeZone(date, 'MMMM DD, YYYY HH:mm:ss z', { timeZone: timezone }); + $('.lastCronBatch').html(zonedDate); + $('.status').html(JSON.stringify(result, true, 2)) + cb(null, $.html()); + }) +} \ No newline at end of file diff --git a/view/curl.html b/view/curl.html index a8ad3a96..c5833d1a 100644 --- a/view/curl.html +++ b/view/curl.html @@ -1,7 +1,4 @@ +
    -

    Curl

    -

    Curl is a command line tool for making HTTP requests. It comes prepackaged on most machines or can be easily installed.

    +

    Curl

    +
    +

    Curl is a command line tool for making HTTP requests. It comes prepackaged on most machines or can be easily installed.

    + +

    Curl is useful not only for debugging simple requests, but also connecting your Hooks directly to the Unix Toolchain through standard Unix Pipes.

    + + + +

    Simple GET Request

    + +

    To perform a simple HTTP Get request with query string variables try the following command:

    -

    Curl is useful not only for debugging simple requests, but also connecting your Hooks directly to the Unix Toolchain through standard Unix Pipes.

    +

    + +

    -

    Simple GET Request

    +
    -

    To perform a simple HTTP Get request with query string variables try the following command:

    +

    Urlencoded Form Post

    -

    - -

    +

    To perform an HTTP POST form submission with some data fields try the following command:

    -
    +

    + +

    -

    Urlencoded Form Post

    +
    -

    To perform an HTTP POST form submission with some data fields try the following command:

    +

    Simple JSON Post

    -

    - -

    +

    To perform a simple HTTP JSON POST with some data fields try the following command:

    -
    +

    + +

    -

    Multipart file upload

    +
    -

    To HTTP POST multipart form files, you'll need a file to send. Assuming "cat.png" is an image on your local machine try the following command:

    +

    Multipart file upload

    -

    - -

    +

    To HTTP POST multipart form files, you'll need a file to send. Assuming "cat.png" is an image on your local machine try the following command:

    -
    +

    + +

    -

    Piping Binary Data Streams

    +
    -

    To stream binary data try one of the following commands:

    +

    Piping Binary Data Streams

    -

    - This example will pipe the output of echo to a simple transform Hook that will append text to the stream. - -

    -
    -

    - This example will cat the local "cat.png" file, pipe it to an image resize Hook and then pipe the result back to the local file system as "upsidedown-cat.png". - -

    +

    To stream binary data try one of the following commands:

    -

    Use Your Imagination

    -

    There are a plethora of Unix Tools available that have been in development for decades.

    -

    Think of the Unix tool-chain and the possibilities of connecting it directly to another set of tools ( Hooks ) which can natively pipe data around the web.

    Combining these tool-chains can yield very powerful results. Try creating a Hook which does that now.

    +

    + This example will pipe the output of echo to a simple transform Hook that will append text to the stream. + +

    + +

    Use Your Imagination

    +

    There are a plethora of Unix Tools available that have been in development for decades.

    +

    Think of the Unix tool-chain and the possibilities of connecting it directly to another set of tools ( Hooks ) which can natively pipe data around the web.

    Combining these tool-chains can yield very powerful results. Try creating a Hook which does that now.

    -
    -

    +
    +

    +
    \ No newline at end of file diff --git a/view/curl.js b/view/curl.js index c3059f2f..d4ddc4a2 100644 --- a/view/curl.js +++ b/view/curl.js @@ -1,3 +1,5 @@ module['exports'] = function view (opts, callback) { - callback(null, this.$.html()); + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); }; \ No newline at end of file diff --git a/view/databases.html b/view/databases.html new file mode 100644 index 00000000..cc7da070 --- /dev/null +++ b/view/databases.html @@ -0,0 +1,115 @@ +
    +

    Databases

    +
    +
    + +
    +
    + + + + + + + + +
    +
    + + +
    + +
    +
    + +

    Multi-cloud Databases

    + +

    {{appName}} works with all major known database technologies.

    +

    We've listed some of the Database we've tested, but most Database technologies should work.

    + +

    Supported Databases

    +
      +
    • Built-in Datastore
    • +
    • CouchDB
    • +
    • Redis
    • +
    • MongoDB
    • +
    • DynamoDB
    • +
    • more...
    • +
    + + +

    Using Databases in Hooks

    +

    All JavaScript based Hooks have built in support for npm. Using standard npm packages, {{appName}} has support for all major databases.

    +

    Currently, we don't have much integrated support for provisioning new databases and automatically connecting to them. You'll need to provision your own database and require the necessary npm package for communicating to that database in the Service source code.

    +

    We'll be adding more examples and better integrated Database support soon. Please let us know if there is anything you'd like to see added.

    + + +

    Built-in Cloud Datastore

    + +

    Don't want to provision your own Database? We've got you covered!

    + +

    {{appName}} provides a simple integrated Datastore API using our own multi-cloud database technology.

    +

    The Cloud Datastore allows you to immediately start storing and retrieving persistent data without having to worry about provisioning a new Database.

    + + + +

    Additional Resources

    + + +

    Considerations

    +

    Should you choose to communicate with an outside database, storing of the Databases access should be done as Service Environment Variables

    +

    The Cloud Datastore should currently be considered insecure, and you should not store any sensitive data in it.

    +

    Hooks should never deal with large sets of data or long running Database Queries. If you find yourself having to query a large amount of records, consider refactoring your query or dataset to retreive less items at a time ( using techniques such as pagination ).

    +

    Request a new Database Feature

    +
    +
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/view/databases.js b/view/databases.js new file mode 100644 index 00000000..41b6f8f6 --- /dev/null +++ b/view/databases.js @@ -0,0 +1,6 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, + req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/datastore/del.html b/view/datastore/del.html new file mode 100644 index 00000000..dcf80408 --- /dev/null +++ b/view/datastore/del.html @@ -0,0 +1,23 @@ +
    +

    Datastore.del

    +
    +

    Deletes an existing Document based on it's key.

    +

    Returns 0 if no Document is found.

    +

    Required parameters

    +
      +
    • key
    • +
    +
    +

    Datastore.del Form

    +
    + + +
    +
    +
    + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/view/datastore/del.js b/view/datastore/del.js new file mode 100644 index 00000000..978b4214 --- /dev/null +++ b/view/datastore/del.js @@ -0,0 +1,76 @@ +/* + +module['exports'] = function get (opts, cb) { + var $ = this.$, + req = opts.request, + res = opts.response; + + var types = []; + + if (req.headers && req.headers.accept) { + types = req.headers.accept.split(','); + } + + if (types.indexOf('application/json') !== -1) { + return cb(null, JSON.stringify({ foo: "bar"}, true, 2)) + } + + + + console.log(req.headers) + cb(null, $.html()); +}; + +*/ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); + +var Datastore = require('../../lib/resources/datastore').Datastore; + +module['exports'] = function datasourceDelView (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + if (typeof params.key !== 'undefined') { + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "datastore::del" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "datastore::del" )); + } else { + var datastore = new Datastore({ + root: req.resource.owner, + password: config.redis.password + }); + datastore.del(params.key, function(err, result){ + if (err) { + return callback(err); + } + return callback(null, JSON.stringify(result, true, 2)); + }); + } + }); + } else { + return callback(null, $.html()); + } + } + +}; + +module['exports'].schema = { + "key": { + "type": "string", + "required": true + } +}; \ No newline at end of file diff --git a/view/datastore/get.html b/view/datastore/get.html new file mode 100644 index 00000000..b471e338 --- /dev/null +++ b/view/datastore/get.html @@ -0,0 +1,25 @@ +
    +

    Datastore.get

    +
    +

    Retrieve an existing Document based on it's key

    +

    Returns null if no Document is found

    + +

    Required parameters

    +
      +
    • key
    • +
    + +
    +

    Datastore.get Form

    +
    + + +
    +
    +
    + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/view/datastore/get.js b/view/datastore/get.js new file mode 100644 index 00000000..f31a0ae9 --- /dev/null +++ b/view/datastore/get.js @@ -0,0 +1,84 @@ +/* + +module['exports'] = function get (opts, cb) { + var $ = this.$, + req = opts.request, + res = opts.response; + + var types = []; + + if (req.headers && req.headers.accept) { + types = req.headers.accept.split(','); + } + + if (types.indexOf('application/json') !== -1) { + return cb(null, JSON.stringify({ foo: "bar"}, true, 2)) + } + + + + console.log(req.headers) + cb(null, $.html()); +}; + +*/ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var Datastore = require('../../lib/resources/datastore').Datastore; + +module['exports'] = function view (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + // if not logged in, simply show documentation page + if (!req.isAuthenticated()) { + //return callback(null, $.html()); + } + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + + if (typeof params.key !== 'undefined') { + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "datastore::get" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "datastore::get")); + } else { + var datastore = new Datastore({ + root: req.resource.owner, + password: config.redis.password + }); + datastore.get(params.key, function(err, result){ + if (err) { + return callback(err); + } + return callback(null, JSON.stringify(result, true, 2)); + }); + } + }); + + + } else { + return callback(null, $.html()); + } + } + + +}; + +module['exports'].schema = { + "key": { + "type": "string", + "required": true + } +}; \ No newline at end of file diff --git a/view/datastore/index.html b/view/datastore/index.html new file mode 100644 index 00000000..38e36e91 --- /dev/null +++ b/view/datastore/index.html @@ -0,0 +1,155 @@ + + + +
    +

    Cloud Datastore

    +
    +
    + +
    + +
    + + +
    + +
    +
    + + +
    + +

    What is the Datastore?

    + +

    + Hook.io's Datastore API allows developers to seamlessly set and retrieve documents from a persistent datastore using unique keys. +

    + + +

    Hosted Services

    + +

    There are two ways to use access the Datastore API:

    + +

    Datastore HTTP API

    + + +

    From inside a Hook service using the hook.datastore object

    +

    Simply call any of the following Datastore methods from inside your Hook's source code.

    + +

    Hook Datastore Example:

    +
    Loading Gist...
    + + +

    From any location using HTTP requests

    +

    All Datastore API methods are also available over HTTP.

    +

    Each of the following urls is a fully qualified API gateway capable of key based authentication.

    + + + +

    Datastore HTTP API

    + + +

    Using the HTTP API will require logging in or providing a hook_private_key to access your private data.

    +

    Note: All HTTP verbs are loosely enforced. Any of the Datastore API methods will work with any HTTP verb as long as the required API parameters are satisfied.

    + + +

    Limitations

    +

    This Datastore API is powered internally by Redis, so any native Redis commands could become available. If you require an additional method not listed please file a support issue.

    +

    Currently free development accounts have a soft-limit of 100 unique documents per account. If you require more than 100 documents you can updgrade to a paid account or contact hookmaster@hook.io

    + +

    Security

    +

    You should not store any un-encrypted sensitive information in the Cloud Datastore ( such as passwords / credit cards / keys ). +

    +

    At this time, hook.io does not offer automatic encryption of data stored in the Cloud Datastore. In the future, we will most likely add this feature.

    + + +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/view/datastore/index.js b/view/datastore/index.js new file mode 100644 index 00000000..7a449096 --- /dev/null +++ b/view/datastore/index.js @@ -0,0 +1,26 @@ +var Datastore = require('../../lib/resources/datastore').Datastore; + +module['exports'] = function view (opts, callback) { + + var $ = this.$, + req = opts.request, + params = req.resource.params; + + $ = req.white($); + + // if not logged in, simply show documentation page + if (!req.isAuthenticated()) { + $('.last').remove(); + req.session.user = "anonymous"; + $('.currentRoot').html("/" + req.session.user); + $('.currentRoot').attr('href', '/datastore/recent') + return callback(null, $.html()); + } + + $('.currentRoot').html("/" + req.session.user); + $('.currentRoot').attr('href', '/datastore/recent'); + $('.root .hint').remove(); + $('.last').remove(); + return callback(null, $.html()); + +}; \ No newline at end of file diff --git a/view/datastore/layout.html b/view/datastore/layout.html new file mode 100644 index 00000000..7e737e64 --- /dev/null +++ b/view/datastore/layout.html @@ -0,0 +1 @@ +
    \ No newline at end of file diff --git a/view/datastore/layout.js b/view/datastore/layout.js new file mode 100644 index 00000000..256437f4 --- /dev/null +++ b/view/datastore/layout.js @@ -0,0 +1,42 @@ +module['exports'] = function get (opts, cb) { + //console.log(this.parent.parent.layout.template) + // TODO: fix issue with view package, should probably auto-load the parent + //var $$ = this.$.load(this.parent.layout.template); + //var $ = this.$.load(this.parent.parent.layout.template); + //var $ = this.parent.$; + var $ = this.$, req = opts.request; + + return cb(null, $.html()); + + // console.log('hi', this.parent.parent.layout.template) + //$('body').html($$.html()) + + // TODO: implement datastore root assignement based on API access key role granting... + + // TODO: view package should understand nested sub layouts with optional disabling / override behaviors + + // load the parent template into a new $$ context + var $$ = this.$.load(this.parent.parent.layout.template); + + // in addition, we need to apply the parent layout presenter logic + // TODO: this should be part of the view package + var parentLayoutPresenter = this.parent.parent.layout.presenter; + //console.log(parentLayoutPresenter.toString()) + parentLayoutPresenter.call(this, opts, function(err, result){ + // yield the current layout template into the parent template + // don't show the datastore root information unless we are on a specific API method landing page + + $$('.yield').append(result); + + $$('.datastoreRoot .owner').html(req.session.user); + if (req.session.user !== "anonymous") { + $$('.anonymousLogin').remove(); + } + if (req.url === "/datastore") { + $$('.datastoreRoot').remove(); + } + + cb(null, $$.html()); + }) + +}; diff --git a/view/datastore/recent.html b/view/datastore/recent.html new file mode 100644 index 00000000..14bcffc9 --- /dev/null +++ b/view/datastore/recent.html @@ -0,0 +1,21 @@ + + +
    +
    +

    Datastore.recent

    +

    Shows the last 5 documents created in the datastore.

    +

    +

      +

      +

      Note: This page will return JSON results if you cURL it +

      +
      \ No newline at end of file diff --git a/view/datastore/recent.js b/view/datastore/recent.js new file mode 100644 index 00000000..93d3f00a --- /dev/null +++ b/view/datastore/recent.js @@ -0,0 +1,60 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); + +var Datastore = require('../../lib/resources/datastore').Datastore; + +module['exports'] = function view (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + if (req.session.user !== "anonymous") { + $('.anonymousLogin').remove(); + } + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "datastore::recent" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "datastore::recent")); + } else { + finish(); + } + }); + }); + + function finish () { + $('.root .owner').html(req.resource.owner); + var datastore = new Datastore({ + root: req.resource.owner, + password: config.redis.password + }); + datastore.recent(function(err, keys){ + if (err) { + return callback(err.message); + } + if (req.jsonResponse) { + return callback(null, JSON.stringify(keys, true, 2)) + } + if (keys.length === 0) { + $('.lastKeys').html('No documents exists in datastore yet. Try creating one?'); + return callback(null, $.html()); + } + var str = ''; + keys.forEach(function(k){ + str += '
    • ' + str += k; + str += '
    • ' + }); + $('.lastKeys').html(str); + return callback(null, $.html()); + }); + } + +}; \ No newline at end of file diff --git a/view/datastore/set.html b/view/datastore/set.html new file mode 100644 index 00000000..bb7acb86 --- /dev/null +++ b/view/datastore/set.html @@ -0,0 +1,32 @@ +
      +

      Datastore.set

      +
      + +

      Sets a Document in the Datastore based on a uniquekey

      +

      Will either create or update depending if a Document already exists for the key

      + +

      Required parameters

      +
        +
      • key
      • +
      • value
      • +
      + +
      +

      Datastore.set Form

      +
      + + +
      +
      + + +
      + +
      +
      + +
      +
      +
      +
      +
      \ No newline at end of file diff --git a/view/datastore/set.js b/view/datastore/set.js new file mode 100644 index 00000000..75bbef84 --- /dev/null +++ b/view/datastore/set.js @@ -0,0 +1,85 @@ +/* + +module['exports'] = function set (opts, cb) { + var $ = this.$, + req = opts.request, + res = opts.response; + + var types = []; + + if (req.headers && req.headers.accept) { + types = req.headers.accept.split(','); + } + + if (types.indexOf('application/json') !== -1) { + return cb(null, JSON.stringify({ foo: "bar"}, true, 2)) + } + console.log(req.headers) + cb(null, $.html()); +}; +*/ + +var psr = require('parse-service-request'); + +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); + +var Datastore = require('../../lib/resources/datastore').Datastore; + +module['exports'] = function set (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response; + + // if not logged in, simply show documentation page + if (!req.isAuthenticated()) { + + } + + var params; + psr(req, res, function(req, res, fields){ + params = req.resource.params; + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + + if (typeof params.key === 'string' && params.key.length > 0) { + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "datastore::set" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "datastore::set")); + } else { + var datastore = new Datastore({ + root: req.resource.owner, + password: config.redis.password + }); + datastore.set(params.key, params.value, function(err, result){ + if (err) { + return callback(err); + } + return callback(null, JSON.stringify(result, true, 2)); + }); + } + }); + } else { + return callback(null, $.html()); + } + } + +}; + +module['exports'].schema = { + "key": { + "type": "string", + "required": true + }, + "value": { + "type": "any", + "required": true + } +}; \ No newline at end of file diff --git a/view/dedicated.js b/view/dedicated.js deleted file mode 100644 index 8f9b84cb..00000000 --- a/view/dedicated.js +++ /dev/null @@ -1,10 +0,0 @@ -var request = require('hyperquest'); - -module['exports'] = function (opts, callback) { - - var $ = this.$; - console.log(opts) - var hook = request('http://dev.hook.io:9999/Marak/hook.io/dedicatedHostingSignup'); - hook.pipe(opts.response); - -}; \ No newline at end of file diff --git a/view/docs.html b/view/docs.html index c650191c..c6502b60 100644 --- a/view/docs.html +++ b/view/docs.html @@ -1,173 +1,572 @@ - -
      -
      + + +

      Documentation

      -
      -
      - -

      Getting Started

      -

      A Hook is represented by a unique URL.

      -

      The Hook can be executed by sending HTTP requests to the this URL.

      -

      -

      -

      -
      - - -

      Creating a Hook

      -

      To create a Hook visit: http://hook.io/new

      -

      On this page, the following propreties can be specified to create a Hook:

      -

          Name required - Name of hook

      -

          Gist required - Source code of the Hook as a Github Gist

      -

          Theme optional - URL of the Theme that will render the output of the Hook, see: Themes

      -

      Once a Hook is created the browser will redirect to the landing page for that Hook.

      -
      - - - -

      Running a Hook

      -

      To run a hook you can click the button.

      -

      You can also specify the run parameter as a query string variable or form field when accessing the Hook URL and the Hook will execute.

      -

      Important: If you access a Hook from a non-browser source ( no text/html Accept header ), the hook will execute without specifying the run parameter. This behavior is achieved through routing incoming HTTP requests based on their accepted Content-Type ( see Content-Type Routing )

      -
      - - -

      Forking a Hook

      -

      To fork a Hook simply click the button.

      -

      You will be taken to a new page where you can start customizing your Fork.

      - -
      -

      Sending data to the Hook

      -

      Data is sent to the Hook through the query string or posted form data.

      - - -

      Query String Data

      -

      To add a parameter via query string simply append it to the url of the Hook

      -

      Example: http://hook.io/Marak/echo?foo=bar&hello=there&run=true

      -

      Nested parameters are also supported.

      -

      Example: http://hook.io/Marak/echo?foo['bar']=one&foo['tar']=two&run=true

      - -

      Form Data

      -

      Data can also be sent via form POST

      -

      Curl Example:

      -

      Notice that we don't specify the run parameter in the Curl example? This is because the hook is being called from a non-browser source ( no text/html Accept header ) and will automatically execute.

      - -

      Streaming Data

      -

      To send streaming data to the hook, simply open up a streaming http request.

      -

      Here is an example of streaming data to a hook

      -
      - - -

      Editing Hook source code

      -

      The source code for a Hook is a Github Gist

      -

      - You can edit the Hook Source Code by clicking on the bottom of it's embedded Github Gist. -

      -

      - If you want to edit a Hook which you do not own, you can Fork It. -

      -
      - - -

      Accessing the Hook object inside your code

      -

      Every hook receives a Hook object as it's first argument.

      -

      - Hook.debug - Logging method for sending debug messages to the debug console
      - Hook.params - Incoming HTTP request data ( query string and form fields )
      - Hook.req - Incoming http.IncomingMessage stream
      - Hook.res - Outgoing httpServer.ServerResponse stream
      - Hook.open - Method for opening URLs ( such as another Hook ). Options pass through to Hyperquest API.
      - Hook.schema - Optional mschema for hook.params
      - Hook.env - Optional. Key / value pairs stored by the owner of the Hook. See: Setting Hook Environment
      - Hook.streaming - Boolean. Is the Hook.req still streaming data. For most browser requests, this will be false.
      -

      -
      - - -

      Specifying Optional Schema

      -

      Through the use of mschema, it is possible to enable validation and default values for incoming hook parameters.

      - -

      To create a mschema for the incoming hook parameters, simply specify the schema property of the hook.

      -

      - Example: - -

      -

      - Documentation for mschema can be found here. -

      - -

      Important: For performance reasons, changes made to the Hook schema will not appear in the User Interface until the Hook has been run once. The schema is applied to the Hook immediately when the gist is updated, just the User Interface will lag behind. This performance setting may be configurable in the future. The work-around is to simply run the Hook once to update the UI with new schema data.

      -
      - - -

      Using Schema Based Forms

      -

      Through the use of mschema-forms, all Hooks have the ability to auto-generate an HTML form element populated with input fields matching that Hook's Hook.schema

      - -

      Forms are automatically generated in the friendly format when a Hook.schema is specified.

      -

      -

      - Important: Forms will not be updated until the Hook is run at least once. -

      - -
      - -

      Setting Hook Environment Variables

      -

      To set Hook Environment variables visit: http://hook.io/env

      -

      Arbitrary key / value pairs can be set on your account so that all your Hooks will have access to the pairs in the Hook.env variable.

      -

      This is useful for protecting private authorization credentials from public view ( such as an API key or password ).

      -

      After you have updated your environment variables they will be available in your hook's Hook.env

      -
      - - - - -

      Content-Type Routing

      -

      Based on the Content-Type header, hook.io will process the incoming request differently.

      -
      -

      application/json
      - Will buffer the incoming request in order to parse the body as JSON. Incoming JSON maps to Hook.params. Streaming the incoming request is no longer possible at this point.

      - -

      application/x-www-form-urlencoded
      - Will buffer the incoming request in order to process form fields. Incoming form fields maps to Hook.params.Streaming the incoming request is no longer possible at this point.

      - -

      multipart/form-data
      Will attach any multipart file uploads as streams to Hook.params. File uploads can be then be handled as streams inside the Hook.

      - -

      application/octet-stream
      Will treat the incoming request as a binary data stream.

      - -
      -

      Accept Header Routing

      -

      The hook will also respond to requests differently based on their incoming Accept HTTP header.

      -
      -

      text/html
      Returns a friendly HTML landing page ( useful for humans )

      -

      */*
      Executes the hook and returns the response type specified in the Hook ( useful for programs )

      -

      Accept Header Routing can be overridden by setting Hook.params.format to raw. - -

      -
      \ No newline at end of file +
      +
      + +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + +
      + +
      +
      + + +
      +
      + +
      +
      +

      Getting Started

      +

      A Hook is single function or block of code represented by a unique URL.

      +

      The Hook can be executed by sending HTTP requests to this URL.

      +

      To see live examples of using Hooks visit the: Interactive Microservice Tutorial

      +

      To see example Hooks which you can fork visit: Examples

      +

      +

      +

      +
      + + +

      Choosing a Programming Language

      +

      {{appName}} supports writing services in other programming languages besides JavaScript.

      +
        +
      +

      You may select new languages while creating or editing a service.

      +

      Click here for an Interactive Microservice Tutorial

      + + +

      Creating a Hook

      +

      To create a Hook visit: {{appUrl}}/new

      + + + + +

      Forking an existing Hook

      +

      To fork another Hook, simply add /fork to the url of the Hook you want to fork.

      +

      By specifying the fork parameter, you will be taken to a new page where you can start customizing your Fork.

      + +

      Once a Hook is created the browser will redirect to the admin page for that Hook.

      + +

      Try It Now: {{appUrl}}/examples/echo/fork

      +

      Many Examples To Fork From: {{appUrl}}/examples

      +
      + + +

      Running a Hook

      +

      Hooks are automatically executed when an HTTP request is sent to the Hook's url.

      +

      This HTTP request can be sent from any source ( such as the browser, custom application, or the command line).

      +

      Important Note about Service Timeouts:

      +

      All hooks have a 10 second timeout value by default. If the execution of your service takes longer then this value, the client will receive a service timeout error message.

      +

      In the majority of cases, a time-out indicates that the service failed to end properly and usually indicates a programming error.

      +

      Attempting to run an HTTP or TCP server in a Hook will not work!
      Hooks are microservices ( think functions ), not servers.
      +

      In some other cases, you may be dealing with transferring a larger amount of data from a third-party service and the default timeout of ten seconds is not long enough to complete the transmission.

      +

      If you think your service may require more than 10 seconds to execute, {{appName}} allows for Custom Timeouts per service.

      +
      + + +

      View Hook Source

      +

      To view the source code of any Hook simply attach /_src to the Hook service url.

      + + +

      Source Code: {{appUrl}}/examples/echo/_src

      +

      Logs: {{appUrl}}/examples/echo/logs

      +

      Resource: {{appUrl}}/examples/echo/resource

      +

      View: {{appUrl}}/examples/echo/view

      + + +

      If you require the source code of a Hook to remain private, upgrade to a paid account and set the Hook to Private + + +

      Hook Admin Panel

      +

      To access the Administration page for a Hook simply attach /admin to the Hook service url.

      +

      This will only work for Hooks which you own.

      + + +
      +

      Hook Debugging and Logs

      +

      {{appName}} provides a seamless streaming log service which Hooks can write to with console.log. Simply call console.log from inside the Hook source code and the logging statement will be saved.

      +

      Streaming log endpoints are available for real-time consumption on all Hook service urls as /logs.

      +

      Example: {{appUrl}}/examples/echo/logs +

      Complete logging documentation can be found at {{appUrl}}/logs + + +


      +

      Sending data to the Hook

      +

      Data can be sent to a Hook through any methods HTTP supports.

      +

      The most common way to send data to a Hook is through the URL query string or posted through forms or binary data transfers.

      +

      Query String Data

      +

      To add a parameter via query string simply append it to the url of the Hook

      +

      Example: {{appUrl}}/examples/echo?foo=bar&hello=there&run=true

      +

      Nested parameters are also supported.

      +

      Example: {{appUrl}}/examples/echo?foo['bar']=one&foo['tar']=two&run=true

      + +

      Form Data

      +

      Data can also be sent via form POST

      +

      Curl Example:

      + +

      Streaming Data

      +

      To send streaming data to the hook, simply open up a streaming http request.

      +

      Here is an example of streaming data to a hook

      + +

      Websockets

      +

      To send data to a Hook over Websocket, simply connect to the endpoint using any ws client

      +

      More detailed instructions can be found at {{appUrl}}/websockets

      +
      + + +

      Editing Hook source code

      +

      There are three options for specifying the source code for a Hook. Source code can be set on the /admin page for the Hook. To quickly get started, we recommend Forking an existing service.

      + +

      Code Editor

      +

      Uses a built-in Code Editor and stores source code of the service in {{appName}}

      + +

      Github Repository

      +

      Pulls the source code of the service from a Github Repository.

      + +

      Github Gist

      +

      Pulls the source code of the service from a Github Gist.

      + +
      + + +

      Accessing the Hook object inside your code

      +

      All services are populated with a Hook object containing many useful properties.

      +

      For JavaScript based services, Hook will be the first function argument and Hook.req and Hook.req will be streams. For other programming languages, Hook will be defined globally in the script and STDIN and STDOUT streams will be available. The best way to understand how this works is by looking at the examples.

      +

      + + Hook.params - Incoming HTTP request data ( query string and form fields )
      + Hook.req - Incoming http.IncomingMessage stream
      + Hook.res - Outgoing httpServer.ServerResponse stream
      + Hook.datastore - Contains methods for getting and setting key-values from the datastore
      + Hook.fs - Contains methods for using the Cloud Files API
      + Hook.sdk - Contains a reference copy of the {{appName}}-sdk
      + + Hook.schema - Optional mschema for hook.params
      + Hook.env - Optional. Key / value pairs stored by the owner of the Hook. See: Setting Hook Environment
      + Hook.streaming - Boolean. Is the Hook.req still streaming data. For most browser requests, this will be false.
      +

      +
      + + +

      Using the Cloud Datastore

      + +

      Hooks have access to a key-value cloud datastore which can be used to store ange manage persistent data. This is useful for storing persistent information which you may want to retrieve later.

      +

      Read more about cloud datastore: {{appUrl}}/datastore

      + + +

      Specifying Optional Schema

      +

      Through the use of mschema, it is possible to enable validation and default values for incoming hook parameters.

      +

      To create a schema for the incoming hook parameters, simply specify the schema property on the /admin page for the hook. HTTP params will then be available in Hook.params.

      + +

      + Documentation for mschema can be found here. +

      + +
      + + +

      Using the Mutli-Cloud Virtual File System

      +

      Hooks have access to a robust multi-cloud virtual file-system which can be used to manage files across multiple adapters. This is useful for storing persistent files which you may want to retrieve later.

      +

      Read more about multi-cloud virtual file system: {{appUrl}}/files

      + +
      + + +
      + +

      Setting Hook Environment Variables

      +

      To set Hook Environment variables visit: {{appUrl}}/env

      +

      Arbitrary key / value pairs can be set on your account so that all your Hooks will have access to the pairs in the Hook.env variable.

      +

      This is useful for protecting private authorization credentials from public view ( such as an API key or password ).

      +

      After you have updated your environment variables they will be available in your hook's Hook.env

      +
      + + +

      Setting Custom Domains

      +

      Custom domains are a great way to brand the appearance of your service with it's own unique domain name.

      +

      To add a Custom Domain, you will first need to set the domains A record to {{balancerIP}} ( which is the ip address of {{appName}} ).

      +

      After setting the A record in your domain's DNS manager, simply visit: {{appUrl}}/domains to complete the process by mapping any of your Hooks to the Custom Domain.

      +
      + + +

      Content-Type Routing

      +

      Based on the Content-Type header, {{appName}} will process the incoming request differently.

      +
      +

      + application/json
      + Will buffer the incoming request in order to parse the body as JSON. Incoming JSON maps to Hook.params. Streaming the incoming request is no longer possible at this point. +

      + +

      + application/x-www-form-urlencoded
      + Will buffer the incoming request in order to process form fields. Incoming form fields maps to Hook.params.Streaming the incoming request is no longer possible at this point. +

      + +

      multipart/form-data
      + Will attach any multipart file uploads as streams to Hook.params. File uploads can then be handled as streams inside the Hook. +

      + +

      application/octet-stream
      + Will treat the incoming request as a binary data stream. +

      + + +
      + + + +

      Rate Limiting

      +

      {{appName}} will limit the total amount of API requests made against every account. This includes all API endpoints and every execution of any Hooks associated with the account.

      +

      In the event the total amount of API requests exceeds the amount of requests allocated with the accounts plan, further requests will be rate-limited. To increase rate limits, you can upgrade the account to a larger plan or email support for a temporary reset.

      +

      Rate Limits will automatically reset at the start of every billing cycle.

      +

      Rate Limiting HTTP Headers

      +

      Every API response from {{appName}} will include special HTTP headers indicating the current status of Rate Limits and Concurrency Limits.

      +

      + X-RateLimit-Limit - Total amount of requests allowed per cycle
      + X-RateLimit-Remaining - Total amount of requests remaining in cycle
      + X-RateLimit-Running - Total amount of actively running requests
      +


      + + +

      Hook Concurrency Limitations

      +

      {{appName}} will limit the of total amount services running concurrently per account. This means that every account may only have a certain amount of services running at the exact same time. In the event that too many services are running concurrently per account, further incoming HTTP requests will be rate-limited until some of the running services complete.

      +

      + To increase concurrency limits, you can upgrade the account to a larger plan or email support. +

      +
      + + + +

      Custom Service Timeouts

      +

      All hooks have a 10000 millisecond timeout ( 10 second ) value by default. If the execution of your service takes longer than this value, the client will receive a service timeout error.

      +

      In the majority of cases, a time-out error indicates that the service failed to end properly and usually indicates a programming error. Attempting to run an HTTP or TCP server in a Hook will not work! Hooks are microservices ( think functions ), not servers.

      +

      In some other cases, you may be dealing with transferring a larger amount of data from a third-party service or communicating with a slow responding third-party service and the default timeout of ten seconds is not long enough to complete the transmission. If you think your service may require more than 10 seconds to execute, simply visit the /admin page for the Hook and set the form field for "Custom Timeout" in milliseconds. Note: A paid account may be required.

      +
      +
      + +

      Hot-Code Execution Gateway

      +

      In most cases, you will want to save your service's source-code as a Hook.

      +

      In other situations, you may need to send code that has to be executed immediately and without any additional requests ( such as testing and developing hooks or executing dynamically created source code from a third-party service ).

      +

      Read more on {{appUrl}}/gateways

      +

      {{appName}} provides a hot-code gateway which allows users to send code to be executed immediately. In fact, if you look at the Interactive Example on the {{appName}} homepage, you will notice it is powered by several hot-code gateways ( a separate gateway for each specific programming language ).

      + +

      Examples of using the Hot-Code Gateways

      +

      Here are two simple examples of using echo and curl to execute hot-code against {{appName}}'s gateway.

      + +

      JavasScript

      +

      +

      echo 'module["exports"] = function helloWorld (h) { h.res.end("Hello world!"); };' | curl --data-urlencode source@- http://javascript.{{appName}}
      +

      + +

      Python

      +

      +

      echo 'print "hello world"' | curl --data-urlencode source@- http://python.{{appName}}/
      +

      + +

      You can also simply open a url to the gateway like this: http://python.{{appName}}/?source=print "hello world" +
      + +

      Available Gateways

      + Note: Most gateways will an return an error or blank response if the source parameter is not provided + + + +
      +
      + + + +

      System Events

      + +

      All actions on {{appName}} are tracked internally as System Events. +

      These events are useful for monitoring the actions your services are performing, and who is performing them.

      + +

      The relation of System Events and Role Access

      +

      It's important to note that all {{appName}} events are available as Access Roles. This mapping allows granular role based key access per System Event.

      +

      Read More about Role Based Access Control

      + +
      +
      + + + +

      SSL / HTTPS

      + +

      Currently, HTTPS is supported on {{appName}} through a single shared SSL certificate.

      +

      All URLs and services on the site are available over HTTPS

      +

      If you require a custom SSL certificate for your microservices, please contact hookmaster@{{appName}}

      + +
      +
      + + +

      Using {{appName}} as an HTTP API

      + +

      A graphical interface exists for all of {{appName}}'s supported features at {{appUrl}} In some cases, you may want a third party script or client ( not your browser window ), to be able to access a specific {{appName}} platform feature ( such as logs, the datastore, or creating a new hook service ). Using {{appName}}'s HTTP API, you can access any platform feature that is available in our web app.

      +

      To keep things simple ( and self documenting ), the routes in our web app are 1:1 with our API. The /datastore or /logs endpoints are well documented examples of how this works, but other endpoints like /new might require a small amount of reverse engineering.

      + +

      {{appName}} SDK

      +

      An easy way to access the {{appName}} API programmatically is through the {{appName}} SDK. Read more on {{appUrl}}/sdk

      + + +

      API Access Keys

      + +

      {{appName}} provides a robust role based API key access system.

      +

      All events on {{appName}} are registered as roles which can be granted per generated API access key

      + +

      All API endpoints are backed by Role Based Access Control, so if you provide a hook_private_key parameter in your request which matches a known API key containing the required role that request will be granted access to the resource. Read More about Keys and Roles

      + + + +
      +
      +
      +
      +
      + +
      +
      + +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/docs.js b/view/docs.js index d2ff86da..2e8c681c 100644 --- a/view/docs.js +++ b/view/docs.js @@ -1,4 +1,19 @@ +var langs = require('../lib/resources/programmingLanguage'); + module['exports'] = function doc (opts, callback) { - var $ = this.$; + var $ = this.$, + req = opts.request; + var i18n = require('./helpers/i18n'); + i18n(req.i18n, $); + Object.keys(langs.languages).forEach(function(l){ + $('.left_middle_widget .tag').append('
    • ' + l + '
    •  '); + + var alpha = ['gcc', 'go', 'ocaml', 'rust', 'r', 'java']; + if (alpha.indexOf(l) === -1) { + $('.programmingLanguages').append('
    • ' + langs.languages[l].name + '
    • '); + } + + }); + $ = req.white($); return callback(null, $.html()); }; \ No newline at end of file diff --git a/view/domains.html b/view/domains.html index 5e5c1762..e2dad7ab 100644 --- a/view/domains.html +++ b/view/domains.html @@ -1,49 +1,57 @@ - - - - -
      -

      Custom Web Domains

      -

      Here you can add / remove custom web domains that will route to your hooks.

      -

      This is a great way to use a custom domain like www.marak.com to point to a hook.

      -
      - Add new Domain +
      +

      Domains

      + +
      + + +

      {{appName}} subdomains

      +

      + Subdomains at *.{{appName}} are available on a first come first serve basis.

      To associate a subdomain with a Hook, simply add the subdomain entry in the form below ( login required ). +

      +

      + Example:
      + name: echo.{{appName}} hook: /examples/echo
      + Now http://{{appName}}/examples/echo will redirect to : http://echo.{{appName}}/ +

      + +
      +

      Custom Domains

      + +

      To add a Custom Domain, you will need to associate the domain's A record to {{balancerIP}} ( which is the ip address of {{appName}} ).

      + +

      Changing the A record of the domain is done in the domain's DNS manager. This setting is not managed by {{appName}} The DNS manager for the domain is usually located on the site which the domain is currently hosted or where the domain was purchased from.

      + +

      Once the A record is updated. Add the custom domain entry in the form below ( login required ). +

      + Example:
      + name: marak.com hook: /Marak/view
      + name: www.marak.com hook: /Marak/view
      + Now http://marak.com/ and http://www.marak.com/ will redirect to: http://{{appName}}/Marak/view +

      + + +
      +
      +

      -
      - - - - - - -
       Domain

      - - +
      + +
      +
      +
      +
      +
      + +
      \ No newline at end of file diff --git a/view/domains.js b/view/domains.js index ab06e43c..83d2b529 100644 --- a/view/domains.js +++ b/view/domains.js @@ -1,138 +1,137 @@ -// TODO: remove copy-pasta from env.js +var forms = require('resource-forms'), +mschemaForms = require('mschema-forms'); -var user = require('../lib/resources/user'); +var mergeParams = require('merge-params'); +var config = require('../config'); var bodyParser = require('body-parser'); -module['exports'] = function view (opts, callback) { - var req = opts.request, res = opts.response; - var $ = this.$; - if (!req.isAuthenticated()) { - req.session.redirectTo = "/domains"; - return res.redirect('/login'); - } - - bodyParser()(req, res, function bodyParsed(){ - mergeParams(req, res, function(){}); - - var params = req.resource.params; - - user.find({ name: req.user.username }, function(err, results) { +var domain = require('../lib/resources/domain'); +var checkRoleAccess = require('../lib/server/routeHandlers/checkRoleAccess'); +var psr = require('parse-service-request'); - if (err) { - return callback(null, err.message); - } - if (results.length === 0) { - return callback(null, 'No user found'); - } +module['exports'] = function view (opts, callback) { - var _user = results[0]; - console.log('we have ', _user.domains.items) - if (params.update) { - // update is destructive and complete - // entire User.env will now be replaced by the contents of the form submitted - // all old fields are deleted and replaced with new values - //_user.env = {}; - if (params.key) { - if (typeof params.key === "string") { - params.key = [params.key]; - } - if (typeof params.value === "string") { - params.value = [params.value]; + var req = opts.request, + res = opts.response, + $ = this.$; + + var appName = req.hostname; + + psr(req, res, function(req, res){ + var params = req.resource.params + if (req.method === "POST") { + checkRoleAccess({ req: req, res: res, role: "domain::create" }, function (err, hasPermission) { + // console.log('check for role access', err, hasPermission, req.resource.owner) + req.resource.params.owner = req.resource.owner; + + if (!hasPermission || req.resource.owner === "anonymous") { // don't allow anonymous hook update + if (req.jsonResponse !== true && typeof params.hook_private_key === "undefined") { + req.session.redirectTo = "/domains"; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, "domain::create")); + } else { + if (req.jsonResponse) { + // console.log('attempting to create domain'.yellow, req.resource.params); + domain.create(req.resource.params, function (err, d) { + if (err) { + return res.end(err.message); + } + res.json(d); + }) + } else { + renderForm(); + } + } + }); + + } else { + checkRoleAccess({ req: req, res: res, role: "domain::get" }, function (err, hasPermission) { + // console.log('check for role access', err, hasPermission, req.resource.owner) + req.resource.params.owner = req.resource.owner; + + if (!hasPermission || req.resource.owner === "anonymous") { // don't allow anonymous hook update + if (req.jsonResponse !== true && typeof params.hook_private_key === "undefined") { + req.session.redirectTo = "/domains"; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, "domain::get")); + } else { + if (req.jsonResponse) { + domain.find({ owner: req.resource.params.owner}, function (err, _domains){ + if (err) { + return res.end(err.message); + } + res.json(_domains); + }) + } else { + renderForm(); + } + } + }); + } + }); + + function renderForm () { + + req.resource.params.owner = req.session.user; + + var middle = forms.generate({ + view: 'grid-with-form', + resource: domain, + action: '/domains', + req: req, + res: res, + params: req.resource.params, + query: { owner: req.session.user }, + useLayout: false, + form: { + create: { + legend: 'Add a new Domain or Subdomain', + submit: "Add Entry" + }, + grid: { + legend: 'Your Domains', + keys: ['name', 'forwardUrl'] + }, + showDestroyButton: true + }, + schema: { + name: { + type: 'string', + label: 'name', + description: 'Your custom domain name. Example: marak.com', + placeholder: 'marak.com', + required: true, + minLength: 1, + maxLength: 50 + }, + forwardUrl: { + type: 'string', + label: "hook", + placeholder: "/examples/echo", + description: 'The Service to point your domain to. Example: /examples/echo', + required: true, + minLength: 1, + maxLength: 50, + formatter: function (str) { + return '' + str + ''; } - params.key.forEach(function(k, i){ - if (k.length) { - // console.log('about to save', typeof val, val) - _user.domains.items.push(k); - } - }) } - _user.save(function(err){ - if (err) { - return res.end(err.message); - } - showEnv(); - }); - } else { - showEnv(); } - - function quoteattr(s, preserveCR) { - preserveCR = preserveCR ? ' ' : '\n'; - return ('' + s) /* Forces the conversion to string. */ - .replace(/&/g, '&') /* This MUST be the 1st replacement. */ - .replace(/'/g, ''') /* The 4 other predefined entities, required. */ - .replace(/"/g, '"') - .replace(//g, '>') - /* - You may add other replacements here for HTML only - (but it's not necessary). - Or for XML, only if the named entities are defined in its DTD. - */ - .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */ - .replace(/[\r\n]/g, preserveCR); - ; + }, function (err, result){ + if (err) { + return res.end(err.message); } - function showEnv () { - var env = _user.domains.items || []; - function addRow (k, v) { - if (typeof v === "object") { - v = JSON.stringify(v); - } - var _delete = 'X'; - var _key = ''; - var _value = ''; - $('.env').append('' + _delete + '' + k + ''); - }; - - if (env.length === 0) { - addRow('', ''); - } - - env.forEach(function(key){ - console.log('ff', key) - addRow(key.id); - }); - - callback(null, $.html()); + $('.domains').html(result); + if (req.session.user !== "anonymous") { + $('.loginBar').remove(); } - }); - - }); - -}; - - - -// -// Middleware for merging all querystring / request.body and route parameters, -// into a common scope bound to req.resource.params -// -function mergeParams (req, res, next) { - - req.resource = req.resource || {}; - req.resource.params = {}; - req.body = req.body || {}; - - // - // Iterate through all the querystring and request.body values and - // merge them into a single "data" argument - // - if (typeof req.params === 'object') { - Object.keys(req.params).forEach(function (p) { - req.resource.params[p] = req.param(p); - }); - } + $ = req.white($); + callback(null, $.html()); - if (typeof req.query === 'object') { - Object.keys(req.query).forEach(function (p) { - req.resource.params[p] = req.query[p]; - }); - } + }); + } - Object.keys(req.body).forEach(function (p) { - req.resource.params[p] = req.body[p]; - }); - next(); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/view/edit.html b/view/edit.html deleted file mode 100644 index 32c90040..00000000 --- a/view/edit.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - -
      -

      Edit Hook

      - -
      -
      - - - - - - - - - - - - - - - - - - - -
      Hook Name
      Hook Source Code:
      Active Cron - - If checked, the Hook will run as a scheduled task. see: hook.io/cron
      - -
      -
      - - -
      \ No newline at end of file diff --git a/view/edit.js b/view/edit.js deleted file mode 100644 index c4625b30..00000000 --- a/view/edit.js +++ /dev/null @@ -1,126 +0,0 @@ -var hook = require('../lib/resources/hook'); -var mergeParams = require('./mergeParams'); -var bodyParser = require('body-parser'); - -/* -var themes = { - "debug": { - "theme": config.defaultTheme, - "presenter": config.defaultPresenter - }, - "simple": { - "theme": "http://hook.io/themes/simple/index.html", - "presenter": "http://hook.io/themes/simple/index.js" - }, - "simple-form": { - "theme": "http://hook.io/themes/simple-form/index.html", - "presenter": "http://hook.io/themes/simple-form/index.js" - }, -}; -*/ - -module['exports'] = function doc (opts, callback) { - var $ = this.$, self = this; - - var req = opts.request, res = opts.response, params = req.resource.params; - //console.log('got params', params) - $('.hookLink').html('' + params.hook + '') - bodyParser()(req, res, function bodyParsed(){ - mergeParams(req, res, function(){}); - if (typeof params.hook === "undefined" || params.hook.length === 0) { - return res.end('hook not specified'); - } - - // check if user is logged in - if (!req.isAuthenticated()) { - req.session.redirectTo = "/edit?hook=" + params.hook; - return res.redirect('/login'); - } - - var parts = params.hook.split('/'); - - if(req.user.username !== parts[0] && req.user.username !== "Marak") { - return res.end('No permission to edit this Hook.') - } - - $('form').attr('action', '/edit?hook=' + params.hook); - - var query = { owner: parts[0], name: parts[1] }; - return hook.find(query, function(err, results){ - if (err) { - return res.end(err.stack); - } - if (results.length === 0) { - return res.end('Hook not found!'); - } - - var h = results[0]; - // console.log("FOUND", h) - // if params.update, update the hook - if (params.update) { - // don't allow users to overwrite hooks - if (h.name !== params.name) { - var query = { owner: h.owner, name: params.name }; - hook.find(query, function(err, _results){ - if (err) { - return res.end(err.stack); - } - if (_results.length === 0) { - updateHook(); - } else { - return res.end('Unable to rename ' + h.name + ' to ' + params.name + ' since ' + h.name + ' already exists!'); - } - }); - } else { - updateHook(); - } - - function updateHook () { - h.name = params.name; - h.gist = params.gist; - // TODO: move this to mergeParams - if (params.cronActive === "on") { - h.cronActive = true; - } else { - h.cronActive = false; - } - h.cron = params.cronString; - h.theme = params.theme; - h.presenter = params.presenter; - return h.save(function(err){ - if (err) { - return res.end(err.stack); - } - // redirect to hook edit page ( as hook name might have changed) - return res.redirect(301, '/edit?hook=' + h.owner + "/" + h.name); - }); - } - - } else { - renderForm(); - } - - function renderForm () { - // bind hook data to form - $('form input[name="name"]').attr('value', h.name); - $('form input[name="gist"]').attr('value', h.gist); - if (h.cronActive !== false) { - $('form input[name="cronActive"]').attr('checked', 'checked'); - $('.cronRow').attr('showMe', 'true'); - } - $('.cronRow').attr('cronString', h.cron); - $('form input[name="cronString"]').attr('value', h.cron); - self.parent.components.themeSelector.present({}, function(err, html){ - var el = $('.table-condensed > tr').eq(1); - el.after(html); - $('form input[name="theme"]').attr('value', h.theme); - $('form input[name="presenter"]').attr('value', h.presenter); - // $('form select[name="theme"] option[value="' + "simple" + '"]').attr('selected', 'selected'); - return callback(null, $.html()); - }); - } - - }); - }); - -}; \ No newline at end of file diff --git a/view/editor/index.html b/view/editor/index.html new file mode 100644 index 00000000..00eb62d5 --- /dev/null +++ b/view/editor/index.html @@ -0,0 +1,470 @@ + + +
      +
      + + + + + + +

      Interactive Coding Playground

      + +
      +
      +
      + +
      +
      + + + + +
      +
      + + + + +
      + +
      + + +
      + | | +
      + + +
      +
      + + + + + + + +
      +
      + +
      +
      + + +
      +
      + + + +
      +
      + + +
      +
      + + + +
      +
      +
      + + +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/editor/index.js b/view/editor/index.js new file mode 100644 index 00000000..2cb4c0f1 --- /dev/null +++ b/view/editor/index.js @@ -0,0 +1,102 @@ +var config = require('../../config'); +var hook = require('../../lib/resources/hook'); +var hooks = require('microcule-examples'); +var psr = require('parse-service-request'); + +module['exports'] = function view (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params, + user = req.session.user; + + var boot = {}; + + boot.baseUrl = config.app.url || ""; + var i = req.i18n; + + boot.messages = {}; + req.session.redirectTo = "/new"; + // TODO: gateway.hook.io for production + $('#gatewayForm').attr('action', config.app.url + '/gateway'); + + var services = hooks.services; + var examples = {}; + + // pull out helloworld examples for every langauge + Object.keys(hook.languages).forEach(function(l){ + examples[l] = services[l]; + }); + + // list of js examples by order + var jsExamples = [ + "hello-world", + "request-parameters", + "send-http-request", + "input-schema", + "pipe-hook", + "stream-merge", + "stream-transform", + "datastore", + "files-writefile", + "files-readfile", + "fake-data" + ]; + jsExamples = jsExamples.reverse(); + jsExamples.forEach(function (item){ + var ex = services['javascript-' + item]; + if (ex) { + $('.selectSnippet').prepend('') + } + }); + + /* + for (var s in services) { + var e = services[s]; + var type = s.split('-')[0], + lang = s.split('-')[1]; + if (type === "examples" && lang === "javascript") { + $('.selectSnippet').prepend('') + } + } + */ + + boot.examples = examples; + + //req.i18n.setLocale('de'); + + // Localize page based on request lauguage + $('.deploymentsLink').html(i.__("Deployments")); + $('.callToAction').html(i.__("Sign up Instantly! It's Free!")); + + //$('.featuresDiv h2').html(i.__("Features")); + + $('.features li a').each(function(index, item){ + var v = $(item).html(); + $(item).html(i.__(v)); + }); + + psr(req, res, function(){ + var params = req.resource.params; + + if (params.source) { + // TODO: better saving of temporary source + // Could implement a "clipboard" scratch type pattern for services / service code in the request session + // Similiar to revision history, but separate and temporary + // Could show a UI for it whenever an editor is around + req.session.tempSource = params.source; + req.session.tempLang = params.language; + return res.redirect('/new'); + // redirect to /new with new source, do not create + } + + $ = req.white($); + + var out = $.html(); + out = out.replace('{{hook}}', JSON.stringify(boot, true, 2)); + return callback(null, out); + + }); + +}; \ No newline at end of file diff --git a/view/email-required.html b/view/email-required.html new file mode 100644 index 00000000..aaf40f3a --- /dev/null +++ b/view/email-required.html @@ -0,0 +1,69 @@ + + +
      +

      Update Account with Email Address

      +
      +
      +
      +
      + +

      Attention: A valid email address is now required for all accounts. Please add an email address to this account to continue.
      + Without a valid email address it's possible you may get locked out of your account.

      + +
      +
      + + +
      + +
      +
      +
      +
      +
      +
      + + diff --git a/view/email-required.js b/view/email-required.js new file mode 100644 index 00000000..a7ededf7 --- /dev/null +++ b/view/email-required.js @@ -0,0 +1,38 @@ +var psr = require('parse-service-request'); +var user = require('../lib/resources/user'); + +module.exports = function (opts, cb) { + var $ = this.$, res = opts.res, req = opts.req; + if (!req.isAuthenticated()) { + return res.redirect(302, '/login'); + } + psr(req, res, function () { + var params = req.resource.params; + if (req.method === "POST") { + // check to see if valid email was posted + if (typeof params.email === 'string' && params.email.length > 0 && params.email.search('@') !== -1) { + user.findOne({ name: req.session.user }, function(err, _user){ + if (err) { + res.status(500) + return res.json({ error: true, message: err.message }); + } + _user.email = params.email; + _user.save(function(err){ + if (err) { + res.status(500) + return res.json({ error: true, message: err.message }); + } + req.session.email = params.email; + return res.end('set-email') + }); + }) + } else { + res.status(400) + return res.json({ error: true, message: 'Invalid email address'}); + } + //return cb(null, $.html()); + } else { + return cb(null, $.html()); + } + }) +} \ No newline at end of file diff --git a/view/emails/0_referral_madness.html b/view/emails/0_referral_madness.html deleted file mode 100644 index 2f5f2c4b..00000000 --- a/view/emails/0_referral_madness.html +++ /dev/null @@ -1,80 +0,0 @@ - -
      -

      - Greetings {{username}}! -

      -

      You are receiving this email because recently signed up for http://hook.io. If you do not wish to receive anymore emails from hook.io you can unsubscribe below. -

      -

      Great news everyone! A free Bitcoin!

      -

      - For the entire month of November 2014 hook.io will be running an incentive program... -

      -

      - ...that awards the hook.io user with the most amount of user referrals in the month of November... -

      - -

      An entire Bitcoin!

      -

      - You now have a special link which you can distribute to your developer friends and colleagues to sign up for a free hook.io account: -

      - -

      SHARE THIS LINK

      -

      - http://hook.io/{{username}}?s -

      - -

      - Now anytime someone clicks on that link and signs up ( with a valid Github account ) one referral will be added to your account. -

      -

      - Click here to Tweet your referral link right now! -

      - -

      You can track your referrals here:

      -

      - http://hook.io/referrals -

      - - -

      - On December 1st, 2014 the hook.io user with the most referrals will receive 1 Bitcoin ( currently worth about $350 )

      - -

      - If you have any questions please feel free to open up a Github Issue.

      - - -

      - Want to see the coin? Here is it's blockchain address. -

      -

      Remember, no cheating and no spamming!

      - -

      Need a creative way to spread the word? Try:

      -

        -
      • Writing a blog post about hook.io
      • -
      • Creating a useful Hook and sharing it with others
      • -
      • Opening a Pull Request for your favorite npm module to include a live hosted demo on hook.io
      • -
      • Tell your co-workers about hook.io
      • -
      • Give a talk about hook.io at your local meetup
      • -
      - -

      We really hope you like our service and genuinely want to share it with your peers.

      -

      - - Marak
      - Creator of hook.io
      - http://hook.io/Marak - -

      - -

      Here is a signed message from our Bitcoin address verifying that we control the funds. You can verify it here.

      -

      - -----BEGIN BITCOIN SIGNED MESSAGE-----
      - hook.io November 2014 referral bonus
      - -----BEGIN SIGNATURE-----
      - 17aAjrX4tczRJPa7LV5FwDsmtrhdYf1nzV
      - HCL7pM6NwR0VWZoVOMnSxylXXnS3z1Yjtwn+uvfYfJX7LCyvHWn87FW/YsDiW2l0tKjaY1JPUUk3svmpQ7zkiWI=
      - -----END BITCOIN SIGNED MESSAGE-----
      -

      -
      -
      - -
      \ No newline at end of file diff --git a/view/emails/0_referral_madness.js b/view/emails/0_referral_madness.js deleted file mode 100644 index 9e20eac4..00000000 --- a/view/emails/0_referral_madness.js +++ /dev/null @@ -1,3 +0,0 @@ -module['exports'] = function view (opts, callback) { - callback(null, this.$.html().replace(/\{\{username\}\}/g, opts.request.query.user)); -}; \ No newline at end of file diff --git a/view/emails/layout.html b/view/emails/layout.html deleted file mode 100644 index 976de5cf..00000000 --- a/view/emails/layout.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - -
      - - - - - \ No newline at end of file diff --git a/view/emails/layout.js b/view/emails/layout.js deleted file mode 100644 index 04ad7757..00000000 --- a/view/emails/layout.js +++ /dev/null @@ -1,8 +0,0 @@ -module['exports'] = function view (opts, callback) { - var req = opts.request - $ = this.$; - if (req.user && req.user.username) { - $('.myHooks').attr('href', '/' + req.user.username); - } - callback(null, this.$.html()); -}; \ No newline at end of file diff --git a/view/env.html b/view/env.html index d5757a55..08e33dc7 100644 --- a/view/env.html +++ b/view/env.html @@ -1,47 +1,99 @@ -
      + +

      Environment Variables

      -

      Here you can set key / value pairs that will be available in all your hooks as Hook.env

      -

      This is a great way to protect private authorization credentials from public view ( such as an API key or password )

      -
      - Add new Key -
      -
      - -
      - - - - - - -
       KeyValue
      -
      - -
      -
      +
      +

      Here you can set key value pairs that will be available in all your services as Hook.env

      +

      Environment variables can be used to protect private authorization credentials from public view ( such as an API key or password )

      +

      +
      +

      Click to Add new Key / Value

      + + + + + + + +
       KeyValue
      + +
      +
      + +
      +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/env.js b/view/env.js index b5f5911a..03218a4c 100644 --- a/view/env.js +++ b/view/env.js @@ -1,61 +1,121 @@ var user = require('../lib/resources/user'); -var bodyParser = require('body-parser'); +var cache = require("../lib/resources/cache"); +var resource = require('resource'); +var checkRoleAccess = require('../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../config'); +var psr = require('parse-service-request'); + module['exports'] = function view (opts, callback) { var req = opts.request, res = opts.response; var $ = this.$; - if (!req.isAuthenticated()) { - req.session.redirectTo = "/env"; - return res.redirect('/login'); - } - - bodyParser()(req, res, function bodyParsed(){ - mergeParams(req, res, function(){}); + var _user; + psr(req, res, function (req, res, fields){ var params = req.resource.params; - - user.find({ name: req.user.username }, function(err, results) { - if (err) { - return callback(null, err.message); - } - if (results.length === 0) { - return callback(null, 'No user found'); + checkRoleAccess({ req: req, res: res, role: "env::read" }, function (err, hasPermission) { + if (!hasPermission || req.resource.owner === "anonymous") { // don't allow anonymous hook update + if (req.jsonResponse !== true && typeof params.hook_private_key === "undefined") { + req.session.redirectTo = "/env"; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, "env::read")); + } else { + _user = req.resource.owner; + next(); } + }); - var _user = results[0]; + function next () { + user.findOne({ name: _user }, function(err, _user) { + // console.log('found old user', params) + if (err) { + return callback(null, err.message); + } - if (params.update) { + var _env = _user.env || {}; // default to empty object if no env exists + if (params.update || req.method === "POST") { // update is destructive and complete // entire User.env will now be replaced by the contents of the form submitted // all old fields are deleted and replaced with new values - _user.env = {}; - if (params.key) { - if (typeof params.key === "string") { - params.key = [params.key]; - } - if (typeof params.value === "string") { - params.value = [params.value]; + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "env::write" }, function (err, hasPermission) { + + if (!hasPermission) { // don't allow anonymous hook update + if (req.jsonResponse !== true && typeof params.hook_private_key === "undefined") { + req.session.redirectTo = "/env"; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, "env::write")); + } else { + //_user = req.resource.owner; + _updateEnv(); } - params.key.forEach(function(k, i){ - if (k.length) { - // try to JSON.parse since value might be array or object - var val = params.value[i]; - try { - val = JSON.parse(val); - } catch (err) { - val = params.value[i]; + }); + + function _updateEnv () { + _user.env = {}; + // new JSON based API with env property containing object hash + if (params.env) { + for (var k in params.env) { + if (params.env[k] === null || params.env[k] === "null") { + delete _env[k]; + } else { + _env[k.trim()] = params.env[k]; } - // console.log('about to save', typeof val, val) - _user.env[k] = val; } - }) - } - _user.save(function(err){ - if (err) { - return res.end(err.message); + //_env = params.env; } - showEnv(); - }); + // old form based API with comma delimited key / value fields + if (params.key) { + if (typeof params.key === "string") { + params.key = [params.key.trim()]; + } + if (typeof params.value === "string") { + params.value = [params.value]; + } + + params.key.forEach(function (k, i) { + if (k.length) { + // try to JSON.parse since value might be array or object + var val = params.value[i]; + try { + val = JSON.parse(val); + } catch (err) { + val = params.value[i]; + } + // TODO: fix merging from /env form, delete undefined keys..s. + if (val === null || typeof val === "undefined") { + delete _env[k]; + } else { + _env[k.trim()] = val; + } + } + }); + } + + _user.env = _env; + + // console.log('making save to user', _user) + _user.save(function(err){ + if (err) { + return res.end(err.message); + } + // update user cache + cache.set('/user/' + _user.name, _user, function() { + resource.emit('env::write', { + ip: req.connection.remoteAddress, + owner: req.session.user, + url: req.url + }); + if (req.jsonResponse) { + return res.json({ status: 'updated'}); + } else { + showEnv(true); + } + }); + }); + } } else { showEnv(); } @@ -77,6 +137,7 @@ module['exports'] = function view (opts, callback) { .replace(/[\r\n]/g, preserveCR); ; } + function showEnv () { var env = _user.env || {}; function addRow (k, v) { @@ -84,8 +145,8 @@ module['exports'] = function view (opts, callback) { v = JSON.stringify(v); } var _delete = 'X'; - var _key = ''; - var _value = ''; + var _key = ''; + var _value = ''; $('.env').append('' + _delete + '' + _key + '' + _value + ''); }; if (Object.keys(env).length === 0) { @@ -94,47 +155,19 @@ module['exports'] = function view (opts, callback) { Object.keys(env).forEach(function(key){ addRow(key, env[key]); }); - + resource.emit('env::read', { + ip: req.connection.remoteAddress, + owner: req.session.user, + url: req.url + }); + if (req.jsonResponse) { + return res.json(_user.env || {}); + } callback(null, $.html()); } - }); + }); + } }); - }; - - - -// -// Middleware for merging all querystring / request.body and route parameters, -// into a common scope bound to req.resource.params -// -function mergeParams (req, res, next) { - - req.resource = req.resource || {}; - req.resource.params = {}; - req.body = req.body || {}; - - // - // Iterate through all the querystring and request.body values and - // merge them into a single "data" argument - // - if (typeof req.params === 'object') { - Object.keys(req.params).forEach(function (p) { - req.resource.params[p] = req.param(p); - }); - } - - if (typeof req.query === 'object') { - Object.keys(req.query).forEach(function (p) { - req.resource.params[p] = req.query[p]; - }); - } - - Object.keys(req.body).forEach(function (p) { - req.resource.params[p] = req.body[p]; - }); - - next(); -} \ No newline at end of file diff --git a/view/events.html b/view/events.html new file mode 100644 index 00000000..d821d153 --- /dev/null +++ b/view/events.html @@ -0,0 +1,136 @@ + + + + +
      +

      System Events

      +
      +
      + +
      +
      + + + + + + +
      +
      + + +
      + +
      +
      + +
      +

      View your System Events Log

      + +

      System Events:

      + +

      All actions on {{appName}} are tracked internally as System Events. These events are useful for monitoring the actions your services are performing.

      + + + +

      Events and Role Access

      +

      It's important to note that all {{appName}} events are available as Access Roles. This mapping allows granular role based key access per System Event.

      + + + + +

      Writing to System Events

      +

      Several useful events ( such as hook::run or keys::authCheck) are automatically written to your event service log.

      +

      A full listing of available events can currently be found here.

      +

      Currently, {{appName}} does not support writing custom system events.

      + + +

      Events API

      + +

      To access the System Event log for an account, simply attach /events to the end of your account homepage.

      +

      {{appUrl}}/marak/events?hook_private_key=b4b1153f-ae6c-49f5-9cd7-1f554a6c1a1e

      +

      The events endpoint for a Hook will intelligently respond based on the incoming HTTP Accept Header.

      + + +

      Authentication

      +

      By default, {{appName}} system events are private. In order to access them from a third-party service, you'll need to generate a valid API Access Key with the events::read role.

      +

      Below is a link which contains a limited role API Access Key created only for demonstration purposes.

      +

      {{appUrl}}/marak/events?hook_private_key=b4b1153f-ae6c-49f5-9cd7-1f554a6c1a1e

      + + +

      Streaming events

      +

      To access streaming events, request the events endpoint with an Accept Header value of */*

      +

      Example: curl -N {{appUrl}}/marak/events?hook_private_key=b4b1153f-ae6c-49f5-9cd7-1f554a6c1a1e

      + + +

      Plain Text Events

      +

      To access plain text events, request the events endpoint with an Accept Header value of text/plain

      +

      Example: curl -H "Accept: text/plain" {{appUrl}}/marak/events?hook_private_key=b4b1153f-ae6c-49f5-9cd7-1f554a6c1a1e

      + + +

      Client SDK

      +

      Using the Client SDK to access events is simple

      +

      Here are two examples of using the Client SDK. See SDK for more information

      +

      +

      +var sdk = require("hook.io-sdk");
      +var client = sdk.createClient({});
      +// callback style
      +client.events.get('marak', function (err, events) {
      +  console.log(err, events);
      +});
      +// streaming
      +client.events.stream('marak', process.stdout);
      +                
      +

      + + +
      + +
      +
      + +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/events.js b/view/events.js new file mode 100644 index 00000000..65eb246a --- /dev/null +++ b/view/events.js @@ -0,0 +1,33 @@ +var events = require('../lib/resources/events'); +var config = require('../config'); + +module['exports'] = function view (opts, callback) { + var $ = this.$, + req = opts.request, + res = opts.response; + var appName = req.hostname; + + $ = req.white($); + + if (!req.isAuthenticated()) { + $('.recent').html('Log in to view your ' + appName + ' system events.'); + var out = $.html(); + out = out.replace(/\{\{appName\}\}/g, appName); + callback(null, out); + } + + $('.currentRoot').html("/" + req.session.user); + $('.currentRoot').attr('href', '/events') + $('.systemEvents').attr('href', config.app.url + "/" + req.session.user + "/events"); + $('.systemEvents').html(config.app.url + "/" + req.session.user + "/events"); + + $('.exampleSystemEventsLink').attr('href', config.app.url + "/" + req.session.user + "/events"); + $('.exampleSystemEventsLink').html(config.app.url + "/" + req.session.user + "/events"); + + events.recent('/' + req.session.user, function(err, results){ + // results = JSON.parse(results[0]) + $('.recent').html(JSON.stringify(results, true, 2)); + return callback(null, $.html()); + }) + +}; \ No newline at end of file diff --git a/view/examples.html b/view/examples.html index 0d839511..67230593 100644 --- a/view/examples.html +++ b/view/examples.html @@ -1,13 +1,127 @@ -

      Interactive Examples

      -

      These examples can be run directly in the browser or from any client which speaks HTTP.

      - + + +
      \ No newline at end of file diff --git a/view/examples.js b/view/examples.js index f76ec6fa..2970dc24 100644 --- a/view/examples.js +++ b/view/examples.js @@ -1,4 +1,23 @@ module['exports'] = function (opts, callback) { - var $ = this.$; - return callback(null, $.html()); -}; \ No newline at end of file + var hook = require('../lib/resources/hook'); + //return opts.res.end('fml'); + var $ = this.$, req = opts.req, res = opts.res; + var appName = req.hostname; + + hook.find({ owner: 'examples', isPublic: true }, function (err, results) { + if (err) { + return res.end(err.message); + } + + if (req.jsonResponse) { + return res.json(results); + } + + $ = req.white($); + return callback(null, $.html()); + + }); + +}; + +module['exports'].cache = 0; \ No newline at end of file diff --git a/view/faq.html b/view/faq.html new file mode 100644 index 00000000..ccc76ff7 --- /dev/null +++ b/view/faq.html @@ -0,0 +1,252 @@ +
      +

      Frequently Asked Questions

      +
      +
      + +
      +
      + + + + + + + + + + + + + + + + + + +
      +
      + + +
      + +
      +
      +

      Microservices

      + +

      What are microservices?

      +

      + Microservice are small, independent processes that communicate with each other to form complex applications which utilize language-agnostic APIs. +

      +

      These services are small building blocks, highly decoupled and focused on doing a small task, facilitating a modular approach to system-building

      +

      On {{appName}}, microservices are represented by a single function matched to a single unique URL

      + +

      Why use microservice?

      +

      + Building with microservices is advantageous for several reasons +

      +

      +

        +
      • Decoupled Modular Application Design
      • +
      • Stateless and Highly Fault Tolerant
      • +
      • Easy to reason about and maintain smaller functions
      • +
      +

      + +

      Do you support Webhooks?

      +

      Yes! Every service on {{appName}} is a fully qualified HTTP Webhook endpoint

      +

      Click Webhooks for more details.

      + + +

      Do you support Websockets?

      +

      Yes! Every service on {{appName}} is a capable of accepting Websocket ws:// connections.

      +

      Click Websockets for more details.

      + + +

      Why can't I start a server?

      +

      On {{appName}}, we don't allow the deployment of long running services aimed to start listening TCP or HTTP servers. This is by design.

      +

      The reasoning for this is simple, {{appName}} is a hosting platform for Microservices and Webhooks. Instead of deploying a server to our platform, you simply deploy a single function. This function can then be accessed through a unique URL which we provide.

      +

      In other words, we've already built a server for you! All you need to do is give us the code which will be mapped to a single route, which is the Microservice.

      + +
      + + +

      How can I create a Service?

      +

      + Creating microservices on {{appName}} is very easy! +

      +

      First, you'll need to register for a Free Developer Account. Once you have an account visit the /new page to create a new service.

      +

      When asked to build the service you'll need to provide a few key pieces of information such as:

      +

      Service Name / Service Code / explain defaults

      + + +

      Running your service

      +

      To run a service, simply visit the URL which was created with that service in your browser.

      +

      Any HTTP request from any source ( including streaming HTTP and Websockets ) will trigger the service to run. It's also possible to schedule services to run on a timer.

      +

      Here are some examples of using the command line tool cURL to run services on {{appName}}

      + + +

      Sending data to your service

      +

      All services are automatically capable of parsing most types of incoming HTTP request data including:

      +

      +

        +
      • Query String Parameters
      • +
      • Form / Multipart Parameters
      • +
      • Streaming HTTP / Streaming Binary Data
      • +
      • JSON
      • +
      + Read More About Sending Data +

      + + +

      Editing a Service

      +

      Login to your account and visit /services. From here you can click on the service name to load an editing page.

      + + +

      Private Services

      +

      Private services are an optional feature for paid account. A private service will be restricted for general access.

      +

      If a service is set to "Private", it will only be accessible through an API Access Key with an associated role of hook::run.

      +

      Read More About Access Keys and Roles

      + +
      + +

      Logging and Events

      +

      Service Logs

      +

      {{appName}} provides a simple integrated interface for logging from any Hook service. This integrated logging service is can be helpful for Service debugging or monitoring.

      +

      + Read more about Service Logs +

      + +

      System Events

      +

      All actions on {{appName}} are tracked internally as System Events. These events are useful for monitoring the actions your services are performing.

      +

      + Read more about System Events +

      + + +
      + +

      Multi-cloud Databases

      +

      supports multi-cloud databases across multiple providers

      + + +

      Supported Databases

      +
        +
      • Built-in Datastore
      • +
      • CouchDB
      • +
      • Redis
      • +
      • MongoDB
      • +
      • DynamoDB
      • +
      • more...
      • +
      + Read More About Databases + +
      + +

      Multi-cloud Files

      +

      supports multi-cloud files across multiple providers

      + + +

      Cloud Providers

      +

      +

      + Read More About Files +

      + +
      + +

      Additional Resources

      + + Documentation
      + Open-source Code
      + Feature Wishlist
      + Contact Us
      + + + +
      +
      + +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/faq.js b/view/faq.js new file mode 100644 index 00000000..a84363be --- /dev/null +++ b/view/faq.js @@ -0,0 +1,13 @@ +var langs = require('../lib/resources/programmingLanguage'); + +module['exports'] = function presentFaq (opts, callback) { + + var $ = this.$, req = opts.req; + Object.keys(langs.languages).forEach(function(l){ + $('.left_middle_widget .tag').append('
    • ' + l + '
    •  '); + }); + + $ = req.white($); + return callback(null, $.html()); + +}; \ No newline at end of file diff --git a/view/forms.html b/view/forms.html index 05043cab..94c595ad 100644 --- a/view/forms.html +++ b/view/forms.html @@ -1,7 +1,7 @@

      Forms

      -

      Hook.io is able to generate forms for any Hook based on the hook's Hook.schema

      +

      {{appName}} is able to generate forms for any Hook based on the hook's Hook.schema

      Form generation is powered by the mschema-forms library.

      \ No newline at end of file diff --git a/view/forms.js b/view/forms.js index c3059f2f..4c9d4d92 100644 --- a/view/forms.js +++ b/view/forms.js @@ -1,3 +1,7 @@ module['exports'] = function view (opts, callback) { - callback(null, this.$.html()); + var $ = this.$, req = opts.req; + var appName = req.hostname; + var out = $.html(); + out = out.replace(/\{\{appName\}\}/g, appName); + callback(null, out); }; \ No newline at end of file diff --git a/view/gateway.html b/view/gateway.html new file mode 100644 index 00000000..ea0fba24 --- /dev/null +++ b/view/gateway.html @@ -0,0 +1,63 @@ +
      + + + + + + + + + +

      Hot-code Gateways

      +
      +

      run code with curl

      + +
      + + +
      +
      +
      + +
      \ No newline at end of file diff --git a/view/gateway.js b/view/gateway.js new file mode 100644 index 00000000..ea684937 --- /dev/null +++ b/view/gateway.js @@ -0,0 +1,95 @@ +var hook = require('../lib/resources/hook'); +var metric = require('../lib/resources/metric'); +var config = require('../config'); +var checkRoleAccess = require('../lib/server/routeHandlers/checkRoleAccess') +var microcule = require('microcule'); +var RateLimiter = microcule.plugins.RateLimiter; +var rateLimiter = new RateLimiter({ + provider: metric +}); + +// Ensure that anonymous user has been registered in rate limiter +// If this is not done, all anonymous gateway requests would be blocked +rateLimiter.registerService({ + owner: 'anonymous', + name: 'gateway' +}); + +module['exports'] = function view (opts, callback) { + var $ = this.$, + res = opts.res, + req = opts.req, + params = req.resource.params; + + var pool = config.pools.worker; + var remoteHandler = hook.runRemote({ pool: pool }); + + // TODO: determine owner by API key or session + + // Remark: is all session code already being handled? + /* + // TODO: replace hook::run with gateway::run role + checkRoleAccess({ req: req, res: res, role: "hook::run" }, function (err, hasPermission) { + // console.log('check for role access', err, hasPermission) + if (!hasPermission || req.resource.owner === "anonymous") { // don't allow anonymous hook update + if (req.jsonResponse !== true && typeof params.hook_private_key === "undefined") { + req.params.owner = "anonymous"; + return runService(); + } + // return res.end(config.messages.unauthorizedRoleAccess(req, "hook::run")); + } else { + req.para1ms.owner = req.resource.owner; + runService(); + } + }); + */ + runService(); + function runService () { + // line will be removed when role checks are working + req.params.owner = req.params.owner || "anonymous"; + // TODO: Get total amount of hits the user has for this billing period + // TODO: check total hits against plan limits, + // TODO: reset limits on the start of every month, or at the start of the billing cycle? + // TODO: pull value from config / paidPlans.js + // TODO: add back specific gateway hits + // WARNING: currently using default limit values ( see above note ) + // TODO: Scope gateway limits to current user session or default to anonymous limits + rateLimiter.middle({ + maxLimit: 1000000, + maxConcurrency: 50 // limit public gateway to 50 requests, we should key this per account / anonymous + })(req, res, function (err) { + if (err) { + // TODO: better handling of specific error codes + if (err.code === 'RATE_LIMIT_EXCEEDED') { + res.statusCode = 410; + return res.json({ + error: true, + message: 'Rate limited: Max monthly limit hit: ' + err.monthlyLimit + }); + } + if (err.code === 'RATE_CONCURRENCY_EXCEEDED') { + res.statusCode = 410; + return res.json({ + error: true, + message: 'Rate limited: Max concurrency limit hit: ' + err.maxConcurrency + }); + } + return res.end(err.message); + } + _runRemote(); + }); + } + + // run hook on remote worker + function _runRemote () { + // console.log('calling remote handler', req.url); // TODO: not full url + return remoteHandler(req, res, function (err, r) { + //console.log('remote handler ended?'.green) + //console.log(err, r); + // TODO: check if callback makes it here everytime... + // do nothing with the result + // if the hook has been properly formatted, it should be able to call res.write res.end on it's own + }); + } + +}; \ No newline at end of file diff --git a/view/gateways.html b/view/gateways.html new file mode 100644 index 00000000..384856a9 --- /dev/null +++ b/view/gateways.html @@ -0,0 +1,39 @@ +
      +

      Hot-code Gateways

      +
      + + +

      In most cases, you will want to save your Service's source-code as a Hook with an associated URL.

      +

      In other situations, you may need to send code that has to be executed immediately and without any additional requests ( such as development testing or executing dynamically created source code from a third-party service ).

      +

      {{appName}} provides a hot-code gateway which allows users to send code to be executed immediately. In fact, if you look at the Interactive Playground, you may notice it is powered by several hot-code gateways ( a separate gateway for each specific programming language ).

      +

      Examples of using the Hot-Code Gateways

      +

      Here are two simple examples of using echo and curl to execute hot-code against {{appName}}'s gateway.

      + +

      JavasScript

      +
      echo 'module["exports"] = function helloWorld (h) { h.res.end("Hello world!"); };' | curl --data-urlencode source@- http://javascript.{{appName}}
      + +

      Python

      +
      echo 'print "hello world"' | curl --data-urlencode source@- http://python.{{appName}}/
      + +

      You can also simply open a url to the gateway like this: http://python.{{appName}}/?source=print "hello world" +
      + +

      Available Gateways

      + Note: Most gateways will an return an error or blank response if the source parameter is not provided + + +
      +
      \ No newline at end of file diff --git a/view/gateways.js b/view/gateways.js new file mode 100644 index 00000000..41b6f8f6 --- /dev/null +++ b/view/gateways.js @@ -0,0 +1,6 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, + req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/gist.html b/view/gist.html index d0fab9cd..08021a45 100644 --- a/view/gist.html +++ b/view/gist.html @@ -1,17 +1,22 @@ -
      +

      Github Gist

      http://gist.github.com is a free service for hosting small snippets of code.

      The source code for a Hook is stored as a Github Gist.

      This allows for seamless revisions, code-highlighting, source history, and shareability through forking.


      Here is an example of a Hook hosted on Github

      You can run this hook, or fork your own copy and instantly begin modifying it on Github.

      -
      +
      - +
      Loading...
      +
      + +
      +

      About to create a new Hook

      +
      \ No newline at end of file diff --git a/view/gist.js b/view/gist.js index c3059f2f..6217b777 100644 --- a/view/gist.js +++ b/view/gist.js @@ -1,3 +1,50 @@ +var gist = require('../lib/resources/gist'); +var mergeParams = require('merge-params'); +var bodyParser = require('body-parser'); + module['exports'] = function view (opts, callback) { - callback(null, this.$.html()); + var req = opts.request, + res = opts.response + $ = this.$; + + $ = req.white($); + + bodyParser()(req, res, function bodyParsed(){ + mergeParams(req, res, function(){ + var params = req.resource.params; + // if no session, check if source exists + // if source exists, save it to the current session in new variable + // set redirect action back to this page with session variable + if (typeof params.source !== 'undefined') { + req.session.gistSource = params.source; + } + if (!req.isAuthenticated()) { + req.session.redirectTo = "/gist"; + return res.redirect('/login'); + } + // check for session, if exists, create gist + if (req.isAuthenticated() && typeof req.session.gistSource !== 'undefined') { + $('.gistSplash').remove(); + if (typeof req.session.gistSource !== 'undefined') { + $('.gistSource').html(req.session.gistSource); + gist.create({ source: req.session.gistSource, accessToken: req.user.accessToken }, function (err, result){ + if (err) { + return res.end(err.message); + } + req.session.gistLink = result.html_url; + req.session.gistLink = req.session.gistLink.replace('gist.github.com/', 'gist.github.com/' + req.session.user + "/"); + return res.redirect('/new') + }) + } else { + callback(null, $.html()); + } + } else { + return res.redirect('/'); + //$('.gistForm').remove(); + //callback(null, $.html()); + } + + }); + }); + }; \ No newline at end of file diff --git a/view/helpers/html.js b/view/helpers/html.js new file mode 100644 index 00000000..4c9d0bc8 --- /dev/null +++ b/view/helpers/html.js @@ -0,0 +1,19 @@ +var html = {}; +html.makeLink = function makeLink (url, text) { + var str = '' + text + ''; + return str; +}; +html.rowToString = function rowToString (row) { + var str = ''; + for (var col in row) { + var val = row[col]; + if (!val) { + val = ' ' + } + str += '' + val + '' + } + //str += '' + r.name + '' + r.cronPattern + ''; + str += ''; + return str; +} +module.exports = html; \ No newline at end of file diff --git a/view/helpers/i18n.html b/view/helpers/i18n.html new file mode 100644 index 00000000..e69de29b diff --git a/view/helpers/i18n.js b/view/helpers/i18n.js new file mode 100644 index 00000000..81d94880 --- /dev/null +++ b/view/helpers/i18n.js @@ -0,0 +1,25 @@ +module['exports'] = function i18nViewHelper (i, $) { + // TODO: make into helper function + $('.i18n').each(function(index, item){ + translate(i, $, item) + }); +}; + +var translate = module['exports'].translate = function (i, $, item) { + var el = $(item), + tagType = el[0].name; + switch (tagType) { + case 'input': + var v = $(item).attr('value'); + $(item).attr('value', i.__(v)); + var ph = $(item).attr('placeholder'); + $(item).attr('placeholder', i.__(ph)); + var t = $(item).attr('title'); + $(item).attr('title', i.__(t)); + break; + default: + var v = $(item).html(); + $(item).html(i.__(v)); + break; + } +} \ No newline at end of file diff --git a/view/hiring.html b/view/hiring.html new file mode 100644 index 00000000..3087fed3 --- /dev/null +++ b/view/hiring.html @@ -0,0 +1,42 @@ +
      +

      Support Developer

      + +

      hook.io is hiring our first employee! The position is for a Support Developer. The description can be found below.

      + +

      Primary Responsibilities

      +

      +

        +
      • Handle incoming technical support requests ( through Email / Github Issues / Twitter / IRC )
      • +
      • Create new product features based on user feedback
      • +
      • Be a power-user of the hook.io platform
      • +
      • Improve platform tooling ( Testing / Continuous Integration / Accessibility / Localization )
      • +
      • Improve developer tools ( API Clients / Command Line Tools / Documentation )
      • +
      +

      +

      Required Skills

      +

      +

        +
      • Intermediate knowledge of JavaScript / HTML / CSS
      • +
      • Experience using Github / Twitter / IRC
      • +
      • Basic knowledge of web-services and APIs
      • +
      +

      +

      Preferred Skills

      +

      +

        +
      • Experience developing software with Node.js and NPM
      • +
      • Experience managing open-source projects on Github
      • +
      • Experience working in a Support role
      • +
      +

      + +

      Compensation

      +

      Compensation will be based on experience. Pay will be on an hourly rate with a minimal of 10 hours per week. Equity is available.

      + +

      How to Apply?

      +

      You would be the first employee. The best way to apply would be to sign up for the platform at https://hook.io and try it out. The next step is to visit http://github.com/bigcompany/hook.io/issues. From here, you will need to open up a Pull Request that helps make the project better. If you can do that, you will most likely get the position.

      + +
      +
      +
      +
      \ No newline at end of file diff --git a/view/hook.js b/view/hook.js deleted file mode 100644 index 2c9d45ee..00000000 --- a/view/hook.js +++ /dev/null @@ -1,91 +0,0 @@ -var hook = require('../lib/resources/hook'); -var request = require('hyperquest'); -var dateFormat = require('dateformat'); -var forms = require('mschema-forms'); -var mustache = require('mustache'); -var View = require('view').View; - -var config = require('../config'); - -module['exports'] = function view (opts, callback) { - - var params = opts.request.resource.params; - var req = opts.request, - res = opts.response; - var $ = this.$; - var gist = opts.gist || params.gist; - - var run = params.run; - - if (params.run) { - opts.req = req; - opts.res = res; - return hook.runHook(opts - , function(err, result){ - if (err) { - return res.end(err.message); - } - return callback(null, result.output); - }); - - } - - // not forking the hook, not running the hook, we need to present it - var theme, presenter; - - if (typeof req.hook.theme === "undefined" || req.hook.theme.length === 0) { - // no theme HTML - theme = config.defaultTheme; - } else { - theme = req.hook.theme; - } - - if (typeof req.hook.presenter === "undefined" || req.hook.presenter.length === 0) { - // no theme Presenter - if (theme === config.defaultTheme) { - presenter = config.defaultPresenter; - } else { - presenter = "http://hook.io/themes/simple/index.js"; - - } - } else { - presenter = req.hook.presenter; - } - - // if a theme was set ( not the default debug theme, and no presenter was given, use simple.js ) - hook.fetchHookTheme(theme, function(err, _theme){ - if (err) { - return res.end('Unable to fetch theme: ' + theme + ' ' + err.message); - } - hook.fetchHookPresenter(presenter, function(err, _presenter){ - if (err) { - return res.end(hook.formatError(err)); - } - - var _view = new View({ template: _theme.toString(), presenter: _presenter }); - - // give the presenter 3 seconds to render, or else it has failed - var completedTimer = setTimeout(function(){ - if (!completed) { - return callback(new Error('Hook presenter took more than 3 seconds to load. Aborting request. \n\nA delay of this long usually means the presenter never fired it\'s callback. Check the presenter code for error. \n\nIf this is not the case and you require more than 3 seconds to present your view, please contact hookmaster@hook.io')); - } - }, 3000); - - var completed = false; - completedTimer = clearTimeout(completedTimer); - try { // this will catch user run-time errors in the presenter - _view.present({ - request: req, - response: res, - gist: req.hook.gist - }, function(err, rendered){ - return callback(null, rendered); - }); - } catch (err) { - return res.end(err.stack); - } - - }); - }); - -}; \ No newline at end of file diff --git a/view/hook/_rev.html b/view/hook/_rev.html new file mode 100644 index 00000000..db0e32b9 --- /dev/null +++ b/view/hook/_rev.html @@ -0,0 +1,20 @@ + +

      Service Revision history

      +
      +

      This page contains a list of up to the past twenty revisions (saves) of

      + + + + + + + +
      Save DateService Name_rev
      +
      \ No newline at end of file diff --git a/view/hook/_rev.js b/view/hook/_rev.js new file mode 100644 index 00000000..3cd5669d --- /dev/null +++ b/view/hook/_rev.js @@ -0,0 +1,57 @@ +var hook = require('../../lib/resources/hook'); +var config = require('../../config'); +var request = require('request'); +var df = require('dateformat'); + +module['exports'] = function _revPresenter (opts, callback) { + var $ = this.$, + req = opts.req, + res = opts.res; + + hook.findOne({ name: req.params.hook, owner: req.params.owner }, function(err, _u) { + if (err) { + return res.end(err.message); + } + var nanoConfig = 'http://' + config.couch.username + ":" + config.couch.password + "@" + config.couch.host + ":" + config.couch.port; + var nano = require('nano')(nanoConfig); + var HookDB = nano.use('hook'); + HookDB.get(_u.id, { revs_info: true, include_docs: true } , function (err, _hook) { + if (err) { + return res.end(err.message); + } + $('.hookName').attr('href', _hook.owner + '/' + _hook.name + '/admin'); + $('.hookName').html( _hook.owner + '/' + _hook.name); + + //console.log('revs found', _hook._revs_info.length) + var _revs = []; + _hook._revs_info.forEach(function(r){ + if (r.status === "available") { + _revs.push(r.rev); + } + }); + HookDB.get(_u.id, { revs_info: true, open_revs: JSON.stringify(_revs, true, 2) } , function (err, docs) { + if (err) { + return res.end(err.message); + } + docs = docs.reverse(); + //return res.json(docs); + // show all revs as JSON or HTML list + if (req.jsonResponse === true) { + res.json(docs, true, 2); + } else { + var str = ''; + docs.forEach(function(rev){ + if (rev.ok) { + var url = '/' + req.params.owner + '/' + req.params.hook + '?_rev=' + rev.ok._rev; + $('.table').append('' + df(rev.ok.mtime, "ddd mmm dd yyyy HH:mm:ss") + '' + rev.ok.name + '' + '' + rev.ok._rev + '' + ''); + //var url = '/' + req.params.owner + '/' + req.params.hook + '?_rev=' + rev.rev; + //str += '' + rev.rev + '
      ' + } + }) + callback(null, $.html()); + } + }); + }); + }); + +}; \ No newline at end of file diff --git a/view/hook/_src.html b/view/hook/_src.html new file mode 100644 index 00000000..19a0958b --- /dev/null +++ b/view/hook/_src.html @@ -0,0 +1,44 @@ + + + + + +
      +

      Source Code

      +

      Run Service

      + +

      These examples can be run directly in the browser or from any client which speaks HTTP.
      You can also send data using tools like cURL.

      + +

      + + +
      +

      Code

      + +
      +
      +

      To make your own copy of this service or "Fork" it simply click the button. You will need to register for a Free Account, but it's very quick and easy! +

      \ No newline at end of file diff --git a/view/hook/_src.js b/view/hook/_src.js new file mode 100644 index 00000000..59bccbd9 --- /dev/null +++ b/view/hook/_src.js @@ -0,0 +1,147 @@ +var hook = require('../../lib/resources/hook'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); + +module['exports'] = function view (opts, callback) { + var req = opts.req, res = opts.res, $ = this.$; + var appName = req.hostname; + + $ = req.white($); + + var params = req.resource.params; + + if (req.user && req.session.user) { + $('.loggedOut').remove(); + } + + return hook.find({owner: req.params.owner, name: req.params.hook }, function (err, result) { + if (err) { + return res.end(err.message); + } + + if (result.length === 0) { + return res.end('Not found'); + } + + var h = result[0]; + req.resource.owner = req.params.owner; + checkRoleAccess({ req: req, res: res, role: "hook::source::read" }, function (err, hasPermission) { + + // only protect source of private services + if (h.isPrivate !== true) { + hasPermission = true; + } + + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::source::read")); + } + next(); + }); + + function next () { + + // note: we could be using the new pkg format + var extentions = { + ".js": "javascript", + ".coffee": "coffee-script", + ".lua": "lua", + ".php": "php", + ".pl": "perl", + ".py": "python", // Remark: You can also use the "--language python" option + ".py3": "python3", // Remark: You can also use the "--language python3" option + ".sh": "bash", + ".rb": "ruby", + ".tcl": "tcl", + ".ss": "scheme", + ".st": "smalltalk" + }; + var file = "index"; + Object.keys(extentions).forEach(function(ext){ + if (extentions[ext] === h.language) { + file += ext; + } + }); + + + $('.service').html(file); + $('.service').attr('href', '?f=' + file); + $('#forkLink').attr('href', '/' + h.owner + '/' + h.name + '/fork'); + + if (typeof h.themeSource === "undefined" || h.themeSource.length === 0) { + $('.view').parent().remove(); + } + + if (typeof h.presenterSource === "undefined" || h.presenterSource.length === 0) { + $('.presenter').parent().remove(); + } + + if (typeof h.mschema === "undefined") { + $('.schema').parent().remove(); + } + + /* + h.themeSource + h.presenterSource + h.mschema + $('.service').html(); + $('.view').html(); + $('.presenter').html(); + $('.schema').html(); + */ + + if (typeof h.inputs === "undefined" || h.inputs.length < 0) { + $('.hooks').remove(); + } else { + $('.inputs').html(JSON.stringify(h.inputs || [])); + } + req.hook = h; + + //$('.hookName').html(h.owner + "/" + h.name); + //$('.hookLink').attr('href', config.app.url + '/' + h.owner + '/' + h.name); + var hookLink = config.app.url + '/' + h.owner + '/' + h.name; + $('.hookRun').attr('href', hookLink); + $('.hookRun').html(hookLink); + + $('.hookAdmin').attr('href', config.app.url + '/' + h.owner + '/' + h.name + '/admin'); + $('.hookAdmin').html(h.owner + '/' + h.name); + + switch (params.f) { + + case 'view.html': + $('#code').text(h.themeSource); + break; + + case 'presenter.js': + $('#code').text(h.presenterSource); + break; + + case 'schema.js': + $('#code').text(JSON.stringify(h.mschema, true, 2)); + break; + + default: + $('#code').text(h.source); + break; + + } + + $('.currentFile').html(params.f); + + var lang = h.language; + // since python and python3 are considered the same for rainbow highlighting library + if (lang === 'python3') { + lang = 'python'; + } + $('#code').attr('data-language', lang); + + var out = $.html(); + out = out.replace(/\{\{appName\}\}/g, appName); + callback(null, out); + + } + + }); + +}; + +module['exports'].useParentLayout = false; \ No newline at end of file diff --git a/view/hooks.html b/view/hooks.html deleted file mode 100644 index 06bb19f2..00000000 --- a/view/hooks.html +++ /dev/null @@ -1,60 +0,0 @@ - - -
      -
      - - -
      -

      It looks like you haven't created any hooks yet!

      -

      You can Create a new Hook by clicking here

      -
      - - -
      -
      -
      -
      -
      \ No newline at end of file diff --git a/view/hooks.js b/view/hooks.js deleted file mode 100644 index 1fe1e660..00000000 --- a/view/hooks.js +++ /dev/null @@ -1,29 +0,0 @@ -module['exports'] = function view (opts, callback) { - - var $ = this.$; - - var req = opts.request, - res = opts.response, - params = req.resource.params; - - if (typeof params.signedMeUp !== "undefined" || typeof params.s !== "undefined") { - req.session.referredBy = req.params.username; - return res.redirect("/"); - } - - if (!opts.request.isAuthenticated()) { - $('.navBar').remove() - } - - for(var h in opts.hooks) { - $('.hooks').append('
      ' + opts.hooks[h].name + '') - } - - if (typeof opts.hooks === "object") { - if (Object.keys(opts.hooks).length > 0) { - $('.noHooks').remove(); - } - } - - callback(null, this.$.html()); -}; \ No newline at end of file diff --git a/view/i18n.html b/view/i18n.html new file mode 100644 index 00000000..70b973b6 --- /dev/null +++ b/view/i18n.html @@ -0,0 +1,6 @@ +
      +

      Internationalization

      +

      {{appName}} supports localization of several major languages.

      +

      The language of the site should change automatically based on your browser settings. You can manually adjust the language of the site in the upper right corner.

      +

      If you'd like to help add additional translations you can open up a Pull Request to this repository

      +
      diff --git a/view/i18n.js b/view/i18n.js new file mode 100644 index 00000000..1d6ba78d --- /dev/null +++ b/view/i18n.js @@ -0,0 +1,10 @@ +module['exports'] = function view (opts, callback) { + var req = opts.request + $ = this.$; + //$('title').html('hook.io/blog - all things microservice, the hook.io microservice blog'); + var out = $.html(); + var appName = req.hostname; + out = out.replace(/\{\{appName\}\}/g, appName); + //req.i18n.setLocale('de'); + return callback(null, out); +}; \ No newline at end of file diff --git a/view/index.html b/view/index.html index effe489a..202e3b9f 100644 --- a/view/index.html +++ b/view/index.html @@ -1,283 +1,10 @@ - - - - -

      Build and deploy HTTP microservices in seconds

      - -

      The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms. These services are built around business capabilities and are independently deployable by fully automated deployment machinery.

      - - - -
      - - JavaScript! - Node.js - npm - Github - Unix - -
      - - - - -
      - - - -
      - - - - - - - - - - - - - - - - -
      -

      Pricing

      -
      -

      Documentation

      -
      -

      Support

      -
      -

      Unix Pipe Integration / Cron

      -
      -

      Examples

      -
      -

      Themes

      -
      - -
      - + + \ No newline at end of file diff --git a/view/index.js b/view/index.js index 71b86439..6648c785 100644 --- a/view/index.js +++ b/view/index.js @@ -1,4 +1,8 @@ +var config = require('../config'); +var hook = require('../lib/resources/hook'); +var hooks = require('microcule-examples'); var slug = require('slug'); + slug.defaults.modes['rfc3986'] = { replacement: '-', // replace spaces with replacement symbols: true, // replace unicode symbols or not @@ -9,27 +13,42 @@ slug.defaults.modes['rfc3986'] = { slug.charmap['@'] = "-"; slug.charmap['.'] = "-"; -// TODO: this should be part of mschema. see: https://github.com/mschema/mschema/issues/10 -var address = { - "type": "string", - "regex": /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i, -}; module['exports'] = function view (opts, callback) { + // TODO: this should be part of mschema. see: https://github.com/mschema/mschema/issues/10 + var address = { + "type": "string", + "regex": /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i, + }; + var userResource = require('../lib/resources/user'); - var domain = require('../lib/resources/domain'); var $ = this.$, req = opts.request, res = opts.response, params = req.resource.params, user = req.session.user; + + var boot = {}; + + boot.baseUrl = config.app.url; + var i = req.i18n; + + boot.messages = { + "passwordBlank": i.__("password cannot be blank..."), + "passwordMismtach": i.__("passwords do not match..."), + "passwordConfirm": i.__("confirm account password..."), + "passwordInvalid": i.__("invalid password. try again..."), + "passwordReset": i.__("A password reset link has been emailed to:"), + "createAccount": i.__('Create New Account') + }; + // // enables curl signups // EASYTODO: move this into separate module // - // curl http://hook.io?signup=youremail@marak.com + // curl https://hook.io?signup=youremail@marak.com if (typeof params.signup !== "undefined" && params.signup.length > 0) { // TODO: email validation // TODO: this should be part of mschema. see: https://github.com/mschema/mschema/issues/10 if(!address.regex.test(params.signup)) { // test email regex @@ -54,17 +73,125 @@ module['exports'] = function view (opts, callback) { // // end curl signups // + // Render a cURL friendly response + if (req.headers.accept === "*/*") { + var address = ""; + if (typeof req.connection !== "undefined" && typeof req.connection.remoteAddress !== "undefined") { + address = req.connection.remoteAddress.toString(); + } + // TODO: move curl welcome screen to new module + var message = "Greetings " + address + '\n'; + message += "Thank you for cURLing hook.io! \n\n" + message += "We understand that not everyone is super thrilled to use a 'web-browser',\n"; + message += "so we also provide terminal services for accessing our hosting platform.\n\n"; + + message += "Here are some tricks to start! \n\n"; + message += "cURL us back later for a more comprehensive terminal interface with ssh support. \n\n"; + + message += "Sign up for a free hook.io account!\n\n".underline; + message += " curl https://hook.io?signup=youremail@marak.com \n\n"; + + message += "Send Query String Data\n\n".underline; + message += " curl --data 'foo=bar&hello=there' http://echo.hook.io/ \n\n"; + + message += "Post JSON Data\n\n".underline; + message += " curl -H \"Content-Type: application/json\" -X POST -d '{\"foo\":\"bar\",\"hello\":\"there\"}' http://echo.hook.io \n\n"; + + message += "Pipe and Transform Data\n\n".underline; + message += " echo 'foo' | curl --header 'content-type: application/octet-stream' --data-binary @- http://transform.hook.io/ \n\n"; + + message += "Pipe JavaScript Functions to the Cloud\n\n".underline; + message += " Note: This example requires a echo.js microservice file\n"; + message += " See: http://echo.hook.io/source for example source code\n\n"; + + message += " cat echo.js | curl --data-urlencode source@- http://gateway.hook.io\n\n"; + + message += "Pipe Binary Data As Multipart Form Upload\n\n".underline; + message += " Note: This example requires a cat.png image file.\n\n"; + message += " cat cat.png | curl -F 'degrees=180' -F 'image=@-;type=image/png' http://image.rotate.hook.io/ > upsidedown-cat.png \n\n"; + + return res.end(message); + } + // TODO: gateway.hook.io for production + $('#gatewayForm').attr('action', config.app.url + '/Marak/gateway-javascript'); + + + var services = hooks.services; + var examples = {}; + + /* + // pull out helloworld examples for every langauge + Object.keys(hook.languages).forEach(function(l){ + examples[l] = services['examples-' + l + '-hello-world']; + }); + + // list of js examples by order + var jsExamples = [ + "hello-world", + "request-parameters", + "send-http-request", + "input-schema", + "pipe-hook", + "stream-merge", + "stream-transform", + "datastore", + "fake-data" + ]; + jsExamples = jsExamples.reverse(); + jsExamples.forEach(function (item){ + var ex = services['javascript-' + item]; + if (typeof ex !== "undefined") { + $('.selectSnippet').prepend('') + } + }); + */ + + /* + for (var s in services) { + var e = services[s]; + var type = s.split('-')[0], + lang = s.split('-')[1]; + if (type === "examples" && lang === "javascript") { + $('.selectSnippet').prepend('') + } + } + */ + + boot.examples = examples; + + //req.i18n.setLocale('de'); + + // Localize page based on request lauguage + $('.deploymentsLink').html(i.__("Deployments")); + $('.callToAction').html(i.__("Sign up Instantly! It's Free!")); + + //$('.featuresDiv h2').html(i.__("Features")); + + $('.features li a').each(function(index, item){ + var v = $(item).html(); + $(item).html(i.__(v)); + }); + + var i18n = require('./helpers/i18n'); + i18n(i, $); if (typeof user === "undefined") { $('.userBar').remove(); - $('.userSplash').remove(); + var out = $.html(); + out = out.replace('{{hook}}', JSON.stringify(boot, true, 2)); + return callback(null, out); } else { - $('.userBar .welcome').html('Welcome ' + user + "!") + user = user.toLowerCase(); + var query = { name: user }; + $('.userBar .welcome').html(i.__('Welcome') + ' ' + user + "!") $('.loginBar').remove(); - $('.tagline').remove(); + $('.featuresDiv').remove(); + $('.hookStats').remove(); + // $('.tagline').remove(); $('.yourHooks').attr("href", "/" + user); - $('.splash').remove(); + // $('.splash').remove(); + var out = $.html(); + out = out.replace('{{hook}}', JSON.stringify(boot, true, 2)); + return callback(null, out); } - - callback(null, this.$.html()); }; \ No newline at end of file diff --git a/view/insight.html b/view/insight.html new file mode 100644 index 00000000..615e5cb5 --- /dev/null +++ b/view/insight.html @@ -0,0 +1,121 @@ + + + + + \ No newline at end of file diff --git a/view/insight.js b/view/insight.js new file mode 100644 index 00000000..41b6f8f6 --- /dev/null +++ b/view/insight.js @@ -0,0 +1,6 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, + req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/javascript.html b/view/javascript.html index 443be909..36b39322 100644 --- a/view/javascript.html +++ b/view/javascript.html @@ -3,7 +3,7 @@

      JavaScript

      hook.io services are written in JavaScript

      This allows a wealth of functionality through the community of well defined and well tested JavaScript libraries.

      -

      See also: http://hook.io/modules

      +

      See also: https://hook.io/modules

      Getting started with JavaScript

      eloquentjavascript.net

      nodestreams.com

      diff --git a/view/keys/all.html b/view/keys/all.html new file mode 100644 index 00000000..e69de29b diff --git a/view/keys/all.js b/view/keys/all.js new file mode 100644 index 00000000..1bb0c6b9 --- /dev/null +++ b/view/keys/all.js @@ -0,0 +1,56 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var mschema = require('mschema'); +var keys = require('../../lib/resources/keys'); + +module['exports'] = function allKeysPresenter (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + var self = this; + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + if (typeof params.hook_private_key !== 'undefined') { + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "keys::read" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "keys::read")); + } else { + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = "error"; + return res.json(validate); + } else { + return keys.find({ owner: req.resource.owner }, function (err, result) { + if (err) { + return res.end(err.message) + } + return res.json(result); + }); + } + } + }); + } else { + return res.json({ hasAccess: false }); + } + } + +}; + +module['exports'].schema = { + hook_private_key: { + type: 'string', + required: true + } +}; \ No newline at end of file diff --git a/view/keys/checkAccess.html b/view/keys/checkAccess.html new file mode 100644 index 00000000..e69de29b diff --git a/view/keys/checkAccess.js b/view/keys/checkAccess.js new file mode 100644 index 00000000..2f1adfa0 --- /dev/null +++ b/view/keys/checkAccess.js @@ -0,0 +1,62 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var user = require('../../lib/resources/user'); + +module['exports'] = function keysCheckAccessPresenter (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + + // TODO: only allow for accounts with enabled featured 'customRoleChecks' + if (typeof params.hook_private_key !== 'undefined') { + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: params.role, include_doc: true }, function (err, result, _key) { + if (err) { + return res.end(err.message); + } + if (result === false) { + return res.json({ hasAccess: false }); + } + user.findOne({ name: _key.owner }, function (err, _user) { + if (err) { + return res.end(err.message); + } + var rsp = {}; + rsp.key = _key; + rsp.user = { + name: _user.name, + email: _user.email, + }; + rsp.hasAccess = true; + return res.json(rsp); + }); + }); + } else { + return res.json({ hasAccess: false }); + } + } + +}; + +module['exports'].schema = { + "hook_private_key": { + "type": "string", + "required": true + }, + "role": { + "type": "string", + "required": true + } +}; \ No newline at end of file diff --git a/view/keys/create.html b/view/keys/create.html new file mode 100644 index 00000000..e69de29b diff --git a/view/keys/create.js b/view/keys/create.js new file mode 100644 index 00000000..43cca232 --- /dev/null +++ b/view/keys/create.js @@ -0,0 +1,69 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var mschema = require('mschema'); +var keys = require('../../lib/resources/keys'); + +module['exports'] = function createKeysPresenter (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + var self = this; + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + if (typeof params.hook_private_key !== 'undefined') { + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "keys::create" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "keys::create")); + } else { + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = "error"; + res.status(400); + return res.json(validate); + } else { + req.resource.params.owner = req.resource.owner; + // do not allow user to set a custom private key + delete req.resource.params.hook_private_key; + return keys.create(req.resource.params, function (err, result) { + if (err) { + res.status(400); + return res.json({ error: true, message: err.message }); + } + return res.json(result); + }); + } + } + }); + } else { + return res.json({ hasAccess: false }); + } + } + +}; + +module['exports'].schema = { + name: { + type: 'string', + required: true + }, + hook_private_key: { + type: 'string', + required: true + }, + roles: { + type: 'string', + required: true + } +}; \ No newline at end of file diff --git a/view/keys/destroy.html b/view/keys/destroy.html new file mode 100644 index 00000000..e69de29b diff --git a/view/keys/destroy.js b/view/keys/destroy.js new file mode 100644 index 00000000..b1c13f7b --- /dev/null +++ b/view/keys/destroy.js @@ -0,0 +1,73 @@ +var psr = require('parse-service-request'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../../config'); +var mschema = require('mschema'); +var keys = require('../../lib/resources/keys'); +var cache = require('../../lib/resources/cache'); + +module['exports'] = function destroyKeysPresenter (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + + var self = this; + + psr(req, res, function(req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + finish(); + }); + + function finish () { + if (typeof params.hook_private_key !== 'undefined') { + // TODO: move to resource.before hooks + checkRoleAccess({ req: req, res: res, role: "keys::destroy" }, function (err, hasPermission) { + if (!hasPermission) { + return res.end(config.messages.unauthorizedRoleAccess(req, "keys::destroy")); + } else { + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = "error"; + return res.json(validate); + } else { + return keys.findOne({ name: params.name }, function (err, k) { + if (err) { + return res.end(err.message) + } + var keyPath = '/keys/' + k.owner + '/' + k.hook_private_key; + // TODO: remove key from cache + k.destroy(function(err){ + if (err) { + return res.end(err.message) + } + cache.del(keyPath, function (err) { + if (err) { + console.log('keys.destroy cache.del error', err.message); + } + return res.json({ status: 'deleted' }); + }); + }); + }) + } + } + }); + } else { + return res.json({ hasAccess: false }); + } + } + +}; + +module['exports'].schema = { + name: { + type: 'string', + required: true + }, + hook_private_key: { + type: 'string', + required: true + } +}; \ No newline at end of file diff --git a/view/keys/index.html b/view/keys/index.html new file mode 100644 index 00000000..907eac1f --- /dev/null +++ b/view/keys/index.html @@ -0,0 +1,186 @@ + + + + +
      +

      API Access Keys

      +
      +
      +
      +
      + + +
      +
      + +
      + +
      + +
      +
        +
      • Using  0/1 Available Keys
      • +
      + +
      + +
      +

      +
      +
      +
      + + + + +
      + + +
      +
      + Your API Keys +

      Note: Keys cannot be updated. They can only be created or destroyed.

      +
      +
      +
      +
      + + + +

      What are API Access Keys?

      +

      API Access Keys are used for managing security role access to services on {{appName}}.

      +

      You can create new keys to allow third-party services or users to securely access restricted {{appName}} API endpoints like private hooks, the datastore, or logs.

      +

      A common use-case would be creating an API Access Key with the role hook::logs::read to securely read the logs of your Private Services.

      + +

      Another use-case would be creating an API Access Key with the role of hook::run to allow a third-party client to run a private hook service hosted on {{appName}}. This private service would only accessible to clients who can provide a the generated access key which has the role hook::run

      +
      + + +

      Using API Access Keys

      + +

      Using an API Access Key with {{appName}} is easy!

      + +

      All you have to do is supply the generated hook_private_key variable as a HTTP request parameter ( GET Query Parameter / POST Form Data / JSON RPC / HTTP Request Header / etc ) and that HTTP client request will be granted the roles associated with that hook_private_key.

      +

      Remember: All service level events on {{appName}} are available as roles, allowing granular access control.

      +
      + + +

      What are Roles?

      +

      Roles are unique identifiers representing granular access to the {{appName}} API. Every API method on {{appName}} has a unique Role name.

      +

      When creating a new access key, you must specify a set of roles associated with that key. These roles determine what the new access key has permission to do.

      +

      Role checks are used to determine if an incoming HTTP request has authorization to access the requested resource. This is useful when using Private Services or when accessing the {{appName}} API from a third-party client.

      +

      A full listing of security access roles is available at /roles

      + +
      +
      + +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/keys/index.js b/view/keys/index.js new file mode 100644 index 00000000..71f81c4f --- /dev/null +++ b/view/keys/index.js @@ -0,0 +1,319 @@ +var keys = require('../../lib/resources/keys'), +forms = require('mschema-forms'); + +var uuid = require('node-uuid'); + +var role = require('../../lib/resources/role'); +var checkRoleAccess = require('../../lib/server/routeHandlers/checkRoleAccess'); +var mschema = require('mschema'); + +var mergeParams = require('merge-params'); +var bodyParser = require('body-parser'); +var config = require('../../config'); + +module['exports'] = function view (opts, callback) { + + var req = opts.request, + res = opts.response, + $ = this.$; + + var self = this; + self.schema = self.schema || {}; + + $ = req.white($); + + // TODO: only show form if logged in, if not, show login form + var appName = req.hostname; + + if (!req.isAuthenticated()) { + $('.keys').remove(); + $('.keysStatus').remove(); + // return callback(null, $.html()); + } else { + $('.loginLink').remove(); + } + + // TODO: move to resource.before hooks + var _role = "keys::read"; + if (req.method === "POST") { + _role = "keys::create"; + } + + checkRoleAccess({ req: req, res: res, role: _role }, function (err, hasPermission) { + if (hasPermission) { + $('.loginBar').remove(); + finish(); + } else { + if (req.jsonResponse) { + return res.end(config.messages.unauthorizedRoleAccess(req, role)); + } + // if not logged in, kick out + if (!req.isAuthenticated()) { + $('.keys').remove(); + $('.myKeys').remove(); + req.session.redirectTo = "/keys"; + var out = $.html(); + out = out.replace(/\{\{appName\}\}/g, appName); + return callback(null, out); + //return res.redirect('/login'); + } else { + $('.loginBar').remove(); + finish(); + } + } + }); + + function finish () { + var keySchema = { + name: { + type: 'string', + label: '

      Name

      ', + placeholder: 'admin-access-key', + required: true, + minLength: 1, + maxLength: 50 + }, + /* + owner: { + type: 'string', + format: 'hidden', + required: true, + minLength: 1, + maxLength: 50, + default: req.session.user + }, + */ + /* + , + hook_public_key: { + type: 'string', + label: "Public Key", + description: 'your public key', + required: true, + minLength: 1, + maxLength: 255, + size: 40, + disabled: true, + // default: "public-" + new Date().getTime() + default: uuid() + }, + */ + hook_private_key: { + type: 'string', + label: "Private Key", + required: true, + minLength: 1, + maxLength: 255, + size: 40, + default: uuid(), + private: true + }, + roles: { + label: "

      Roles

      ", + description: "pick granular roles", + type: "string", + format: "checkbox", + // TODO: make actual array type instead of string + // type: "array", + customValue: true, + selectAll: true, + selectNone: true, + enum: role.roles, + defaultState: "checked", + required: true + }, + customRoles: { + label: "

      Custom Roles

      ", + hint: 'You can specify a custom string as a new role. This can be checked using a Custom Role Check', + type: "string", + placeholder: 'foo::bar,hook::custom1,files::admin', + // TODO: make actual array type instead of string + // type: "array", + required: false, + default: "" + }, + /* + key_type: { + type: 'string', + label: "Key Type", + enum: ['user', 'service'], + description: 'Your custom domain name. Example: marak.com', + required: true, + minLength: 1, + maxLength: 50 + }, + */ + }; + bodyParser()(req, res, function bodyParsed() { + mergeParams(req, res, function (){}); + + if (typeof req.resource.params.roles === "string") { + req.resource.params.roles = [req.resource.params.roles]; + } + + req.resource.params.roles = req.resource.params.roles || []; + if (typeof req.resource.params.customRoles === "string") { + // split by comma value for multiple roles + // TODO: trim any whitespace ? + var customRoles = req.resource.params.customRoles.split(','); + customRoles.forEach(function(customRole){ + req.resource.params.roles.push(customRole); + }); + } + + var params = req.resource.params; + if (typeof params.submitted !== 'undefined') { + // process posted form data + params.owner = req.session.user; + return keys.create(params, function (err, result) { + if (err) { + return res.end(err.message); + } + $('.status').html('Created!'); + showForms(); + }); + } else { + if (params.destroy) { + // TODO: add role check + $('.status').html('Deleted!'); + return keys.destroy(params.id, function (err) { + if (err) { + return res.end(err.message); + } + showForms(); + }); + } else { + showForms(); + } + } + + function showForms () { + if (req.jsonResponse) { + if (req.method === "POST") { + var validate = mschema.validate(req.resource.params, self.schema); + if (!validate.valid) { + validate.status = "error"; + return res.json(validate); + } else { + return keys.create(req.resource.params, function(err, result){ + if (err) { + return res.end(err.message) + } + return res.json(result); + }); + } + } else { + return res.json({ "status": "autodocs"}); + } + } + + //console.log('merged params', req.resource.params) + var middle = forms.generate({ + view: 'generic', + resource: keys, + action: '/keys', + params: req.resource.params, + req: req, + res: res, + query: { owner: req.session.user }, + useLayout: false, + form: { + legend: 'Create New Key', + submit: "Add Key", + showDestroyButton: true + }, + schema: keySchema + }, function (err, result) { + if (err) { + return res.end(err.message); + } + $('.keysForm').html(result); + + keys.find({ owner: req.session.user }, function (err, _keys) { + if (err) { + return res.end(err.message); + } + _keys = _keys.filter(function (item) { + if (item.key_type === "internal") { + // do not show interal api key to user + return false; + } + return true; + }); + if (_keys.length === 0) { + $('.keyHolder').append('

      No API Keys Have Been Created

      ') + return callback(null, $.html()); + } + if (req.session.serviceLimits) { + $('.keyUsage').html(_keys.length + '/' + req.session.serviceLimits.apiKeys); + } + _keys.forEach(function(k){ + var table = ''; + var deleteLink = '' + 'destroy' + ''; + table += '' + ''; + if (typeof k.roles === "string") { + k.roles = k.roles.split(','); + } + k.roles.sort(); + if (typeof k.roles === 'object' && k.roles.length > 0) { + var str = '
        '; + k.roles.forEach(function(r){ + var desc = ''; + if (role.roles[r] && role.roles[r].description) { + desc = role.roles[r].description; + } + str += '
      • ' + r + '
      • '; + }); + str += '
      '; + table += '' + ''; + } + table += '
      name: ' + '' + k.name + 'hook_private_key: ' + deleteLink + '
      ' + str + '
      '; + $('.keyHolder').append(table); + }); + + return callback(null, $.html()); + /* + forms.generate({ + type: 'grid', + data: _keys, + action: '/keys', + params: req.resource.params, + req: req, + res: res, + query: { owner: req.session.user }, + useLayout: false, + form: { + legend: 'Your Keys', + keys: ['name', 'hook_private_key', 'customRoles', 'roles'], + showDestroyButton: true + }, + schema: keySchema + }, function (err, _h) { + }); + */ + }); + }); + } + }); + } + +}; + +module['exports'].schema = { + name: { + type: 'string', + required: true + }, + owner: { + type: 'string', + required: true + }, + hook_private_key: { + type: 'string', + required: true + }, + roles: { + type: 'string', + required: true + } +}; \ No newline at end of file diff --git a/view/languages.html b/view/languages.html new file mode 100644 index 00000000..53017baa --- /dev/null +++ b/view/languages.html @@ -0,0 +1,14 @@ + + +
      +

      Supported Programming Languages

      +
      +

      See Examples

      +
        +
      +
      +
      + +


      \ No newline at end of file diff --git a/view/languages.js b/view/languages.js new file mode 100644 index 00000000..175ddb5d --- /dev/null +++ b/view/languages.js @@ -0,0 +1,11 @@ +var langs = require('../lib/resources/programmingLanguage'); + +module['exports'] = function (opts, cb) { + var $ = this.$; + var req = opts.req; + Object.keys(langs.languages).forEach(function (key) { + $('.languages').append('
    • ' + key + '
    • '); + }); + $ = req.white($); + cb(null, $.html()); +}; \ No newline at end of file diff --git a/view/layout.html b/view/layout.html index 2883b00b..447d647c 100644 --- a/view/layout.html +++ b/view/layout.html @@ -1,139 +1,547 @@ - + + - hook.io - Free Microservice and Webhook Hosting. Deploy your code in seconds. - + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +
      + main slider + main slider +
      +
      +
      +
      +
      +
      +

      Microservice and Webhook Hosting

      +
      +
      +

      free to try

      +
      +
      +

      {{appName}} is the leading open-source provider of microservice and webhook hosting.
      Our service is built with by Developers for Developers

      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +

      WELCOME to {{appName}}

      +
      +
      +

      {{appName}} is the leading open-source provider of microservice and webhook hosting.
      Our service is built with love by Developers for Developers

      +
      + +
      +
      +
      -
      +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      +
      +

      Hide [X]

      +
      + Notice: + x days left in 60 day Trial Period.
      +
      + +
      +
      + +
      +
      +
      +
      -
      + -
      + + + + + + + + + + + + + + + + + + + + + - ga('create', 'UA-56831462-1', 'auto'); - ga('send', 'pageview'); + + + + + + + + + + + + - --> - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/view/layout.js b/view/layout.js index 75a07152..bec4b13c 100644 --- a/view/layout.js +++ b/view/layout.js @@ -1,10 +1,257 @@ +var config = require('../config'); +var i18n = require('i18n-2') +var moment = require('moment'); +var TRIAL_DAYS_LIMIT = 60; + module['exports'] = function view (opts, callback) { - var req = opts.request + var req = opts.req, res = opts.res, + params = req.resource.params, $ = this.$; - if (req.user && req.user.username) { - $('.myHooks').attr('href', '/' + req.user.username); + + var opt = { + request: req, + locales: config.locales.locales, + directory: require.resolve('hook.io-i18n').replace('index.js', '/locales') + }; + opt.request = req; + req.i18n = new i18n(opt); + + if (req.user && req.session.user) { + $('.myHooks').attr('href', '/' + req.session.user); } - $('title').html('hook.io - Free Microservice and Webhook Hosting. Deploy your code in seconds.'); - + + if (!req.isAuthenticated()) { + //req.session.user = "anonymous"; + $('.logoutLink').remove(); + $('.loggedInOnly').remove(); + } else { + $('.loginLink').remove(); + } + + var _url = require('url').parse(req.url).pathname; + // console.log('WHAT IS PATH', _url) + if (_url !== "/login" && _url !== "/signup" && _url !== "" && _url !== "/" && _url !== "/reset") { + req.session.redirectTo = req.url; + } + + if (req.session.lang) { + $('.currentLang').html(req.session.lang); + req.i18n.setLocale(req.session.lang); + } + + if (params.lang) { + $('.currentLang').html(params.lang); + req.i18n.setLocale(params.lang); + req.session.lang = params.lang; + } + + var acceptTypes = []; + + if (req.headers && req.headers.accept) { + acceptTypes = req.headers.accept.split(','); + } + if (acceptTypes.indexOf('text/html') === -1) { + req.jsonResponse = true; + } + + if (req.resource.params._json) { + req.jsonResponse = true; + } + + // Express 3 ??? + /* + if (res.locals) { + i18n.registerMethods(res.locals, req) + } + */ + + var i = req.i18n; + //var i18n = require('./helpers/i18n'); + //i18n(i, $); + + var pathName = require('url').parse(req.originalUrl).pathname; + if (pathName !== "/") { + $('.main-slider-area').remove(); + } + /* + $('.features li a').each(function(index, item){ + var v = $(item).html(); + $(item).html(i.__(v)); + }); + */ + + /* + $('.i18n').each(function(index, item){ + var v = $(item).html(); + // console.log(v, i.__(v)) + $(item).html(i.__(v)); + }); + */ + + // TODO: make this a redirect page instead of inserting into layout now that emails are required + if (typeof req.session === "undefined" || typeof req.session.user === "undefined" || req.session.user === "anonymous") { + $('.emailReminder').remove(); + $('.trialPeriod').remove(); + } + + // if no email found on account, we need to update the account! + if (typeof req.session.email === "undefined" || req.session.email.length === 0) { + // do not perform redirect if user is already on redirected page + // do not perform redirect if request has indicated a json response ( most likely an API call ) + // TODO: only if logged in and not anonymous + if (req.isAuthenticated()) { + if (req.url !== '/email-required' && !req.jsonResponse) { + return res.redirect(config.app.url + '/email-required'); + } + } + } else { + $('.emailReminder').remove(); + } + + // if we have a valid session but there is no valid session user name... + // assume we have created a new account with no registered account name + if (req.isAuthenticated()) { + if (typeof req.session.user === 'undefined' || req.session.user === 'anonymous') { + // redirect to the register account page + if (req.url !== '/register' && !req.jsonResponse && req.url !== '/session') { + return res.redirect(307, config.app.url + '/register'); + } + } + } else { + $('.trialPeriod').remove(); + $('.upgradeAccount').remove(); + } + + if (req.session && req.session.user === 'marak') { + $('.logo a').attr('href', config.app.url + '/_admin') + } + + // + // Check to see if logged in account has expired it's trial period + // + + var now = moment(); + var created = moment(req.session.user_ctime); + var daysSinceCreation = now.diff(created, 'days'); + + /* + console.log('now', now); + console.log('created', created); + console.log('days left', daysSinceCreation); + console.log('paidStatus', req.session.paidStatus, TRIAL_DAYS_LIMIT - daysSinceCreation) + */ + var trialPages = ['/account', '/pricing', '/account/expired', '/account/usage', '/account/delete', '/contact', '/docs', '/register']; + + if ((req && req.session && req.session.paidStatus === "paid")) { + // do nothing + // paid users are not subject to 60 day trial + $('.trialPeriod').remove(); + $('.upgradeAccount').remove(); + } else { + if (req.session.servicePlan === 'free' || typeof req.session.servicePlan === 'undefined') { + // `free` is used for accounts created before 5/7/2018 + $('.trialPeriod').remove(); + var now = moment(); + var created = moment('4/2/2019'); + var daysSinceCreation = now.diff(created, 'days'); + $('.daysLeftInTrial').html((TRIAL_DAYS_LIMIT - daysSinceCreation).toString()); + if (trialPages.indexOf(_url) !== -1) { + $('.upgradeAccount').remove(); + } + } + + if (typeof req.session.hideWarning !== 'undefined') { + var warningHidden = moment(req.session.hideWarning); + var minutesSinceClosed = warningHidden.diff(new Date(), 'minutes'); + // console.log('minutes since closed', minutesSinceClosed); + // re-show warning every 30 minutes ( after user clicks hide ) + if (minutesSinceClosed >= -5) { + $('.notice').remove(); + // req.hideWarnings = true; // not being used + } + } + + if (req.session.servicePlan === 'trial') { + $('.upgradeAccount').remove(); + } + + if (trialPages.indexOf(_url) !== -1) { + $('.boxAlert').remove(); + } + + if ((TRIAL_DAYS_LIMIT - daysSinceCreation) < 0) { + // trial has expired, redirect to expired page ( but still allow to view pricng page ) + // todo: create array of allowed url values for expired accounts ( support / pricing / etc ) + // $('.upgradeAccount').remove(); + if (trialPages.indexOf(_url) === -1 && !req.jsonResponse) { + return res.redirect(307, config.app.url + '/account/expired'); + } + if (req.url !== '/account/expired' && req.url !== '/pricing' && req.url !== '/contact' && !req.jsonResponse) { + } + } else { + if (trialPages.indexOf(req.url) !== -1) { + $('.trialPeriod').remove(); + $('.upgradeAccount').remove(); + } else { + // trial has not yet expired, but since account is not paid we should show a countdown on each page + $('.daysLeftInTrial').html((TRIAL_DAYS_LIMIT - daysSinceCreation).toString()); + $('.accountCreated').html(created.toString()); + } + } + + } + + /* + $('.daysLeftInTrial').html((TRIAL_DAYS_LIMIT - daysSinceCreation).toString()); + $('.accountCreated').html(created.toString()); + */ + + // generic white-label function for performing {{mustache}} style replacements of site data + // Note: Site requires absolute links ( no relative links! ) + req.white = function whiteLabel ($, options) { + $('.i18n').each(function(index, item){ + var v = $(item).html(); + // console.log(v, i.__(v)) + $(item).html(i.__(v)); + }); + + var out = $.html(); + var appName = "hook.io", + appAdminEmail = "hookmaster@hook.io", + appPhonePrimary = ""; + + // TODO: move configuration override to hook.io-white + var white = {}; + if (req.hostname === "stackvana.com") { + white.logo = "https://stackvana.com/img/stackvana-logo-inverse.png"; + white.logoInverse = "https://stackvana.com/img/stackvana-logo-inverse.png"; + white.name = "Stackvana"; + white.url = "https://stackvana.com"; + //white.url = "http://stackvana.com:9999"; + white.email = "support@stackvana.com"; + } + out = out.replace(/\{\{appName\}\}/g, white.name || appName); + out = out.replace(/\{\{appSdkName\}\}/g, white.appSdkName || "hook.io-sdk"); + out = out.replace(/\{\{appLogo\}\}/g, white.logo || config.app.logo); + out = out.replace(/\{\{appLogoInverse\}\}/g, white.logoInverse || config.app.logoInverse); + out = out.replace(/\{\{appDomain\}\}/g, config.app.domain); + out = out.replace(/\{\{appUrl\}\}/g, white.url || config.app.url); + out = out.replace(/\{\{appWs\}\}/g, white.url || config.app.ws); + out = out.replace(/\{\{appDomain\}\}/g, white.host || config.app.domain); + out = out.replace(/\{\{appPort\}\}/g, white.port || config.app.port); + out = out.replace(/\{\{appAdminEmail\}\}/g, white.email || appAdminEmail); + out = out.replace(/\{\{appPhonePrimary\}\}/g, appPhonePrimary); + out = out.replace(/\{\{balancerIP\}\}/g, config.balancer.publicIP); + if (typeof req.session !== 'undefined') { + out = out.replace(/\{\{userName\}\}/g, req.session.user || 'anonymous'); + out = out.replace(/\{\{userEmail\}\}/g, req.session.email); + out = out.replace(/\{\{user.email\}\}/g, req.session.email); + } + out = out.replace(/\{\{username\}\}/g, req.session.user || 'anonymous'); + return $.load(out); + }; + + $ = req.white($); callback(null, $.html()); + }; \ No newline at end of file diff --git a/view/login.html b/view/login.html new file mode 100644 index 00000000..03a8d176 --- /dev/null +++ b/view/login.html @@ -0,0 +1,217 @@ + +
      +
      +
      +
      +

      Login

      +

      The requested page requires a valid login. Please log in to continue.

      +      Log In with Github +
      +
      + +
      +
      + + \ No newline at end of file diff --git a/view/login.js b/view/login.js new file mode 100644 index 00000000..e7980097 --- /dev/null +++ b/view/login.js @@ -0,0 +1,121 @@ +var user = require('../lib/resources/user'); + +var psr = require('parse-service-request'); + +module['exports'] = function view (opts, callback) { + + var $ = this.$; + + var req = opts.request, + res = opts.response; + + $ = req.white($); + + psr(req, res, function(req, res) { + var params = req.resource.params; + + params.name = params.name || params.email; + if (params.restricted !== 'true') { + $('.restricted').remove(); + } + + /* we could display the intended redirect page to the user + if (req.session.redirectTo) { + $('.redirectPage').html(req.session.redirectTo) + } + */ + + // if a name and password have been supplied + if (params.name && params.password) { + params.name = params.name.toLowerCase(); + var type = "name"; + // determine if username or email + if (params.name.search('@') !== -1) { + type = "email"; + } + var query = {}; + query[type] = params.name; + + // check to see if the user email is available + user.find(query, function (err, results) { + if (err) { + res.status(500); + return res.end(err.message); + } + if (results.length === 0) { + // removed this, since it seems to auto-set the user name in an unexpected / legacy way + // req.session.user = params.name; + var r = { + result: "available", + }; + return res.json(r); + } + var u = results[0]; + if (typeof u.salt == 'undefined') { + if (typeof u.githubAccessToken === 'string' && u.githubAccessToken.length > 0) { + var r = {}; + // user has logged in with github before, lets try that + r.result = "valid"; + r.redirect = "/login/github"; + return res.json(r); + } + var r = { + result: "not-set", + }; + return res.json(r) + } + var crypto = require('crypto'); + + var hash = crypto.createHmac("sha512", u.salt).update(params.password).digest("hex"); + if (hash !== u.password) { + var r = { + result: "invalid", + }; + return res.end(JSON.stringify(r)); + // Do not attempt autologin? seems too much + // TODO: only attempt github oauth if no password has been supplied + if (u.githubOauth === true && typeof params.password === 'undefined') { + r.result = "redirect"; + r.redirect = "/login/github"; + return res.redirect(r.redirect); + // if the user has already oauthed with github before, + // and is attempting to sign in with a bad password, + // lets just redirect them to the github auth! easy. + } + + // TODO: if password supplied, attempt login + return res.end(JSON.stringify(r)); + } + // Valid password, performing login + return user.login({ req: req, res: res, user: u }, function (err){ + if (err) { + return res.end(err.message); + } + var r = { + result: "valid", + }; + // is hookAccessKey already set in user.login now? + req.session.hookAccessKey = u.hookAccessKey; + req.session.timezone = u.timezone || 'America/New_York'; + r.redirect = req.session.redirectTo || "/services"; + if (req.jsonResponse) { + return res.json(r); + } else { + return res.redirect(r.redirect); + } + }); + }); + } else { + if (req.jsonResponse) { + if (typeof params.name === 'undefined') { + return res.json({ status: 'invalid', message: 'name or email required'}); + } else { + return res.json({ status: 'invalid', message: 'password required'}); + } + } else { + return callback(null, $.html()); + } + } + }); + +}; \ No newline at end of file diff --git a/view/logs.html b/view/logs.html new file mode 100644 index 00000000..1ffea8cf --- /dev/null +++ b/view/logs.html @@ -0,0 +1,150 @@ + + + + +
      +

      Service Logs

      +
      +
      + +
      +
      + + + + + + +
      +
      + + +
      + +
      +
      + + +
      + +

      {{appName}} provides a simple integrated interface for logging from any Hook service. This integrated logging service is can be helpful for Service debugging or monitoring.

      + + +

      Writing to Hook Logs

      +

      To write to a Hook's logging output, simply call console.log('hello logging'); from inside the Hook.

      +

      Calling console.log in the Hook will automatically make the console log statement available as a logging entry.

      + + +

      Logs API

      + +

      Accessing Hook Logs

      +

      To access the logs for a Hook simply attach /logs to the end of the Hook service url.

      +

      For example: {{appUrl}}/examples/echo/logs

      +

      The logging endpoint for a Hook will intelligently respond based on the incoming HTTP Accept Header.

      + + +

      Authentication

      +

      For Private Hooks, service logs are private. In order to access them from a third-party service, you'll need to generate a valid API Access Key with the logs::read role.

      + + + + + +

      Streaming logs

      +

      To access streaming logs, request the logging endpoint with an Accept Header value of */*

      +

      Example: curl -N {{appUrl}}/examples/echo/logs

      + + +

      Plain Text Logs

      +

      To access plain text logs, request the logging endpoint with an Accept Header value of text/plain

      +

      Example: curl -H "Accept: text/plain" {{appUrl}}/examples/echo/logs

      + + +

      Clear / Flush Logs

      +

      To flush or clear the logs for a Hook, simply append ?flush=true to the logging endpoint ( valid session or hook_private_key required ).

      +

      http://{{appName}}/examples/echo/logs?flush=true

      + + +

      Client SDK

      +

      Using the Client SDK to access events is simple

      +

      Here are two examples of using the Client SDK. See SDK for more information

      +

      +

      +var sdk = require("hook.io-sdk");
      +var client = sdk.createClient({});
      +// callback style
      +client.logs.get('marak/echo', function (err, logs) {
      +  console.log(err, logs);
      +});
      +// streaming
      +client.events.stream('marak/echo', process.stdout);
      +                
      +

      + + +
      + +
      +
      + +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/logs.js b/view/logs.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/logs.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/mergeParams.js b/view/mergeParams.js deleted file mode 100644 index 46db106f..00000000 --- a/view/mergeParams.js +++ /dev/null @@ -1,34 +0,0 @@ -// -// Middleware for merging all querystring / request.body and route parameters, -// into a common scope bound to req.resource.params -// -function mergeParams (req, res, next) { - - req.resource = req.resource || {}; - req.resource.params = req.resource.params || {}; - req.body = req.body || {}; - - // - // Iterate through all the querystring and request.body values and - // merge them into a single "data" argument - // - if (typeof req.params === 'object') { - Object.keys(req.params).forEach(function (p) { - req.resource.params[p] = req.param(p); - }); - } - - if (typeof req.query === 'object') { - Object.keys(req.query).forEach(function (p) { - req.resource.params[p] = req.query[p]; - }); - } - - Object.keys(req.body).forEach(function (p) { - req.resource.params[p] = req.body[p]; - }); - - next(); -} - -module['exports'] = mergeParams; \ No newline at end of file diff --git a/view/metrics.html b/view/metrics.html new file mode 100644 index 00000000..9b0cb779 --- /dev/null +++ b/view/metrics.html @@ -0,0 +1,24 @@ +
      +

      Metrics

      +
      + +

      Accessing Metrics

      + +

      Service Executions

      +

      /metrics/:owner/:hook/hits

      +

      The total amount of times a Service has been run

      + +

      Total Executions per Account

      +

      /metrics/:owner/hits

      + +

      Running Services

      +

      /metrics/:owner/running

      + +

      We are currently storing many more metrics, but do not have them exposed as public API endpoints.

      +

      Please let us know if you require additional service metrics.

      + +

      Additional System Logs and Events

      +

      Service Logs

      +

      System Events Logs

      +
      +
      \ No newline at end of file diff --git a/view/metrics.js b/view/metrics.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/metrics.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/microservices.html b/view/microservices.html new file mode 100644 index 00000000..48a3b64c --- /dev/null +++ b/view/microservices.html @@ -0,0 +1,13 @@ +
      +

      Microservices

      +
      +

      {{appName}} is the ideal place to host microservices.

      +

      Our platform fully supports multiple programming languages, and all the features you may need while building streaming HTTP microservices.

      +

      Frequently Asked Questions

      +

      New to Microservices? The FAQ is the perfect place to start.

      +

      Interactive Microservice Playground

      +

      Want to start playing with live code, check out the playground!

      +

      Create new Microservice

      +

      Ready to immediately deploy your first Microservice? Create a new service instantly!

      +
      +
      \ No newline at end of file diff --git a/view/microservices.js b/view/microservices.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/microservices.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/modules.html b/view/modules.html index 896282ab..72c7c8ab 100644 --- a/view/modules.html +++ b/view/modules.html @@ -1,10 +1,10 @@

      Modules

      -
      +

      Native Node.js Module Support

      -

      hook.io supports the Node.js API out of the box.

      +

      {{appName}} supports the Node.js API out of the box.

      This is useful for performing a variety of basic operations.

      Documentation on using the Node.js API can be found at: nodejs.org/api

      @@ -17,29 +17,11 @@

      NPM Module Support

      - hook.io utilizes the power of the NPM eco-system to deliver support for thousands of modules. + {{appName}} utilizes the power of the NPM eco-system to deliver support for thousands of modules.

      - A curated list of packages is available for hosted Hooks. You can find the current list below. + Modules will automatically install if you attempt to require them in a Hook. The first time the Hook is run, {{appName}} will install the dependency. Re-run the hook a few moments later and it should just work.

      -

      Need a module not listed here?

      -

      Open a Github support issue request.

      -

      Most modules from npmjs.org can be added within a day.

      - -
      - -
      - - - - - -
      NameVersion
      bluebird ^2.3.11
      cheerio ^0.17.0
      colors ^1.0.3
      contentful ^0.1.2
      contentful-management ^0.1.0
      cron ^1.0.5
      geocodio ^0.0.1
      gengo ^0.1.8
      github ^0.2.2
      github-url-from-git ^1.4.0
      gm ^1.16.0
      hyperquest ^1.0.1
      irc ^0.3.7
      map-async ^0.1.1
      map-sync ^0.1.1
      moment ^2.8.3
      mschema ^0.5.3
      object-path ^0.6.0
      once ^1.3.1
      passport ^0.2.1
      passport-github ^0.1.5
      questor ^1.0.0
      resource ^0.5.3
      resource-user 0.5.x
      slack-notify ^0.1.2
      stream-buffers ^1.1.0
      stream-transcoder 0.0.5
      stripe ^2.8.0
      through2 ^0.6.3
      twilio ^1.7.0
      twit ^1.1.18
      xtend ^4.0.0
      - - -
      -
      -
      \ No newline at end of file diff --git a/view/modules.js b/view/modules.js index c3059f2f..bd7976a3 100644 --- a/view/modules.js +++ b/view/modules.js @@ -1,3 +1,14 @@ module['exports'] = function view (opts, callback) { - callback(null, this.$.html()); + // attempt to reload view manually + var req = opts.request, + res = opts.response; + var v = req.url; + + /* + var appName = req.hostname; + var out = $.html(); + out = out.replace(/\{\{appAdminEmail\}\}/g, appAdminEmail); + */ + return res.redirect(301, '/packages'); + //callback(null, this.$.html()); }; \ No newline at end of file diff --git a/view/new.html b/view/new.html index 8d112b4c..b5e01457 100644 --- a/view/new.html +++ b/view/new.html @@ -1,4 +1,34 @@ - - - + + + -
      -

      Create Hook

      -
      -

      To create a new hook, first specify the source code as a Github Gist.

      -
      -

      Create new Github Gist

      - -
      -

      Paste the Github Gist URL below as the Hook's source code.

      -
      - - - - - - - - - - - - - - - - - - - -
      Hook Name
      Hook Source Code:
      Active Cron - - If checked, the Hook will run as a scheduled task. see: hook.io/cron
      - -
      +
      +

      Create New Microservice

      + +
      +
      Hey there! We just created a new Gist on Github using your account: here
      +
      + The Gist will now be associated with the Hook service once you've completed this form. +
      +
      +
      +

      Service Endpoint

      + + Submitting this form will register a new service in the api gateway. + +
      +
      + + +
      + Will be part of the following url to run the Service. Cannot contain non-url safe characters. +
      +
      +
      + + ... +
      +
      +
      + + +   + + +
      +
      + Public services and their source codes will be accessible to anyone. Private services are protected through API Access Keys. +
      + Notice: In order to enable private services, you must upgrade to a paid account. +
      +
      +
      + +
      +

      Type of Service

      +

      Which of these best describes the kind of service you'd like to create? Don't worry, we can always change this later

      + +

      Respond to HTTP requests using custom functions. Works with many programming languages

      + +

      Perform a redirect from this url to another url. Allows for custom host, uris, upstream_url,

      + +

      Execution a task on a scheduled timer.

      + + +
      +
      + + +
      + +
      +

      Cron Settings

      +
      + CRON GOES HERE +
      + +
      +

      Redirect Options

      +
      + + +
      + + + +
      + + +
      + +
      +

      Webhook Plugins

      +

      Request

      +

      Which of these best describes the kind of service you'd like to create? Don't worry, we can always change this later

      + + +
      + + + + +
      + + + +
      + + + + +

      Response

      + +
      + + + +
      + + +
      + +
      +

      Simple Bot

      +
      + Simple Bot Man +

      Simple bots response to commands and invoke services. Works well as stand-alone with services like Slack, Twilio, Discord, or simple announcement style bots.

      +
      + +
      +

      Bot Agent

      +
      +

      Bot Agents are persistent bot processes that run continually and expect to maintain outgoing connections. Works well with services like IRC or Twitter Firehouse

      +
      + +
      + -
      +
      -

      To fork an existing hook you can fork the echo example below.

      + +
      + + diff --git a/view/new.js b/view/new.js index 041940a4..d4f1e538 100644 --- a/view/new.js +++ b/view/new.js @@ -1,147 +1,226 @@ -var hook = require("../lib/resources/hook") -var bodyParser = require('body-parser'); +// TODO: if there is copied code in session ( from save to url or fork ), post message that content being created will be preopulated. have clear button as well [X] +// TODO: make `gateway` a reserved hook name since it might be confusing for users with /gateway logs +var hook = require('../lib/resources/hook'); +var hooks = require('microcule-examples'); +var psr = require('parse-service-request'); var config = require('../config'); - -// lookup themes from curated theme list -// TODO: Create "themes" resource and persist this to database -var themes = { - "debug": { - "theme": config.defaultTheme, - "presenter": config.defaultPresenter - }, - "simple": { - "theme": "http://hook.io/themes/simple/index.html", - "presenter": "http://hook.io/themes/simple/index.js" - }, - "simple-form": { - "theme": "http://hook.io/themes/simple-form/index.html", - "presenter": "http://hook.io/themes/simple-form/index.js" - }, - "none": { - "theme": "http://hook.io/themes/none/index.html", - "presenter": "http://hook.io/themes/none/index.js" - }, - "custom": { - "theme": "", - "presenter": "" - } -}; +var themes = require('../lib/resources/themes'); +var hooks = require('microcule-examples'); +var resource = require('resource'); +var checkRoleAccess = require('../lib/server/routeHandlers/checkRoleAccess'); module['exports'] = function view (opts, callback) { var req = opts.request, - res = opts.response; - + res = opts.response; var $ = this.$, - self = this; - - if (!req.isAuthenticated()) { - req.session.redirectTo = "/new"; - return res.redirect('/login'); - } - var user = req.user.username; - bodyParser()(req, res, function bodyParsed(){ - mergeParams(req, res, function(){}); - var params = req.resource.params; + self = this; + + var appName = req.hostname; + + var user, boot; + + var params; + psr(req, res, function(req, res, fields){ + params = req.resource.params; + for (var p in fields) { + params[p] = fields[p]; + } + + // TODO: move to resource.before hooks...maybe not. better to avoid the pre-processing logic... + checkRoleAccess({ req: req, res: res, role: 'hook::create' }, function (err, hasPermission) { + if (!hasPermission /* || req.resource.owner === "anonymous" */ ) { // don't allow anonymous hook creation + if (req.jsonResponse !== true && typeof params.hook_private_key === 'undefined') { + req.session.redirectTo = '/new'; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, 'hook::create')); + } else { + user = req.resource.owner; + boot = { + owner: user + }; + finish(); + } + }); + + }); + + function finish () { + $('title').html(appName + ' - Create new Service'); + var gist = params.gist; - // load gist embed based on incoming gist url parameter + //$('.codeEditor').html(html); + if (req.method === 'POST' && typeof params.scaffold === 'undefined') { + + if (typeof params.name === 'undefined' || params.name.length === 0) { + + var msg = 'Service name is required!'; + if (req.jsonResponse === true) { + msg = { + error: true, + message: msg, + property: 'name', + constraint: 'required', + required: true, + actual: false + }; + return res.end(JSON.stringify(msg, true, 2)); + } else { + return res.end(msg); + } + + } + + if (typeof params.view === 'string' && params.view.length > 1) { + params.themeSource = params.view; + params.themeActive = true; + } + + if (typeof params.theme === 'string' && params.theme.length === 0) { + delete params.theme; + } + if (typeof params.presenter === 'string' && params.presenter.length === 0) { + delete params.presenter; + } + + if (typeof params.hookSource === 'undefined') { + params.hookSource = 'code'; + } + + if (params.isPrivate === 'true') { + params.isPrivate = true; + } - if (params.create) { - - if (params.name.length === 0) { - return res.end('Hook name is required!'); + params.sourceType = params.hookSource; + + if (params.themeActive) { + params.themeStatus = 'enabled'; } - - // do not recreate hooks that already exist with that name - params.owner = user || "Marak"; // hardcode Marak for testing - - var query = { name: params.name, owner: req.user.username }; - return hook.find(query, function(err, results){ - if (results.length > 0) { - var h = results[0]; - return res.redirect('/' + h.owner + "/" + h.name + "?alreadyExists=true"); + + req.resource.owner = req.resource.owner.toLowerCase(); + params.name = params.name.toLowerCase(); + + params.cron = params.cronString; + + if (params.hookSource === 'code') { + delete params.gist; + // updated 9/2/16 to allow .code parameter ( instead of source ) + params.source = params.source || params.code || params.codeEditor; + if (typeof params.source === 'undefined') { + // Remark: It might be better to return an error here instead of assigning a default value... + // It can be a bit surprising if user intended to supply source code but ended up with this default value due to bad parameters to Hook.create + // params.source = "module['exports'] = function myService (req, res, next) { \n res.end('source not provided'); \n};"; + } + } + // TODO: remove this line + params.owner = req.resource.owner; + + if (params.isPrivate === true || params.isPrivate === 'true') { + params.isPrivate = true; + } else { + params.isPrivate = false; + } + + // Only allow fields which exist on the Hook resource + // Should this be an option in resource library itself? + var safeParams = {}; + Object.keys(hook.schema.properties).forEach(function (p) { + if (params[p] !== 'undefined') { + safeParams[p] = params[p]; } - params.cron = params.cronString; - - return hook.create(params, function(err, result){ - if (err) { - return callback(null, err.message); - } - - var h = result; - req.hook = h; - - // fetch the hook from github and check if it has a schema / theme - // if so, attach it to the hook document - - opts.gist = gist; - opts.req = opts.request; - opts.res = opts.response; - hook.fetchHookSourceCodeFromGithub(opts, function(err, code){ - if (err) { - return opts.res.end(err.message); - } - hook.attemptToRequireUntrustedHook(opts, function(err, _module){ - if (err) { - return opts.res.end(hook.formatError(err)) - } - h.mschema = _module.schema; - h.theme = _module.theme; - h.presenter = _module.presenter; - h.save(function(){ - // redirect to new hook friendly page - return res.redirect('/' + h.owner + "/" + h.name + "?created=true"); - //return callback(null, JSON.stringify(result, true, 2)); - }); - - }); - - }); - + }); + + if (typeof safeParams.language === 'undefined' && typeof safeParams.source === 'undefined') { + safeParams.language = 'javascript'; + safeParams.source = `module.exports = function helloWorld (req, res) { + // req is a Node.js http.IncomingMessage + var host = req.host; + // res is a Node.js httpServer.ServerResponse + // Respond to the request with a simple string + console.log('sent to the /logs endpoint'); + res.json(req.params); +};`; + } + + return hook.create.call({ req: req, res: res }, safeParams, function (err, result) { + if (err) { + return callback(null, err.message); + } + var h = result; + req.hook = h; + + resource.emit('hook::created', { + ip: req.connection.remoteAddress, + owner: params.owner, + name: params.name }); - + + if (req.jsonResponse) { + return res.json({ status: 'created', hook: result }); + } else { + // if not, redirect to admin page for newly created hook + return res.redirect('/admin?owner=' + h.owner + '&name=' + h.name + '&status=created'); + } }); + } - self.parent.components.themeSelector.present({}, function(err, html){ - var el = $('.table-condensed > tr').eq(1); - el.after(html); - callback(null, $.html()); - }) + if (typeof req.session.gistLink === 'string') { + // todo: after created, unset gistSource so it doesn't keep popping up + $('.gist').attr('value', req.session.gistLink); + } else { + $('.gistStatus').remove(); + } - }); + // TODO: update to use req.session.servicePlan instead? + if (req.session.paidStatus === 'paid') { + $('.paidAccount').remove(); + } else { + $('.publicPrivate').remove(); + //$('.hookPrivate').attr('DISABLED', 'DISABLED'); + //$('.hookPrivateLabel').css('color', '#aaa'); + } + $('.typeOfService').remove(); + $('.cronSettings').remove(); + $('.redirectOptions').remove(); -}; + $('.simpleBot').remove(); + $('.webhookPlugins').remove(); + $('.botAgent').remove(); + var services = hooks.services; + var examples = {}; + boot.examples = examples; -// -// Middleware for merging all querystring / request.body and route parameters, -// into a common scope bound to req.resource.params -// -function mergeParams (req, res, next) { + /* + for (var e in examples) { + for (var code in examples[e]) { + // $('.services').append(examples[e][code]); + } + } + */ + $ = req.white($); - req.resource = req.resource || {}; - req.resource.params = {}; - req.body = req.body || {}; + return callback(null, $.html()); - // - // Iterate through all the querystring and request.body values and - // merge them into a single "data" argument - // - if (typeof req.params === 'object') { - Object.keys(req.params).forEach(function (p) { - req.resource.params[p] = req.param(p); - }); } - if (typeof req.query === 'object') { - Object.keys(req.query).forEach(function (p) { - req.resource.params[p] = req.query[p]; - }); - } - - Object.keys(req.body).forEach(function (p) { - req.resource.params[p] = req.body[p]; - }); +}; - next(); -} \ No newline at end of file +module['exports'].schema = { + 'name': { + 'type': 'string', + 'required': true + }, + 'path': { + 'type': 'string' + }, + 'isPrivate': { + 'type': 'boolean', + 'default': false + }, + 'source': { + 'type': 'string', + 'required': false + } +}; \ No newline at end of file diff --git a/view/packages/index.html b/view/packages/index.html new file mode 100644 index 00000000..4f865965 --- /dev/null +++ b/view/packages/index.html @@ -0,0 +1,12 @@ + + +
      +

      Available Package Mangers

      + +
      \ No newline at end of file diff --git a/view/packages/index.js b/view/packages/index.js new file mode 100644 index 00000000..5b19c58c --- /dev/null +++ b/view/packages/index.js @@ -0,0 +1,4 @@ +module['exports'] = function (opts, cb) { + var $ = this.$; + cb(null, $.html()); +}; \ No newline at end of file diff --git a/view/packages/npm/error.html b/view/packages/npm/error.html new file mode 100644 index 00000000..23c9aa76 --- /dev/null +++ b/view/packages/npm/error.html @@ -0,0 +1,28 @@ + + +
      +

      Errored

      + + + + + + +
      Package NameVersion
      +
      + +
      \ No newline at end of file diff --git a/view/packages/npm/error.js b/view/packages/npm/error.js new file mode 100644 index 00000000..fe0c3edb --- /dev/null +++ b/view/packages/npm/error.js @@ -0,0 +1,24 @@ +var packages = require('../../../lib/resources/packages'); + +module['exports'] = function npmErrored (opts, cb) { + var $ = this.$; + function npmLink (m) { + return '' + m + '' + }; + packages.all({ status: 'errored' }, function(err, results){ + if (err) { + return res.end(err.message); + } + results = results.sort(); + results.forEach(function(m){ + var parts = m.split('@'); + var v = parts[1] || "latest"; + $('.errored').append('' + npmLink(parts[0]) + '' + v + '') + }); + if (results.length === 0) { + $('.errored').remove(); + $('.status').html('No packages errored.'); + } + cb(null, $.html()); + }); +}; \ No newline at end of file diff --git a/view/packages/npm/index.html b/view/packages/npm/index.html new file mode 100644 index 00000000..cba9fb15 --- /dev/null +++ b/view/packages/npm/index.html @@ -0,0 +1,17 @@ + + + \ No newline at end of file diff --git a/view/packages/npm/install.html b/view/packages/npm/install.html new file mode 100644 index 00000000..d10ee8a0 --- /dev/null +++ b/view/packages/npm/install.html @@ -0,0 +1,30 @@ + + +
      +

      Install NPM Packages into hook.io

      + +
      +
      + +
      +
      + +
      +
      +
      \ No newline at end of file diff --git a/view/packages/npm/install.js b/view/packages/npm/install.js new file mode 100644 index 00000000..08ebb7a4 --- /dev/null +++ b/view/packages/npm/install.js @@ -0,0 +1,28 @@ +var packages = require('../../../lib/resources/packages'); + +module['exports'] = function (opts, cb) { + var $ = this.$; + var req = opts.req, res = opts.res; + var params = req.resource.params; + function npmLink (m) { + return '' + m + '' + }; + // don't attempt to re-install existing packages by default ( force override parameter ) + packages.installed(req.resource.params.packages, function (err, _result){ + if (err) { + return res.end(err.message); + } + if (_result !== null && typeof params.force === "undefined") { + return res.json({ + status: 'installed', + message: 'package is already detected as installed. override installation with ?force=true' + }); + } + packages.install(req.resource.params.packages, function (err, result) { + if (err) { + return res.end(err.message); + } + return res.json(result); + }); + }); +}; \ No newline at end of file diff --git a/view/packages/npm/installed.html b/view/packages/npm/installed.html new file mode 100644 index 00000000..2770eacc --- /dev/null +++ b/view/packages/npm/installed.html @@ -0,0 +1,28 @@ + + +
      +

      Installed

      + + + + + + +
      Package NameVersion
      +
      + +
      \ No newline at end of file diff --git a/view/packages/npm/installed.js b/view/packages/npm/installed.js new file mode 100644 index 00000000..19b04e88 --- /dev/null +++ b/view/packages/npm/installed.js @@ -0,0 +1,29 @@ +var packages = require('../../../lib/resources/packages'); + +module['exports'] = function (opts, cb) { + var $ = this.$; + function npmLink (m) { + return '' + m + '' + }; + $('.installed').remove(); + $('.status').html('The NPM Package Listing has been temporarily removed.'); + return cb(null, $.html()); + /* Removed ( for now / was not accurate ) + packages.all({ status: 'installed' }, function(err, results){ + if (err) { + return res.end(err.message); + } + results = results.sort(); + results.forEach(function(m){ + var parts = m.split('@'); + var v = parts[1] || "latest"; + $('.installed').append('' + npmLink(parts[0]) + '' + v + '') + }); + if (results.length === 0) { + $('.installed').remove(); + $('.status').html('No packages installed'); + } + cb(null, $.html()); + }); + */ +}; \ No newline at end of file diff --git a/view/packages/npm/pending.html b/view/packages/npm/pending.html new file mode 100644 index 00000000..e5fcaa8b --- /dev/null +++ b/view/packages/npm/pending.html @@ -0,0 +1,28 @@ + + +
      +

      Pending

      + + + + + + +
      Package NameVersion
      +
      + +
      \ No newline at end of file diff --git a/view/packages/npm/pending.js b/view/packages/npm/pending.js new file mode 100644 index 00000000..416365cf --- /dev/null +++ b/view/packages/npm/pending.js @@ -0,0 +1,29 @@ +var packages = require('../../../lib/resources/packages'); + +module['exports'] = function npmPending (opts, cb) { + var $ = this.$; + function npmLink (m) { + return '' + m + '' + }; + $('.pending').remove(); + $('.status').html('No packages pending installation.'); + cb(null, $.html()); + /* Remove ( for now) + packages.all({ status: 'pending' }, function(err, results){ + if (err) { + return res.end(err.message); + } + results = results.sort(); + results.forEach(function(m){ + var parts = m.split('@'); + var v = parts[1] || "latest"; + $('.pending').append('' + npmLink(parts[0]) + '' + v + '') + }); + if (results.length === 0) { + $('.pending').remove(); + $('.status').html('No packages pending installation.'); + } + cb(null, $.html()); + }); + */ +}; \ No newline at end of file diff --git a/view/pricing.html b/view/pricing.html index fd2a8fbb..9ac95794 100644 --- a/view/pricing.html +++ b/view/pricing.html @@ -1,60 +1,485 @@ -
      -

      Pricing

      -
      -

      Free for Developers

      -

      - Accounts used for development purposes are free.

      -

      Currently there are no limitations on developer accounts, so have fun!

      -

      In the future we will limit certain resources and production features for development accounts. -

      -
      - -

      Basic

      -

      - Basic Hook hosting starts at $5.00 USD per month.

      -

      -

      - - -
      -

      - -
      -

      Disk Space

      -

      A small amount of disk space is available for hooks to read and write to using Node's fs module.

      -

      This space should be considered temporary and not for use in storing files for any period of time.

      - -
      -

      Usage Limitations

      - -

      There are currently no specific technical limitations on usage.

      - -

      The basic hosting plan should be sufficient for most small to mid-size projects.

      - -

      If you are interested in hosting for larger projects please contact hookmaster@hook.io

      -
      + + + + +
      + +
      +
      + +
      + +

      Hosting Plans

      + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       BasicPremiumAdvancedProBusiness
      Monthly Requests*1,00010,00050,000100,0001,000,000+
      Concurrent Requests*2481632
      Services20501002001,000
      API Access Keys ( Seats )15102050+
      Max Service Time-out10s10s60s60sUnlimited
      Cron Jobs4850100500+
      Cron Minimal Interval60 minutes30 minutes1 minute1 minute1 minute
      Logs
      Events API
      Private Services
      Custom Domains
      Role Based Access Control
      24 Hour Email Support
      Cost Per Month +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
            
      +
      +
      + + +
      +
      + +

      * Most stated limits for are "soft-limits", and not "hard-limits". This means {{appName}} will not immediately de-activate your services in the event that your account usage exceeds it's rate limits.

      +

      Our goal is to keep your services online and running. In the event your account usage is exceeding it's service plan limits, we'll attempt to contact you to find a solution.

      +
      + +

      Any of these plans may change in the future with no notice. Our goal is to maintain a fair pricing model. New features will be added and service classes may be adjusted.
      + + Dedicated Servers and White Label solutions are available on request. Please reach out if you have any questions about Dedicated Hosting.

      +
      +
      +
      -
      -
      \ No newline at end of file +
      + \ No newline at end of file diff --git a/view/pricing.js b/view/pricing.js index c3059f2f..e3cabcd5 100644 --- a/view/pricing.js +++ b/view/pricing.js @@ -1,3 +1,58 @@ +var config = require("../config"); +var servicePlan = require('../lib/resources/servicePlan'); +var user = require('../lib/resources/user'); + module['exports'] = function view (opts, callback) { - callback(null, this.$.html()); + var $ = this.$, req = opts.req; + if (req.session.user !== "anonymous") { + $('.freeAccount').remove(); + } + $('.boxAlert').remove(); + if (req.session && req.session.user && req.session.user !== 'anonymous') { + user.findOne({ name: req.session.user }, function (err, _u){ + if (err) { + return res.end(err.message); + } + var plan = null, pos = 0, cost; + + _u.servicePlan = _u.servicePlan || 'trial'; + + // free accounts no longer avaialble, free accounts are now trial accounts + if (_u.servicePlan === 'free') { + _u.servicePlan = 'trial'; + } + + Object.keys(servicePlan).forEach(function(key, i){ + if (key === _u.servicePlan) { + pos = i; + cost = servicePlan[key].cost; + } + }) + pos = pos - 1; + if (pos !== 0) { + // highlight column of current plan + $($('#colgroup col').get(pos)).addClass('bg-info'); + $($('#colgroup col').get(pos)).addClass('boxShadow'); + + // remove buy button from current plan + $('button', $('.cost td').get(pos)).attr('disabled', 'DISABLED'); + $('button span', $('.cost td').get(pos)).css('background-color', '#000'); + $($('.currentPlan td').get(pos)).html('Subscribed'); + } + finish(); + }) + } else { + finish(); + } + + //$('#colgroup col')['0'].addClass('bg-info'); + //$('#colgroup col').get(0).addClass('bg-info'); + + function finish () { + $ = req.white($); + var out = $.html(); + out = out.replace('{{stripePK}}', config.stripe.publicKey); + callback(null, out); + } + }; \ No newline at end of file diff --git a/view/privacy.html b/view/privacy.html new file mode 100644 index 00000000..071adcc2 --- /dev/null +++ b/view/privacy.html @@ -0,0 +1,184 @@ +
      +

      Privacy Policy

      + +
      +
      +
      +

      Last Updated May 7, 2016

      +
      +
      + +
      + +
      +
      + +

      This Privacy Policy describes the policies and procedures of {{appName}} and its affiliates (“{{appName}}”, “we”, “our” or “us”) on the collection, use and disclosure of your information, including any Personal Information (as below), on www.{{appName}} (the “Site”) and the services, features, content or applications we offer to users of our Site (collectively with the Site, the “Services”) This Privacy Policy, which is incorporated into and subject to the {{appName}} Terms of Service, does not apply to information our Subscribers or customers may collect about individuals when using our Site, our Services, or any other functionality, product, or service offered by {{appName}}.

      + +

      We may collect and receive information about users of our Site and/or Services (“users”, “you” or “your”) from various sources, including: (i) through your user account on the Services (your “Account”) if you register for the Site and the Services; (ii) your use of the Services; and (iii) from third party websites and services. When you access or use the Services, you are consenting to the collection, transfer, manipulation, storage, disclosure and other uses of your information, including any Personal Information, as set out in this Privacy Policy.

      + +

      Please read this Privacy Policy carefully. Should you have any questions about this Privacy Policy or {{appName}}'s data collection, use and disclosure practices, please contact us as set forth below.

      + +

      What Does This Privacy Policy Cover?

      + +

      This Site is hosted in the United States by {{appName}}, a company headquartered in New York, New York, USA. This Privacy Policy explains our practices for gathering and disseminating information we collect when you visit our Site and associated web pages.

      + +

      This Privacy Policy covers the treatment of any information or set of information that identifies or that is used by or on behalf of {{appName}} to identify an individual, which may, in certain contexts, include information such as an identifiable individual’s first and last name; home address, billing address, or other physical address; email address (so we may contact you); a telephone number; social security number; or date of birth (collectively “Personal Information”) we gather through the Services. This Privacy Policy also covers our treatment of any Personal Information that our business partners share with us or that we share with our business partners. You can navigate the majority of the website without giving us any Personal Information about yourself. However, we may track the technical information (see “Information Collected Using Cookies” below) provided to us by your browsing the Site to improve the navigation, content and design of our Site.

      + +

      This Privacy Policy does not apply to the privacy practices of third parties that we do not own or control, including but not limited to any third party websites, services, applications, online resources to which this Site may link, frame or otherwise reference (collectively “Third Party Services”) that you may access through the Services. We take no responsibility for the content or privacy practices of those Third Party Services. We encourage you to carefully review the privacy policies of any Third Party Services you access.

      + +

      What Information Do We Collect?

      + +

      The information we gather aides us to personalize, improve and operate the Services. You may voluntarily provide additional information about yourself to enable us to provide the information or Services you are requesting. We store the information you provide about yourself in order to provide you with the information or Services you request. Such information, with the exception of any Personal Information, is typically stored for the lifetime of the database unless you request that it be removed. The information you provide us about yourself will be shared with our employees or contractors to the extent necessary to accommodate your request. We will obtain your consent in order for us to share your Personal Information with third parties in a manner that is not permitted under the terms of this Privacy Policy. Unless otherwise described below, we will not use Personal Information provided to us online for purposes other than those you have requested without also providing you an opportunity to agree to or otherwise limit such uses.

      + +

      We may collect the following types of information from our users, some of which will be Personal Information and some of which will not be Personal Information.

      + +

      User Content and Account Information:

      + +

      Some features of the Services may allow you to provide content to the Services that is not Personal Information. All such content submitted by you to the Services may be retained by us indefinitely, even after you terminate your Account. We may continue to disclose such content to third parties in a manner that does not reveal Personal Information, as described in this Privacy Policy.

      + +

      As part of your Account information, {{appName}} collects and stores information, including the full name, company name, billing address, email address, IP address, landing page, and referring URL, for all users upon registration and use of the Services. You acknowledge that this information may be personal to you, and by creating an Account on the Services and providing Personal Information to us, you allow us, our affiliates, our service providers and our contractors to identify you and, therefore, your use of the Services may not be anonymous.

      + +

      {{appName}} may process or store your Personal Information outside the jurisdiction in which you reside and, if it does so, your Personal Information will be governed by the privacy laws of the jurisdiction in which it is processed or stored and government and law enforcement agencies of that jurisdiction may be able to access such Personal Information without your consent. Wherever we are required to transfer your Personal Information, regardless of where this occurs, we will strive to take steps to ensure that your information is treated securely and in accordance with this Privacy Policy. Please note that your submission of information or use of our Services will be deemed by us to signify explicit consent on your part to such transfer of information to any part of the world.

      + +

      Email Communications:

      + +

      In addition to any information you convey in emails to us, we may also receive a confirmation when you open an email from us. We collect and use this information to improve our customer service.

      + +

      Financial Information:

      + +

      We use third party payment processing companies (“Payment Processors”) who collect and store financial information, such as your payment method (valid credit card number, type, expiration date or other financial information), and their use and storage of that information is governed by the Payment Processors’ applicable terms of service and privacy policy.

      + +

      IP Address Information and Other Information Collected Automatically:

      + +
        +
      • We may automatically receive and record information from your web browser when you interact with the Services, including your IP address. An IP address is a number assigned to you by your Internet service provider so you can access the Internet. We receive IP addresses in the normal course of the operation of our Site. We may automatically collect and record information about your use of features of our Services, about the functionality of our Services, and other information related to your interactions with the Services. We use the information we automatically collect and record to analyze trends, to administer, monitor and improve the Site and Services, to track users’ use of the Site and Services, and to gather broad demographic information for aggregate use. We do not use IP addresses to identify you personally or disclose them to others. This information is used for fighting spam/malware and also to facilitate collection of data concerning your interaction with the Services (e.g., what links you have clicked on).
      • +
      • Generally, the Services may automatically collect usage information, such as the number and frequency of visitors to the Site. We may use this data in aggregate form, that is, as a statistical measure, but not in a manner that would identify you personally. This type of aggregate data enables us and third parties authorized by us to figure out how often individuals use parts of the Services so that we can analyze and improve them.
      • +
      +
      +

      Information Collected Using Cookies:

      + +
        +
      • As is true of most web sites, we gather certain information automatically and store it in log files. This information may include internet protocol (IP) addresses, browser type, internet service provider (ISP), referring/exit pages, operating system, date/time stamp, and clickstream data. To collect this information, when you visit our Site, a "cookie" may be set on your computer. Cookies contain a small amount of information that allows our web servers to recognize you whenever you visit. We store information that we collect through cookies, log files and/or clear gifs to create "settings" regarding your preferences. We do not tie your Personal Information to information in these settings.
      • +
      • Our cookies do not, by themselves, contain Personal Information, and we do not combine the general information collected through cookies with other Personal Information to tell us who you are. We do not link cookie data to any Personal Information collected on our Site. We use cookie information, which does not identify individual users, to analyze trends, administer the website, track users’ movements and gather demographic information for aggregate use. No Personal Information is stored in cookies and we do not link collected data to Personal Information. {{appName}} also may use cookies to enable our servers to recognize your web browser and tell us how and when you visit the Site and otherwise use the Services through the Internet.
      • +
      • {{appName}} also uses Google Analytics, an analytics service provided by Google, Inc. (“Google”). Google Analytics uses cookies to collect non-identifying information, which may be transmitted to and stored by Google on servers in the United States. Google provides some additional privacy options described at www.google.com/policies/privacy/partners/ regarding Google Analytics cookies.
      • +
      • Many browsers have an option for disabling cookies, which may prevent your browser from accepting new cookies or enable selective use of cookies . A user who does not accept cookies from our Site may not be able to access certain areas of the Site.
      • +
      + +
      + +

      Information Related to Advertising:

      + +

      To support and enhance the Services, we may serve advertisements, and also allow third parties advertisements, through the Services. These advertisements are sometimes targeted and served to particular users. {{appName}} may partner with third parties to display advertisements targeted to visitors of the Services. To do so, {{appName}} stores a cookie for visitors for the purpose of displaying advertising.

      + +

      Aggregate Information:

      + +

      We may collect statistical information about how users use the Services (“Aggregate Information”). Some of this information may be derived from Personal Information. This statistical information is not Personal Information.

      + +

      Children’s Privacy:

      + +

      Protecting the privacy of children is especially important. For that reason, we do not collect or maintain information at our Site from those we know are under 13 years of age, and our Site is a general audience site not intended for users under the age of 13..

      + +

      How, and With Whom, Is My Information Shared?

      + +

      Summary of Information Sharing:

      + +

      Except as described in this Privacy Policy, {{appName}} will never intentionally share, sell, or rent individual Personal Information with anyone without your permission or unless ordered by a court of law.

      + +

      Information submitted to us is only available and utilized by employees and systems responsible for providing services to our customers and to contracted service providers for the purposes of providing Services relating to our communications with you.

      + +

      {{appName}} may share your information with contracted service providers if sharing your information is necessary to provide a Service you have requested, as part of a joint sales promotion or to pass sales leads to one of our distribution partners, or to keep you up-to-date on product announcements, software updates, special offers or other information. If your information is shared with third parties, we will strive to only provide the information they need to deliver the Service. We will aim to restrict these companies from using your information for any other purpose, and you may choose not to have your information given to such a third party.

      + +

      {{appName}}'s employees and contracted service providers have been trained to handle such data properly and in accordance with our security protocols and strict standards of confidentiality. Although we cannot guarantee against any loss, misuse, unauthorized disclosure, alteration or destruction of data, we take reasonable precautions to prevent such unfortunate occurrences.

      + +

      We will not otherwise use or disclose any of your Personal Information, except as described in this Privacy Policy, including, to the extent reasonably necessary: to correct technical problems and malfunctions and to technically process your information; to protect the security and integrity of our Site; to protect our rights and property and the rights and property of others; to take precautions against liability; to the extent required by law or to respond to judicial process; or to the extent permitted under other provisions of law, to provide information to law enforcement agencies or for an investigation on a matter related to public safety, or to effectuate the sale, merger, bankruptcy, or similar disposition of {{appName}} or its assets.

      + +

      Public Information About Your Activity on the Services:

      + +

      Some of your activity on and through the Services may be public, such as content you post publicly on the Site or otherwise through the Services. By posting such information on the Site, you agree that it may be viewed and used by other users or otherwise. Please also remember that if you choose to provide Personal Information using certain public features of the Services, then that information is governed by the privacy settings of those particular features and may be publicly available. Individuals reading such information may use or disclose it to other individuals or entities without our control and without your knowledge, and search engines may index that information.

      + +

      IP Address Information:

      + +

      While we may collect and store IP address information, we do not make that information public. We may at times, however, share this information with our partners and service providers, and as otherwise specified in this Privacy Policy.

      + +

      Information You Elect to Share:

      + +

      You may access other Third Party Services through the Services, for example by clicking on links to those Third Party Services from within the Site. We are not responsible for the privacy policies and/or practices of these Third Party Services, and you are responsible for reading and understanding those Third Party Services’ privacy policies. This Privacy Policy only governs your Personal Information and other information collected on the Services.

      + +

      Aggregate Information:

      + +

      We may share Aggregate Information with our partners, service providers and other persons with whom we conduct business. We share this type of statistical data so that our partners can understand how and how often people use our Services and their services or websites, which facilitates improving both their services and how our Services interface with them. In addition, these third parties may share with us non-private, aggregated or otherwise non Personal Information about you that they have independently developed or acquired.

      + +

      Email Communications with Us:

      + +

      {{appName}} may send occasional promotional materials to you via email. We try to respect your time and attention by controlling the frequency of our mailings. Each email sent contains an easy way for you to cease receiving email from us. If you wish to do this, simply follow the instructions found at the end of any email. If you have received unwanted, unsolicited email sent via this system or purporting to be sent via this system, please forward a copy of that email with your comments to {{appAdminEmail}}. Regardless, we reserve the right to contact you for informational or account-related purposes when we believe it is necessary.

      + +

      Access to Payment Processor Information:

      + +

      As stated above, we use Payment Processors to collect and store financial information. However, we may from time to time request and receive some of your financial information from our Payment Processors for the purposes of completing transactions you have initiated through the Services, protecting against or identifying possible fraudulent transactions, and otherwise as needed to manage our business.

      + +

      Information Shared with Our Service Providers:

      + +

      We employ and contract with people and other entities that perform certain tasks on our behalf (our “Service Providers”). We may need to share Personal Information with our Service Providers in order to provide products or services to you. Our Service Providers do not have any right to use Personal Information or other information we share with them beyond what is necessary to assist us in order to provide the products or services requested by you.

      + +

      Information Disclosed Pursuant to Business Transfers:

      + +

      Information about our users, including Personal Information, may be disclosed and otherwise transferred to an acquirer, successor, or assignee as part of any merger, acquisition, debt financing, sale of assets, or similar transaction, or in the event of an insolvency, bankruptcy, or receivership in which information is transferred to one or more third parties as one of our business assets.You acknowledge that such transfers may occur, and that any acquirer of us or our assets may continue to use your Personal Information as set forth in this Privacy Policy.

      + +

      Information Disclosed for Our Protection and the Protection of Others:

      + +

      We also reserve the right to access, read, preserve, and disclose any information as we reasonably believe is necessary to (i) satisfy any applicable law, regulation, legal process or governmental request, (ii) enforce this Privacy Policy and our Terms of Service, including investigation of potential violations hereof, (iii) detect, prevent, or otherwise address fraud, security or technical issues, (iv) respond to user support requests, or (v) protect our rights, property or safety, our users and the public. This includes exchanging information with other companies and organizations for fraud protection and spam/malware prevention.

      + +

      Information We Share With Your Consent:

      + +

      Except for data sharing described in this Privacy Policy, you will be notified when your Personal Information may be shared with third parties, and will be able to refuse the sharing of such information.

      + +

      Is Information About Me Secure?

      + +

      {{appName}} is committed to protecting Personal Information. To do so we employ a variety of security technologies and measures intended to protect our information from unauthorized access, use, or disclosure. {{appName}} has a corporate policy in place that requires all personnel not to disclose Personal Information to any third party that is not entitled to have that information. If you have any questions about the security at our Site, you can send an email to {{appAdminEmail}}. Please note, however, that {{appName}} cannot fully eliminate security risks associated with the collection, disclosure and storage of your information and that mistakes and security breaches may occur. We do not guarantee or warrant that such techniques will prevent unauthorized access to information about you that we store, Personal Information or otherwise.

      + +

      What Information of Mine Can I Access?

      + +

      If you are a registered user, you can access information and ask for correction of your information, if necessary, that is associated with your Account by logging into the Services. Registered and unregistered users can access and delete cookies through their web browser settings.

      + +

      How Can I Delete My Personal Information or Account?

      + +

      Registered users shall have the ability to delete Personal Information related to their full name, company name, and identifiable billing information through their Accounts. Such deleted Personal Information will no longer be stored in {{appName}}’s databases, with the exception of: email address, network logs or cases where fraudulent or otherwise illegal activity has been deemed to have occurred as determined by law enforcement or {{appName}}, in which case such information may remain in {{appName}}’s database for the purposes of addressing any such fraudulent or otherwise illegal activity.

      + +

      To have your email permanently removed, or to delete your Account, please email {{appName}} at {{appAdminEmail}}.

      + +

      If you terminate your Account, any association between your Account and information we store will no longer be accessible through your Account. However, any public activity on your Account prior to deletion will remain stored on our servers and will remain accessible to the public.

      + +

      What Choices Do I Have Regarding My Information?

      + +

      You can use some of the features of the Services without registering, thereby limiting the type of information that we collect.

      + +

      You can always opt not to disclose certain information to us, even though it may be needed to take advantage of some of our Services' features.

      + +

      You may update or remove the information you provided to us by contacting us as described in the Section "What If I Have Questions or Concerns?" below or by emailing us at {{appAdminEmail}}. To protect your privacy and security, we may also take reasonable steps to verify your identity before updating or removing your information. The information you provide us may be archived or stored periodically by us according to backup processes conducted in the ordinary course of business for disaster recovery purposes. Where permitted by law, your ability to access and correct Personal Information will be limited where access and correction could: inhibit {{appName}}'s ability to comply with a legal or ethical obligation; inhibit {{appName}}'s ability to investigate, make or defend legal claims; result in disclosure of Personal Information about a third party; or result in breach of a contract or disclosure of trade secrets or other proprietary business information belonging to {{appName}} or a third party.

      + +

      What Happens When There Are Changes to this Privacy Policy?

      + +

      This Privacy Policy may be modified from time to time, so please review it frequently. Changes to this Privacy Policy will be posted on the Site and we recommend that you review the Privacy Policy to ensure you are aware of any changes made to it. A user is bound by any changes to the Privacy Policy when he or she uses the Services after such changes have been first posted. In the event that the modifications materially alter your rights or obligations hereunder, we will make reasonable efforts to notify you of the change. For example, we may send a message to your email address, if we have one on file, or generate a pop-up or similar notification when you access the Services for the first time after such material changes are made. Please check the Privacy Policy each time you use our Site for the most current information.

      + +

      International Data Transfers

      + +

      {{appName}} is a global business. We may transfer Personal Information to countries other than the country in which the data was originally collected. These countries may not have the same data protection laws as the country in which you initially provided the information. When we transfer your Personal Information to other countries, we will protect that information as described in this Privacy Notice.

      + +

      In particular, to offer our services, we need to transfer your Personal Information to the United States, where we are headquartered. The United States is not considered under EU data protection law to provide an adequate level of data protection. By agreeing to the Privacy Policy, you consent to the transfer of your Personal Information for the purposes described in the Privacy Policy. You have the right to withdraw your consent at any time, but please note that if you do not consent to the transfer or if you withdraw your consent, you may not be able to benefit from all or some of our services.

      + +

      {{appName}} complies with the U.S.-EU Safe Harbor Framework and the U.S.-Swiss Safe Harbor Framework as set forth by the U.S. Department of Commerce regarding the collection, use, and retention of Personal Information from European Union member countries and Switzerland. {{appName}} has certified that it adheres to the Safe Harbor Privacy Principles of notice, choice, onward transfer, security, data integrity, access, and enforcement. To learn more about the Safe Harbor program, and to view {{appName}}'s certification, please visit http://www.export.gov/safeharbor/.

      + +

      What If I Have Questions or Concerns?

      + +

      {{appName}} is responsible for the processing of your Personal Information. If you have any questions or concerns regarding privacy using the Services, or if you’d like to exercise your rights or choices, including the right to withdraw your consent, please send us a detailed message to {{appAdminEmail}}.

      + +

      Please note the name of the Site or other online resource to which you provided the information, as well as the nature of the information that you provided. We will use reasonable efforts to respond promptly to requests, questions or concerns you may have regarding our use of Personal Information about you. Except where required by law, {{appName}} cannot ensure a response to questions or comments regarding topics unrelated to this Privacy Policy or our privacy practices.

      + +

      Effective Date
      + This Privacy Policy became effective on: May 7, 2016.
      + ©2016 {{appName}} All rights reserved.

      +
      +
      +
      +
      \ No newline at end of file diff --git a/view/privacy.js b/view/privacy.js new file mode 100644 index 00000000..15d0896d --- /dev/null +++ b/view/privacy.js @@ -0,0 +1,16 @@ +module['exports'] = function view (opts, callback) { + + var $ = this.$, + req = opts.request, + res = opts.response, + params = req.resource.params; + var appName = "hook.io", + appAdminEmail = "hookmaster@hook.io", + appPhonePrimary = "1-555-555-5555"; + var out = $.html(); + out = out.replace(/\{\{appName\}\}/g, appName); + out = out.replace(/\{\{appAdminEmail\}\}/g, appAdminEmail); + out = out.replace(/\{\{appPhonePrimary\}\}/g, appPhonePrimary); + callback(null, out); + +}; \ No newline at end of file diff --git a/view/referrals.html b/view/referrals.html index d8e65fd2..6c17d761 100644 --- a/view/referrals.html +++ b/view/referrals.html @@ -1,7 +1,7 @@ -
      +

      Referrals

      - Your referral link: http://hook.io/{{username}}?s + Your referral link: https://hook.io/{{username}}?s
      @@ -9,7 +9,8 @@

      Referrals

      Current Referral Incentives

      -

           - One Bitcoin for user with most referrals in month of November 2014

      +

      There are currently no active offers

      +

      Have no fear! All referrals are always tracked and our affiliate program gives our users a percentage of all revenue from their referrals.

      Your referrals

      diff --git a/view/referrals.js b/view/referrals.js index c3f70646..a9ceac6e 100644 --- a/view/referrals.js +++ b/view/referrals.js @@ -11,8 +11,10 @@ module['exports'] = function view (opts, callback) { return res.redirect('/login'); } + $ = req.white($); + return user.find({ - referredBy: req.user.username + referredBy: req.session.user }, function (err, results){ if (err) { return res.end(err.message); @@ -20,10 +22,10 @@ module['exports'] = function view (opts, callback) { if (results.length > 0) { $('.referrals').html(''); results.forEach(function(u){ - $('.referrals').append('' + u.name + '' + '
      ') + $('.referrals').append('' + u.name + '' + '
      ') }); } - callback(null, $.html().replace(/\{\{username\}\}/g, req.user.username)); + callback(null, $.html().replace(/\{\{username\}\}/g, req.session.user)); }); }; \ No newline at end of file diff --git a/view/register.html b/view/register.html new file mode 100644 index 00000000..e0dd0971 --- /dev/null +++ b/view/register.html @@ -0,0 +1,179 @@ + + + + +
      +

      Register Account

      +
      +

      To continue, we require you register an account name and password

      +
      +
      + + +
      +
      +

      + +
      +
      +

      + +
      +
      +

      + +
      +
      +
      + +
      + I'd rather Sign-in with Github +
      +
      +
      +
      +
      +

      Your Home

      +
      +

      Your account name is the home for your services on hook.io

      +

      {{appUrl}}account-name

      + +

      You will be able to manage your services from this location.

      +
      + +
      +
      + +
      + +

      +
      \ No newline at end of file diff --git a/view/register.js b/view/register.js new file mode 100644 index 00000000..f4829360 --- /dev/null +++ b/view/register.js @@ -0,0 +1,130 @@ +var user = require('../lib/resources/user'); +var psr = require('parse-service-request'); +var config = require('../config'); +module.exports = function (opts, cb) { + var $ = this.$, + res = opts.res, + req = opts.req; + + $ = req.white($); + + var params = req.resource.params; + + if (!req.isAuthenticated()) { + if (req.jsonResponse) { + res.status(401); + return res.json({ error: true, message: 'session-required' }); + } + return res.redirect(config.app.url + '/login'); + } + if (req.session.user && req.session.user !== 'anonymous') { + if (req.jsonResponse) { + return res.json({ result: 'already-registered' }); + } + return res.redirect(config.app.url + '/account'); + } + + function passwordRequired () { + res.status(400); + var r = { + error: true, + message: 'Password field is required.' + } + return res.json(r); + } + + psr(req, res, function(){ + if (req.method === "POST") { + if (typeof params.password !== 'undefined' && typeof params.confirmPassword !== 'undefined') { + if (params.password.length > 0) { + if (params.password !== params.confirmPassword) { + res.status(400); + var r = { + error: true, + message: 'Passwords do not match.' + } + return res.json(r); + } + } else { + return passwordRequired(); + } + } else { + return passwordRequired(); + } + + if (typeof params.account_name !== 'string' || params.account_name.length === 0) { + res.status(400); + return r.json({ error: true, message: 'account_name parameter is required'}); + } + // TODO: slug check for account name? i hope its already there in resource-user + // TODO: if incoming account name is being registered, update it on the user document and session + user.find({ name: params.account_name }, function (err, _users) { + if (err) { + return res.json({ error: true, message: err.message }); + } + if (_users.length === 0) { + // account name is available, register it with current logged in account! + } else { + // account name is take + var r = { + error: true, + message: 'Account name is already taken. Try again with new name.', + result: 'exists' + } + res.status(400); + return res.json(r); + } + user.findOne({ email: req.session.email }, function (err, _user) { + if (err) { + res.status(400); + return res.json({ error: true, message: err.message }); + } + + // TOOD: replace with user.signUp logic? + // user.register() logic? + // needs to perform req.login? + + // needs to perform password check and upate + + //_user.name = params.account_name; + // _user.password = params.password; + + var _update = { + id: _user.id, + name: params.account_name, + timezone: params.timezone, + password: params.password + }; + // console.log('attemping to update user', _update) + // Remark: Must use User.update instead of User.save for password before hooks to work ( salt + one-way hash ) + // Would be nice if resource had .save() hooks working + user.update(_update, function(err, re){ + if (err) { + res.status(400) + return res.json({ error: true, message: err.message }); + } + req.session.user = params.account_name; + user.login({ req: req, res: res, user: re }, function (err) { + if (err) { + return res.json(err.message); + } + var r = { + result: 'success' + } + return res.json(r); + // TODO: perform redirect ?? + return res.redirect(config.app.url + '/services'); + }); + }); + }); + }); + } else { + // auto-suggest account name based on current email + var suggest = req.session.email.split('@')[0]; + $('#account_name').val(suggest); + $('.accountName').html('/' + suggest); + cb(null, $.html()) + } + }); + +} \ No newline at end of file diff --git a/view/reset.html b/view/reset.html new file mode 100644 index 00000000..05eea5a1 --- /dev/null +++ b/view/reset.html @@ -0,0 +1,70 @@ +
      +

      Reset Password

      + +
      + + \ No newline at end of file diff --git a/view/reset.js b/view/reset.js new file mode 100644 index 00000000..ea493dac --- /dev/null +++ b/view/reset.js @@ -0,0 +1,118 @@ +var user = require('../lib/resources/user'); +var email = require('resource-email'); +var config = require('../config'); +var uuid = require('node-uuid'); +var psr = require('parse-service-request'); + +module['exports'] = function resetPassword (opts, cb) { + var req = opts.request, + res = opts.response, + $ = this.$; + + psr(req, res, function (req, res) { + var params = req.resource.params; + // return cb(null, $.html()); + var appName = req.hostname; + // If token has been provided check if it is valid and attempt to bypass password + if (params.t && params.t.length > 0) { + return user.find({ token: params.t }, function(err, results){ + if (err) { + return res.end(err.message); + } + if (results.length === 0) { + if (req.jsonResponse) { + var r = { + result: 'invalid' + }; + return res.json(r); + } else { + return res.end('Invalid or expired account reset token.'); + } + } + var u = results[0]; + req.session.sessionID = uuid(); + req.login(u, function () { + req.session.user = u.name; + req.session.email = u.email; + req.session.user_ctime = u.ctime; + req.session.hookAccessKey = u.hookAccessKey; + // TODO: We could invalidate / regenerate user.token here to make login tokens one-time use only + return res.redirect('/account?reset=true'); + }); + }); + } + + var nameOrEmail = params.email; + + if (typeof nameOrEmail === "undefined" || nameOrEmail.length === 0) { + + if (req.jsonResponse) { + var r = { + result: 'invalid' + }; + return res.json(r); + } else { + return cb(null, $.html()); + } + } + + var type = "name"; + if (nameOrEmail.search('@') !== -1) { + type = "email"; + } + + var query = {}; + nameOrEmail = nameOrEmail.toLowerCase(); + query[type] = nameOrEmail; + user.reset(query, function (err, u) { + if (err) { + var r = { + result: 'invalid', + error: err.message + }; + return res.json(r); + // return res.end(err.message); + } + if (typeof u.email === "undefined" || u.email.length === 0) { + var r = { + result: 'no-email', + }; + return res.json(r); + } + // TODO: move template to /emails folder + // TODO: use before / after resource hooks for user.reset action + var ip = req.connection.remoteAddress.toString(); + // TODO: add ip address, requested from: ' + ip + var link = config.app.url + "/reset?t=" + u.token; + var tmpl = 'A password reset for your ' + appName + ' account was requested.' + '

      '; + tmpl += (' Account Name: ' + u.name + '
      '); + tmpl += (' Reset Link: ' + link + '
      '); + // if user was found, check to see if they have an email + email.send({ + //provider: 'sendgrid', + provider: config.email.provider, + api_user: config.email.api_user, + api_key: config.email.api_key, + to: u.email, + from: config.app.adminEmail, + subject: appName + " password reset request", + html: tmpl + }, function (err, result) { + if (err) { + return res.end('Error in sending email ' + err.message); + } + // if email is found, send reset password email + // TODO: send partial email string back to user with ****** to show where email was sent + // TODO: obfuscate email to client with **** interpolation. + var r = { + result: 'reset', + data: { + email: u.email + } + }; + return res.json(r); + }); + }); + }); + +}; \ No newline at end of file diff --git a/view/roles.html b/view/roles.html new file mode 100644 index 00000000..007d3722 --- /dev/null +++ b/view/roles.html @@ -0,0 +1,74 @@ + + + +
      +

      Roles

      +
      + +

      What are role Checks?

      +

      Roles are unique identifiers representing granular access to the hook.io API. Every API method on hook.io has a unique Role name. Roles provide an easy way to grant access to your {{appName}} services based on access keys.

      + +

      Roles can be assigned during the creation of an API Access Key, allowing granular role access checks per generated key. All System Events on {{appName}} have a one-to-one mapping to Roles.

      +

      Role checks are used to determine if an incoming HTTP request has authorization to access the requested resource. {{appName}} offers several integrated roles and custom roles that may be associated with a generated Access Key.

      +

      +
      + +
      +

      + +
      +

      Available Roles

      + + + + + +
      + Name + + Description +
      + +
      + +

      Custom Role Checks

      +

      Aside from the built-in roles {{appName}} provides, it's also possible to specify a custom role value when creating an API Access Key which can used later to check access using the keys.checkAccess HTTP API method ( available in SDK)

      +

      + For Example, inside a Hook service as req.checkAccess(role, cb): +

      +module['exports'] = function checkAccess (req, res) {
      +  req.checkAccess('custom::role', function(err, hasAccess){
      +    res.json(hasAccess);
      +  });
      +};
      +

      +

      This will be scoped to the hook_private_key HTTP parameter or to the current user session.

      +
      +
      + +
      \ No newline at end of file diff --git a/view/roles.js b/view/roles.js new file mode 100644 index 00000000..7c575f3c --- /dev/null +++ b/view/roles.js @@ -0,0 +1,29 @@ +var user = require('../lib/resources/user'); +var cache = require("../lib/resources/cache"); +var role = require('../lib/resources/role'); + +module['exports'] = function view (opts, callback) { + var req = opts.request, res = opts.response; + var $ = this.$; + + var appName = req.hostname; + + /* + if (!req.isAuthenticated()) { + req.session.redirectTo = "/env"; + return res.redirect('/login'); + } + */ + + var params = req.resource.params; + + Object.keys(role.roles).sort().forEach(function(r){ + $('.availableRoles').append('
      ' + r + '
      '); + $('.roles').append('' + r + '' + (role.roles[r].description || "n/a") +'') + }); + + + $ = req.white($); + callback(null, $.html()); + +}; \ No newline at end of file diff --git a/view/sdk.html b/view/sdk.html new file mode 100644 index 00000000..68b00e43 --- /dev/null +++ b/view/sdk.html @@ -0,0 +1,68 @@ +
      +

      Software Development Kit

      +
      +

      Getting Started

      +

      The {{appName}} client SDK makes it easy to interact with the {{appName}} platform from any service or third-party application. If you intend to quickly run a block of code in response to HTTP requests or as a scheduled task we recommend Creating a new hosted Microservice.

      + +

      For programmatic access outside of the {{appName}} website, we recommend installing the {{appName}}-sdk.

      + +

      Installation

      +

      +

      
      +  npm install -g {{appSdkName}}
      +        
      +

      + +

      Official SDK Open-Source Repository

      +

      https://github.com/bigcompany/hook.io-sdk

      +

      Note: All SDKs are powered by the hook.io API, which you may access directly at anytime using standard HTTP requests.

      + +

      Usage

      + +

      Command Line Interface

      +
      
      +  Usage: hook [options] [command]
      +
      +  Commands:
      +
      +    clone       clones existing hook
      +    config      manage SDK config
      +    deploy      deploy hook to the cloud
      +    alias       create hostname alias for hook
      +    examples    show list of example hooks
      +    get         get hook document
      +    init        initialize new local service
      +    list        list all hooks
      +    run         run hook in the cloud
      +    whoami      returns currently logged in user
      +    help [cmd]  display help for [cmd]
      +
      +  Options:
      +
      +    -h, --help     output usage information
      +    -V, --version  output the version number
      +
      +  Notes:
      +
      +    All commands are available as separate git style binaries
      +    Such as: hook-list, hook-info, hook-run, hook-deploy, etc
      +    
      +
      +

      Programmatic

      +

      +

      +  var client = sdk.createClient({ hook_private_key: "12345"});
      +
      +  client.hook.create('echo', {
      +    "source": 'echo "hello"',
      +    "language": "bash"
      +  }, function (err, res) {
      +    console.log(err, res);
      +  });
      +  
      + +

      +

      Click Here For 30+ SDK Code Examples

      + +
      +
      \ No newline at end of file diff --git a/view/sdk.js b/view/sdk.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/sdk.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/services.html b/view/services.html new file mode 100644 index 00000000..2afcf561 --- /dev/null +++ b/view/services.html @@ -0,0 +1,239 @@ + + + + + + +
      +
      + +
      + + +
      + +
      + + +
      +
      +

      It looks like you haven't created any Services yet!

      +

      You can Create a new Service by clicking

      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameMonthlyTimeRunning    
      + No results found for: +
      + echo + +
      + Last Run: n/a   + Status Code n/a +
      +
      Error: Hook last returned 500 error code status.
      Click here to view error logs
      +
      Error: Hook is exceeding maximum timeout limitations, this is bad.
      Please check service to ensure it is ending the response in time.
      + +
      + 0 + + n/a + + 0 + + + + + + + + +
      +
      +
      +
      +
      +
      \ No newline at end of file diff --git a/view/services.js b/view/services.js new file mode 100644 index 00000000..0e4caafe --- /dev/null +++ b/view/services.js @@ -0,0 +1,212 @@ +var hook = require('../lib/resources/hook'); +var checkRoleAccess = require('../lib/server/routeHandlers/checkRoleAccess'); +var config = require('../config'); +var psr = require('parse-service-request'); +var metric = require('../lib/resources/metric'); +var df = require('dateformat'); +var ms = require('ms'); + +module['exports'] = function view (opts, callback) { + + var $ = this.$; + + var req = opts.req, + res = opts.res, + params = req.resource.params; + + if (typeof params.signedMeUp !== "undefined" || typeof params.s !== "undefined") { + req.session.referredBy = req.params.owner; + //return res.redirect("/"); + } + + if (!req.isAuthenticated() && req.url === "/services") { + return res.redirect('/login'); + } + + /* + var loggedIn = false; + */ + + psr(req, res, function (req, res, fields){ + for (var p in fields) { + params[p] = fields[p]; + } + return finish(); + /* bypass role check for now, anyone can view other users non-private hooks ( filter is performed below ) + checkRoleAccess({ req: req, res: res, role: "hook::find" }, function (err, hasPermission) { + if (!hasPermission) { // don't allow anonymous hook listing? + if (req.jsonResponse !== true && typeof params.hook_private_key === "undefined") { + req.session.redirectTo = "/services"; + return res.redirect('/login'); + } + return res.end(config.messages.unauthorizedRoleAccess(req, "hook::find")); + } else { + finish(); + } + }); + */ + }); + + function finish () { + $ = req.white($); + params.query = params.query || {}; + // TODO: allow public listing / index of public services + // currently it will only allow viewing of your own services + var _owner = params.query.owner || req.resource.owner || req.params.owner || req.session.user; + var query = {}; + + for(var p in params.query) { + query[p] = params.query[p]; + } + query.owner = _owner; + + hook.find(query, function (err, hooks) { + if (err) { + return res.end(err.message); + } + // TODO: filter out private hooks if session owner doesn't match hook owner + // if current user is not owner, filter out private hooks + hooks = hooks.filter(function(item){ + if (typeof req.session.user === "undefined" && item.isPrivate) { + return false + } + if (item.isPrivate && typeof req.session.user !== "undefined" && (item.owner !== req.session.user.toLowerCase())) { + return false; + } + return item; + }); + + if (req.jsonResponse) { + return res.json(hooks); + } + // if there is no referral set, assign one based on the owner of the current hook + if (typeof req.session.referredBy === "undefined") { + req.session.referredBy = req.params.owner; + } + if (req.params.owner !== req.session.user) { + //$('.navBar').remove(); + $('.servicesHeader').html(req.params.owner); + } + if (params.registered) { + $('.message').html(req.session.email + ' is now registered.
      '); + } + if (hooks.length > 0) { + // sort hooks alphabetically by name + hooks = hooks.sort(function(a,b){ + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + + var metricKeys = []; + var multiGet = []; + hooks.forEach(function (h) { + multiGet.push(['hgetall', "/metric/" + h.owner + "/" + h.name + "/report"]); + }); + + // perform redis multi command to get all metrics for all services at once ( should be better performance than separate requests ) + metric.client.multi(multiGet).exec(function(err, reports){ + if (err) { + return res.end(err.message); + } + renderTable(reports); + }) + + function renderTable (report) { + var keys = {} + // only show search bar if more than 3 services + if (hooks.length <= 3) { + $('.searchServices').remove() + } + hooks.forEach(function(h, i){ + var tpl = $('.hookTemplate').clone(); + var hookLink = "/" + h.owner + "/" + h.name; + var lastRun = keys[hookLink + '/lastRun']; + var lastComplete = keys[hookLink + '/lastComplete']; + var hits = keys[hookLink + '/hits']; + var running = keys[hookLink + '/running']; + var priv = ""; + var hookUrl = config.app.url + '/' + h.owner + '/' + h.name; + $('.hookName', tpl).html(h.name); + $('.hookName', tpl).attr("href", hookUrl + '/admin'); + $('.hookAdmin', tpl).attr("href", hookUrl + '/admin'); + $('.runLink', tpl).attr('href', hookUrl); + $('.hookLogs', tpl).attr('href', hookUrl + '/logs'); + $('.hookDelete', tpl).attr('href', hookUrl + '/delete'); + $('.hookDelete', tpl).attr('data-name', hookLink); + $('.hookSource', tpl).attr('href', hookUrl + '/_src'); + //$('.hooks').append('' + priv + '' + h.name + '' + '
      ' +  JSON.stringify(report[i], true, 2) + '
      ' +''); + if (report[i] !== null) { + // $('.hookReport', tpl).html(JSON.stringify(report[i])); + // var diff = Number(report[i].lastEnd) - Number(report[i].lastStart); + var lastStart = new Date(Number(report[i].lastStart)).toString(); + + try { + lastStart = df(lastStart , "mm/dd/yyyy HH:MM:ss Z"); + } catch (err) { + lastStart = 'n/a'; + } + + var lastTime = Number(report[i].lastTime); + + try { + // $('.hookLastTime', tpl).html(ms(diff).toString()); + $('.hookLastTime', tpl).html(ms(lastTime).toString()); + } catch (err) { + // do nothing. this error means that ms library had invalid parse value ( perhaps null or undefined ) + } + if (lastTime > 0 && h.customTimeout <= lastTime) { + $('.hookLastTime', tpl).addClass('error'); + if (h.language === "javascript") { + $('.timeoutMessage', tpl).html('Please ensure that res.end(); or hook.res.end(); are called in the script.'); + } + } else { + $('.hookTimeoutError', tpl).remove(); + } + + $('.hookLastStart', tpl).html(lastStart); + $('.hookStatusCode', tpl).html(report[i].statusCode); + $('.hookTotalHits', tpl).html(report[i].totalHits); + $('.hookRunning', tpl).html(report[i].running); + + var now = new Date(); + var monthlyHitsKey = 'monthlyHits - ' + (now.getMonth() + 1) + '/' + now.getFullYear(); + + $('.hookMonthlyHits', tpl).html(report[i][monthlyHitsKey]); // TODO: date format uplook + if (report[i].statusCode === "500") { + // 500 status code means the last time the hook completed it ended with an error + // if this is the case, we should show user a warning and give information of viewing the logs + // $('.hookStatus', tpl).remove(); + // $('.hookStatus', tpl).addClass('error'); + } else { + $('.hookError', tpl).remove(); + } + } else { + $('.hookReportHolder', tpl).remove(); + $('.hookTimeoutError', tpl).remove(); + $('.hookError', tpl).remove(); + } + $(tpl).removeClass('hookTemplate'); + if (h.isPrivate) { + if (req.params.owner === req.session.user || req.url === "/services") { + // TODO: update with clone and html template! + $('.hooks').append('' + tpl + ''); + } + } else { + $('.privateHook', tpl).remove(); + $('.hooks').append('' + tpl + ''); + } + }); + $('.noHooks').remove(); + $('.hookTemplate').remove(); + callback(null, $.html()); + } + } else { + //$('.navBar').remove(); + $('.hooks').remove(); + $('.searchServices').remove() + callback(null, $.html()); + } + }); + } +}; \ No newline at end of file diff --git a/view/session.js b/view/session.js new file mode 100644 index 00000000..a0d34326 --- /dev/null +++ b/view/session.js @@ -0,0 +1,9 @@ +module.exports = function (opts, cb) { + var res = opts.res, req = opts.req; + var o = { + user: req.user, + session: req.session + } + //res.json(o); + res.json(req.session); +} \ No newline at end of file diff --git a/view/signup.html b/view/signup.html new file mode 100644 index 00000000..1d90ab5d --- /dev/null +++ b/view/signup.html @@ -0,0 +1 @@ +

      Hello!

      \ No newline at end of file diff --git a/view/signup.js b/view/signup.js new file mode 100644 index 00000000..d7c5c2cb --- /dev/null +++ b/view/signup.js @@ -0,0 +1,97 @@ +var hook = require("../lib/resources/hook"); +var user = require("../lib/resources/user"); +var config = require('../config'); +var themes = require('../lib/resources/themes'); + +var psr = require('parse-service-request'); + +var slug = require('slug'); +slug.defaults.modes['rfc3986'] = { + replacement: '-', // replace spaces with replacement + symbols: true, // replace unicode symbols or not + remove: null, // (optional) regex to remove characters + charmap: slug.charmap, // replace special characters + multicharmap: slug.multicharmap // replace multi-characters +}; +slug.charmap['@'] = "-"; +slug.charmap['.'] = "-"; + +module['exports'] = function signup (opts, cb) { + var req = opts.request, + res = opts.response; + + var $ = this.$, + self = this; + + psr(req, res, function (req, res) { + var params = req.resource.params; + var email = params.email; + + // if email is invalid + if(typeof email === "undefined" || email.length < 3) { + var r = { + result: 'invalid' + }; + return res.json(r); + } + + // TODO: validate email? + + // if an valid email has been provided + email = email.toLowerCase(); + + // attempt to find if email conflicts with existing user + return user.find({ email: email }, function (err, results) { + if (err) { + return res.end(err.stack); + } + // if user exists, abort + if (results.length > 0) { + var r = { + result: "exists" + }; + return res.json(r); + } + // TODO: remove legacy code for auto username + var data = {}; + data.type = "email" + data.email = email; + + // create the new hook.io user with email address + return user.create(data, function (err, u) { + if (err) { + err.message = JSON.parse(err.message) + var r = { + result: "error", + error: true, + message: err.message.errors[0].message + }; + res.status(500); + return res.json(r); + } + user.login({ req: req, res: res, user: u }, function (err) { + var r = { + result: "valid", + }; + // r.res = "redirect"; + r.redirect = req.session.redirectTo || "/services"; + // if json response, send back json message + if (req.jsonResponse) { + return res.json(r); + } else { + // if not json response, assume browser and redirect to logged in `/services` page + return res.redirect(r.redirect); + } + }); + }); + }); + }); +}; + +module['exports'].schema = { + "email": { + "type": "string", + "format": "email", + "required": true + } +}; \ No newline at end of file diff --git a/view/ssl.html b/view/ssl.html new file mode 100644 index 00000000..75c7e9fb --- /dev/null +++ b/view/ssl.html @@ -0,0 +1,9 @@ +
      +

      SSL / HTTPS

      +
      +

      Currently, HTTPS is supported on {{appName}} through a single shared SSL certificate.

      +

      All URLs and services on the site are available over HTTPS

      +

      If you require a custom SSL certificate for your microservices, please contact hookmaster@{{appName}}

      +

      If you have questions about encryption or security please email us or file a support request issue. +

      +
      \ No newline at end of file diff --git a/view/ssl.js b/view/ssl.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/ssl.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/stats.html b/view/stats.html new file mode 100644 index 00000000..c89c6569 --- /dev/null +++ b/view/stats.html @@ -0,0 +1,32 @@ + + +
      +

      Site Statistics

      +
      + 1 hooks executed
      + 1 active hooks
      + 1 active users

      +
      +
      + \ No newline at end of file diff --git a/view/stats.js b/view/stats.js new file mode 100644 index 00000000..b347c122 --- /dev/null +++ b/view/stats.js @@ -0,0 +1,45 @@ +var user = require('../lib/resources/user'), + metric = require('../lib/resources/metric'), + hook = require('../lib/resources/hook'), + request = require('request'); + +function numberWithCommas(x) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); +} +module['exports'] = function view (opts, callback) { + var $ = this.$; + metric.get('/user/count', function(err, userCount){ + if (userCount === null) { + userCount = 0; + } + $('.activeUsers').html(numberWithCommas(userCount.toString())); + metric.get('/hook/count', function(err, hookCount){ + metric.zscore('totalHits', 'tallies', function (err, m) { + //metric.get('/hook/totalHits', function(err, m){ + if (hookCount === null) { + hookCount = 0; + } + if (m === null) { + m = 0; + } + $('.activeServices').html(numberWithCommas(hookCount.toString())); + var count = m.toString(); + $('.totalRun').html(numberWithCommas(count)); + request('https://api.github.com/repos/bigcompany/hook.io', { + headers: { + "User-Agent": "hook.io stats" + }, + json: true + }, function (err, res, output) { + if (err) { + console.log(err.message); + } + callback(null, $.html()); + }); + }); + }); + }); +}; + +// cache this page to only reload every 60 seconds +// module['exports'].cache = 60000; // TODO: make longer \ No newline at end of file diff --git a/view/stripe/webhook.js b/view/stripe/webhook.js new file mode 100644 index 00000000..826487cc --- /dev/null +++ b/view/stripe/webhook.js @@ -0,0 +1,40 @@ +var stripe = require('stripe'); +var bodyParser = require('body-parser'); +var config = require('../../config'); +var billing = require('../../lib/resources/billing'); + +module.exports = function (opts, cb) { + var $ = this.$, + res = opts.res, + req = opts.req; + + const sig = req.headers['stripe-signature']; + + bodyParser.raw({type: 'application/json'})(req, res, function () { + let event; + let endpointSecret = config.stripe.endpointSecret; + try { + event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret); + } + catch (err) { + res.status(400).send(`Webhook Error: ${err.message}`); + return; + } + console.log('received valid webhook from stripe', event); + console.log(event); + console.log(req.resource.params); + if (event.type === 'invoice.payment_succeeded') { + // attempt to find user internally based on customer id + console.log('incoming customer id', event.data.object.customer); + billing.find({ stripeID: event.data.object.customer }, function (err, results) { + console.log(err, results); + res.end('ended request'); + }); + // successful billing event indicates subscription should be extended + } else { + res.status(500); + res.end('unknown event.type ' + event.type); + } + }); + +}; \ No newline at end of file diff --git a/view/support.html b/view/support.html new file mode 100644 index 00000000..74b6bf7a --- /dev/null +++ b/view/support.html @@ -0,0 +1,79 @@ + + +
      +

      Support

      +
      +
      +
      +
      +
      + Contact Information +
      +
        +
      • +
        + +
        + +
        +
        + + +
      • + +
      • +
        + +
        + +
        +
        +
      • +
      • +
        + +
        + +
        +
        +
      • +
      +
      +
      +

      * Required Fields

      +
      + +
      +
      +
      + +
      + We have received your message. Thank you!
      + Someone will get back to you soon. +
      +
      +
      \ No newline at end of file diff --git a/view/support.js b/view/support.js new file mode 100644 index 00000000..3e1aa97c --- /dev/null +++ b/view/support.js @@ -0,0 +1,52 @@ +var email = require('resource-email'); +var config = require('../config'); +var psr = require('parse-service-request'); + +module['exports'] = function view (opts, callback) { + var req = opts.req, + res = opts.res, + $ = this.$; + + // Only allow logged in users to send contact emails + // It could be better to have the contact form not require a login, but this is necessary to help prevent email spam from bots which auto submit the contact forms + if (!req.isAuthenticated()) { + req.session.redirectTo = req.url; + return res.redirect(302, '/login?restricted=true'); + } + + psr(req, res, function(){ + var params = req.resource.params; + + if ((typeof params.subject === "string" && typeof params.comment === "string") && (params.subject.length > 0 || params.comment.length > 0)) { + var _config = { + //provider: 'sendgrid', + provider: config.email.provider, + api_user: config.email.api_user, + api_key: config.email.api_key, + to: "marak@hook.io", + from: params.email, + subject: 'hook.io - contact - ' + params.accountName + ' ' + params.subject, + html: params.comment + }; + email.send(_config, function (err, result) { + if (err) { + return res.end(err.message); + } + $('#contactForm').remove(); + return callback(null, $.html()); + }); + } else { + + $('#email').attr('value', req.session.email); + $('#accountName').attr('value', req.session.user); + + if (typeof params.t === "string") { + $('#Subject').attr('value', params.t); + } + $('.sent').remove(); + return callback(null, $.html()); + } + + }); + +}; \ No newline at end of file diff --git a/view/themes.html b/view/themes.html index 339b2e4e..f0750bc4 100644 --- a/view/themes.html +++ b/view/themes.html @@ -1,71 +1,73 @@ -
      +

      Themes

      -

      Hooks are fully theme-able through a simple and extensible theming engine.

      +
      +

      Services are fully theme-able through a simple and extensible theming engine.

      -

      Creating a Theme is a great way to customize the appearance of a Hook.

      -

      Theming is ideal for user facing Hooks which are intended to be visited in the browser.

      +

      Creating a Theme is a great way to customize the appearance of a Service.

      +

      Theming is ideal for user facing Services which are intended to be visited in the browser.

      -

      Creating a Hook Theme

      +

      Creating a Service Theme

      -

      A Hook Theme consists of two parts:

      +

      A Service Theme consists of two parts:

      1. A single HTML file ( the Theme View )

      2. An optional single JavaScript file ( the Theme Presenter )

      -

      Theme View




      A HTML file which contains the layout of the Hook. This file can also contain links to other assets such as images, CSS, and client-side JavaScript.
      -

      Theme Presenter




      An optional JavaScript module which exports *one* method. When the Presenter is run on hook.io, it is given special properties to help perform server-side rendering of the Theme HTML.
      +

      Theme View




      A HTML file which contains the layout of the Service. This file can also contain links to other assets such as images, CSS, and client-side JavaScript.
      +

      Theme Presenter




      An optional JavaScript module which exports *one* method. When the Presenter is run on {{appName}}, it is given special properties to help perform server-side rendering of the Theme HTML.

      -

      Important: If no Theme is set, the Hook will default to the debug Theme View and Theme Presenter.

      Adding a Theme View

      Adding HTML to a Theme is easy!

      -

      To add a Theme View, simply set the Hook.theme property to url of the Theme HTML.

      - -

      Here is an example of a Hook which uses the "simple" theme:

      - +

      To add a Theme View, simply enabled the Theme option in the /admin page for the Hook.

      + + -

      Once a Theme HTML file is specified, the Hook will use that file for rendering output.

      -

      Important: For performance reasons, changes made to the Hook.theme and Hook.presenter properties will not be reflected in the User Interface until the Hook has been run at least once. Updates to the actual contents of the Theme or Presenter will however; appear immediately.

      +

      Once a Theme HTML file is specified, the Service will use that file for rendering output.

      Mustache Style Replacements

      -

      By default, a {{key.value}} style replacement will be performed on the Theme HTML using the completed Hook object.

      +

      By default, a {{key.value}} style replacement will be performed on the Theme HTML using the completed Service object.

      For example:

      - -

      When the Hook runs, {{hook.output}} will be replaced with the outputted result of the Hook.

      +
      Loading...
      +

      When the Service runs, {{Service.output}} will be replaced with the outputted result of the Service.

      To bypass these replacements, simply do not place any matching {{key.value}} tags in the Theme HTML.

      -

      For most themes, this simple replacement style should be sufficient. If additional server-side rendering is required, a Hook Theme Presenter can be added.

      -

      Hook Theme Presenter

      - - -

      The Hook Theme Presenter is an optional JavaScript method which is performed server-side on the rendered Theme View before it's piped to the response. When the Presenter method is run on hook.io, it becomes a View class. This will give the Presenter special properties which allow for server-side rendering of the Theme View with JavaScript.

      - +

      For most themes, this simple replacement style should be sufficient. If additional server-side rendering is required, a Service Theme Presenter can be added.

      +

      Adding a Theme Presenter

      + +

      The Service Theme Presenter is an optional JavaScript method which is performed server-side on the rendered Theme View before it's piped to the response. When the Presenter method is run on {{appName}}, it becomes a View class. This will give the Presenter special properties which allow for server-side rendering of the Theme View with JavaScript.

      +

      For example:

      - - +
      Loading...

      For full documentation on using the $ object see: cheeriojs/cheerio

      For seeing how the View class is implemented see: bigcompany/view

      Important Tips

      + +

      Separation of Concerns

      -

      Performing a lot of logic in the Presenter is not recommended. While it is possible to do a lot inside a Theme Presenter, it's always better to create new Hooks and pipe them together. The Presenter should only focus on taking in data and outputting HTML.

      - +

      Performing a lot of logic in the Presenter is not recommended. While it is possible to do a lot inside a Theme Presenter, it's always better to create new Services and pipe them together. The Presenter should only focus on taking in data and outputting HTML.

      +

      Custom Templating Engines

      Want to use a custom templating engine? Not a problem! There are two methods in which custom templating engines can be implemented.

      -

      Method 1 ( Not Recommended ) - Require the custom templating engine directly in the Hook Presenter and modify the contents of $.html() directly.

      -

      Method 2 ( Recommended ) - Create a new Hook that is a generic processor for the custom templating engine. Once this Hook is created, output from other Hook's can be piped to it ( which will process the input using the custom templating engine ).

      - +

      Method 1 ( Not Recommended ) - Require the custom templating engine directly in the Service Presenter and modify the contents of $.html() directly.

      +

      Method 2 ( Recommended ) - Create a new Service that is a generic processor for the custom templating engine. Once this Service is created, output from other Service's can be piped to it ( which will process the input using the custom templating engine ).

      +

      Client-side JavaScript

      Any type of client-side JavaScript library can be used in the Theme HTML through the use of regular <script> tags.

      Be Creative, Have Fun!

      -

      Themes are a great way to customize a Hook's appearance. They can also easily be shared for use with other Hooks. If you make a theme, open a Pull Request! Your theme could very easily appear as a default theme option in the future.

      +

      Themes are a great way to customize a Service's appearance. They can also easily be shared for use with other Services. If you make a theme, open a Pull Request! Your theme could very easily appear as a default theme option in the future.



      +
      \ No newline at end of file diff --git a/view/themes.js b/view/themes.js index c3059f2f..4c9d4d92 100644 --- a/view/themes.js +++ b/view/themes.js @@ -1,3 +1,7 @@ module['exports'] = function view (opts, callback) { - callback(null, this.$.html()); + var $ = this.$, req = opts.req; + var appName = req.hostname; + var out = $.html(); + out = out.replace(/\{\{appName\}\}/g, appName); + callback(null, out); }; \ No newline at end of file diff --git a/view/top.html b/view/top.html new file mode 100644 index 00000000..17208bb3 --- /dev/null +++ b/view/top.html @@ -0,0 +1,7 @@ +
      +
      +
      +
      +
      +
      +
      \ No newline at end of file diff --git a/view/top.js b/view/top.js new file mode 100644 index 00000000..43527217 --- /dev/null +++ b/view/top.js @@ -0,0 +1,152 @@ +var forms = require('mschema-forms'); +var config = require('../config'); +var metric = require('../lib/resources/metric'); +var dateFormat = require('dateformat'); +let fnst = require('date-fns-timezone') +var util = require('util'); +var generate = util.promisify(forms.generate); +var recent = util.promisify(metric.recent); +var top = util.promisify(metric.top); + +// takes in array of data and formats it into smaller object to render in grid +function formatResults (data) { + var sorted = []; + while (data.length > 0) { + var user = data.shift(); + var hits = data.shift(); + sorted.push({ user: user, hits: hits, reset: user }); + } + return sorted; +} + +// TODO: add top running services / top hits report using sets and http://redis.io/commands/zrevrangebyscore methods +module['exports'] = async function topPresenter (opts, callback) { + + var req = opts.req, res = opts.res, $ = this.$; + var appName = req.hostname; + if (req.session.user !== "marak") { + return res.end('unauthorized'); + } + + // get all metrics data for rendering view + let recentMembers = await recent('recent'); + let recentErrors = await recent('recent:500'); + let recentAlerts = await recent('alerts'); + let mostAccountHits = await top('hits'); + let runningServices = await top('running'); + + var formSchema = {}; + formSchema.user = { + "type": "string", + "default": "bob" + }; + + formSchema.hits = { + "type": "string", + "default": "42" + }; + + formSchema.user.formatter = function (val) { + return '' + val + ''; + } + + formSchema.hits.formatter = function (val) { + let timezone = req.session.timezone || 'America/New_York'; + let zonedDate = fnst.formatToTimeZone(new Date(Number(val)), 'MMMM DD, YYYY hh:mm:ss A z', { timeZone: timezone }); + return zonedDate; + } + + recentMembers = formatResults(recentMembers); + recentErrors = formatResults(recentErrors); + recentAlerts = formatResults(recentAlerts); + runningServices = formatResults(runningServices); + mostAccountHits = formatResults(mostAccountHits); + + let recentlyRunHtml = await generate({ + type: "grid", + name: 'hits-forms', + form: { + legend: 'Recently Run', + submit: "Submit", + action: "" + }, + data: recentMembers, + schema: formSchema + }); + + formSchema.user.formatter = function (val) { + return '' + val + ''; + } + + let recentErrorsHtml = await generate({ + type: "grid", + name: 'recent-error-forms', + form: { + legend: 'Recent Errors', + submit: "Submit", + action: "" + }, + data: recentErrors, + schema: formSchema + }); + + formSchema.user.formatter = function (val) { + return '' + val + ''; + } + + let recentAlertsHtml = await generate({ + type: "grid", + name: 'recent-alerts-forms', + form: { + legend: 'Recent Rate Limit Alerts', + submit: "Submit", + action: "" + }, + data: recentAlerts, + schema: formSchema + }); + + formSchema.hits.formatter = function (val) { + return val; + } + + let mostAccountHitsHtml = await generate({ + type: "grid", + form: { + legend: 'Most Account Hits', + submit: "Submit", + action: "" + }, + data: mostAccountHits, + schema: formSchema + }); + + + formSchema.reset = { + "type": "string" + }; + + formSchema.reset.formatter = function (val, obj) { + return 'reset link'; + } + + let runningServicesHtml = await generate({ + type: "grid", + form: { + legend: 'Currently Running', + submit: "Submit", + action: "" + }, + data: runningServices, + schema: formSchema + }); + + $('.running').html(runningServicesHtml); + $('.hits').html(mostAccountHitsHtml); + $('.recentErrors').html(recentErrorsHtml); + $('.recent').html(recentlyRunHtml); + $('.recentAlerts').html(recentAlertsHtml); + + callback(null, $.html()); + +}; \ No newline at end of file diff --git a/view/tos.html b/view/tos.html new file mode 100644 index 00000000..7a765678 --- /dev/null +++ b/view/tos.html @@ -0,0 +1,429 @@ +
      +

      Terms of Service Agreement

      + +
      +
      +
      +

      Last Updated May 7th, 2016

      +
      +
      + +
      + +
      +
      +

      {{appName}}., including {{appName}}.'s subsidiaries, affiliates, divisions, contractors and all data sources and suppliers, (collectively “{{appName}}”, “we”, “us” or “our”) welcomes you to {{appUrl}} (the “Website”). These terms and conditions of service (collectively, with {{appName}}’s Privacy Policy, located at {{appUrl}}/legal/privacy and DMCA Copyright Policy located at {{appUrl}}/legal/copyright, the “Terms of Service” or “Agreement”) govern your use of the Website and the services, features, content or applications operated by {{appName}} (together with the Website, the “Services”), and provided to the Subscriber (the “Subscriber”, “user”, “sub-user”, “you” or “your”).

      +

      Please read these Terms of Service carefully before using the Services. These Terms of Service apply to all users of the Services. If you are using the Services on behalf of an entity, organization, or company, you represent and warrant that you have the authority to bind such organization to these Terms of Service and you agree to be bound by these Terms of Service on behalf of such organization. Agreeing to use the Services by clicking “Sign Up” constitutes your acceptance and agreement to be bound by these Terms of Service, and all other operating rules, policies and procedures that may be published from time to time on the Website by us, each of which is incorporated by reference and each of which may be modified from time to time without notice to you. You acknowledge receipt of our Privacy Policy. If you ordered the Services on the Website, use the Website, or otherwise engage in any electronic transaction with respect to the Services, then you agree to receive any updates to our Privacy Policy by accessing the Website. By using our Website or purchasing our products or services, you agree that we may use and share your personal information in accordance with the terms of our Privacy Policy.

      +

      These Terms of Service provide that all disputes between you and {{appName}} will be resolved by BINDING ARBITRATION. YOU AGREE TO GIVE UP YOUR RIGHT TO GO TO COURT TO ASSERT OR DEFEND YOUR RIGHTS UNDER THIS CONTRACT (except for matters that may be taken to small claims court). Your rights will be determined by a NEUTRAL ARBITRATOR and NOT A JUDGE OR JURY and your claims cannot be brought as a class action. Please review Section 15 below for the details regarding your agreement to arbitrate any disputes with {{appName}}. NOTHING IN THESE TERMS OF USE SHALL AFFECT ANY NON-WAIVABLE STATUTORY RIGHTS THAT APPLY TO YOU. If any provision or provisions of these Terms of Use shall be held to be invalid, illegal, or unenforceable, the validity, legality and enforceability of the remaining provisions shall remain in full force and effect.

      +

      {{appName}} reserves the right, at any time and from time to time, to amend or to modify these Terms of Service without prior notice to you, provided that if any such alterations constitute a material change to these Terms of Service, {{appName}} will notify you by posting an announcement on the Website. Amendments and modifications shall take effect immediately when posted on the Website. By continuing to access or use the Services after any such amendments or modifications, you agree to be bound by such amended or modified Terms of Service. For this reason, we encourage you to review the Terms of Service whenever you use the Services. If you do not agree to any change to these Terms of Services, then you must immediately stop using the Services.

      +

      Beside the official version of these Terms of Service, we have put a simplified commentary entitled “In other words” to assist you in your comprehension of these Terms. However, it is the “Terms of Service” which govern your access and use of the Services not the commentary set out in “In other words” or included in other similar explanations which are provided for informational purposes only and do NOT include all of the information in the Terms of Service. You should always read the full text of the Terms of Service and not just the “In other words” commentary or other explanations.

      +

      SOME JURISDICTIONS HAVE CONSUMER PROTECTION AND OTHER LEGISLATION WHICH MAY APPLY TO THE SERVICES AND WHICH DO NOT ALLOW CERTAIN PROVISIONS SUCH AS LIMITATIONS OF LIABILITY AND EXCLUSION OF CERTAIN WARRANTIES, AMONG OTHERS. TO THE EXTENT THAT A LIMITATION, EXCLUSION, RESTRICTION OR OTHER PROVISION SET OUT BELOW IS SPECIFICALLY PROHIBITED BY APPLICABLE LAW, SUCH LIMITATION, EXCLUSION, RESTRICTION OR PROVISION MAY NOT APPLY TO YOU.

      +
      +
      + +
      +
      +

      In other words,

      +

      Welcome to our Terms of Service agreement! This document exists to protect both you and us. By using our services, you agree to our Terms of Service agreement.

      +

      If you’re like us, you don’t always have time to read through the entire document. That’s why we’ve summarized the key points for you, and will update you when any major changes are made. You should also read our DMCA Copyright Policy and the Privacy Policy pages.

      +
      +
      + +
      + +
      +
      +

      1. Eligibility & Registration

      +

      1.1 The Services are not targeted towards, nor intended for use by, anyone under the age of 13. By using the Services, you represent and warrant that you are 13 years of age or older. If you are under the age of 13, you may not, under any circumstances or for any reason, use the Services. We may, in our sole discretion, refuse to offer the Services to any person or entity and change its eligibility criteria at any time. You are solely responsible for ensuring that these Terms of Service are in compliance with all laws, rules and regulations applicable to you and the right to access the Services is revoked where these Terms of Service or use of the Services is prohibited or to the extent offering, sale or provision of the Services conflicts with any applicable law, rule or regulation. Further, the Services are offered only for your use, and not for the use or benefit of any third party.

      +

      1.2 To sign up for the Services, you must register for an account on the Services (an “Account”). You must provide accurate and complete information and keep your Account information updated. You shall not: (i) select or use as a username a name of another person with the intent to impersonate that person; (ii) use as a username a name subject to any rights of a person other than you without appropriate authorization; or (iii) use, as a username, a name that is otherwise offensive, vulgar or obscene. You are solely responsible for the activity that occurs on your Account, regardless of whether the activities are undertaken by you, your employees or a third party (including your contractors or agents), and for keeping your Account password secure. You may never use another person’s user account or registration information for the Services without permission. You must notify us immediately of any change in your eligibility to use the Services (including any changes to or revocation of any licenses from state, provincial, territorial or other authorities), breach of security or unauthorized use of your Account. You should never publish, distribute or post login information for your Account. You shall have the ability to delete your Account, either directly or through a request made to one of our employees or affiliates. You agree to provide accurate information in your registration and not to share your password with third parties. You agree not to impersonate another person or to select or use a user name or password of another person. You agree to notify {{appName}} promptly of any unauthorized use of your account and of any loss, theft or disclosure of your password. Failure to comply with these requirements shall constitute a breach of these Terms of Service and shall constitute grounds for immediate termination of your account and your right to use the Website. {{appName}} WILL NOT BE LIABLE FOR ANY LOSS OR DAMAGE AS A RESULT OF YOUR FAILURE TO PROVIDE US WITH ACCURATE INFORMATION OR TO KEEP YOUR ACCOUNT SECURE.

      +
      +
      + +
      +
      +

      In other words,

      +

      You must be at least 13 years old to use our service (sorry, young devs!). By registering with your information, you are guaranteeing the services offered are solely for your use and not a third party – and that all of the information is accurate. Make sure to keep all of your account information updated!

      +
      +
      + +
      + +
      +
      +

      2. Content

      +

      2.1 For purposes of these Terms of Service, the term “Content” includes, without limitation, information, data, text, written posts and comments, software, scripts, graphics, and interactive features generated, provided, or otherwise made accessible on or through the Services. For the purposes of this Agreement, “Content” also includes all User Content (as defined below).

      +

      2.2 All Content added, created, uploaded, submitted, distributed, or posted to the Services by users (collectively “User Content”), whether publicly posted or privately transmitted, is the sole responsibility of the person who originated such User Content. You represent that all User Content provided by you is accurate, complete, up-to-date, and in compliance with all applicable laws, rules and regulations. You acknowledge that all Content, including User Content, accessed by you using the Services is at your own risk and you will be solely responsible for any damage or loss to you or any other party resulting therefrom. We do not guarantee that any Content you access on or through the Services is or will continue to be accurate.

      +

      2.3 The Services may contain Content specifically provided by us, our partners or our users and such Content is protected by copyrights, trademarks, service marks, patents, trade secrets or other proprietary rights and laws. You shall abide by and maintain all copyright notices, information, and restrictions contained in any Content accessed through the Services.

      +

      2.4 Subject to these Terms of Service, we grant each user of the Services a worldwide, non-exclusive, revocable, non-sublicensable and non-transferable license to use (i.e., to download and display locally) Content solely for purposes of using the Services. Use, reproduction, modification, distribution or storage of any Content for other than purposes of using the Services is expressly prohibited without prior written permission from us. You shall not sell, license, rent, or otherwise use or exploit any Content for commercial use or in any way that violates any third party right.

      +

      2.5 By submitting any User Content to the Website, excluding privately transmitted User Content, you hereby do and shall grant us a worldwide, non-exclusive, perpetual, irrevocable, royalty-free, fully paid, sublicensable and transferable license to use, aggregate, reproduce, distribute, prepare derivative works of, display, perform, and otherwise fully exploit such User Content in connection with the Website, the Services and our (and our successors’ and assigns’) businesses, including without limitation for promoting and redistributing part or all of the Website or the Services (and derivative works thereof) in any media formats and through any media channels (including, without limitation, third party websites and feeds), and including after your termination of your Account or the Services. You also hereby do and shall grant each user of the Website and/or the Services a non-exclusive, perpetual license to access any of your User Content that is available to such user on the Website, and to use, reproduce, distribute, prepare derivative works of, display and perform such User Content, including after your termination of your Account or the Services. By submitting any User Content to the Services other than on the Website, you hereby do and shall grant us a worldwide, non-exclusive, perpetual, irrevocable, royalty-free, fully paid, sublicensable and transferable license to use, aggregate, reproduce, distribute, prepare derivative works of, display, and perform such User Content solely for the purpose of providing the Services. For clarity, the foregoing licenses granted to us and our users does not affect your other ownership or license rights in your User Content, including the right to grant additional licenses to your User Content, unless otherwise agreed in writing. You represent and warrant that you have all rights to grant such licenses to us without infringement or violation of any third party rights, including without limitation, any privacy rights, publicity rights, copyrights, trademarks, contract rights, or any other intellectual property or proprietary rights.

      +

      2.6 Some Content will be marked on the Service as “Creative Commons Content”. Creative Commons Content will be identified with a Creative Commons icon. We hereby grant each user of the Services a license to Creative Commons Content under the Creative Commons CC BY-NC-SA 4.0 US license, available at the the "Creative Commons License. You agree to abide by the terms of the Creative Commons License when using Creative Commons Content.

      +
      +
      + +
      +
      +

      In other words,

      +

      We can’t be held responsible for the content you create or content you access elsewhere while using {{appName}}.

      + +

      Additionally, feel free to share and adapt content we have that's marked with the Creative Commons icon, as long as you let others share it the same way. If you write a tutorial for DO, you agree to this license too. Here's a super easy read as to what a Creative Commons license is all about: http://creativecommons.org/licenses/by-nc-sa/4.0/

      +
      +
      + +
      + +
      +
      +

      3. Rules of Conduct

      + +

      3.1 As a condition of use, you promise not to use the Services for any purpose that is prohibited by these Terms of Service. You are responsible for all of your activity in connection with the Services and the activity of any sub-user that uses your access code or Account.

      +

      3.2 You agree that you will not transmit, distribute, post, store, link, or otherwise traffic in Content, information, software, or materials on or through the Service that (i) is unlawful, threatening, abusive, harassing, defamatory, libelous, deceptive, fraudulent, invasive of another's privacy, tortious, offensive, profane, contains or depicts pornography that is unlawful, or is otherwise inappropriate as determined by us in our sole discretion, (ii) you know is false, misleading, untruthful or inaccurate, (iii) constitutes unauthorized or unsolicited advertising, (iv) impersonates any person or entity, including any of our employees or representatives, or (v) includes anyone’s identification documents or sensitive financial information. {{appName}} may permit, in its sole discretion, adult websites that abide by state and federal law and regulation.

      +

      3.3 You shall not: (i) take any action that imposes or may impose (as determined by us in our sole discretion) an unreasonable or disproportionately large load on our (or our third party providers’) infrastructure; (ii) interfere or attempt to interfere with the proper working of the Services or any activities conducted on the Services; (iii) bypass, circumvent or attempt to bypass or circumvent any measures we may use to prevent or restrict access to the Services (or other accounts, computer systems or networks connected to the Services); (iv) run any form of auto-responder or “spam” on the Services; (v) use manual or automated software, devices, or other processes to “crawl” or “spider” any page of the Website; (vi) harvest or scrape any Content from the Services; (vii) use the Services for high risk activities including but not limited to the operation of nuclear facilities, air traffic control, life support systems, or any other use where the failure of service could lead to death, personal injury, or environmental damage; or (viii) otherwise take any action in violation of our guidelines and policies.

      +

      3.4 You shall not (directly or indirectly): (i) decipher, decompile, disassemble, reverse engineer or otherwise attempt to derive any source code or underlying ideas or algorithms of any part of the Services (including without limitation any application), except to the limited extent applicable laws specifically prohibit such restriction, (ii) modify, translate, or otherwise create derivative works of any part of the Services, or (iii) copy, rent, lease, distribute, or otherwise transfer any of the rights that you receive hereunder. You shall abide by all applicable local, state, national and international laws and regulations.

      +

      3.5 We also reserve the right to access, read, preserve, and disclose any information as we reasonably believe is necessary to (i) satisfy any applicable law, regulation, legal process or governmental request, (ii) enforce these Terms of Service, including investigation of potential violations hereof, (iii) detect, prevent, or otherwise address fraud, security or technical issues, (iv) respond to user support requests, or (v) protect the rights, property or safety of us, our users and the public.

      +

      3.6 Subscribers are restricted from registering multiple Accounts with the same billing details without first notifying {{appName}} of that intent. Otherwise, {{appName}} shall have the right to automatically flag such Accounts as fraudulent or abusive, and {{appName}} may, without notification to the Subscriber of such Account, suspend the service of such Account or any other Account used by such Subscriber. The use of referral codes by multiple Accounts having the same billing profile is not allowed. {{appName}} also reserves the right to terminate a Subscriber's Account if it is targeted by malicious activity from other parties.

      +

      3.7 As a reward for being early adopters of the Services, Subscribers with grandfathered Accounts shall receive free bandwidth for the duration that such Account is operative and conducts its operations in compliance with these Terms of Service (“Grandfathered Accounts”). The free bandwidth may only be used directly by the Subscriber of such Grandfathered Account. Notwithstanding the foregoing, Subscribers of Grandfathered Accounts must NOT: (i) run Torrents for download or Seed Servers, TOR, or services that include content of an adult or pornographic nature; (ii) resell services through their Account to provide free bandwidth to other individuals; or (iii) transfer the Account ownership to another individual or entity, or otherwise circumvent the intended fair usage of free bandwidth by distributing it freely to others. Failure of Subscribers of Grandfathered Accounts to follow these terms will result in the revocation of their Accounts’ grandfathered status.

      +

      3.8 You may not use the Services to obtain information about or make decisions about anyone but yourself. You are solely responsible for any reliance by you on the Services or other use you make of the Services. Comments, suggestions or materials sent or transmitted to {{appName}} (collectively "Feedback"), shall be deemed to be non-confidential. Subject to the conditions described in {{appName}}’s Privacy Policy, {{appName}} shall have no obligation of any kind with respect to such Feedback and shall be free to use and distribute the Feedback to others without limitation, including, but not limited to developing and marketing products incorporating such Feedback. {{appName}} reserves the right to publish or use any responses, questions or comments emailed to {{appName}} for promotional or other purposes without any further permission, notice or payment of any kind to the sender. All such submissions will be the property of {{appName}}.

      +

      3.9 The enumeration of violations in this Section 3 of these Terms of Service is not meant to be exclusive, and {{appName}} provides notice hereby that it has and will exercise its authority to take whatever action is necessary to protect the Services, Subscribers, and third parties from acts that would be inimical to the purposes of this Section 3 of these Terms of Service.

      + +

      Lawful Use of the Network

      +

      3.10 In using the Services, Subscribers must comply with, and refrain from violations of, any right of any other person, entity, law, or contractual duty, including without limitation the laws of the United States and the laws of New York, and including without limitation those laws forbidding: (a) distribution of child pornography, (b) forgery, identity theft, misdirection or interference with electronic communications, (c) invasion of privacy, (d) unlawful sending of commercial electronic messages or other marketing or electronic communications, (e) collection of excessive user data from children, or other improper data collection activities, (f) securities violations, wire fraud, money laundering, or terrorist activities, or (f) false advertising, propagating or profiting from frauds and unfair schemes. Subscribers will also comply with the affirmative requirements of law governing use of the Services, including but not limited to: (i) disclosure requirements, including those regarding notification of security breaches, (ii) records maintenance for regulated industries, and (iii) financial institution safeguards.

      + +

      Agreed Use of Allotted Network Resources

      +

      3.11 Subscribers shall not use any method to circumvent the provisions of these Terms of Service, or to obtain Services in excess of those for which they contract with {{appName}}. Subscribers shall use only those IP addresses that are assigned to them by {{appName}}, and shall not use any IP addresses outside of their assigned range. Subscribers shall not use any mechanism to exceed the amount of resources assigned to them through the Services, or to conceal such activities.

      + +

      Injurious Code

      +

      3.12 Subscribers may not use the Services to distribute, receive communications or data gleaned from, or execute any action directed by any type of injurious code, including but not limited to: (i) trojans, (ii) key loggers, (iii) viruses, (iv) malware, (v) botnets, (vi) denial of service attacks, (vii) flood or mail bombs, (viii) logic bombs, or (ix) other actions which {{appName}} reserves the sole right to determine to be malicious in intent.

      + +

      Email Violations

      +

      3.13 Subscribers shall not send bulk email utilizing their resources on the Services unless they maintain a double-authorized list of subscribed members including IP addresses and relevant contact information, along with following guidelines for including removal links with all sent emails according to the such legislation. Subscribers shall comply with all laws regarding the sending of commercial electronic messages or other marketing or electronic communications. Subscribers are forbidden from taking any action that would result in their IP addresses, or any IP address associated with {{appName}} or other Subscribers, being placed on the Spamhaus.org blacklist. {{appName}} reserves the sole and absolute right to determine whether an email violation has occurred.

      + +

      Invasion of Privacy, Defamation, or Harassment

      +

      3.14 Subscribers may not use the Services in a manner that would violate the lawful privacy rights of any person, or to publish or republish defamatory or libelous statements, or to harass or embarrass, which shall be determined in {{appName}}’s sole and absolute discretion.

      + +

      Violation of Copyright, Trademark, Patent or Trade Secret

      +

      3.15 Subscribers may not use the Services in violation of the copyrights, trademarks, patents or trade secrets of third parties, nor shall they utilize the Services to publish such materials in a manner that would expose them to public view in violation of the law. The provisions of the Digital Millennium Copyright Act of 1998 (“DMCA”) (as required under 17 U.S.C. §512) and all other applicable international trademark, copyright, patent or other intellectual property laws will apply to issues presented by allegations of copyright violations by third parties. {{appName}} will, in appropriate circumstances, terminate the accounts of repeat violators. If a third party believes that a Subscriber of {{appName}} is violating its intellectual property rights, it should notify us by email at abuse@{{appDomain}}. A notification should include information reasonably sufficient to permit {{appName}} to locate the allegedly infringing material, such as the IP address or URL of the specific online location where the alleged infringement is occurring. Please see our DMCA Copyright Policy.

      + +

      Export

      +

      3.16 Subscriber shall comply with all applicable export and import control laws and regulations in its use of the Services, and, in particular, Subscriber shall not utilize the Services to export or re-export data or software without all required United States and foreign government licenses. Subscriber assumes full legal responsibility for any access and use of the Services from outside the United States, with full understanding that the same may constitute export of technology and technical data that may implicate export regulations and/or require export license. Should such a license be required, it shall be Subscriber's responsibility to obtain the same, at Subscriber’s sole cost and expense, and in the event of any breach of this duty resulting in legal claims against {{appName}}, Subscriber shall defend, indemnify and hold {{appName}} harmless from all claims and damages arising therefrom.

      + +

      Acts of Sub-Users

      +

      3.17 Subscribers are responsible for the acts of others utilizing their access to the Services, and will be held responsible for violations of the Services by their sub-users or persons who gain access to the Services using the Subscriber's access codes. Any activity that a Subscriber is prohibited from performing by these Terms of Services is equally prohibited to anyone using the access to the Services of the Subscriber.

      + +

      Access Code Protection

      +

      3.18 Subscribers shall utilize proper security protocols, such as setting strong passwords and access control mechanisms, safeguarding access to all logins and passwords, and verifying the trustworthiness of persons who are entrusted with account access information.

      + +

      Notification Regarding these Terms of Service

      +

      3.19 Subscribers shall notify all persons who receive access to the Services of the provisions of these Terms of Service, and shall inform them that the terms of these Terms of Service are binding upon them.

      + +

      Remedial Action

      +

      3.20 Subscribers shall notify {{appName}} if and when they learn of any security breaches regarding the Services, and shall aid in any investigation or legal action that is taken by authorities and/or {{appName}} to cure the security breach.

      + + +
      +
      + +
      +
      +

      In other words,

      +

      Be mindful about how you use our services. If you are breaking laws, being obscene, or abusing our infrastructure, we have the right to stop you from doing these things. You must also notify {{appName}} before registering multiple accounts with the same billing address to prevent being flagged for abuse. You’re also responsible for the acts of others utilizing your access to our services, as we have no way of distinguishing your actions from the actions of your sub-users. Be careful who you give your information to – it’s to protect both us and you!

      +
      +
      + +
      + +
      +
      +

      4. Third Party Services

      +

      4.1 The Services may permit you to link to other websites, services or resources on the Internet, and other websites, services or resources may contain links to the Services. When you access third party resources on the Internet, you do so at your own risk. These other resources are not under our control, and you acknowledge that we are not responsible or liable for the content, functions, accuracy, legality, appropriateness or any other aspect of such websites or resources. The inclusion of any such link does not imply our endorsement or any association between us and their operators. You further acknowledge and agree that we shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods or services available on or through any such website or resource. It is your responsibility to protect your system from such items as viruses, worms, Trojan horses and other items of a destructive nature.

      +
      +
      + +
      +
      +

      In other words,

      +

      It’s up to you to be careful when accessing third-party resources on the Internet — they’re outside of our control and we cannot be blamed for any negative outcomes. If you have a problem with them, please let those services know directly!

      +
      +
      + +
      + +
      +
      +

      5. Payments and Billing

      +

      Acceptable Payment Methods

      + +

      5.1 {{appName}} accepts major credit cards, debit cards, and Paypal payments. Subscribers who choose to pay with PayPal will be strictly limited to a single {{appName}} Account per PayPal account. Virtual credit cards and gift cards typically will not be accepted. Other forms of payment may be arranged by contacting {{appName}} at contact@{{appDomain}}. Please note that any payment terms presented to you in the process of using or signing up for paid Services are deemed part of this Agreement.

      + +

      5.2 We use third-party payment processors (the “Payment Processors”) to bill you through a payment account linked to your Account on the Services (your “Billing Account”) for use of the paid Services. The processing of payments may be subject to the terms, conditions and privacy policies of the Payment Processors in addition to this Agreement. We are not responsible for error by the Payment Processors. By choosing to use paid Services, you agree to pay us, through the Payment Processors, all charges at the prices then in effect for any use of such paid Services in accordance with the applicable payment terms and you authorize us, through the Payment Processors, to charge your chosen payment provider (your “Payment Method”). You agree to make payment using that selected Payment Method. We reserve the right to correct any errors or mistakes that it makes even if it has already requested or received payment

      + +

      Billing and Terms

      +

      5.3 The term of this Agreement shall be monthly, to commence on the date that the Subscriber signs up electronically for the Services by creating an Account with an email address. All invoices are denominated, and Subscriber must pay, in U.S. Dollars. Subscribers are typically billed monthly on or about the first day of each month, with payment due no later than ten (10) days past the invoice date. On rare occasions, a Subscriber may be billed an amount up to the Subscriber's current balance in an effort to verify the authenticity of the Subscriber's account information. This process ensures that Subscribers without a payment history are not subjected to additional scrutiny. Subscribers are entirely responsible for the payment of all taxes. For specific pricing policies, please refer to {{appUrl}}pricing. Monthly fees and renewal fees will be billed at the rate agreed to at purchase. You may cancel the Services at any time by logging into your Control Panel at {{appUrl}}/account/cancel. At cancellation, your Account will be inactivated and you will no longer be able to log into our site and/or have any access to the Services. Except in the case of subscription commitments you have agreed to, which shall be nonrefundable, as permitted by law, if you cancel, you agree that fees for the first month of Services and any start-up costs associated with setting up your Account (“Start-up Costs”) shall be nonrefundable, as permitted by law. With the exception of any subscription commitments agreed by you, if you paid fees in advance for any period longer than one month, then you may, with the exception of fees for the first month of Services and any Start-up Costs, obtain a refund on a pro rata basis for the period remaining after you cancel.

      + +

      5.4 Some of the paid Services may consist of an initial period, for which there is a one-time charge, followed by recurring period charges as agreed to by you. By choosing a recurring payment plan, you acknowledge that such Services have an initial and recurring payment feature and you accept responsibility for all recurring charges prior to cancellation. WE MAY SUBMIT PERIODIC CHARGES (E.G., MONTHLY) WITHOUT FURTHER AUTHORIZATION FROM YOU, UNTIL YOU PROVIDE PRIOR NOTICE (RECEIPT OF WHICH IS CONFIRMED BY US) THAT YOU HAVE TERMINATED THIS AUTHORIZATION OR WISH TO CHANGE YOUR PAYMENT METHOD. SUCH NOTICE WILL NOT AFFECT CHARGES SUBMITTED BEFORE WE REASONABLY COULD ACT. TO TERMINATE YOUR AUTHORIZATION OR CHANGE YOUR PAYMENT METHOD, GO TO {{appUrl}}/user_payment_profiles.

      +

      5.5 YOU MUST PROVIDE CURRENT, COMPLETE AND ACCURATE INFORMATION FOR YOUR BILLING ACCOUNT. YOU MUST PROMPTLY UPDATE ALL INFORMATION TO KEEP YOUR BILLING ACCOUNT CURRENT, COMPLETE AND ACCURATE (SUCH AS A CHANGE IN BILLING ADDRESS, CREDIT CARD NUMBER, OR CREDIT CARD EXPIRATION DATE), AND YOU MUST PROMPTLY NOTIFY US OR OUR PAYMENT PROCESSORS IF YOUR PAYMENT METHOD IS CANCELED (E.G., FOR LOSS OR THEFT) OR IF YOU BECOME AWARE OF A POTENTIAL BREACH OF SECURITY, SUCH AS THE UNAUTHORIZED DISCLOSURE OR USE OF YOUR USER NAME OR PASSWORD. CHANGES TO SUCH INFORMATION CAN BE MADE AT {{appUrl}}/settings. IF YOU FAIL TO PROVIDE ANY OF THE FOREGOING INFORMATION, YOU AGREE THAT WE MAY CONTINUE CHARGING YOU FOR ANY USE OF PAID SERVICES UNDER YOUR BILLING ACCOUNT UNLESS YOU HAVE TERMINATED YOUR PAID SERVICES AS SET FORTH ABOVE.

      + +

      Arrearages

      +

      5.6 Payments not made within ten (10) days of invoicing will be deemed in arrears. For accounts in arrears, if any amount is more than ten (10) days overdue, without the requirement of providing notice of such arrears, {{appName}} may suspend service to such account and bring legal action to collect the full amount due, including any attorneys’ fees and costs.

      + +

      Suspension for Nonpayment

      +

      5.7 If a Subscriber is past due on their balance, {{appName}} may send up to three (3) email notifications within a fifteen (15) day period before suspending the Subscriber's account. Servers will be temporarily powered off during the suspension period. {{appName}} reserves the right to delete the Subscriber's suspended machines after the final termination notice.

      +
      +
      + +
      +
      +

      In other words,

      +

      We currently accept payment through major credit cards and PayPal. Virtual credit cards and gift cards will often not be accepted, as we’ve seen repeated fraud and abuse with these methods of payment. You may, however, reach out to us at contact@{{appDomain}} to discuss potential alternate methods of payment. You’re typically billed monthly (around the first of the month) for the prior month’s usage. And please make sure to keep your billing account profile updated!

      +
      +
      + +
      +
      +

      Promotional Credit

      +

      5.8 As of March 6, 2015, redemption of promotional credit is limited to 12 months from the date of issue (unless otherwise stated), at which time the credit will expire. Upon redemption, promotional credit expires after 12 months unless otherwise defined in the terms of the promotion. For instances where promotional credit was issued or redeemed prior to March 6, 2015, that credit will expire on March 6, 2016.

      +

      5.9 Only one promotional code is permitted per customer, and may be redeemed only by “new users,” defined as users who are within 30 days of launching their first Droplet.

      + +

      Other Credit

      + +

      5.10 Earned credit from making a referral will expire after 12 consecutive inactive months. An inactive month is a month without an invoice billing event. All referral payouts will be paid in {{appName}} credit.

      +

      5.11 Sign-up credit earned via referral will expire in accordance with the terms stated in section 5.8.

      +

      5.12 Furthermore, sign-up credit earned via referral credit will be counted as the one promotional code permitted, in accordance with the terms stated in section 5.9.

      +

      5.13 SLA credit will expire after 12 consecutive inactive months. An inactive month is a month without an invoice billing event.

      +

      5.14 Credits not explicitly stated in 5.10-5.14 are to be considered promotional credit.

      +
      +
      + +
      +
      +

      In other words,

      +

      If you have received promotional DO credit, you must redeem it within 12 months of the date it was issued. All other credit will remain on your account as long as you’re active in a given 12 month period.

      +
      +
      + +
      + +
      +
      +

      6. Warranty Disclaimer and Beta Services

      +

      6.1 We have no special relationship with or fiduciary duty to you. You acknowledge that we have no duty to take any action regarding:

      +
        +
      1. which Subscribers gain access to the Services;
      2. +
      3. what Content you access via the Services; or
      4. +
      5. how you may interpret or use the Content.
      6. +
      +

      6.2 TO THE EXTENT PERMITTED BY APPLICABLE LAW, YOU RELEASE US FROM ALL LIABILITY FOR YOU HAVING ACQUIRED OR NOT ACQUIRED CONTENT THROUGH THE SERVICES. WE MAKE NO REPRESENTATIONS CONCERNING ANY CONTENT CONTAINED IN OR ACCESSED THROUGH THE SERVICES, AND WE WILL NOT BE RESPONSIBLE OR LIABLE FOR THE ACCURACY, COPYRIGHT COMPLIANCE, OR LEGALITY OF MATERIAL OR CONTENT CONTAINED IN OR ACCESSED THROUGH THE SERVICES.

      +

      6.3 THE SERVICES, INCLUDING WITHOUT LIMITATION ANY INFORMATION DELIVERED AS PART OF THE SERVICES, AND CONTENT ARE PROVIDED “AS IS”, “AS AVAILABLE” AND WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NONINTERFERENCE WITH DATA, AVAILABILITY, ACCURACY, THAT YOU WILL HAVE CONTINUOUS, UNINTERRUPTED OR SECURE ACCESS TO OUR WEBSITE OR THAT THE SERVICES ARE ERROR FREE AND ANY WARRANTIES IMPLIED BY ANY COURSE OF PERFORMANCE OR USAGE OF TRADE, ALL OF WHICH ARE EXPRESSLY DISCLAIMED. WE, AND OUR DIRECTORS, EMPLOYEES, AGENTS, SUPPLIERS, PARTNERS AND CONTENT PROVIDERS DO NOT WARRANT THAT: (I) THE SERVICES WILL BE SECURE OR AVAILABLE AT ANY PARTICULAR TIME OR LOCATION; (II) ANY DEFECTS OR ERRORS WILL BE CORRECTED; (III) ANY CONTENT OR SOFTWARE AVAILABLE AT OR THROUGH THE SERVICES IS FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS; OR (IV) THE RESULTS OF USING THE SERVICES WILL MEET YOUR REQUIREMENTS. YOUR USE OF THE SERVICES IS SOLELY AT YOUR OWN RISK. THE SERVICES CONTAIN INFORMATION PROVIDED BY ONE OR MORE THIRD PARTY DATA PROVIDERS. {{appName}} DOES NOT CONTROL AND IS NOT RESPONSIBLE FOR THE INFORMATION PROVIDED BY ANY SUCH THIRD PARTY PROVIDER. YOU ACKNOWLEDGE AND AGREE THAT NEITHER {{appName}} NOR ANY SUCH THIRD PARTY PROVIDER HAS ANY OBLIGATION TO CORRECT INFORMATION ABOUT YOU EXCEPT AS REQUIRED BY APPLICABLE LAW. INFORMATION YOU REQUEST MAY NOT BE AVAILABLE OR MAY NOT BE PROVIDED, AND {{appName}} HAS NO LIABILITY FOR SUCH FAILURE. IN NO EVENT WILL {{appName}} WARRANT OR GUARANTEE THE CORRECTNESS, COMPREHENSIVENESS, COMPLETENESS, ACCURACY, TIMELINESS OF ANY INFORMATION, PRODUCTS, OR SERVICES ON THIS WEBSITE. THE INFORMATION, PRODUCTS, AND SERVICES AVAILABLE ON THE WEBSITE MAY INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. THEREFORE, YOU AGREE THAT YOUR ACCESS TO AND USE OF OUR WEBSITE, PRODUCTS, SERVICES AND CONTENT ARE AT YOUR OWN RISK.

      +
      +
      + +
      +
      +

      In other words,

      +

      We as a company have rights, just like you the customer.

      +
      +
      + +
      +
      +

      Beta Services

      + +

      6.4. {{appName}} may offer “beta” versions or features of the Services (each, a “Beta Service”). {{appName}} will determine, at its sole discretion, the availability, duration (the “Trial Period”), features, and components of each Beta Service. For avoidance of doubt, any Beta Service is a form of the Services and the provision and use of any Beta Service is subject to the entirety of this Agreement, unless otherwise provided for in this Section 6.4.

      +

      ANY BETA SERVICE IS PROVIDED “AS IS” WITHOUT ANY WARRANTIES OF ANY KIND, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE. {{appName}} SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE WITH REGARD TO ANY BETA SERVICE. Notwithstanding anything to the contrary in this Agreement, in no event will {{appName}} be liable to you or any third party for any damages or liability related to, arising out of, or caused by any Beta Service and/or any modification, suspension, or termination thereof. If {{appName}} permits you to use a Beta Service, you agree to provide {{appName}} Feedback and respond to {{appName}}’s questions or other inquiries regarding your use of the Beta Service, if requested and as applicable. If {{appName}} permits you to use a Beta Service, you specifically agree, in addition to the requirements set forth in Section 3 of this Agreement, to not: (i) use the Beta Service for benchmarking or performance testing or publicly disseminate performance information or analysis from any source relating to the Service; (ii) modify or create derivative works of the Beta Service or remove any product identification, proprietary, copyright or other notices contained in the Beta Service; or (iii) allow any other individual to access or use the Beta Service. {{appName}} at its sole discretion shall determine whether or not to continue to offer any Beta Service, and may cease offering any Beta Service at any time. Upon completion of a Trial Period, you may lose access to the applicable Beta Service, unless or until the features of the Beta Service are incorporated into the Services, and you agree to return or destroy all copies of documentation and confidential information related to the Beta Service. Any production candidate or non-production version of the Services will be considered a Beta Service.

      +

      Subscriber grants to {{appName}} a limited license to use, reproduce, distribute, and display any data provided to {{appName}} by Subscriber and/or any user of a Beta Service solely for facilitating the purposes of this Agreement (such data collectively, “Beta Data”) (i) as required to provide the Beta Service; and (ii) in de-identified form, to tune, enhance and improve the Service and other {{appName}} products and services. Subscriber represents and warrants that it has all necessary rights to grant {{appName}} the rights set forth in this Section, and that it will comply with all applicable laws, regulations, and other obligations regarding the collection, use and disclosure of Beta Data. {{appName}} may use de-identified or aggregated Beta Data collected through a Beta Service for any purpose, including, without limitation, to enhance and improve the Services.

      +
      +
      + +
      +
      +

      In other words,

      +

      We may let you try new features, but these are provided “as-is” and are subject to special terms.

      +
      +
      + +
      + +
      +
      +

      7. Limitation of Liability

      +

      7.1 IN NO EVENT SHALL WE, NOR OUR DIRECTORS, EMPLOYEES, AGENTS, PARTNERS, SUPPLIERS OR CONTENT PROVIDERS, BE LIABLE UNDER CONTRACT, TORT, STRICT LIABILITY, NEGLIGENCE OR ANY OTHER LEGAL OR EQUITABLE THEORY WITH RESPECT TO THE SERVICES (I) FOR ANY LOST PROFITS, DATA LOSS, COST OF PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, OR SPECIAL, INDIRECT, INCIDENTAL, PUNITIVE, COMPENSATORY OR CONSEQUENTIAL DAMAGES OF ANY KIND WHATSOEVER, SUBSTITUTE GOODS OR SERVICES (HOWEVER ARISING), (II) FOR ANY BUGS, VIRUSES, TROJAN HORSES, OR THE LIKE (REGARDLESS OF THE SOURCE OF ORIGINATION), OR (III) FOR ANY DIRECT DAMAGES IN EXCESS OF (IN THE AGGREGATE) OF FEES PAID TO US FOR THE PARTICULAR SERVICES DURING THE IMMEDIATELY PREVIOUS ONE MONTH PERIOD, EVEN IF {{appName}} HAD BEEN ADVISED OF, KNEW, OR SHOULD HAVE KNOWN, OF THE POSSIBILITY THEREOF. SUBSCRIBER ACKNOWLEDGES THAT THE FEES PAID BY HIM OR HER REFLECT THE ALLOCATION OF RISK SET FORTH IN THIS AGREEMENT AND THAT {{appName}} WOULD NOT ENTER INTO THIS AGREEMENT WITHOUT THESE LIMITATIONS. SUBSCRIBER HEREBY WAIVES ANY AND ALL CLAIMS AGAINST {{appName}} ARISING OUT OF SUBSCRIBER'S PURCHASE OR USE OF THE SERVICES, OR ANY CONDUCT OF {{appName}}’S DIRECTORS, OFFICERS, EMPLOYEES, AGENTS OR REPRESENTATIVES. YOUR SOLE AND EXCLUSIVE RIGHT AND REMEDY IN CASE OF DISSATISFACTION WITH THE SERVICES OR ANY OTHER GRIEVANCE SHALL BE YOUR TERMINATION AND DISCONTINUATION OF ACCESS TO OR USE OF THE SERVICES.

      +

      IN ADDITION, YOU AGREE THAT {{appName}} IS NOT RESPONSIBLE FOR ANY DATA COMPILED BY OUR SERVICES AND THAT {{appName}} WILL NOT BE LIABLE, IN ANY MANNER, AS A RESULT OF YOUR EXPOSURE TO ANY DEFAMATORY, LIBELOUS, THREATENING, UNLAWFULLY HARASSING, OBSCENE OR OTHERWISE UNLAWFUL CONTENT OR DATA. IN NO EVENT SHALL {{appName}}, OR ANY THIRD PARTY PROVIDER OF ANY COMPONENT OF THE SERVICES OR OF ANY INFORMATION DELIVERED AS PART OF THE SERVICES, BE LIABLE TO YOU AND/OR ANY PARTY FOR ANY DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DIRECT, INDIRECT, SPECIAL, EXEMPLARY, PUNITIVE, CONSEQUENTIAL OR SIMILAR DAMAGES ARISING OUT OF OR RELATED TO THE SERVICES, CONTENT, PRODUCTS, THE USE OR INABILITY TO USE THIS WEBSITE, OR ANY LINKED WEBSITE, INCLUDING WITHOUT LIMITATION, LOST PROFITS, LOSS OF USE, BUSINESS INTERRUPTION, OR OTHER ECONOMIC LOSSES, LOSS OF PROGRAMS OR OTHER DATA, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, EVEN IF {{appName}} IS ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, INCLUDING LIABILITY ASSOCIATED WITH ANY VIRUSES WHICH MAY INFECT YOUR COMPUTER EQUIPMENT.

      +

      SOME JURISDICTIONS LIMIT OR PROHIBIT THE FOREGOING LIMITATIONS, AND IN SUCH JURISDICTIONS THE FOREGOING LIMITATIONS SHALL BE APPLIED TO THE MAXIMUM EXTENT PERMITTED BY LAW.

      +
      +
      + +
      +
      +
      +
      + +
      + +
      +
      +

      8. Confidentiality

      +

      8.1 Subscriber shall keep confidential any confidential information to which it is given access, and shall cooperate with {{appName}}'s efforts to maintain the confidentiality thereof. Subscriber shall not publish to third parties or distribute information or documentation that {{appName}} provides for purposes of operating and maintaining its systems, including material contained in estimates, invoices, work orders, or other such materials.

      +
      +
      + +
      +
      +

      +

      It’s pretty unlikely we’ll be sharing confidential information with any of our customers.

      +
      +
      + +
      + +
      +
      +

      9. Backup

      +

      9.1 Subscriber is solely responsible for the preservation of Subscriber's data which Subscriber saves onto its virtual server (the “Data”). EVEN WITH RESPECT TO DATA AS TO WHICH SUBSCRIBER CONTRACTS FOR BACKUP SERVICES PROVIDED BY {{appName}}, TO THE EXTENT PERMITTED BY APPLICABLE LAW, {{appName}} SHALL HAVE NO RESPONSIBILITY TO PRESERVE DATA. {{appName}} SHALL HAVE NO LIABILITY FOR ANY DATA THAT MAY BE LOST, OR UNRECOVERABLE, BY REASON OF SUBSCRIBER’S FAILURE TO BACKUP ITS DATA OR FOR ANY OTHER REASON.

      +
      +
      + +
      +
      +

      +

      We trust that you’ll be responsible and back up your own data. Things happen!

      +
      +
      + +
      + +
      +
      +

      10. Publicity

      +

      10.1 Each Subscriber is permitted to state publicly that such Subscriber is a Subscriber of the Services. Subject to {{appName}}’s Privacy Policy, each Subscriber agrees that {{appName}} may include such Subscriber’s name and trademarks in a list of {{appName}} Subscriber, online or in promotional materials. Each Subscriber also agrees that {{appName}} may verbally reference such Subscriber as a Subscriber of the Services. Subscriber may opt out of the provisions in this Section 10.1 by e-mailing a request to contact@{{appDomain}}.

      +
      +
      + +
      +
      +

      +

      We’re proud to have the quality of customers that we do. If it comes up, we may mention you!

      +
      +
      + +
      + +
      +
      +

      11. Indemnification

      +

      11.1 YOU SHALL DEFEND, INDEMNIFY, AND HOLD HARMLESS US, OUR AFFILIATES, PARENTS, SUBSIDIARIES, ANY RELATED COMPANIES, LICENSORS AND PARTNERS, AND EACH OF OUR AND THEIR RESPECTIVE EMPLOYEES, OFFICERS, DIRECTORS, AGENTS, CONTRACTORS, DIRECTORS, SUPPLIERS AND REPRESENTATIVES FROM ALL LIABILITIES, CLAIMS, AND EXPENSES, INCLUDING REASONABLE ATTORNEYS’ FEES, THAT ARISE FROM OR RELATE TO YOUR (OR ANY THIRD PARTY USING YOUR ACCOUNT OR IDENTITY IN THE SERVICES) USE OR MISUSE OF, OR ACCESS TO, THE SERVICES, CONTENT, OR OTHERWISE FROM YOUR USER CONTENT, VIOLATION OF THESE TERMS OF SERVICE OR OF ANY LAW, OR INFRINGEMENT OF ANY INTELLECTUAL PROPERTY OR OTHER RIGHT OF ANY PERSON OR ENTITY. WE RESERVE THE RIGHT TO ASSUME THE EXCLUSIVE DEFENSE AND CONTROL OF ANY MATTER OTHERWISE SUBJECT TO INDEMNIFICATION BY YOU, IN WHICH EVENT YOU WILL ASSIST AND COOPERATE WITH US IN ASSERTING ANY AVAILABLE DEFENSES.

      +
      +
      + +
      +
      +

      +

      If, for example, you’re angry with someone for something they write on a website that’s hosted on {{appName}}, we can’t be held responsible for what they say or do.

      +
      +
      + +
      + +
      +
      +

      12. Termination and Access

      +

      12.1 {{appName}} reserves the right, in our sole discretion, to terminate your access to all or any part of the Services at any time, with or without notice, effective immediately, including but not limited to as a result of your violation of any of these Terms of Service or any law, or if you misuse system resources, such as, by employing programs that consume excessive network capacity, CPU cycles, or disk IO. Any such termination may result in the forfeiture and destruction of information associated with your Account. {{appName}} may provide prior notice of the intent to terminate Services to you if such notice will not, in {{appName}}'s discretion, run counter to the intents and purposes of these Terms of Service. Except as otherwise set forth hereunder, any and all fees paid hereunder are non-refundable and any and all fees owed to {{appName}} before such termination shall be immediately due and payable, including any liabilities that may have been incurred prior to termination such as {{appName}}’s costs for collection (including attorneys’ fees) of any such charges or other liabilities. Upon termination, any and all rights granted to Subscriber by this Agreement will immediately be terminated, and Subscriber shall promptly discontinue all use of the Services. If you wish to terminate your Account, you may do so by following the instructions on the Website or through the Services. All provisions of these Terms of Service which by their nature should survive termination shall survive termination, including, without limitation, licenses of User Content, ownership provisions, warranty disclaimers, indemnity and limitations of liability.

      +
      +
      + +
      +
      +

      If you violate these Terms of Service, then we have the right to put a hold on your account. These actions are reserved for the most drastic offenses; you will more likely receive a warning and will be able to continue using our services as long as the undesirable behavior ceases. You, however, are free to terminate your account without reason at any time.

      +
      +
      + +
      + +
      +
      +

      13. Choice of Law, Venue, Consent to Email Service and Waiver of Hague Convention Service Formalities

      +

      13.1 Any claim arising hereunder shall be construed in accordance with the substantive and procedural laws of the State of New York, without regard to principles of conflict of laws. Subject to Section 15 below, you agree that any dispute arising from or relating to the subject matter of these Terms of Service shall be governed by the exclusive jurisdiction and venue of the state and Federal courts of New York County, New York. Subscriber consents to service of process via email at the email address(es) provided by Subscriber, and waives any requirement under the Hague Convention or other judicial treaty requiring that legal process be translated into any language other than English.

      +
      +
      + +
      +
      +

      Basically, we live in New York.

      +
      +
      + +
      + +
      +
      +

      14. Dispute Resolution

      +

      14.1 Mindful of the high cost of litigation, you and {{appName}} agree to the following dispute resolution procedure: in the event of any controversy, claim, action or dispute arising out of or related to: (i) the Website; (ii) this Agreement; (iii) the Services; (iv) the breach, enforcement, interpretation, or validity of this Agreement; or (v) any other dispute between you and {{appName}} (“Dispute”), the party asserting the Dispute shall first try in good faith to settle such Dispute by providing written notice to the other party (by first class or registered mail) describing the facts and circumstances (including any relevant documentation) of the Dispute and allowing the receiving party 30 days in which to respond to or settle the Dispute. Notice shall be sent (1) if to {{appName}}. at: 101 Avenue of the Americas, 10th Floor, New York, NY 10013 or (2) if to you at: your last-used billing address or the billing and/or shipping address in your Account information. Both you and {{appName}} agree that this dispute resolution procedure is a condition precedent that must be satisfied prior to initiating any arbitration or filing any claim against the other party.

      +
      +
      + +
      +
      +

      We truly hope that we never have to enter into litigation with our subscribers and we imagine that most people feel the same way. If it gets to that point, we’ll agree to discuss it and figure out a solution, first.

      +
      +
      + +
      + +
      +
      +

      15. Mandatory Arbitration Agreement and Class Action Waiver

      +

      15.1 In the interest of resolving disputes between you and {{appName}} in the most expedient and cost effective manner, you and {{appName}} agree that every dispute arising in connection with these Terms will be resolved by binding arbitration. Arbitration is less formal than a lawsuit in court. Arbitration uses a neutral arbitrator instead of a judge or jury, may allow for more limited discovery than in court, and can be subject to very limited review by courts. Arbitrators can award the same damages and relief that a court can award. This agreement to arbitrate disputes includes all claims arising out of or relating to any aspect of these Terms, whether based in contract, tort, statute, fraud, misrepresentation, or any other legal theory, and regardless of whether a claim arises during or after the termination of these Terms. YOU UNDERSTAND AND AGREE THAT, BY ENTERING INTO THESE TERMS, YOU AND {{appName}} ARE EACH WAIVING THE RIGHT TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION. This Section 15 will not apply to disputes arising under the U.S.-EU/U.S.-Swiss Safe Harbor frameworks, which shall instead be administered under the rules for the resolution of disputes arising under the Safe Harbor frameworks specified in the {{appName}} Safe Harbor filing with the U.S. Department of Commerce.

      +

      15.2 Despite the provisions of Section 15.1, nothing in these Terms will be deemed to waive, preclude, or otherwise limit the right of either party to: (i) bring an individual action in small claims court; (ii) pursue an enforcement action through the applicable federal, state, or local agency if that action is available; (iii) seek injunctive relief in a court of law; or (iv) to file suit in a court of law to address an intellectual property infringement claim.

      +

      15.3 Any arbitration between you and {{appName}} will be settled under the Federal Arbitration Act, and governed by the Commercial Dispute Resolution Procedures and the Supplementary Procedures for Consumer Related Disputes (collectively, “AAA Rules”) of the American Arbitration Association (“AAA”), as modified by these Terms, and will be administered by the AAA. The AAA Rules and filing forms are available online at www.adr.org, by calling the AAA at 1-800-778-7879, or by contacting {{appName}}.

      +

      15.4 A party who intends to seek arbitration must first send a written notice of the dispute to the other party by certified U.S. Mail or by Federal Express (signature required) or, only if such other party has not provided a current physical address, then by electronic mail (“Notice”). {{appName}}'s address for Notice is: {{appName}}., 101 Avenue of the Americas, 10th Floor, New York, NY 10013. The Notice must: (a) describe the nature and basis of the claim or dispute; and (b) set forth the specific relief sought (“Demand”). The parties will make good faith efforts to resolve the claim directly, but if the parties do not reach an agreement to do so within 30 days after the Notice is received, you or {{appName}} may commence an arbitration proceeding. During the arbitration, the amount of any settlement offer made by you or {{appName}} must not be disclosed to the arbitrator until after the arbitrator makes a final decision and award, if any. If the dispute is finally resolved through arbitration in your favor, {{appName}} will pay you the highest of the following: (i) the amount awarded by the arbitrator, if any; (ii) the last written settlement amount offered by {{appName}} in settlement of the dispute prior to the arbitrator’s award; or (iii) $1,000.

      +

      15.5 If you commence arbitration in accordance with these Terms, {{appName}} will reimburse you for your payment of the filing fee, unless your claim is for more than $10,000, in which case the payment of any fees will be decided by the AAA Rules. Any arbitration hearing will take place at a location to be agreed upon in New York, New York, but if the claim is for $10,000 or less, you may choose whether the arbitration will be conducted: (i) solely on the basis of documents submitted to the arbitrator; (ii) through a non-appearance based telephone hearing; or (iii) by an in-person hearing as established by the AAA Rules in the county (or parish) of your billing address. If the arbitrator finds that either the substance of your claim or the relief sought in the Demand is frivolous or brought for an improper purpose (as measured by the standards set forth in Federal Rule of Civil Procedure 11(b)), then the payment of all fees will be governed by the AAA Rules. In that case, you agree to reimburse {{appName}} for all monies previously disbursed by it that are otherwise your obligation to pay under the AAA Rules. Regardless of the manner in which the arbitration is conducted, the arbitrator must issue a reasoned written decision sufficient to explain the essential findings and conclusions on which the decision and award, if any, are based. The arbitrator may make rulings and resolve disputes as to the payment and reimbursement of fees or expenses at any time during the proceeding and upon request from either party made within 14 days of the arbitrator’s ruling on the merits.

      +

      15.6 YOU AND {{appName}} AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. Further, unless both you and {{appName}} agree otherwise, the arbitrator may not consolidate more than one person’s claims, and may not otherwise preside over any form of a representative or class proceeding.

      +

      15.7 If {{appName}} makes any future change to this arbitration provision, other than a change to {{appName}}'s address for Notice, you may reject the change by sending us written notice within 30 days of the change to {{appName}}'s address for Notice, in which case your account with {{appName}} will be immediately terminated and this arbitration provision, as in effect immediately prior to the changes you rejected will survive.

      +

      15.8 If Section 15.6 is found to be unenforceable or if the entirety of this Section 15 is found to be unenforceable, then the entirety of this Section 15 will be null and void and, in that case, the parties agree that the exclusive jurisdiction and venue described in Section 13 will govern any action arising out of or related to these Terms. The arbitrator has exclusive authority to resolve any dispute relating to the interpretation, applicability, or enforceability of this binding arbitration agreement.

      +
      +
      + +
      +
      +

      If we can’t work it out, let’s avoid spending a lot of money in court and agree to pay an arbitrator for a fraction of the cost instead.

      +
      +
      + +
      + +
      +
      +

      16. Miscellaneous Provisions

      +

      16.1 Neither you nor {{appName}} shall be liable for nonperformance of the terms herein to the extent that either you or {{appName}} are prevented from performing as a result of any act or event which occurs and is beyond your or {{appName}}’s reasonable control, including, without limitation, acts of God, war, unrest or riot, strikes, any action of a governmental entity, weather, quarantine, fire, flood, earthquake, explosion, utility or telecommunications outages, Internet disturbance, or any unforeseen change in circumstances, or any other causes beyond either party’s reasonable control. The party experiencing the force majeure shall provide the other party with prompt written notice thereof and shall use reasonable efforts to remedy effects of such force majeure.

      +

      16.2 You are granted a limited, non-exclusive right to create a hypertext link to the Website found at {{appUrl}}; provided such link does not portray {{appName}} and/or its affiliates or any of their respective products and services in a false, misleading, derogatory or otherwise defamatory manner. This limited right may be revoked at any time. You may not use, frame or utilize framing techniques to enclose any {{appName}} trademark, logo or other proprietary information, including the images found at the Website, the content of any text or the layout/design of any page or form contained on a page without {{appName}}'s express written consent. Except as noted above, you are not conveyed any right or license by implication, estoppel, or otherwise in or under any patent, trademark, copyright, or proprietary right of {{appName}} or any third party.

      +

      16.3 The Website contains many of the valuable trademarks, service marks, names, titles, logos, images, designs, copyrights and other proprietary materials owned, registered and used by {{appName}}, Including but not limited to, the mark " {{appName}}". {{appName}} and the {{appName}} product names referenced in the Website are either trademarks, service marks or registered trademarks of {{appName}}. Any unauthorized use of same is strictly prohibited and all rights in same are reserved by {{appName}}. No use of any {{appName}} trademark may be made by any third party without express written consent of {{appName}}. Other products and company names mentioned in the Website may be the trademarks of their respective owners.

      +

      16.4 Elements of {{appName}}'s Website are protected by trade dress, trademark, unfair competition, and other laws and may not, unless otherwise permitted hereunder, be copied in whole or in part. No logo, graphic, or image from the Website may be copied or retransmitted without {{appName}}'s express written permission. The images, text, screens, web pages, materials, data, Content and other information used and displayed on the Website are the property of {{appName}} or its licensors and are protected by copyright, trademark and other laws. In addition to our rights in individual elements of the Website, {{appName}} owns copyright or patent rights in the selection, coordination, arrangement and enhancement of any images, text, screens, web pages, materials, data, Content and other information used and displayed on the Website. You may copy such images, text, screens, web pages, materials, data, Content and other information used and displayed on the Website for your personal or educational use only, provided that each copy includes any copyright, trademark or service mark notice or attribution as they appear on the pages copied. Except as provided in the preceding sentence, none of such images, text, screens, web pages, materials, data, Content and other information used and displayed on the Website may be copied, displayed, distributed, downloaded, licensed, modified, published, reposted, reproduced, reused, sold, transmitted, used to create a derivative work or otherwise used for public or commercial purposes without the express written permission of {{appName}}.

      +

      16.5 This Agreement, including all related agreements and policies incorporated by reference herein, constitutes the entire agreement between the parties related to the subject matter hereof and supersedes any prior or contemporaneous agreement between the parties relating to the Services. A valid waiver hereunder shall not be interpreted to be a waiver of that obligation in the future or any other obligation under this Agreement. The failure of either party to exercise in any respect any right provided for herein shall not be deemed a waiver of any further rights hereunder. In order for any waiver of compliance with these Terms of Service to be binding, we must provide you with written notice of such waiver through one of our authorized representatives. If any provision of this Agreement is prohibited by law or held to be unenforceable, that provision will be severed and the remaining provisions hereof shall not be affected such that this Agreement shall continue in full force and effect as if such unenforceable provision had never constituted a part hereof. This Agreement may be executed in counterparts, each of which shall be deemed an original, but all of which together shall constitute the same instrument. This Agreement may be signed electronically or, as set out above, your access and use of the Services will manifest your consent to this Agreement. These Terms of Service are personal to you, and are not assignable, transferable or sublicensable by you except with our prior written consent. We may assign, transfer or delegate any of our rights and obligations hereunder without consent. No agency, partnership, joint venture, or employment relationship is created as a result of these Terms of Service and neither party has any authority of any kind to bind the other in any respect. The section and paragraph headings in these Terms of Service are for convenience only and shall not affect their interpretation. All references to “laws,” “rules,” or “regulations” references any and all applicable laws, rules and regulations, whether domestic or foreign. Unless otherwise specified in these Terms of Service, all notices under these Terms of Service will be in writing and will be deemed to have been duly given when received, if personally delivered or sent by certified or registered mail, return receipt requested; when receipt is electronically confirmed, if transmitted by facsimile or e-mail; or the day after it is sent, if sent for next day delivery by recognized overnight delivery service. Electronic notices should be sent to legal@{{appDomain}}.

      +
      +
      + +
      +
      +

      In other words,

      +

      Neither you nor us can be held responsible for non-performance of these terms given circumstances outside of reasonable control (e.g. extreme weather, natural disasters, telecommunications outages, Internet disturbances, a zombie apocalypse... you get the idea).

      +
      +
      + +
      + +
      +
      +

      Contact

      +

      Contact. You may contact us at the following address:
      {{appName}}, 101 Avenue of the Americas, 10th Floor, New York, NY 10013.

      + +

      Effective Date: March 15, 2016

      + +

      Copyright 2016 {{appName}}. All rights reserved. No part of {{appName}}'s Website may be reproduced, modified, or distributed in any form or manner without the prior written permission of {{appName}}.

      +
      +
      + +
      +
      +
      +
      + +
      +
      \ No newline at end of file diff --git a/view/tos.js b/view/tos.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/tos.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/webhooks.html b/view/webhooks.html new file mode 100644 index 00000000..b0a2a752 --- /dev/null +++ b/view/webhooks.html @@ -0,0 +1,15 @@ +
      +

      Webhooks

      +
      +

      {{appName}} is the ideal place to host Webhooks.

      +

      Our platform fully supports multiple programming languages, and all the features you may need while building custom HTTP Webhooks.

      +

      Premade Webhook Examples

      +

      Want to see some actual webhook examples? These examples can be run or even forked!

      +

      Interactive Webhook Playground

      +

      Want to start playing with live code, check out the playground!

      +

      Create new Webhook

      +

      Ready to immediately deploy your first Webhook? Create a new Webhook service instantly!

      +

      Frequently Asked Questions

      +

      New to Webhooks? The FAQ is the perfect place to start.

      +
      +
      \ No newline at end of file diff --git a/view/webhooks.js b/view/webhooks.js new file mode 100644 index 00000000..d4ddc4a2 --- /dev/null +++ b/view/webhooks.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var $ = this.$, req = opts.req; + $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/websockets.html b/view/websockets.html new file mode 100644 index 00000000..7be4819f --- /dev/null +++ b/view/websockets.html @@ -0,0 +1,18 @@ +
      +

      Websockets

      +
      +

      All services on {{appName}} have the ability to receive JSON data over a Websocket connection. {{appName}} will hold the Websocket connection, attempting to parse any incoming messages as JSON and forward them to the hook microservice. If a string or non-JSON message is received, {{appName}} will simply pass the data to the service as hook.params.body Any Websocket client will work, including the browser.

      +

      Below is an example of using the wscat tool to connect to a {{appName}} service over a Websocket connection to send and receive data.

      +

      Example

      +

      wscat -c {{appWs}}/examples/echo

      + +
      +
      +

      + Currently, {{appName}} Websockets can only receive data as text or JSON messages. In the near future, we will most likely add support for streaming binary data over Websocket with the ability to perform transforms on the data stream. Remember, you can still perform streaming transformations of binary data using the regular HTTP API:

      + echo "foo" | curl --header "content-type: application/octet-stream" --data-binary @- http://{{appName}}/examples/javascript-stream-transform
      +

      +
      +
      +
      +
      \ No newline at end of file diff --git a/view/websockets.js b/view/websockets.js new file mode 100644 index 00000000..1d74b99c --- /dev/null +++ b/view/websockets.js @@ -0,0 +1,5 @@ +module['exports'] = function view (opts, callback) { + var req = opts.req, $ = this.$; + var $ = req.white($); + callback(null, $.html()); +}; \ No newline at end of file diff --git a/view/wishlist.html b/view/wishlist.html new file mode 100644 index 00000000..ef3e6300 --- /dev/null +++ b/view/wishlist.html @@ -0,0 +1,36 @@ +
      +

      Feature Wishlist

      +

      Click Here to Request a Feature

      + +