master 6f5a39e80273 cached
416 files
65.0 MB
2.4M tokens
2194 symbols
1 requests
Download .txt
Showing preview only (9,580K chars total). Download the full file or copy to clipboard to get everything.
Repository: shekhargulati/52-technologies-in-2016
Branch: master
Commit: 6f5a39e80273
Files: 416
Total size: 65.0 MB

Directory structure:
gitextract_yi40xssl/

├── .gitignore
├── 01-finatra/
│   ├── README.md
│   └── fitman/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   ├── build.properties
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   ├── resources/
│           │   │   └── logback.xml
│           │   └── scala/
│           │       └── com/
│           │           └── shekhargulati/
│           │               └── fitman/
│           │                   ├── FitmanApp.scala
│           │                   └── api/
│           │                       └── WeightResource.scala
│           └── test/
│               └── scala/
│                   └── com/
│                       └── shekhargulati/
│                           └── fitman/
│                               ├── HelloControllerFeatureTest.scala
│                               └── api/
│                                   └── WeightResourceFeatureTest.scala
├── 02-sbt/
│   ├── README.md
│   └── tasky/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       ├── scalastyle-config.xml
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       ├── HelloSbt.scala
│           │       ├── datamodels.scala
│           │       └── taskmanager.scala
│           └── test/
│               └── scala/
│                   └── TaskManagerSpec.scala
├── 03-stanford-corenlp/
│   ├── README.md
│   └── sentiment-analyzer/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── com/
│           │           └── shekhargulati/
│           │               └── sentiment_analyzer/
│           │                   └── SentimentAnalyzer.scala
│           └── test/
│               └── scala/
│                   └── com/
│                       └── shekhargulati/
│                           └── sentiment_analyzer/
│                               └── SentimentAnalyzerSpec.scala
├── 04-slick/
│   ├── README.md
│   └── tasky/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── datamodel/
│           │           └── DataModel.scala
│           └── test/
│               ├── resources/
│               │   └── application.conf
│               └── scala/
│                   └── datamodel/
│                       └── DataModelSpec.scala
├── 05-slick/
│   ├── README.md
│   └── tasky/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       ├── datamodel/
│           │       │   ├── Priority.scala
│           │       │   ├── columnDataMappers.scala
│           │       │   └── dataModel.scala
│           │       └── queries/
│           │           └── queries.scala
│           └── test/
│               ├── resources/
│               │   └── application.conf
│               └── scala/
│                   ├── datamodel/
│                   │   └── DataModelSpec.scala
│                   └── queries/
│                       └── QueriesSpec.scala
├── 06-okhttp/
│   ├── README.md
│   └── medium-scala-client/
│       ├── .gitignore
│       ├── build.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── medium/
│           │           ├── MediumApiProtocol.scala
│           │           ├── MediumClient.scala
│           │           └── domainObjects.scala
│           └── test/
│               └── scala/
│                   └── medium/
│                       └── MediumClientSpec.scala
├── 07-hugo/
│   ├── README.md
│   ├── bookshelf/
│   │   ├── config.toml
│   │   ├── content/
│   │   │   └── post/
│   │   │       ├── art-of-thinking-clearly.md
│   │   │       ├── confessions-of-a-public-speaker.md
│   │   │       ├── good-to-great.md
│   │   │       ├── hen-who-dreamed-she-could-fly.md
│   │   │       └── seven-habbits-of-highly-effective-people.md
│   │   ├── layouts/
│   │   │   ├── _default/
│   │   │   │   └── li.html
│   │   │   ├── index.html
│   │   │   └── partials/
│   │   │       ├── default_foot.html
│   │   │       └── default_head.html
│   │   └── themes/
│   │       └── hugo_theme_robust/
│   │           ├── .gitignore
│   │           ├── LICENSE.md
│   │           ├── README.md
│   │           ├── config.yaml
│   │           ├── layouts/
│   │           │   ├── _default/
│   │           │   │   ├── li.html
│   │           │   │   ├── list.html
│   │           │   │   ├── single.html
│   │           │   │   └── terms.html
│   │           │   ├── index.html
│   │           │   ├── partials/
│   │           │   │   ├── default_foot.html
│   │           │   │   ├── default_head.html
│   │           │   │   ├── pagination.html
│   │           │   │   └── sidebar.html
│   │           │   └── rss.xml
│   │           ├── static/
│   │           │   └── css/
│   │           │       ├── custom.css
│   │           │       └── styles.css
│   │           └── theme.toml
│   └── bookshelf-public/
│       ├── 404.html
│       ├── categories/
│       │   └── index.html
│       ├── css/
│       │   ├── custom.css
│       │   └── styles.css
│       ├── index.html
│       ├── index.xml
│       ├── page/
│       │   └── 1/
│       │       └── index.html
│       ├── post/
│       │   ├── art-of-thinking-clearly/
│       │   │   └── index.html
│       │   ├── confessions-of-a-public-speaker/
│       │   │   └── index.html
│       │   ├── good-to-great/
│       │   │   └── index.html
│       │   ├── hen-who-dreamed-she-could-fly/
│       │   │   └── index.html
│       │   ├── index.html
│       │   ├── index.xml
│       │   ├── page/
│       │   │   └── 1/
│       │   │       └── index.html
│       │   └── seven-habbits-of-highly-effective-people/
│       │       └── index.html
│       ├── sitemap.xml
│       └── tags/
│           └── index.html
├── 08-coreos/
│   └── README.md
├── 09-cloudvision/
│   ├── README.md
│   └── people-counter/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   └── java/
│           │       └── com/
│           │           └── shekhargulati/
│           │               └── peoplecounter/
│           │                   ├── FaceDetector.java
│           │                   ├── GoogleVisionServiceFactory.java
│           │                   ├── ImagePeopleCount.java
│           │                   ├── ImageWriter.java
│           │                   ├── PeopleCounter.java
│           │                   ├── PeopleCounterApp.java
│           │                   └── TweetObservable.java
│           └── test/
│               ├── java/
│               │   └── com/
│               │       └── shekhargulati/
│               │           └── peoplecounter/
│               │               ├── FaceDetectorTest.java
│               │               └── PeopleCounterTest.java
│               └── resources/
│                   └── response.json
├── 10-gatling/
│   ├── README.md
│   └── blog/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           └── test/
│               └── scala/
│                   └── loadtests/
│                       └── AccessHomePageSimulation.scala
├── 11-textblob/
│   ├── README.md
│   └── sentiment-analyzer/
│       ├── .gitignore
│       ├── .openshift/
│       │   ├── action_hooks/
│       │   │   ├── README.md
│       │   │   └── deploy
│       │   ├── cron/
│       │   │   ├── README.cron
│       │   │   ├── daily/
│       │   │   │   └── .gitignore
│       │   │   ├── hourly/
│       │   │   │   └── .gitignore
│       │   │   ├── minutely/
│       │   │   │   └── .gitignore
│       │   │   ├── monthly/
│       │   │   │   └── .gitignore
│       │   │   └── weekly/
│       │   │       ├── README
│       │   │       ├── chronograph
│       │   │       ├── jobs.allow
│       │   │       └── jobs.deny
│       │   └── markers/
│       │       └── .gitkeep
│       ├── requirements.txt
│       ├── sentimentanalyzer.py
│       ├── static/
│       │   ├── css/
│       │   │   ├── bootstrap-theme.css
│       │   │   └── bootstrap.css
│       │   └── js/
│       │       └── jquery.js
│       ├── templates/
│       │   └── index.html
│       └── wsgi.py
├── 11-tweet-deduplication/
│   └── README.md
├── 12-play/
│   └── .gitkeep
├── 13-arangodb/
│   ├── README.md
│   └── localjobs/
│       └── jobs.json
├── 14-kafka/
│   └── README.md
├── 15-huginn/
│   └── README.md
├── 16-newspaper/
│   └── README.md
├── 17-typescript/
│   ├── README.md
│   └── code/
│       ├── getting-started.ts
│       ├── js/
│       │   └── getting-started.js
│       └── tsconfig.json
├── 18-mesos/
│   ├── README.md
│   └── images/
│       └── diagrams.key
├── 19-bees/
│   └── README.md
├── 20-json/
│   ├── README.md
│   └── code/
│       └── db.json
├── 21-strman/
│   └── README.md
├── 22-regex/
│   ├── README.md
│   └── code/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   └── java/
│           │       └── week22/
│           │           └── regex/
│           │               └── GaddafiSpellingMatcher.java
│           └── test/
│               └── java/
│                   └── week22/
│                       └── regex/
│                           └── GaddafiSpellingMatcherTest.java
├── 23-android-part1/
│   └── README.md
├── 24-jekyll-to-wordpress/
│   └── README.md
├── 25-angular-dragula/
│   ├── README.md
│   └── trello/
│       ├── .bowerrc
│       ├── .editorconfig
│       ├── .eslintrc
│       ├── .gitignore
│       ├── .yo-rc.json
│       ├── bower.json
│       ├── e2e/
│       │   ├── .eslintrc
│       │   ├── main.po.js
│       │   └── main.spec.js
│       ├── gulp/
│       │   ├── .eslintrc
│       │   ├── build.js
│       │   ├── conf.js
│       │   ├── e2e-tests.js
│       │   ├── inject.js
│       │   ├── scripts.js
│       │   ├── server.js
│       │   ├── unit-tests.js
│       │   └── watch.js
│       ├── gulpfile.js
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── src/
│           ├── app/
│           │   ├── index.config.js
│           │   ├── index.css
│           │   ├── index.module.js
│           │   ├── index.run.js
│           │   └── main/
│           │       ├── main.controller.js
│           │       └── main.controller.spec.js
│           └── index.html
├── 26-android-part2/
│   └── README.md
├── 27-learn-golang-for-great-good/
│   ├── README.md
│   └── programs/
│       ├── closestpair.go
│       ├── concatenator.go
│       ├── dedup.go
│       ├── duplicate-cli.go
│       ├── duplicate.go
│       ├── equalornotequal.go
│       ├── fizzbuzz.go
│       ├── greeter.go
│       ├── import_examples.go
│       ├── reverse.go
│       └── wordcount.go
├── 28-ionic/
│   ├── README.md
│   └── dailyreads/
│       ├── backend/
│       │   ├── .gitignore
│       │   ├── app.py
│       │   ├── requirements.txt
│       │   ├── static/
│       │   │   ├── css/
│       │   │   │   ├── 1-col-portfolio.css
│       │   │   │   └── bootstrap.css
│       │   │   └── js/
│       │   │       ├── bootstrap.js
│       │   │       └── jquery.js
│       │   └── templates/
│       │       └── index.html
│       └── mobileapp/
│           ├── .bowerrc
│           ├── .editorconfig
│           ├── .gitignore
│           ├── README.md
│           ├── bower.json
│           ├── config.xml
│           ├── gulpfile.js
│           ├── hooks/
│           │   ├── README.md
│           │   └── after_prepare/
│           │       └── 010_add_platform_class.js
│           ├── ionic.project
│           ├── package.json
│           ├── scss/
│           │   └── ionic.app.scss
│           ├── src/
│           │   ├── js/
│           │   │   └── app.js
│           │   └── views/
│           │       └── home/
│           │           └── home.html
│           └── www/
│               ├── README.md
│               ├── css/
│               │   ├── ionic.app.css
│               │   └── style.css
│               ├── index.html
│               └── lib/
│                   ├── angular/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── angular-csp.css
│                   │   ├── angular.js
│                   │   ├── angular.min.js.gzip
│                   │   ├── bower.json
│                   │   ├── index.js
│                   │   └── package.json
│                   ├── angular-animate/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── angular-animate.js
│                   │   ├── bower.json
│                   │   ├── index.js
│                   │   └── package.json
│                   ├── angular-sanitize/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── angular-sanitize.js
│                   │   ├── bower.json
│                   │   ├── index.js
│                   │   └── package.json
│                   ├── angular-ui-router/
│                   │   ├── .bower.json
│                   │   ├── CHANGELOG.md
│                   │   ├── CONTRIBUTING.md
│                   │   ├── LICENSE
│                   │   ├── README.md
│                   │   ├── api/
│                   │   │   └── angular-ui-router.d.ts
│                   │   ├── bower.json
│                   │   ├── release/
│                   │   │   └── angular-ui-router.js
│                   │   └── src/
│                   │       ├── common.js
│                   │       ├── resolve.js
│                   │       ├── state.js
│                   │       ├── stateDirectives.js
│                   │       ├── stateFilters.js
│                   │       ├── templateFactory.js
│                   │       ├── urlMatcherFactory.js
│                   │       ├── urlRouter.js
│                   │       ├── view.js
│                   │       ├── viewDirective.js
│                   │       └── viewScroll.js
│                   ├── babel-polyfill/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── bower.json
│                   │   └── browser-polyfill.js
│                   └── ionic/
│                       ├── css/
│                       │   └── ionic.css
│                       ├── js/
│                       │   ├── angular/
│                       │   │   ├── angular-animate.js
│                       │   │   ├── angular-resource.js
│                       │   │   ├── angular-sanitize.js
│                       │   │   └── angular.js
│                       │   ├── angular-ui/
│                       │   │   └── angular-ui-router.js
│                       │   ├── ionic-angular.js
│                       │   ├── ionic.bundle.js
│                       │   └── ionic.js
│                       ├── scss/
│                       │   ├── _action-sheet.scss
│                       │   ├── _animations.scss
│                       │   ├── _backdrop.scss
│                       │   ├── _badge.scss
│                       │   ├── _bar.scss
│                       │   ├── _button-bar.scss
│                       │   ├── _button.scss
│                       │   ├── _checkbox.scss
│                       │   ├── _form.scss
│                       │   ├── _grid.scss
│                       │   ├── _items.scss
│                       │   ├── _list.scss
│                       │   ├── _loading.scss
│                       │   ├── _menu.scss
│                       │   ├── _mixins.scss
│                       │   ├── _modal.scss
│                       │   ├── _platform.scss
│                       │   ├── _popover.scss
│                       │   ├── _popup.scss
│                       │   ├── _progress.scss
│                       │   ├── _radio.scss
│                       │   ├── _range.scss
│                       │   ├── _refresher.scss
│                       │   ├── _reset.scss
│                       │   ├── _scaffolding.scss
│                       │   ├── _select.scss
│                       │   ├── _slide-box.scss
│                       │   ├── _slides.scss
│                       │   ├── _spinner.scss
│                       │   ├── _tabs.scss
│                       │   ├── _toggle.scss
│                       │   ├── _transitions.scss
│                       │   ├── _type.scss
│                       │   ├── _util.scss
│                       │   ├── _variables.scss
│                       │   ├── ionic.scss
│                       │   └── ionicons/
│                       │       ├── _ionicons-font.scss
│                       │       ├── _ionicons-icons.scss
│                       │       ├── _ionicons-variables.scss
│                       │       └── ionicons.scss
│                       └── version.json
├── 29-go-unit-testing/
│   ├── .gitignore
│   └── README.md
├── 29-golang-github-slacknotification/
│   ├── README.md
│   └── programs/
│       └── main.go
├── 30-dropwizard/
│   ├── README.md
│   └── blog/
│       ├── .gitignore
│       ├── build.gradle
│       ├── configuration.yml
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           └── main/
│               └── java/
│                   └── blog/
│                       ├── AppConfiguration.java
│                       ├── BlogApp.java
│                       ├── model/
│                       │   └── Blog.java
│                       ├── mongo/
│                       │   ├── MongoHealthCheck.java
│                       │   └── MongoManaged.java
│                       └── resources/
│                           ├── BlogResource.java
│                           └── IndexResource.java
├── 31-gradle-tips/
│   └── README.md
├── 32-groovy-ast-transformations/
│   ├── README.md
│   └── sha1-ast/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   ├── groovy/
│           │   │   └── playground/
│           │   │       └── Book.groovy
│           │   ├── java/
│           │   │   └── playground/
│           │   │       ├── Hash.java
│           │   │       └── ToHashAdderAstTransformation.java
│           │   └── resources/
│           │       └── hash.java.txt
│           └── test/
│               └── groovy/
│                   └── playground/
│                       ├── BookTest.groovy
│                       └── HashAstTransformationTests.groovy
├── 34-aws-lambda/
│   ├── README.md
│   └── code/
│       ├── .gitignore
│       ├── json_parsing.py
│       └── tweet_sender.py
├── 36-webpack/
│   ├── README.md
│   └── code/
│       ├── .gitignore
│       ├── css/
│       │   └── style.css
│       ├── dist/
│       │   └── bundle.js
│       ├── index.html
│       ├── js/
│       │   ├── profile.js
│       │   └── timeline.js
│       ├── package.json
│       └── webpack.config.js
├── 37-spring-boot-scala/
│   ├── README.md
│   └── gs-rest-service/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── hello/
│           │           ├── Application.scala
│           │           ├── GreetingController.scala
│           │           └── PingController.scala
│           └── test/
│               └── scala/
│                   └── hello/
│                       └── GreetingControllerTest.scala
├── 38-akka/
│   └── README.md
├── 39-docker/
│   └── README.md
├── 40-docker-cron/
│   ├── README.md
│   └── cron-example/
│       ├── .dockerignore
│       ├── .gitignore
│       ├── Dockerfile
│       ├── app.py
│       ├── crontab
│       ├── requirements.txt
│       └── run-crond.sh
├── 41-akka-dispatcher/
│   └── README.md
├── 42-docker-compose/
│   ├── README.md
│   └── code/
│       ├── with-waitforit/
│       │   ├── Dockerfile
│       │   ├── docker-compose.yml
│       │   ├── taskman.jar
│       │   └── wait-for-it.sh
│       └── without-waitforit/
│           ├── Dockerfile
│           ├── docker-compose.yml
│           └── taskman.jar
├── 43-graphql/
│   └── README.md
├── LICENSE
├── README.md
├── authors.md
└── who-is-talking-about.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store


================================================
FILE: 01-finatra/README.md
================================================
Week 1: Finatra Tutorial -- Build Beautiful REST API The Twitter Way
-------

[Finatra](https://github.com/twitter/finatra) is an open-source project by Twitter that can be used to build REST APIs in Scala programming language. Finatra builds on top of Twitter's Scala stack -- twitter-server, finagle, and twitter-util.

1. [Finagle](http://twitter.github.io/finagle/): It can be used to construct high performance servers.
2. [Twitter Server](http://twitter.github.io/twitter-server/): It defines a template from which servers at Twitter are built. It uses finagle underneath.
3. [Twitter-Util](http://twitter.github.io/util/): A bunch of idiomatic, small, general purpose tools for Scala.

<img src="http://twitter.github.io/finatra/images/finatra_transparent.gif" height="300" align="middle">

In this step-by-step tutorial, we will cover how to build a Scala REST API using Finatra version 2. Finatra version 2 is a complete rewrite of finatra and is significantly faster(50 times according to documentation) than version 1.x.

> This blog is part of my year long blog series [52 Technologies in 2016](https://github.com/shekhargulati/52-technologies-in-2016)

## Prerequisite

1. Scala 2.11.7
2. IntelliJ Idea Community Edition
3. JDK 8

> Code for today's demo application is on Github at [fitman](fitman)

## Building an application from scratch

In this blog, we will build a simple application called **fitman**. The goal of this application is to track weight of an individual. Every week, a user enter his/her weight and a status message describing how they are feeling. This will allow them to view a timeline of their body weight.

### Step 1: Create a Scala SBT project using IntelliJ Idea

Open IntelliJ Idea and select Scala > SBT project. You will see screen as shown below.

<img src="images/step1-scala-sbt-project.png" height="450">

After selecting, press ***Next*** button. Enter the project details and press ***Finish*** button.

<img src="images/step1-enter-project-details.png" height="450">

This will create a Scala based SBT project that we will use.

> You can use any other IDE or tool as well to scaffold a Scala SBT project.

### Step 2: Adding required dependencies to build.sbt

Your `build.sbt` should look like following:

```scala
name := "fitman"

version := "1.0"

scalaVersion := "2.11.7"

lazy val versions = new {
  val finatra = "2.1.2"
  val logback = "1.1.3"
}

resolvers ++= Seq(
  Resolver.sonatypeRepo("releases"),
  "Twitter Maven" at "https://maven.twttr.com"
)

libraryDependencies += "com.twitter.finatra" % "finatra-http_2.11" % versions.finatra
libraryDependencies += "com.twitter.finatra" % "finatra-slf4j_2.11" % versions.finatra
libraryDependencies += "ch.qos.logback" % "logback-classic" % versions.logback
```

Out of three dependencies mentioned above, `finatra-http_2.11` is only required. `finatra-slf4j_2.11` and `logback-classic` are added for logging purpose only.

> Couple of things that disappointed me once I added above mentioned dependencies was 1) time it took to download all the dependencies 2) few transient dependencies like `twitter-metrics` are not present on Maven central so you have to add Twitter's Maven repository located at https://maven.twttr.com.


### Step 3: Fitman says Hello

A finatra app consists of an http server, a list of controllers, and zero or more filters. Let's create a simple Scala class that extends finatra's `com.twitter.finatra.http.HttpServer` as shown below.

```scala
import com.twitter.finatra.http.HttpServer

object FitmanApp extends FitmanServer

class FitmanServer extends HttpServer
```

In the code shown above, we created a server `FitmanServer` that extends `com.twitter.finatra.http.HttpServer`. `HttpServer` extends `TwitterServer` and adds configuration specific to an http server. `TwitterServer` is a template using which other types of servers can be created. `FitmanApp` is an object that is used to launch the server. From the documentation,

> The reason for having a separate object is to allow server to be instantiated multiple times in tests without worrying about static state persisting across test runs in the same JVM.

Also, according to documentation **Finatra convention is to create a Scala object with a name ending in **Main****. I prefer to use convention where name ends with `App` so I am using that.

You can run the application just like you will run any Scala main program. In IntelliJ, right click and click **Fitman App** as shown below.

![](images/step3-run-empty-fitmanapp.png)

This will launch the netty based server at port `8888`. As there is no route configured, so you will not be able to do anything useful.

> If you want to use any other port that 8888 then you can use `-http.port` flag and set the it to your preferred value like -http.port=:8080

Let's write our first controller -- `HelloController` in the `FitmanApp.scala` file as shown below.

```scala
import com.twitter.finagle.http.Request
import com.twitter.finatra.http.routing.HttpRouter
import com.twitter.finatra.http.{Controller, HttpServer}

object FitmanApp extends FitmanServer

class FitmanServer extends HttpServer {
  override protected def configureHttp(router: HttpRouter): Unit = {
    router.add(new HelloController)
  }
}

class HelloController extends Controller {

  get("/hello") { request: Request =>
    "Fitman says hello"
  }

}
```

In the code shown above, we created `HelloController` which extends finatra `Controller` abstract class. `HelloController` is defined with one endpoint - `/hello`. When an HTTP GET request is made to `/hello` then the associated callback function will be called. The callback function has `callback: RequestType => ResponseType` signature. It accepts a `com.twitter.finagle.http.Request` and returns back a response of any type that can be converted to `com.twitter.finagle.http.Response`. The callback function in this case just returns a string.

To make server aware of the controller, we registered `HelloController` with `FitmanServer` by overriding its `configureHttp` method. The `configureHttp` exposes `HttpRouter` that is used to register an instance of `HelloController`.

> You can also ask server to handle instantiation of controller by passing the type of controller to the add method instead of its instance. This becomes very useful when used along with dependency injection framework like Guice. We will discuss it later in detail.
```scala
override protected def configureHttp(router: HttpRouter): Unit = {
  router.add[HelloController]
}
```

Now, when you make an HTTP GET request to `http://localhost:8888/hello` you will receive fitman ascii art as shown below.

```
→ curl -i http://localhost:8888/hello
HTTP/1.1 200 OK
Content-Length: 17
Fitman says hello
```


#### Admin Interface

Every Finatra by default exposes an admin interface at http://localhost:9990/admin that you can use to get system level details like CPU usage profile, heap profile, server information, and many other. To learn about all admin features refer to [Twitter Server documentation](https://twitter.github.io/twitter-server/Features.html#http-admin-interface).

You can configure admin interface to run on any other port by passing `-admin.port` flag. In IntelliJ, edit your run configuration as shown below.

<img src="images/step3-change-admin-port.png" height="450">

From now on Admin interface will be available at http://localhost:10000/admin.

#### Overriding default server configuration

There are two ways you can override default server configuration values. One way is to use flags as discussed previously with admin and http ports.  The other way you can override default server configuration is by overriding fields in the `FitmanServer` as shown below.

```scala
class FitmanServer extends HttpServer {

  override protected def defaultFinatraHttpPort: String = ":8080"
  override protected def defaultTracingEnabled: Boolean = false
  override protected def defaultHttpServerName: String = "FitMan"

  override protected def configureHttp(router: HttpRouter): Unit = {
    router.add(new HelloController)
  }
}
```

### Step 4: Let's write feature test for HelloController

One of the feature of Finatra that impressed me most was its inbuilt support for feature testing. Feature testing is a form of blackbox testing that tests a particular feature from outside.

Let's add dependencies to `build.sbt` file. You can view full build.sbt [here](https://github.com/shekhargulati/52-technologies-in-2016/blob/master/01-finatra/fitman/build.sbt).

```scala
libraryDependencies += "com.twitter.finatra" % "finatra-http_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" % "inject-server_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" % "inject-app_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" % "inject-core_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" %% "inject-modules" % versions.finatra % "test"
libraryDependencies += "com.google.inject.extensions" % "guice-testlib" % versions.guice % "test"
libraryDependencies +=  "com.twitter.finatra" % "finatra-jackson_2.11" % versions.finatra % "test"

libraryDependencies += "com.twitter.finatra" % "finatra-http_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-server_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-app_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-core_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-modules_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.google.inject.extensions" % "guice-testlib" % versions.guice % "test" classifier "tests"
libraryDependencies +=  "com.twitter.finatra" % "finatra-jackson_2.11" % versions.finatra % "test"  classifier "tests"

libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.4" % "test"
libraryDependencies += "org.specs2" %% "specs2" % "2.3.12" % "test"
```


Let's write our first feature test that will test the `/hello` endpoint. To create a feature test, you have to extend a trait called `FeatureTest`. You have to provide implementation for server definition as shown below. We created an instance of `EmbeddedHttpServer` passing it our application twitter server -- `FitmanServer`.

```scala
import com.twitter.finagle.http.Status
import com.twitter.finatra.http.test.EmbeddedHttpServer
import com.twitter.inject.server.FeatureTest

class HelloControllerFeatureTest extends FeatureTest {
  override val server: EmbeddedHttpServer = new EmbeddedHttpServer(
    twitterServer = new FitmanServer)

  "Say Hello" in {
    server.httpGet(
      path = "/hello",
      andExpect = Status.Ok,
      withBody = "Fitman says hello"
    )
  }
}
```

This test will start an embedded http server and will make an actual HTTP GET request to the `/hello` endpoint. We asserted that HTTP status code returned by our service is 200 i.e. OK and response body contains text `Fitman says hello`. If you change the body text to something other than `Fitman says hello` then test will fail with detailed message outlining the difference between texts as shown below.

```
"Fitman says hello[]" did not equal "Fitman says hello[123]"
```

This allows you to test the externally visible features of the API.

### Step 5: Let's capture weight

Let's first write the feature test for our WeightResource. The feature test will test that when an HTTP POST request is made to `/weights` endpoint then weight will be stored in some database. In today's blog, we will use a mutable Map to act as a database. Later in this series, we will cover how to work with databases in Scala. We will update our blog then.

```scala
import com.shekhargulati.fitman.FitmanServer
import com.twitter.finagle.http.Status
import com.twitter.finatra.http.test.EmbeddedHttpServer
import com.twitter.inject.server.FeatureTest

class WeightResourceFeatureTest extends FeatureTest {
  override val server = new EmbeddedHttpServer(
    twitterServer = new FitmanServer
  )

  "WeightResource" should {
    "Save user weight when POST request is made" in {
      server.httpPost(
        path = "/weights",
        postBody =
          """
            |{
            |"user":"shekhar",
            |"weight":85,
            |"status":"Feeling great!!!"
            |}
          """.stripMargin,
        andExpect = Status.Created,
        withLocation = "/weights/shekhar"
      )
    }
  }
}
```

When you will run the test case then this test will fail as we have not yet added functionality for WeightResource.

Our data model looks like as shown below. It should have same field names as JSON.

```scala
case class Weight(
                   user: String,
                   weight: Int,
                   status: Option[String],
                   postedAt: Instant = Instant.now()
                 )
```

Now, let's write our WeightResource.

```scala
import com.twitter.finatra.http.Controller

import scala.collection.mutable

class WeightResource extends Controller {

  val db = mutable.Map[String, List[Weight]]()

  post("/weights") { weight: Weight =>
    val weightsForUser = db.get(weight.user) match {
      case Some(weights) => weights :+ weight
      case None => List(weight)
    }
    db.put(weight.user, weightsForUser)
    response.created.location(s"/weights/${weight.user}")
  }

}
```
Update FitmanServer with new Controller
```
    router.add(new WeightResource)
```

In the code shown above, we did the following:

1. We created a mutable Map to store weight for a user.
2. In the `post("/weights")` callback, we are directly using our case class Weight instead of using Finagle request. Finatra automatically converts the request body to the case class.
3. In the post method callback, we first check whether the user exists in the db or not. If user exists, then we add weight to its existing weights collection else we create new List with weight.
4. Finally, we return the response back to the user. `response.created` makes sure that HTTP status 201 is set. We also set the location header to point to a new resource.

### Step 6: View user weight

Now, let's write our second operation that will return all captured weights for a user. We will start with a feature test as shown below.

```scala
"List all weights for a user when GET request is made" in {
  val response = server.httpPost(
    path = "/weights",
    postBody =
      """
        |{
        |"user":"test_user_1",
        |"weight":80,
        |"posted_at" : "2016-01-03T14:34:06.871Z"
        |}
      """.stripMargin,
    andExpect = Status.Created
  )

  server.httpGetJson[List[Weight]](
    path = response.location.get,
    andExpect = Status.Ok,
    withJsonBody =
      """
        |[
        |  {
        |    "user" : "test_user_1",
        |    "weight" : 80,
        |    "posted_at" : "2016-01-03T14:34:06.871Z"
        |  }
        |]
      """.stripMargin
  )
}
```

Add the following method to `WeightResource`

```scala
get("/weights/:user") { request: Request =>
  db.getOrElse(request.params("user"), List())
}
```

### Step 7: Getting logging right

In step 2, we added `finatra-slf4j_2.11` and `logback-classic` dependencies to the classpath so that we can effectively log in our application. `finatra` uses SLF4J api for framework logging. SLF4J provides an API abstraction for various logging framework like log4j, logback, etc. Developers are free to choose their favorite logging library that works with SLF4J. finatra documentation recommends to use Logback as an SLF4J binding as it is much more superior and performant than other logging libraries.

To log in your application, you have to mixin `com.twitter.inject.Logging` trait into your application's object or class. Let's add some log statements to `WeightResource`.

```scala
import com.twitter.finagle.http.Request
import com.twitter.finatra.http.Controller
import com.twitter.inject.Logging
import org.joda.time.Instant

import scala.collection.mutable

class WeightResource extends Controller with Logging {

  val db = mutable.Map[String, List[Weight]]()

  get("/weights") { request: Request =>
    info("finding all weights for all users...")
    db
  }

  get("/weights/:user") { request: Request =>
    info( s"""finding weight for user ${request.params("user")}""")
    db.getOrElse(request.params("user"), List())
  }

  post("/weights") { weight: Weight =>
    val r = time(s"Total time take to post weight for user '${weight.user}' is %d ms") {
      val weightsForUser = db.get(weight.user) match {
        case Some(weights) => weights :+ weight
        case None => List(weight)
      }
      db.put(weight.user, weightsForUser)
      response.created.location(s"/weights/${weight.user}")
    }
    r
  }

}

case class Weight(
                   user: String,
                   weight: Int,
                   status: Option[String],
                   postedAt: Instant = Instant.now()
                 )
```

### Step 8: Validations

Finatra comes with validation annotations that can be used to add validation support. Out of the box Finatra comes with following validation annotations.

1. CountryCode
2. FutureTime
3. Max
4. Min
5. NotEmpty
6. OneOf
7. PastTime
8. Range
9. Size
10. TimeGranularity
11. UUID

All these annotations are defined in `finatra-jackson_2.11` module so you have to add that to build.sbt

```scala
libraryDependencies +=  "com.twitter.finatra" % "finatra-jackson_2.11" % versions.finatra
```

Let's write a test case

```scala
"Bad request when user is not present in request" in {
  server.httpPost(
    path = "/weights",
    postBody =
      """
        |{
        |"weight":85
        |}
      """.stripMargin,
    andExpect = Status.BadRequest
  )
}
```

If you run the test now, it will fail with Http status code 500 i.e. Internal server error.

To make it work first we have to register a filter in the FitmanServer.

```scala
import com.twitter.finatra.http.filters.CommonFilters

class FitmanServer extends HttpServer {
  override protected def configureHttp(router: HttpRouter): Unit = {

    router
      .filter[CommonFilters]
      .add[HelloController]
      .add[WeightResource]
  }
}
```

Now test will pass.

```scala
"Bad request when data not in range" in {
  server.httpPost(
    path = "/weights",
    postBody =
      """
        |{
        |"user":"testing12345678910908980898978798797979789",
        |"weight":250
        |}
      """.stripMargin,
    andExpect = Status.BadRequest,
    withErrors = Seq(
      "user: size [42] is not between 1 and 25",
      "weight: [250] is not between 25 and 200"
    )
  )
}
```

Update the `Weight` case class

```scala
case class Weight(
                   @Size(min = 1, max = 25) user: String,
                   @Range(min = 25, max = 200) weight: Int,
                   status: Option[String],
                   postedAt: Instant = Instant.now()
                 )
```

That's it for this week. Please give your feedback [https://github.com/shekhargulati/52-technologies-in-2016/issues/1](https://github.com/shekhargulati/52-technologies-in-2016/issues/1)

[![Analytics](https://ga-beacon.appspot.com/UA-59411913-2/shekhargulati/52-technologies-in-2016/01-finatra)](https://github.com/igrigorik/ga-beacon)


================================================
FILE: 01-finatra/fitman/.gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### Scala template
*.class
*.log

# sbt specific
.cache
.history
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/

# Scala-IDE specific
.scala_dependencies
.worksheet
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control

### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties



================================================
FILE: 01-finatra/fitman/build.sbt
================================================
name := "fitman"

version := "1.0"

scalaVersion := "2.11.7"

lazy val versions = new {
  val finatra = "2.1.2"
  val logback = "1.1.3"
  val guice = "4.0"
}

resolvers ++= Seq(
  Resolver.sonatypeRepo("releases"),
  "Twitter Maven" at "https://maven.twttr.com"
)

libraryDependencies += "com.twitter.finatra" % "finatra-http_2.11" % versions.finatra
libraryDependencies +=  "com.twitter.finatra" % "finatra-jackson_2.11" % versions.finatra
libraryDependencies += "com.twitter.finatra" % "finatra-slf4j_2.11" % versions.finatra
libraryDependencies += "ch.qos.logback" % "logback-classic" % versions.logback

libraryDependencies += "com.twitter.finatra" % "finatra-http_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" % "inject-server_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" % "inject-app_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" % "inject-core_2.11" % versions.finatra % "test"
libraryDependencies += "com.twitter.inject" %% "inject-modules" % versions.finatra % "test"
libraryDependencies += "com.google.inject.extensions" % "guice-testlib" % versions.guice % "test"
libraryDependencies +=  "com.twitter.finatra" % "finatra-jackson_2.11" % versions.finatra % "test"

libraryDependencies += "com.twitter.finatra" % "finatra-http_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-server_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-app_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-core_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.twitter.inject" % "inject-modules_2.11" % versions.finatra % "test" classifier "tests"
libraryDependencies += "com.google.inject.extensions" % "guice-testlib" % versions.guice % "test" classifier "tests"
libraryDependencies +=  "com.twitter.finatra" % "finatra-jackson_2.11" % versions.finatra % "test"  classifier "tests"

libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.4" % "test"
libraryDependencies += "org.specs2" %% "specs2" % "2.3.12" % "test"

================================================
FILE: 01-finatra/fitman/project/build.properties
================================================
sbt.version = 0.13.8

================================================
FILE: 01-finatra/fitman/project/plugins.sbt
================================================
logLevel := Level.Warn

================================================
FILE: 01-finatra/fitman/src/main/resources/logback.xml
================================================
<configuration>
    <!-- Console Appender -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date %level %-25X{traceId} %-25logger{0} %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Per Package Config -->
    <logger name="com.twitter" level="info"/>

    <!-- Root Logger -->
    <root level="info">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

================================================
FILE: 01-finatra/fitman/src/main/scala/com/shekhargulati/fitman/FitmanApp.scala
================================================
package com.shekhargulati.fitman

import com.shekhargulati.fitman.api.WeightResource
import com.twitter.finagle.http.Request
import com.twitter.finatra.http.filters.CommonFilters
import com.twitter.finatra.http.routing.HttpRouter
import com.twitter.finatra.http.{Controller, HttpServer}

object FitmanApp extends FitmanServer

class FitmanServer extends HttpServer {
  override protected def configureHttp(router: HttpRouter): Unit = {

    router
      .filter[CommonFilters]
      .add[HelloController]
      .add[WeightResource]
  }
}

class HelloController extends Controller {

  get("/hello") { request: Request =>
    "Fitman says hello"
  }

}





================================================
FILE: 01-finatra/fitman/src/main/scala/com/shekhargulati/fitman/api/WeightResource.scala
================================================
package com.shekhargulati.fitman.api

import com.twitter.finagle.http.Request
import com.twitter.finatra.http.Controller
import com.twitter.finatra.validation.{Range, Size}
import com.twitter.inject.Logging
import org.joda.time.Instant

import scala.collection.mutable

class WeightResource extends Controller with Logging {

  val db = mutable.Map[String, List[Weight]]()

  get("/weights") { request: Request =>
    info("finding all weights for all users...")
    db
  }

  get("/weights/:user") { request: Request =>
    info( s"""finding weight for user ${request.params("user")}""")
    db.getOrElse(request.params("user"), List())
  }

  post("/weights") { weight: Weight =>
    val r = time(s"Total time take to post weight for user '${weight.user}' is %d ms") {
      val weightsForUser = db.get(weight.user) match {
        case Some(weights) => weights :+ weight
        case None => List(weight)
      }
      db.put(weight.user, weightsForUser)
      response.created.location(s"/weights/${weight.user}")
    }
    r
  }

}

case class Weight(
                   @Size(min = 1, max = 25) user: String,
                   @Range(min = 25, max = 200) weight: Int,
                   status: Option[String],
                   postedAt: Instant = Instant.now()
                 )


================================================
FILE: 01-finatra/fitman/src/test/scala/com/shekhargulati/fitman/HelloControllerFeatureTest.scala
================================================
package com.shekhargulati.fitman

import com.twitter.finagle.http.Status
import com.twitter.finatra.http.test.EmbeddedHttpServer
import com.twitter.inject.server.FeatureTest

class HelloControllerFeatureTest extends FeatureTest {
  override val server: EmbeddedHttpServer = new EmbeddedHttpServer(
    twitterServer = new FitmanServer)

  "Say Hello" in {
    server.httpGet(
      path = "/hello",
      andExpect = Status.Ok,
      withBody = "Fitman says hello"
    )
  }
}


================================================
FILE: 01-finatra/fitman/src/test/scala/com/shekhargulati/fitman/api/WeightResourceFeatureTest.scala
================================================
package com.shekhargulati.fitman.api

import com.shekhargulati.fitman.FitmanServer
import com.twitter.finagle.http.Status
import com.twitter.finatra.http.test.EmbeddedHttpServer
import com.twitter.inject.server.FeatureTest

class WeightResourceFeatureTest extends FeatureTest {
  override val server = new EmbeddedHttpServer(
    twitterServer = new FitmanServer
  )

  "WeightResource" should {

    "Save user weight when POST request is made" in {
      server.httpPost(
        path = "/weights",
        postBody =
          """
            |{
            |"user":"shekhar",
            |"weight":85,
            |"status":"Feeling great!!!"
            |}
          """.stripMargin,
        andExpect = Status.Created,
        withLocation = "/weights/shekhar"
      )
    }

    "List all weights for a user when GET request is made" in {
      val response = server.httpPost(
        path = "/weights",
        postBody =
          """
            |{
            |"user":"test_user_1",
            |"weight":80,
            |"posted_at" : "2016-01-03T14:34:06.871Z"
            |}
          """.stripMargin,
        andExpect = Status.Created
      )

      server.httpGetJson[List[Weight]](
        path = response.location.get,
        andExpect = Status.Ok,
        withJsonBody =
          """
            |[
            |  {
            |    "user" : "test_user_1",
            |    "weight" : 80,
            |    "posted_at" : "2016-01-03T14:34:06.871Z"
            |  }
            |]
          """.stripMargin
      )
    }

    "Bad request when user is not present in request" in {
      server.httpPost(
        path = "/weights",
        postBody =
          """
            |{
            |"weight":85
            |}
          """.stripMargin,
        andExpect = Status.BadRequest
      )
    }

    "Bad request when data not in range" in {
      server.httpPost(
        path = "/weights",
        postBody =
          """
            |{
            |"user":"testing12345678910908980898978798797979789",
            |"weight":250
            |}
          """.stripMargin,
        andExpect = Status.BadRequest,
        withErrors = Seq(
          "user: size [42] is not between 1 and 25",
          "weight: [250] is not between 25 and 200"
        )
      )
    }
  }
}

================================================
FILE: 02-sbt/README.md
================================================
SBT: The Missing Tutorial [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/52-technologies-in-2016/blob/master/02-sbt/README.md)](http://ttr.myapis.xyz/)
---

Welcome to the second blog of [52-technologies-in-2016](https://github.com/shekhargulati/52-technologies-in-2016) blog series. From last year, I have started using Scala as my main programming language. One of the tools that you have to get used to while working with a programming language is a build tool. In my office projects, we use Gradle for all our projects be it Scala or Java. In most of my personal Scala projects, I have started using `sbt` as my preferred build tool. **`sbt` is a general purpose build tool written in Scala**. Most of the time we try to hack our way while using a build tool never learning it properly. As Scala will be the language that I will cover most in this series, I decided to thoroughly learn `sbt` this week. We (developers) often underestimate the importance of learning a build tool thoroughly and end up not using build tool in the most effective way. Good working knowledge of a build tool can make us more productive so we should take it seriously.

> **This blog is part of my year long blog series [52 Technologies in 2016](https://github.com/shekhargulati/52-technologies-in-2016)**

## Table of Contents

We will cover the following in this tutorial:

* [What is sbt?](#what-is-sbt)
* [Install sbt on your machine](#install-sbt-on-your-machine)
* [Getting started with sbt](#getting-started-with-sbt)
* [sbt modes](#sbt-modes)
* [Create an sbt Scala project](#create-a-sbt-scala-project)
  * [sbt says Hello](#sbt-says-hello)
  * [Set Scala version](#set-scala-version)
  * [Building Tasky application](#building-tasky-application)
    * [Add dependencies](#add-dependencies)
    * [Run tests](#run-tests)
    * [Rerun tests](#rerun-tests)
* [Writing your own tasks](#writing-your-own-tasks)
* [Using plugins](#using-plugins)
* [Tips](#tips)

## What is sbt?

`sbt` i.e. Simple Build Tool is a general purpose build tool written in Scala for JVM developers. It borrows good ideas from other successful build tools like Ant, Maven, and Gradle.

1. Default project layouts
2. Built-in tasks
3. Plugin architecture
4. Declarative Dependency management
5. Code over Configuration: A DSL for build tool

Apart from the feature set mentioned above `sbt` also provides the following additional features:

1. Interactive nature: It isn't just a build tool, it also provides an interactive environment to work in
2. Scala REPL integration

<img src="http://www.scala-sbt.org/assets/typesafe_sbt_svg.svg" height="100" width="100" align="middle">

## Install sbt on your machine

If you are on mac then, you can use package manager like `brew` to install `sbt` on your machine:

```bash
$ brew install sbt
```

For other systems, download the latest version of sbt from the [website](http://www.scala-sbt.org/download.html). Current production version is `0.13.16`. You can download by clicking [https://cocl.us/sbt01316zip](https://cocl.us/sbt01316zip).

You can refer to manual instructions from `sbt` website http://www.scala-sbt.org/0.13/tutorial/Manual-Installation.html. This blog is written using sbt version `0.13.9`.

Once you have successfully installed `sbt` on your machine, create an empty directory somewhere in your file system which we will use for this blog.

To view the basic information about `sbt`, we can use `about` task. The `sbt about` task and its output is shown below:

```
$ sbt about

[info] Set current project to code (in build file:/Users/shekhargulati/blogs/sbt-playground)
[info] This is sbt 0.13.9
[info] The current project is {file:/Users/shekhargulati/blogs/sbt-playground}code 0.1-SNAPSHOT
[info] The current project is built against Scala 2.10.5
[info] Available Plugins: sbt.plugins.IvyPlugin, sbt.plugins.JvmPlugin, sbt.plugins.CorePlugin, sbt.plugins.JUnitXmlReportPlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.10.5
```

As you can see in the second line of the output we are using sbt version 0.13.9.

> First time you run `sbt`, it will download some jars that are required by sbt to perform its job. The default sbt installation is very minimalistic and does not come bundled with everything.

## Getting started with sbt

`sbt` terminology consists of two terms -- **tasks** and **settings**. A task defines an action which you want to perform like compile. A setting is used to define a value for example name and version of the project.

With `sbt` whenever you want to perform any action you execute a task. Task is the unit of currency in `sbt`. A task can depend on another task to do its job. `sbt` creates a task dependency graph to determine which task should run first. If task `t1` depends on task `t2` then task `t2` will be executed first and then task `t1` will be executed. You can view all the tasks applicable to a project by running `sbt tasks` task:

```bash
$ sbt tasks
```

The above task will produce the following output:

```
[info] Set current project to code (in build file:/Users/shekhargulati/blogs/tasky/)

This is a list of tasks defined for the current project.
It does not list the scopes the tasks are defined in; use the 'inspect' task for that.
Tasks produce values.  Use the 'show' task to run the task and print the resulting value.

  clean            Deletes files produced by the build, such as generated sources, compiled classes, and task caches.
  compile          Compiles sources.
  console          Starts the Scala interpreter with the project classes on the classpath.
  consoleProject   Starts the Scala interpreter with the sbt and the build definition on the classpath and useful imports.
  consoleQuick     Starts the Scala interpreter with the project dependencies on the classpath.
  copyResources    Copies resources to the output directory.
  doc              Generates API documentation.
  package          Produces the main artifact, such as a binary jar.  This is typically an alias for the task that actually does the packaging.
  packageBin       Produces a main artifact, such as a binary jar.
  packageDoc       Produces a documentation artifact, such as a jar containing API documentation.
  packageSrc       Produces a source artifact, such as a jar containing sources and resources.
  publish          Publishes artifacts to a repository.
  publishLocal     Publishes artifacts to the local Ivy repository.
  publishM2        Publishes artifacts to the local Maven repository.
  run              Runs a main class, passing along arguments provided on the task line.
  runMain          Runs the main class selected by the first argument, passing the remaining arguments to the main method.
  test             Executes all tests.
  testOnly         Executes the tests provided as arguments or all tests if no arguments are provided.
  testQuick        Executes the tests that either failed before, were not run or whose transitive dependencies changed, among those provided as arguments.
  update           Resolves and optionally retrieves dependencies, producing a report.
```

> One thing that surprised me about `sbt` is that it runs all the tasks in parallel by default. If the tasks have dependencies, then `sbt` uses the task dependency graph to determine which tasks can run in parallel and which can run in a sequential manner. To make it clear, let's suppose we have three tasks `t1`, `t2`, and `t3`. `t1` and `t3` depends on `t2` then `t2` will run first and `t1` and `t3` will run in parallel.

## sbt modes

You can use `sbt` in two modes -- command-line mode and interactive mode. In the command-line mode, you run `sbt` task from your machine terminal. Once the task successfully finishes then `sbt` exits. For example, when you ran `sbt about` task, it printed `sbt` and build information on the console and then `sbt` exited and you were back to your terminal. In the interactive mode, you run `sbt` command and it launches a `sbt` shell. Inside the `sbt` shell session, you run `sbt` tasks.

## Create an sbt Scala project

In this tutorial, we will build a simple task management app -- `tasky`. A task management app will allow us to work with our daily to-do items. Create a new directory `tasky` at any convenient location on your filesystem. Once created, change directory to `tasky`:

```bash
$ mkdir tasky
$ cd tasky
```

> Code for demo application is on [GitHub](./tasky)

Inside the `tasky` directory, create a new file -- `build.sbt` to house the build script. `build.sbt` is the name of the sbt build script. The content of `build.sbt` is shown below:

```scala
name := "tasky"
version := "0.1.0"
```

`:=` is a function defined in the `sbt` library. It is used to define a setting that overwrites any previous value without referring to other settings. For example, `name := "tasky"` will overwrite any previous value set in the `name` variable.

Now, run the `sbt` command:

```
$ sbt
[info] Set current project to tasky (in build file:/Users/shekhargulati/blogs/tasky)
>
```

Once you are inside the `sbt` shell, you can run various `sbt` tasks. To view all the tasks available you can use `help` task:

```bash
> help
```

```
help                                    Displays this help message or prints detailed help on requested tasks (run 'help <task>').
completions                             Displays a list of completions for the given argument string (run 'completions <string>').
about                                   Displays basic information about sbt and the build.
tasks                                   Lists the tasks defined for the current project.
settings                                Lists the settings defined for the current project.
reload                                  (Re)loads the current project or changes to plugins project or returns from it.
projects                                Lists the names of available projects or temporarily adds/removes extra builds to the session.
project                                 Displays the current project or changes to the provided `project`.
set [every] <setting>                   Evaluates a Setting and applies it to the current project.
session                                 Manipulates session settings.  For details, run 'help session'.
inspect [uses|tree|definitions] <key>   Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.
<log-level>                             Sets the logging level to 'log-level'.  Valid levels: debug, info, warn, error
plugins                                 Lists currently available plugins.
; <task> (; <task>)*              Runs the provided semicolon-separated tasks.
~ <task>                             Executes the specified task whenever source files change.
last                                    Displays output from a previous task or the output from a specific task.
last-grep                               Shows lines from the last output for 'key' that match 'pattern'.
export <tasks>+                         Executes tasks and displays the equivalent task lines.
exit                                    Terminates the build.
--<task>                             Schedules a task to run before other tasks on startup.
show <key>                              Displays the result of evaluating the setting or task associated with 'key'.
all <task>+                             Executes all of the specified tasks concurrently.

More task help available using 'help <task>' for:
  !, +, ++, <, alias, append, apply, eval, iflast, onFailure, reboot, shell
```

By default, `sbt` follows Maven project layout i.e. Scala source files are placed inside `src/main/scala` and test source files are placed inside `src/test/scala`:

```bash
$ mkdir -p src/main/scala
$ mkdir -p src/test/scala
```

### sbt says Hello

Now, let's create a new Scala file `HelloSbt.scala` inside `src/main/scala` and place the following contents in it:

```scala
object HelloSbt extends App {
  println("Sbt says Hello!!")
}
```

Now you can run the code from inside the `sbt` shell by first compiling the code using `compile` task and then running it using the `run` task as shown below:

```
> compile
[info] Compiling 1 Scala source to /Users/shekhargulati/blogs/tasky/target/scala-2.10/classes...
[success] Total time: 2 s, completed 10 Jan, 2016 8:51:55 AM
```

```
> run
[info] Running HelloSbt
Sbt says Hello!!
[success] Total time: 0 s, completed 10 Jan, 2016 8:52:15 AM
```

### Set Scala version

In the build output shown above, you can see that `sbt` chose Scala version 2.10. You can specify a different Scala version using `scalaVersion` setting. Update the `build.sbt` with the `scalaVersion` setting.

```scala
name := "tasky"
version := "0.1.0"
scalaVersion := "2.11.6"
```

`sbt` will not pick any change in the `build.sbt` until you run the `reload` task. Execute the `reload` task to refresh the `sbt` shell with new build script:

```
> reload
[info] Set current project to tasky (in build file:/Users/shekhargulati/blogs/tasky/)
```

Now if you compile the project using `compile` task you will see that project is compiled using scala `2.11.6` version:

```
> compile
[info] Updating {file:/Users/shekhargulati/blogs/tasky/}tasky...
[info] Resolving jline#jline;2.12.1 ...
[info] Done updating.
[info] Compiling 1 Scala sources to /Users/shekhargulati/dev/blogs/tasky/target/scala-2.11/classes...
[info] 'compiler-interface' not yet compiled for Scala 2.11.6. Compiling...
[info]   Compilation completed in 7.316 s
[success] Total time: 8 s, completed 10 Jan, 2016 1:30:06 PM
```

> From the [sbt documentation](http://www.scala-sbt.org/0.13/docs/Howto-Scala.html): If the Scala version is not specified, the version sbt was built against is used. It is recommended to explicitly specify the version of Scala.  
> Please note that because `compile` is a dependency of `run`, you don’t have to run `compile` before each `run`; just type `sbt run`.

### Building Tasky application

Let's create a simple data model for our task management application. Create a new file `datamodels.scala` inside the `src/main/scala`. Fill the file with the following contents.

```scala
import java.time.LocalDate

case class Task(title: String, dueOn: LocalDate, tags: Seq[String] = Seq(), finished: Boolean = false)
```

> Please note we are using Java 8 Date-Time API. If you want to learn Java 8, then you can refer to my Java 8 tutorial  [https://github.com/shekhargulati/java8-the-missing-tutorial](https://github.com/shekhargulati/java8-the-missing-tutorial)

If you are inside the `sbt` shell, then you can compile the code using `compile` task. To experiment with your data model, you can use `sbt` in an interactive mode by executing the `console` task from within the `sbt` shell:

```
> console
[info] Updating {file:/Users/shekhargulati/blogs/tasky/}tasky...
[info] Resolving jline#jline;2.12.1 ...
[info] Done updating.
[info] Compiling 3 Scala sources to /Users/shekhargulati/blogs/tasky/target/scala-2.11/classes...
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions to have them evaluated.
Type :help for more information.

scala>
```

Now you can start using `Task` class. Let's create a task:

```scala
scala> import java.time.{LocalDate,Month}
import java.time.{LocalDate, Month}

scala> val t1 = Task("Write blog on SBT", LocalDate.of(2016,Month.JANUARY,10), Seq("blogging"))
t1: Task = Task(Write blog on SBT,2016-01-10,List(blogging),false)

scala> val t2 = Task("Write a factorial program", LocalDate.of(2016,Month.JANUARY,11), Seq("coding"))
t2: Task = Task(Write a factorial program,2016-01-11,List(coding),false)
```

We can find all the tasks which are due today by writing the following code:

```scala
scala> val tasks = Seq(t1,t2)
tasks: Seq[Task] = List(Task(Write blog on SBT,2016-01-10,List(blogging),false), Task(Write a factorial program,2016-01-11,List(coding),false))

scala> tasks.filter(t => LocalDate.now().isEqual(t.dueOn))
res3: Seq[Task] = List(Task(Write blog on SBT,2016-01-10,List(blogging),false))
```

This is the kind of experiment driven development that sbt promotes. Once you have a rough idea of what you want to do then, you can start using the TDD approach to get things done.

Create a new scala file `taskmanager.scala` inside `src/main/scala` and place the following content:

```scala
object TaskManager {

  def allTasksDueToday(tasks: List[Task]): List[Task] = Nil

}
```

In the code shown above, we have created a scala object `TaskManager` which defines a single method `allTasksDueToday`. Currently, we have not written any implementation as we will first write test case for this method. Let's start with writing a test case for `allTasksDueToday` method. To write a test case, we have to choose a scala library that we can use. For this tutorial, we will use `scalatest` library.

#### Add Dependencies

To add `scalatest` dependency to your Scala sbt project, add the following line to `build.sbt`. `sbt` uses Apache Ivy dependency manager to perform automatic dependency management:

```scala
libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.6" % "test"
```

`sbt` exposes keys that are defined in [Keys.scala](http://www.scala-sbt.org/0.13/sxr/sbt/Keys.scala.html) to `build.sbt`. Keys are of three types: Setting Key, Task Key, and Input Key. From the [sbt documentation](http://www.scala-sbt.org/release/tutorial/Basic-Def.html),

* `SettingKey[T]`: a key for a value computed once (the value is computed when loading the project, and kept around).

* `TaskKey[T]`: a key for a value, called a task, that has to be recomputed each time, potentially with side effects.

* `InputKey[T]`: a key for a task that has task line arguments as input.

Syntax to add a library to build.sbt looks like as shown below:

```
libraryDependencies += groupID % artifactID % version % configuration
```

`configuration` is not required for all dependencies. For `scalatest` dependency we have used `test` configuration.

`libraryDependencies` is a SettingKey that stores all declared managed dependencies. This key is populated only once when a project is loaded and then it is reused. Whenever you add a dependency in `build.sbt` file then you have to call the reload task to update dependencies:

```
> reload
[info] Set current project to tasky (in build file:/Users/shekhargulati/blogs/tasky/)
```

> `reload` will not download the dependencies that you add in the `build.sbt` file. It will only refresh the project model so when you run task next time it will download all the required dependencies.

#### Run Tests

Let's write a test for `TaskManager` `allTasksDueToday` method. There are various testing styles that you can use with `scalatest`. In this tutorial, I am using `FlatSpec` style. You can refer to [scalatest documentation for more information](http://www.scalatest.org/user_guide/selecting_a_style).

Create `src/test/scala/TaskManagerSpec.scala` and add the following code to it:

```scala
import org.scalatest._

class TaskManagerSpec extends FlatSpec with Matchers {

  "An empty tasks list" should "have 0 tasks due today" in {
      val tasksDueToday = TaskManager.allTasksDueToday(List())
      tasksDueToday should have length 0
  }

}
```

To run the test you can execute the test task:

```
> test
[info] Updating {file:/Users/shekhargulati/blogs/tasky/}tasky...
[info] Resolving jline#jline;2.12.1 ...
[info] downloading https://jcenter.bintray.com/org/scalatest/scalatest_2.11/2.2.6/scalatest_2.11-2.2.6.jar ...
[info] 	[SUCCESSFUL ] org.scalatest#scalatest_2.11;2.2.6!scalatest_2.11.jar(bundle) (42568ms)
[info] Done updating.
[info] Compiling 1 Scala source to /Users/shekhargulati/blogs/tasky/target/scala-2.11/classes...
[info] Compiling 1 Scala source to  /Users/shekhargulati/blogs/tasky/target/scala-2.11/test-classes...
[info] TaskManagerSpec:
[info] An empty tasks list
[info] - should have 0 tasks due today
[info] Run completed in 215 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 2 s, completed 10 Jan, 2016 2:15:41 PM
```

> Please note first time you run the `test` task test dependencies will be downloaded from a central repository as can be seen from the output of test task shown above.

#### Rerun Tests

One of the coolest features of `sbt` is that it can rerun your tasks without manual intervention whenever any project source file changes. This is enabled using the `~` operator. If you prefix any sbt task with `~` then `sbt` will wait for changes in the source files. As soon as any file changes, it will rerun that task.

Type the `~test` command inside `sbt` shell:

```
> ~test
[info] TaskManagerSpec:
[info] An empty tasks list
[info] - should have 0 tasks due today
[info] Run completed in 167 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 1 s, completed 10 Jan, 2016 2:43:45 PM
1. Waiting for source changes... (press enter to interrupt)
```

As you can see above, `~test` ran the our `TaskManagerSpec` and then entered into watch mode. If we add any new test or change the existing test then `test` task will run again.

Let's add a new test case for scenario when we have a non-empty task list.

```scala
import org.scalatest._
import java.time.LocalDate

class TaskManagerSpec extends FlatSpec with Matchers {

  "An empty tasks list" should "have 0 tasks due today" in {
      val tasksDueToday = TaskManager.allTasksDueToday(List())
      tasksDueToday should have length 0
  }

  "A task list with one task due today" should "have 1 task due today" in {
    val t1 = Task("Write blog on SBT", LocalDate.now(), Seq("blogging"))
    val t2 = Task("Write a factorial program", LocalDate.now().plusDays(1), Seq("coding"))
    val tasksDueToday = TaskManager.allTasksDueToday(List(t1, t2))
    tasksDueToday should have length 1
  }

}
```

As soon as you save the file, `sbt` will detect file content change and rerun all the tests. The newly added test will fail as we have not yet added the actual implementation in `allTasksDueToday` method. `sbt` console output is shown below.

```
1. Waiting for source changes... (press enter to interrupt)
[info] Compiling 1 Scala source to /Users/shekhargulati/blogs/tasky/target/scala-2.11/test-classes...
[info] TaskManagerSpec:
[info] An empty tasks list
[info] - should have 0 tasks due today
[info] A task list with one task due today
[info] - should have 1 task due today *** FAILED ***
[info]   List() had length 0 instead of expected length 1 (TaskManagerSpec.scala:15)
[info] Run completed in 172 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 1, canceled 0, ignored 0, pending 0
[info] *** 1 TEST FAILED ***
[error] Failed tests:
[error] 	TaskManagerSpec
[error] (test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 3 s, completed 10 Jan, 2016 2:50:53 PM
2. Waiting for source changes... (press enter to interrupt)
```

Let's add the actual implementation to `allTasksDueToday` method:

```scala
import java.time.LocalDate

object TaskManager {

  def allTasksDueToday(tasks: List[Task]): List[Task] = tasks.filter(t => t.dueOn.isEqual(LocalDate.now))

}
```

Now, tests will pass and you will see the following output in the sbt console:

```
2. Waiting for source changes... (press enter to interrupt)
[info] Compiling 1 Scala source to /Users/shekhargulati/blogs/tasky/target/scala-2.11/classes...
[info] TaskManagerSpec:
[info] An empty tasks list
[info] - should have 0 tasks due today
[info] A task list with one task due today
[info] - should have 1 task due today
[info] Run completed in 143 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 1 s, completed 10 Jan, 2016 2:55:37 PM
3. Waiting for source changes... (press enter to interrupt)
```

## Writing your own tasks

`sbt` makes it very easy to define your own tasks. Let's write a simple task that prints total number of commits on the current Git branch. Creating a custom task is a two step process:

1. You have to define a `TaskKey` for your task
2. You have to provide the task definition

To write our task we will first write `gitCommitCountTask` `taskKey` in the `build.sbt` file:

```scala
val gitCommitCountTask = taskKey[String]("Prints commit count of the current branch")
```

The type specified in the taskKey i.e. String in this case becomes the type of the task result.

The task definition of the `gitCommitCountTask` is shown below. It uses `git` command-line to get the relevant information:

```scala
gitCommitCountTask := {
  val branch = scala.sys.process.Process("git symbolic-ref -q HEAD").lines.head.replace("refs/heads/","")
  val commitCount = scala.sys.process.Process(s"git rev-list --count $branch").lines.head
  println(s"total number of commits on [$branch]: $commitCount")
  commitCount
}
```

You can run the `gitCommitCountTask` task as shown below. You can also execute the `gitCommitCountTask` from inside the sbt shell:

```
$ sbt gitCommitCountTask
[info] Set current project to tasky (in build file:/Users/shekhargulati/blogs/tasky/)
total number of commits on [master]: 10
[success] Total time: 0 s, completed 10 Jan, 2016 5:16:07 PM
```

To learn more about writing custom tasks refer to [sbt documentation](http://www.scala-sbt.org/0.13/tutorial/Custom-Settings.html).

## Using plugins

Plugin allows you to package your tasks so that you can distribute and reuse them easily. One of the plugins that I include in my Scala projects is Scalastyle sbt plugin. Scalastyle is a style checkers for the Scala programming language. From the [Scalastyle project website](http://www.scalastyle.org/),

> Scalastyle examines your Scala code and indicates potential problems with it. If you have come across Checkstyle for Java, then you’ll have a good idea what scalastyle is. Except that it’s for Scala obviously.

To include `scalastyle-sbt-plugin` in your build, you have to add `scalastyle-sbt-plugin` inside `project/plugins.sbt` file. Create a new file `project/plugins.sbt` and add the following content in it:

```scala
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")
```

> It is a naming convention to define plugins in the `plugins.sbt` file. You can name it anything else as well.

Once you have defined the plugin, you can use the plugin by executing the task it exposes. The `scalastyle-sbt-plugin` exposes `scalastyle` task. Let's check the quality of our code:

```
→ sbt scalastyle
[info] Loading project definition from /Users/shekhargulati/blogs/tasky/project
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] downloading https://repo1.maven.org/maven2/org/scalastyle/scalastyle-sbt-plugin_2.10_0.13/0.8.0/scalastyle-sbt-plugin-0.8.0.jar ...
[info] 	[SUCCESSFUL ] org.scalastyle#scalastyle-sbt-plugin;0.8.0!scalastyle-sbt-plugin.jar (3199ms)
[info] downloading https://jcenter.bintray.com/org/scalastyle/scalastyle_2.10/0.8.0/scalastyle_2.10-0.8.0.jar ...
[info] 	[SUCCESSFUL ] org.scalastyle#scalastyle_2.10;0.8.0!scalastyle_2.10.jar (20244ms)
[info] downloading https://jcenter.bintray.com/org/scalariform/scalariform_2.10/0.1.7/scalariform_2.10-0.1.7.jar ...
[info] 	[SUCCESSFUL ] org.scalariform#scalariform_2.10;0.1.7!scalariform_2.10.jar (46701ms)
[info] downloading https://jcenter.bintray.com/com/typesafe/config/1.2.0/config-1.2.0.jar ...
[info] 	[SUCCESSFUL ] com.typesafe#config;1.2.0!config.jar(bundle) (9354ms)
[info] Done updating.
[info] Set current project to tasky (in build file:/Users/shekhargulati/blogs/tasky/)
[info] scalastyle using config /Users/shekhargulati/blogs/tasky/scalastyle-config.xml
java.lang.RuntimeException: config does not exist: scalastyle-config.xml
	at scala.sys.package$.error(package.scala:27)
[error] (compile:scalastyle) config does not exist: scalastyle-config.xml
[error] Total time: 0 s, completed 10 Jan, 2016 6:01:23 PM
```

The task will fail because plugin could not find `scalastyle-config.xml`. You can generate the configuration file using the `scalastyleGenerateConfig` task:

```
→ sbt scalastyleGenerateConfig
[info] Loading project definition from /Users/shekhargulati/blogs/tasky/project
[info] Set current project to tasky (in build file:/Users/shekhargulati/blogs/tasky/)
[success] created: /Users/shekhargulati/blogs/tasky/scalastyle-config.xml
[success] Total time: 0 s, completed 10 Jan, 2016 6:04:04 PM
```

Now, re-run the `scalastyle` task to check the quality of your project. This time task will get executed successfully.

You can learn more about Scalastyle from its website [http://www.scalastyle.org/](http://www.scalastyle.org/).

## Tips

These are some of the quick tips that might help you when you use `sbt`.

### Tip 1: Getting the right Scala version with %%

As mentioned before Scala remain binary compatible only between minor versions. This results in various library versions for different scala versions. Few sections back, we used scalatest library. The dependency was defined as follows:

```scala
libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.6" % "test"
```

The scala version was specified in the artifactID "scalatest_2.11". This means every time we update the Scala version we would have to update the dependency. We can implicitly get the Scala version using the `%%` operator:

```scala
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"
```

### Tip 2: Cross-compilation for multiple Scala versions

One thing that we all Java developers take for granted is that Java remain binary compatible between releases. This means you can run code written using JDK 1.0 on JDK 1.8. This is not true for Scala. Scala only remains binary compatible between minor versions i.e. 2.10.1 will remain binary compatible with minor version 2.10.2 but not with major version 2.11.0. Let's suppose you have a library that you want to compile using different versions of Scala. In your `build.sbt` you can .

```scala
scalaVersion := "2.11.1"

crossScalaVersions := Seq("2.9.1", "2.10.1")
```

Now, when you will use sbt to build the project by default, it will build the project against the Scala version 2.11.1 but, you have an option to use other Scala versions defined in your build script.


### Tip 3: Pass options to Scala compiler

You can pass options to `scalac` by defining a setting `scalacOptions` as shown below:

```scala
scalacOptions ++= Seq("-feature", "-language:_", "-unchecked", "-deprecation", "-encoding", "utf8")
```

### Tip 4: View compile classpath dependencies

To view compile classpath dependencies you can run the following task from inside the sbt shell. Task and its output is shown below:

```scala
> show compile:dependencyClasspath

[info] List(Attributed(/Users/shekhargulati/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.11.6.jar), Attributed(/Users/shekhargulati/.ivy2/cache/com.typesafe.slick/slick_2.11/bundles/slick_2.11-3.1.1.jar), Attributed(/Users/shekhargulati/.ivy2/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.7.10.jar), Attributed(/Users/shekhargulati/.ivy2/cache/com.typesafe/config/bundles/config-1.2.1.jar), Attributed(/Users/shekhargulati/.ivy2/cache/org.reactivestreams/reactive-streams/jars/reactive-streams-1.0.0.jar), Attributed(/Users/shekhargulati/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.1.3.jar), Attributed(/Users/shekhargulati/.ivy2/cache/ch.qos.logback/logback-core/jars/logback-core-1.1.3.jar))
```

Similarly, if you have to view test classpath then you can run `show test:dependencyClasspath` task.

### Tip 5: View dependency graph

If you are Maven or Gradle user then one command that you would like to use is to view the dependency graph. sbt does not have a inbuilt command to view the dependency graph. You can view the dependency graph by using [sbt-dependency-graph plugin](https://github.com/jrudolph/sbt-dependency-graph).

To use the plugin, first add the plugin to `project/plugins.sbt`:

```scala
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.1")
```

Once done, reload the build configuration using the `reload` task.

Now, you will be able to use tasks defined by sbt-dependency-graph plugin. You can refer to sbt-dependency-graph plugin [documentation](https://github.com/jrudolph/sbt-dependency-graph#main-tasks) to get an overview of all the defined tasks:

```
> dependencyTree
[info] Updating {file:/Users/shekhargulati/blogs/fitman/}fitman...
[info] Resolving jline#jline;2.12.1 ...
[info] Done updating.
[info] default:fitman_2.11:0.1.0 [S]
[info]   +-ch.qos.logback:logback-classic:1.1.3
[info]   | +-ch.qos.logback:logback-core:1.1.3
[info]   | +-org.slf4j:slf4j-api:1.7.10
[info]   | +-org.slf4j:slf4j-api:1.7.7 (evicted by: 1.7.10)
[info]   |
[info]   +-com.typesafe.slick:slick_2.11:3.1.1 [S]
[info]     +-com.typesafe:config:1.2.1
[info]     +-org.reactivestreams:reactive-streams:1.0.0
[info]     +-org.slf4j:slf4j-api:1.7.10
[info]
[success] Total time: 0 s, completed 17 Jan, 2016 3:00:51 PM
```

That's all for this week. Please provide your valuable feedback by adding a comment to [https://github.com/shekhargulati/52-technologies-in-2016/issues/2](https://github.com/shekhargulati/52-technologies-in-2016/issues/2).

[![Analytics](https://ga-beacon.appspot.com/UA-59411913-2/shekhargulati/52-technologies-in-2016/02-sbt)](https://github.com/igrigorik/ga-beacon)


================================================
FILE: 02-sbt/tasky/.gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### Scala template
*.class
*.log

# sbt specific
.cache
.history
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/

# Scala-IDE specific
.scala_dependencies
.worksheet
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control

### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties



================================================
FILE: 02-sbt/tasky/build.sbt
================================================
name := "tasky"
version := "0.1.0"
scalaVersion := "2.11.6"

libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.6" % "test"

val gitCommitCountTask = taskKey[String]("Prints commit count of the current branch")

gitCommitCountTask := {
  val branch = Process("git symbolic-ref -q HEAD").lines.head.replace("refs/heads/","")
  val commitCount = Process(s"git rev-list --count $branch").lines.head
  println(s"total number of commits on [$branch]: $commitCount")
  commitCount
}


================================================
FILE: 02-sbt/tasky/project/plugins.sbt
================================================
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")


================================================
FILE: 02-sbt/tasky/scalastyle-config.xml
================================================
<scalastyle>
 <name>Scalastyle standard configuration</name>
 <check level="warning" class="org.scalastyle.file.FileTabChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.file.FileLengthChecker" enabled="true">
  <parameters>
   <parameter name="maxFileLength"><![CDATA[800]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.file.HeaderMatchesChecker" enabled="true">
  <parameters>
   <parameter name="header"><![CDATA[// Copyright (C) 2011-2012 the original author or authors.
// See the LICENCE.txt file distributed with this work for additional
// information regarding copyright ownership.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.SpacesAfterPlusChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.file.WhitespaceEndOfLineChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.SpacesBeforePlusChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.file.FileLineLengthChecker" enabled="true">
  <parameters>
   <parameter name="maxLineLength"><![CDATA[160]]></parameter>
   <parameter name="tabSize"><![CDATA[4]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.ClassNamesChecker" enabled="true">
  <parameters>
   <parameter name="regex"><![CDATA[[A-Z][A-Za-z]*]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.ObjectNamesChecker" enabled="true">
  <parameters>
   <parameter name="regex"><![CDATA[[A-Z][A-Za-z]*]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.PackageObjectNamesChecker" enabled="true">
  <parameters>
   <parameter name="regex"><![CDATA[^[a-z][A-Za-z]*$]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.EqualsHashCodeChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.IllegalImportsChecker" enabled="true">
  <parameters>
   <parameter name="illegalImports"><![CDATA[sun._,java.awt._]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.ParameterNumberChecker" enabled="true">
  <parameters>
   <parameter name="maxParameters"><![CDATA[8]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.MagicNumberChecker" enabled="true">
  <parameters>
   <parameter name="ignore"><![CDATA[-1,0,1,2,3]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.NoWhitespaceBeforeLeftBracketChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.NoWhitespaceAfterLeftBracketChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.ReturnChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.NullChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.NoCloneChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.NoFinalizeChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.CovariantEqualsChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.StructuralTypeChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.file.RegexChecker" enabled="true">
  <parameters>
   <parameter name="regex"><![CDATA[println]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.NumberOfTypesChecker" enabled="true">
  <parameters>
   <parameter name="maxTypes"><![CDATA[30]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.CyclomaticComplexityChecker" enabled="true">
  <parameters>
   <parameter name="maximum"><![CDATA[10]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.UppercaseLChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.SimplifyBooleanExpressionChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.scalariform.IfBraceChecker" enabled="true">
  <parameters>
   <parameter name="singleLineAllowed"><![CDATA[true]]></parameter>
   <parameter name="doubleLineAllowed"><![CDATA[false]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.MethodLengthChecker" enabled="true">
  <parameters>
   <parameter name="maxLength"><![CDATA[50]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.MethodNamesChecker" enabled="true">
  <parameters>
   <parameter name="regex"><![CDATA[^[a-z][A-Za-z0-9]*$]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.NumberOfMethodsInTypeChecker" enabled="true">
  <parameters>
   <parameter name="maxMethods"><![CDATA[30]]></parameter>
  </parameters>
 </check>
 <check level="warning" class="org.scalastyle.scalariform.PublicMethodsHaveTypeChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.file.NewLineAtEofChecker" enabled="true"></check>
 <check level="warning" class="org.scalastyle.file.NoNewLineAtEofChecker" enabled="false"></check>
</scalastyle>

================================================
FILE: 02-sbt/tasky/src/main/scala/HelloSbt.scala
================================================
object HelloSbt extends App {
  println("Sbt says Hello!!")
}


================================================
FILE: 02-sbt/tasky/src/main/scala/datamodels.scala
================================================
import java.time.LocalDate

case class Task(title: String, dueOn: LocalDate, tags: Seq[String] = Seq(), finished: Boolean = false)


================================================
FILE: 02-sbt/tasky/src/main/scala/taskmanager.scala
================================================
import java.time.LocalDate

object TaskManager {

  def allTasksDueToday(tasks: List[Task]): List[Task] = tasks.filter(t => t.dueOn.isEqual(LocalDate.now))

}


================================================
FILE: 02-sbt/tasky/src/test/scala/TaskManagerSpec.scala
================================================
import org.scalatest._
import java.time.LocalDate

class TaskManagerSpec extends FlatSpec with Matchers {

  "An empty tasks list" should "have 0 tasks due today" in {
      val tasksDueToday = TaskManager.allTasksDueToday(List())
      tasksDueToday should have length 0
  }

  "A task list with one task due today" should "have 1 task due today" in {
    val t1 = Task("Write blog on SBT", LocalDate.now(), Seq("blogging"))
    val t2 = Task("Write a factorial program", LocalDate.now().plusDays(1), Seq("coding"))
    val tasksDueToday = TaskManager.allTasksDueToday(List(t1, t2))
    tasksDueToday should have length 1
  }

}


================================================
FILE: 03-stanford-corenlp/README.md
================================================
Sentiment Analysis in Scala with Stanford CoreNLP
-----

So far in this [series](https://github.com/shekhargulati/52-technologies-in-2016), we have looked at [finatra](../01-finatra) and [sbt](../02-sbt) open-source Scala projects. This week I decided to learn Stanford CoreNLP library for performing sentiment analysis of unstructured text in Scala.

Sentiment analysis or opinion mining is a field that uses natural language processing to analyze sentiments in a given text. It has applications in many domains ranging from marketing to customer service. Few years back, I wrote a simple Java application using [Naive Bayes classifier](https://en.wikipedia.org/wiki/Naive_Bayes_classifier) to determine whether people liked a movie or not based on sentiment analysis of tweets about a movie.

From the [Stanford CoreNLP website](http://stanfordnlp.github.io/CoreNLP/),

> **Stanford CoreNLP provides a set of natural language analysis tools. It can give the base forms of words, their parts of speech, whether they are names of companies, people, etc., normalize dates, times, and numeric quantities, and mark up the structure of sentences in terms of phrases and word dependencies, indicate which noun phrases refer to the same entities, indicate sentiment, extract open-class relations between mentions, etc.**

<center><img src="https://avatars1.githubusercontent.com/u/3046006" width="150"></center>

## Github repository

The code for today’s demo application is available on github: [sentiment-analyzer](./sentiment-analyzer).

## Getting Started

Start by creating a new directory `sentiment-analyzer` at a convenient location on your filesystem. This directory will house the source code of our application.

```bash
$ mkdir sentiment-analyzer
```

Create a new file `build.sbt` inside the `sentiment-analyzer` directory. `build.sbt` is the sbt build script.
> **If you are new to sbt, then [please refer to my earlier post on it](../02-sbt/README.md).**

Populate `build.sbt` with following contents.

```scala
name := "sentiment-analyzer"
description := "A demo application to showcase sentiment analysis using Stanford CoreNLP and Scala"
version  := "0.1.0"

scalaVersion := "2.11.7"

libraryDependencies += "edu.stanford.nlp" % "stanford-corenlp" % "3.5.2" artifacts (Artifact("stanford-corenlp", "models"), Artifact("stanford-corenlp"))
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"
```

One thing that you might not understand in the above mentioned build script is the usage of `artifacts`. `artifacts` is used when the dependency you have defined in your build script has published multiple artifacts. We have used `artifacts` above to tell sbt that we need to include both `stanford-corenlp` and `stanford-models` dependencies in our classpath. `stanford-corenlp` defines the core API that we will use in our code and `stanford-models` contains all the data model files that `stanford-corenlp` library uses underneath. `stanford-models` library is **378.1 MB** in size so sbt will take some time to download it.

Create a project layout for your Scala source and test files.

```bash
$ mkdir -p src/main/{scala,resources}
$ mkdir -p src/test/scala
```

## Writing SentimentAnalyzer

The main part of the application is to analyze text for sentiments. We will write a sentiment analyzer in Scala that uses `stanford-corenlp` API.

Let's start by writing a test case for positive sentiment. Create a new file `SentimentAnalyzerSpec.scala` inside `src/test/scala` directory. We are using `scalatest` to write our test cases.

```scala
import org.scalatest.{FunSpec, Matchers}

class SentimentAnalyzerSpec extends FunSpec with Matchers {

  describe("sentiment analyzer") {

    it("should return POSITIVE when input has positive emotion") {
      val input = "Scala is a great general purpose language."
      val sentiment = SentimentAnalyzer.mainSentiment(input)
      sentiment should be(Sentiment.POSITIVE)
    }
}
```

The test case shown above calls the `SentimentAnalyzer` API's `mainSentiment` method. If the sentiment returned by `SentimentAnalyzer` is `Sentiment.POSITIVE` then the test will pass. The `mainSentiment` method will return sentiment of the largest line of the text i.e. for input `Scala is a great general purpose language. I don't use it often.` will return `Sentiment.POSITIVE` as the longer line of the text `Scala is a great general purpose language` has positive emotion.

`Sentiment` is an enum that we have defined in our application.

```scala
object Sentiment extends Enumeration {
  type Sentiment = Value
  val POSITIVE, NEGATIVE, NEUTRAL = Value

  def toSentiment(sentiment: Int): Sentiment = sentiment match {
    case x if x == 0 || x == 1 => Sentiment.NEGATIVE
    case 2 => Sentiment.NEUTRAL
    case x if x == 3 || x == 4 => Sentiment.POSITIVE
  }
}
```

`Sentiment` is a Scala enum with a `toSentiment` method defined. The `toSentiment` method is used by `SentimentAnalyzer`(discussed below) to convert integer sentiment value returned by `stanford-corenlp` API to enum constant. The `stanford-corenlp` library gives sentiment of 0 or 1 when text has negative emotion, 2 when text is neutral, 3 or 4 when text has positive emotion.

Let's now discuss about `SentimentAnalyzer`. Full source code of `SentimentAnalyzer` is shown below.

```scala
import java.util.Properties

import com.shekhargulati.sentiment_analyzer.Sentiment.Sentiment
import edu.stanford.nlp.ling.CoreAnnotations
import edu.stanford.nlp.neural.rnn.RNNCoreAnnotations
import edu.stanford.nlp.pipeline.{Annotation, StanfordCoreNLP}
import edu.stanford.nlp.sentiment.SentimentCoreAnnotations

import scala.collection.convert.wrapAll._

object SentimentAnalyzer {

  val props = new Properties()
  props.setProperty("annotators", "tokenize, ssplit, parse, sentiment")
  val pipeline: StanfordCoreNLP = new StanfordCoreNLP(props)

  def mainSentiment(input: String): Sentiment = Option(input) match {
    case Some(text) if !text.isEmpty => extractSentiment(text)
    case _ => throw new IllegalArgumentException("input can't be null or empty")
  }

  private def extractSentiment(text: String): Sentiment = {
    val (_, sentiment) = extractSentiments(text)
      .maxBy { case (sentence, _) => sentence.length }
    sentiment
  }

  def extractSentiments(text: String): List[(String, Sentiment)] = {
    val annotation: Annotation = pipeline.process(text)
    val sentences = annotation.get(classOf[CoreAnnotations.SentencesAnnotation])
    sentences
      .map(sentence => (sentence, sentence.get(classOf[SentimentCoreAnnotations.SentimentAnnotatedTree])))
      .map { case (sentence, tree) => (sentence.toString,Sentiment.toSentiment(RNNCoreAnnotations.getPredictedClass(tree))) }
      .toList
  }

}
```

The code shown above does the following:

1. Creates an instance of `StanfordCoreNLP`. `StanfordCoreNLP` internally constructs a pipeline that takes a text and returns various analyzed linguistic forms. The properties defines which all annotators will be used by the pipeline. For this application `tokenize, ssplit, parse, sentiment` annotators will be used.

2. The `mainSentiment` method checks if string is valid and if valid it calls the `extractSentiment` with the input text.

3. The `extractSentiment` method calls `maxBy` operation on a list of two value tuple returned by `extractSentiments`. The tuple contains a sentence and sentiment. The maxBy operation compares values based on sentence length i.e. a sentence with largest length will be used as main sentiment of text.

4. The `extractSentiments` method uses `StanfordCoreNLP` to process the text. The `process` method processes the text by running the pipeline on the input text. We then get all the sentence annotations from the `annotation`. As we have already imported `scala.collection.convert.wrapAll._` so we can call all the usual Scala collection methods on the `sentences` list. For each sentence annotation in the sentences annotation, we create a tuple of sentence text and sentiment value. Finally, we return the transformed tuple(tuple of sentence text and sentiment) list.

We can also write test cases for negative and neutral scenarios as shown below.

```scala
it("should return NEGATIVE when input has negative emotion") {
  val input = "Dhoni laments bowling, fielding errors in series loss"
  val sentiment = SentimentAnalyzer.mainSentiment(input)
  sentiment should be(Sentiment.NEGATIVE)
}

it("should return NEUTRAL when input has no emotion") {
  val input = "I am reading a book"
  val sentiment = SentimentAnalyzer.mainSentiment(input)
  sentiment should be(Sentiment.NEUTRAL)
}
```

We can also write another public method that just returns all the sentences and their sentiments as shown below.

```scala
def sentiment(input: String): List[(String, Sentiment)] = Option(input) match {
  case Some(text) if !text.isEmpty => extractSentiments(text)
  case _ => throw new IllegalArgumentException("input can't be null or empty")
}
```

That's all for this week. Please provide your valuable feedback by adding a comment to https://github.com/shekhargulati/52-technologies-in-2016/issues/5.

[![Analytics](https://ga-beacon.appspot.com/UA-59411913-2/shekhargulati/52-technologies-in-2016/03-stanford-corenlp)](https://github.com/igrigorik/ga-beacon)


================================================
FILE: 03-stanford-corenlp/sentiment-analyzer/.gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### Scala template
*.class
*.log

# sbt specific
.cache
.history
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/

# Scala-IDE specific
.scala_dependencies
.worksheet
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control

target/
lib_managed/
src_managed/
project/boot/
.history
.cache
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties



================================================
FILE: 03-stanford-corenlp/sentiment-analyzer/build.sbt
================================================
name := "sentiment-analyzer"
description := "A demo application to showcase sentiment analysis using Stanford CoreNLP and Scala"
version  := "0.1.0"

scalaVersion := "2.11.7"

libraryDependencies += "edu.stanford.nlp" % "stanford-corenlp" % "3.5.2" artifacts (Artifact("stanford-corenlp", "models"), Artifact("stanford-corenlp"))
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"


================================================
FILE: 03-stanford-corenlp/sentiment-analyzer/project/plugins.sbt
================================================
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.1")

================================================
FILE: 03-stanford-corenlp/sentiment-analyzer/src/main/scala/com/shekhargulati/sentiment_analyzer/SentimentAnalyzer.scala
================================================
package com.shekhargulati.sentiment_analyzer

import java.util.Properties

import com.shekhargulati.sentiment_analyzer.Sentiment.Sentiment
import edu.stanford.nlp.ling.CoreAnnotations
import edu.stanford.nlp.neural.rnn.RNNCoreAnnotations
import edu.stanford.nlp.pipeline.{Annotation, StanfordCoreNLP}
import edu.stanford.nlp.sentiment.SentimentCoreAnnotations

import scala.collection.convert.wrapAll._

object SentimentAnalyzer {

  val props = new Properties()
  props.setProperty("annotators", "tokenize, ssplit, parse, sentiment")
  val pipeline: StanfordCoreNLP = new StanfordCoreNLP(props)

  /**
    * Extracts the main sentiment for a given input
    */
  def mainSentiment(input: String): Sentiment = Option(input) match {
    case Some(text) if text.nonEmpty => extractSentiment(text)
    case _ => throw new IllegalArgumentException("input can't be null or empty")
  }

  /**
    * Extracts a list of sentiments for a given input
    */
  def sentiment(input: String): List[(String, Sentiment)] = Option(input) match {
    case Some(text) if text.nonEmpty => extractSentiments(text)
    case _ => throw new IllegalArgumentException("input can't be null or empty")
  }

  private def extractSentiment(text: String): Sentiment = {
    val (_, sentiment) = extractSentiments(text)
      .maxBy { case (sentence, _) => sentence.length }
    sentiment
  }

  private def extractSentiments(text: String): List[(String, Sentiment)] = {
    val annotation: Annotation = pipeline.process(text)
    val sentences = annotation.get(classOf[CoreAnnotations.SentencesAnnotation])
    sentences
      .map(sentence => (sentence, sentence.get(classOf[SentimentCoreAnnotations.SentimentAnnotatedTree])))
      .map { case (sentence, tree) => (sentence.toString, Sentiment.toSentiment(RNNCoreAnnotations.getPredictedClass(tree))) }
      .toList
  }

}

object Sentiment extends Enumeration {
  type Sentiment = Value
  val POSITIVE, NEGATIVE, NEUTRAL = Value

  def toSentiment(sentiment: Int): Sentiment = sentiment match {
    case x if x == 0 || x == 1 => Sentiment.NEGATIVE
    case 2 => Sentiment.NEUTRAL
    case x if x == 3 || x == 4 => Sentiment.POSITIVE
  }
}

================================================
FILE: 03-stanford-corenlp/sentiment-analyzer/src/test/scala/com/shekhargulati/sentiment_analyzer/SentimentAnalyzerSpec.scala
================================================
package com.shekhargulati.sentiment_analyzer

import org.scalatest.{FunSpec, Matchers}

class SentimentAnalyzerSpec extends FunSpec with Matchers {

  describe("sentiment analyzer") {

    it("should return POSITIVE when input has positive emotion") {
      val input = "Scala is a great general purpose language."
      val sentiment = SentimentAnalyzer.mainSentiment(input)
      sentiment should be(Sentiment.POSITIVE)
    }

    it("should return NEGATIVE when input has negative emotion") {
      val input = "Dhoni laments bowling, fielding errors in series loss"
      val sentiment = SentimentAnalyzer.mainSentiment(input)
      sentiment should be(Sentiment.NEGATIVE)
    }

    it("should return NEUTRAL when input has no emotion") {
      val input = "I am reading a book"
      val sentiment = SentimentAnalyzer.mainSentiment(input)
      sentiment should be(Sentiment.NEUTRAL)
    }

  }
}

================================================
FILE: 04-slick/README.md
================================================
Slick 3: Functional Relational Mapping for Mere Mortals Part 1
----

Welcome to the fourth blog of [52-technologies-in-2016](https://github.com/shekhargulati/52-technologies-in-2016) blog series. Today, we will get started with Slick. Slick(Scala Language-Integrated Connection Kit) is a powerful Scala library to work with relational databases. **Slick is not an ORM library**. It bases its implementation on **functional programming** and does not hide database behind an ORM layer giving you full control over when a database access should happen. It allows you to work with database just like you are working with Scala collections. Slick API is asynchronous in nature making it suitable for building reactive applications. Although Slick itself is asynchronous in nature, internally it uses JDBC which is a synchronous API. Slick is a big topic so today we will only cover the basics. I will write couple more parts to this blog.

The core idea behind Slick is that as a developer you don't have to write SQL queries. Instead, library will create SQL for you if you build the query using the constructs provided by the library.

<img src="http://slick.typesafe.com/resources/images/slick-logo.png">

Benefits of using slick:

1. Type safety and compile time checking
2. Generate query for any database
3. Composable
4. Back-pressure built-in
5. Streaming support via reactive streams
6. You can use SQL as well

From the [Slick docs](http://slick.typesafe.com/doc/3.1.1/introduction.html#functional-relational-mapping):

> **The language integrated query model in Slick’s FRM is inspired by the LINQ project at Microsoft and leverages concepts tracing all the way back to the early work of Mnesia at Ericsson.**

Slick supports most of the relational databases in the market. You can view full list [here](http://slick.typesafe.com/doc/3.1.1/supported-databases.html). You can work with all open source databases like MySQL, PostgreSQL for free. Databases like Oracle, SQL Server, and DB2 are available as closed extensions that you can use only after buying subscription.


> **This blog is part of my year long blog series [52 Technologies in 2016](https://github.com/shekhargulati/52-technologies-in-2016)**

## Github repository

The code for today’s demo application is available on github: [tasky](./tasky).

## Getting Started

Create a new directory `tasky` on your filesystem. Inside the `tasky` directory create a sbt build file `build.sbt` with the following contents.

```scala
name := "tasky"

description := "A simple task manager for humans"

version := "0.1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.slick" %% "slick" % "3.1.1"
libraryDependencies += "com.h2database" % "h2" % "1.4.191"
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.1.3"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"
```

> **In this tutorial, we will Slick version 3.1.1**

In the `build.sbt` file shown above, we have first defined basic information about the project like name, version, and description. We have also specified that we are going to use Scala version `2.11.7`.  After that we have declared few dependencies. The only required dependency is of `Slick`. `logback` is used for logging and `scalatest` will be used for writing test cases. In this tutorial, we will use `h2` database so we have declared its dependency as well. `h2` is an in-memory SQL database implementation written in `Java`. It runs in the same process as your application and is useful for testing and getting started purposes. For real apps, you should use databases like MySQL or PostgreSQL.

Create the following directory structure inside the `tasky` directory.

```bash
$ mkdir -p src/main/scala
$ mkdir -p src/test/scala
```

Now, we have a basic Scala SBT project setup for Slick application development.

Next, import the project in your favorite IDE.

## Define Database Tables

Tables represent mapping between Scala datatypes and database tables. Create a new package `datamodel` inside the `src/main/scala` directory.

Inside the `datamodel` package, create a scala object `DataModel.scala`.

```scala
package datamodel

import slick.driver.H2Driver.api._

object DataModel {

}
```

The import `slick.driver.H2Driver.api._` is required to tell which Slick database API we will use in our application. As shown above, we are using H2 for our application.

Let's create a new Scala datatype for our task management application. To keep things simple and easy to understand, we will start with only one domain object i.e. Task. `Task` case class is shown below.

```scala
import java.time.LocalDateTime

object DataModel {

  case class Task(
                   title: String,
                   description: String = "",
                   createdAt: LocalDateTime = LocalDateTime.now(),
                   dueBy: LocalDateTime,
                   tags: Set[String] = Set(),
                   id: Long = 0L)
}
```

The case class represent a `Task` datatype with six fields. This will map to a task table that will store a list of tasks that a user has to perform. As you can see, we have used different datatypes like String, Java 8 LocalDateTime, Set, and Long. LocalDateTime is part of Java 8 Date Time API. We have also given default values to some of these fields. This will allow us to not pass these value when we are constructing task objects. So, we can create a task by just providing `title` and `dueBy` values.

> **Please refer to [my Java 8 tutorial](https://github.com/shekhargulati/java8-the-missing-tutorial/blob/master/08-date-time-api.md) if you are new to Java 8**

Now let's create a table mapping for our Task case class.

```scala
object DataModel {

  case class Task(
                   title: String,
                   description: String = "",
                   createdAt: LocalDateTime = LocalDateTime.now(),
                   dueBy: LocalDateTime,
                   tags: Set[String] = Set(),
                   id: Long = 0L)

  class TaskTable(tag: Tag) extends Table[Task](tag, "tasks") {
    def title = column[String]("title")

    def description = column[String]("description")

    def createdAt = column[LocalDateTime]("createdAt")

    def dueBy = column[LocalDateTime]("dueBy")

    def tags = column[Set[String]]("tags")

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

    override def * = (title, description, createdAt, dueBy, tags, id) <>(Task.tupled, Task.unapply)
  }
}
```

Let's understand the `TaksTable` class code shown above.

1. Every table needs to extend `Table` abstract class. Table class needs a type parameter that tells what we will store in our table. Here, we are storing `Task` in the TaskTable. TaskTable constructors needs two mandatory fields - tag and table name. As shown above, we have used `tasks` as the name of our table. `tag` is something internal to slick that you have to pass to the `Table` constructor. This is used by slick to determine shape of a single table row. There is not much mentioned about `tag` in the slick documentation so I might not be 100% correct.

2. Next, we defined definitions of each of the columns. These map one-to-one to our domain class `Task`.

3. `id` is our primary key. In the column definition, we have said slick to make id an auto incrementing primary key. This will make sure database allocate id to each row in auto increment manner.

4. The `*` method is the default projection of our table. You have to define this method in your `Table` class. The type of the `*` projection has to be the same as type specified in the `Table` type parameter. In our case, both have to be `Task`. The `<>` method is used to convert between a tuple `(title, description, createdAt, dueBy, tags, id)` and `Task` data type. The `<>` needs two functions - first takes a tuple and convert it to an object and second a function that converts an object to a tuple.

> It is not required to use a case class you could have also used a regular Scala class as well. If you do use a regular class, then you have to provide two extra functions corresponding to `tupled` and `unapply`. The advantage that we get by using a case class is that it provides `tupled` and `unapply` methods. In the code shown below, we have created a Task object and defined two methods `toTask` and `fromTask`. These methods will serve the purpose of `tupled` and `unapply` methods.

```scala
class Task(
            val title: String,
            val description: String = "",
            val createdAt: LocalDateTime = LocalDateTime.now(),
            val dueBy: LocalDateTime,
            val tags: Set[String] = Set[String](),
            val id: Long = 0L)

object Task {

  def apply(title: String,
            description: String = "",
            createdAt: LocalDateTime = LocalDateTime.now(),
            dueBy: LocalDateTime,
            tags: Set[String] = Set[String](),
            id: Long = 0L): Task = new Task(title, description, createdAt, dueBy, tags, id)

  def toTask(t: (String, String, LocalDateTime, LocalDateTime, Set[String], Long)): Task = new Task(t._1, t._2, t._3, t._4, t._5, t._6)

  def fromTask(task: Task): Option[(String, String, LocalDateTime, LocalDateTime, Set[String], Long)] = Some((task.title, task.description, task.createdAt, task.dueBy, task.tags, task.id))
}
```

## Create TableQuery object

Once we have defined our table definition `TaskTable`, we have to define a value of type `TableQuery` which represents an actual database table. It provides a query DSL that you can use to interact with the table.

```scala
lazy val Tasks = TableQuery[TaskTable]
```

## Define Custom Mapping

If you try to compile the code that we have written so far it will not compile. The reason for that is slick does not support Java 8 `LocalDateTime` and `Set[String]` datatypes for column definition. However, we can write our custom mappers that will convert our types to the type `Slick` understands. Create a new object `ColumnDataMapper` in the same file `DataModel.scala` as shown below.

```scala
object ColumnDataMapper {

  implicit val localDateTimeColumnType = MappedColumnType.base[LocalDateTime, Timestamp](
    ldt => Timestamp.valueOf(ldt),
    t => t.toLocalDateTime
  )

  implicit val setStringColumnType = MappedColumnType.base[Set[String], String](
    tags => tags.mkString(","),
    tagsString => tagsString.split(",").toSet
  )

}
```

In the code shown above, we have defined two mapper -- a) converts between `LocalDateTime` to `java.sql.Timestamp` and vice-versa b) converts between `Set[String]` to `String` and vice-versa.

Now, add the import for your custom data mappings. You have to explicitly add the custom mappers to the column definition.

```scala
import datamodel.ColumnDataMapper.{localDateTimeColumnType, setStringColumnType}

class TaskTable(tag: Tag) extends Table[Task](tag, "tasks") {
  def title = column[String]("title")

  def description = column[String]("description")

  def createdAt = column[LocalDateTime]("createdAt")(localDateTimeColumnType)

  def dueBy = column[LocalDateTime]("dueBy")(localDateTimeColumnType)

  def tags = column[Set[String]]("tags")(setStringColumnType)

  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

  override def * = (title, description, createdAt, dueBy, tags, id) <>(Task.tupled, Task.unapply)
}
```

Now, code will compile successfully. You can use `sbt compile` task to compile the application.

## Define Schema Create Action

Action represents commands that we want to run against database. Let's write our first action that will create the database schema.

```scala
lazy val Tasks = TableQuery[TaskTable]

val createTaskTableAction = Tasks.schema.create
```

The `createTaskTableAction` action will create the schema when it is executed against database. **Defining an action does not execute it**.

## Execute Schema Create Action

Actions are executed against a database. Slick provides a `Database` type that allows our code to interact with the database. It is a handle to a specific database. To get the handle to a database, you use the following code.

```scala
val db = Database.forConfig("taskydb")
```

The `taskydb` is a reference to a configuration object defined using typesafe config project.

Let's write our first test case that will use the database object to create the schema. In the `src/test/scala`, create a new package `datamodel`. Create a new Scala class `CreateDatabaseSpec` as shown below.

```scala
package datamodel

import org.scalatest.{FunSpec, Matchers}

import slick.driver.H2Driver.api._

import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

class CreateDatabaseSpec extends FunSpec with Matchers {

  describe("DataModel Spec") {

    it("should create database") {
      val db = Database.forConfig("taskydb")
      val result = Await.result(db.run(DataModel.createTaskTableAction), 2 seconds)
      println(result)
    }

  }
}
```
In the code shown above:

1. The `scala.concurrent` set of imports are required to tell slick that we will use ExecutionContext defined by the import to execute slick code. We have to do this because slick API is fully asynchronous and executes database calls in a separate thread pool.

2. Then we created our database object using the `taskydb` configuration.  This gives us the handle to interact with database.

3. The db object has a method called `run` that executes an action and returns a `Future`. As slick is async in nature, we have wrapped the future in a `Await.result` call to make it easy to test.

You will have to create a file called `application.conf` in the `src/test/resources` directory. Populate it with content shown below.

```
taskydb = {
  connectionPool      = disabled
  url                 = "jdbc:h2:mem:taskydb"
  driver              = "org.h2.Driver"
  keepAliveConnection = true
}
```

When you will run this code, you will see in the logs that it has create a database schema.

```
18:50:34.955 [ScalaTest-run-running-CreateDatabaseSpec] DEBUG s.backend.DatabaseComponent.action - #1: schema.create [create table "tasks" ("title" VARCHAR NOT NULL,"description" VARCHAR NOT NULL,"createdAt" TIMESTAMP NOT NULL,"dueBy" TIMESTAMP NOT NULL,"tags" VARCHAR NOT NULL,"id" BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY)]

18:50:35.012 [taskydb-1] DEBUG slick.jdbc.JdbcBackend.statement - Preparing statement: create table "tasks" ("title" VARCHAR NOT NULL,"description" VARCHAR NOT NULL,"createdAt" TIMESTAMP NOT NULL,"dueBy" TIMESTAMP NOT NULL,"tags" VARCHAR NOT NULL,"id" BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY)
18:50:35.268 [taskydb-1] DEBUG slick.jdbc.JdbcBackend.benchmark - Execution of prepared statement took 23ms
```

On every run of our test case, we will create a new database.

## Insert tasks into Task table

Let's now write a test case that will insert some records into the `Task` table.

```scala
it("should insert single task into database") {
  val db = Database.forConfig("taskydb")
  val result = Await.result(db.run(DataModel.insertTaskAction(Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1)))), 2 seconds)
  result should be(Some(1))
}
```

The test case shown above calls the `insertTaskAction` passing it a `Task`. The result of `insertTaskAction` is the number of rows affected by the action. As we are only passing one task so we should expect one as result.

Now, let's look at the `insertTaskAction` definition in the `DataModel` object.

```
def insertTaskAction(tasks: Task*) = Tasks ++= tasks.toSeq
```

The insertTaskAction takes a `varargs` of tasks allowing user to pass one or more tasks. To insert tasks, we used `++=` method.  According to [slick documentation](http://slick.typesafe.com/doc/3.0.0/queries.html#inserting),

> **`++=` gives you an accumulated count in an Option (which can be None if the database system does not provide counts for all rows)**


## Query table

Let's query the database to select all the tasks in the database.

```scala
it("should list all tasks in the database") {
  val tasks = Seq(
    Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1)),
    Task(title = "Write blog on Slick", dueBy = LocalDateTime.now().plusDays(2)),
    Task(title = "Build a simple application using Slick", dueBy = LocalDateTime.now().plusDays(3))
  )
  Await.result(db.run(DataModel.insertTaskAction(tasks: _*)), 2 seconds)
  val result = Await.result(db.run(DataModel.listTasksAction), 2 seconds)
  result should have length 3
}
```

The test case shown above queries the database using `listTasksAction` shown below.

```scala
val listTasksAction = Tasks.result
```

The `listTasksAction` makes a `select "title", "description", "createdAt", "dueBy", "tags", "id" from "tasks"` sql query using the default `*` projection.


## Conclusion

Slick is a powerful library to interact with relational databases. Today, we have just scratched the surface of this feature rich library. You leant how to define table definition, insert data, perform `select *` query. I will write couple more blogs on Slick to cover it in more details. So stay tuned!

That's all for this week. Please provide your valuable feedback by adding a comment to [https://github.com/shekhargulati/52-technologies-in-2016/issues/6](https://github.com/shekhargulati/52-technologies-in-2016/issues/6).


[![Analytics](https://ga-beacon.appspot.com/UA-59411913-2/shekhargulati/52-technologies-in-2016/04-slick)](https://github.com/igrigorik/ga-beacon)


================================================
FILE: 04-slick/tasky/.gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control

target/
lib_managed/
src_managed/
project/boot/
.history
.cache
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Scala template
*.class
*.log

# sbt specific
.cache
.history
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/

# Scala-IDE specific
.scala_dependencies
.worksheet



================================================
FILE: 04-slick/tasky/build.sbt
================================================
name := "tasky"

description := "A simple task manager for humans"

version := "0.1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.slick" %% "slick" % "3.1.1"
libraryDependencies += "com.h2database" % "h2" % "1.4.191"
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.1.3"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"

================================================
FILE: 04-slick/tasky/project/plugins.sbt
================================================
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.1")

================================================
FILE: 04-slick/tasky/src/main/scala/datamodel/DataModel.scala
================================================
package datamodel

import java.sql.Timestamp
import java.time.LocalDateTime

import datamodel.ColumnDataMapper._
import slick.driver.H2Driver.api._


object DataModel {

  case class Task(
                   title: String,
                   description: String = "",
                   createdAt: LocalDateTime = LocalDateTime.now(),
                   dueBy: LocalDateTime,
                   tags: Set[String] = Set[String](),
                   id: Long = 0L)

  class TaskTable(tag: Tag) extends Table[Task](tag, "tasks") {
    def title = column[String]("title")

    def description = column[String]("description")

    def createdAt = column[LocalDateTime]("createdAt")(localDateTimeColumnType)

    def dueBy = column[LocalDateTime]("dueBy")(localDateTimeColumnType)

    def tags = column[Set[String]]("tags")(setStringColumnType)

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

    override def * = (title, description, createdAt, dueBy, tags, id) <>(Task.tupled, Task.unapply)
  }

  lazy val Tasks = TableQuery[TaskTable]

  val createTaskTableAction = Tasks.schema.create

  def insertTaskAction(tasks: Task*) = Tasks ++= tasks.toSeq

  val listTasksAction = Tasks.result
}

object ColumnDataMapper {

  implicit val localDateTimeColumnType = MappedColumnType.base[LocalDateTime, Timestamp](
    ldt => Timestamp.valueOf(ldt),
    t => t.toLocalDateTime
  )

  implicit val setStringColumnType = MappedColumnType.base[Set[String], String](
    tags => tags.mkString(","),
    tagsString => tagsString.split(",").toSet
  )

}


================================================
FILE: 04-slick/tasky/src/test/resources/application.conf
================================================
taskydb = {
  connectionPool      = disabled
  url                 = "jdbc:h2:mem:taskydb"
  driver              = "org.h2.Driver"
  keepAliveConnection = true
}

================================================
FILE: 04-slick/tasky/src/test/scala/datamodel/DataModelSpec.scala
================================================
package datamodel

import java.time.LocalDateTime

import datamodel.DataModel.Task
import org.scalatest.{BeforeAndAfterEach, FunSpec, Matchers}
import slick.driver.H2Driver.api._

import scala.concurrent._
import scala.concurrent.duration._

class DataModelSpec extends FunSpec with Matchers with BeforeAndAfterEach {

  var db: Database = _

  override protected def beforeEach(): Unit = {
    db = Database.forConfig("taskydb")
    Await.result(db.run(DataModel.createTaskTableAction), 2 seconds)
  }

  override protected def afterEach(): Unit = Await.result(db.shutdown, 2 seconds)

  describe("DataModel Spec") {

    it("should insert single task into database") {
      val result = Await.result(db.run(DataModel.insertTaskAction(Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1)))), 2 seconds)
      result should be(Some(1))
    }

    it("should insert multiple tasks into database") {
      val tasks = Seq(
        Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1)),
        Task(title = "Write blog on Slick", dueBy = LocalDateTime.now().plusDays(2)),
        Task(title = "Build a simple application using Slick", dueBy = LocalDateTime.now().plusDays(3))
      )
      val result = Await.result(db.run(DataModel.insertTaskAction(tasks: _*)), 2 seconds)
      result should be(Some(3))
    }

    it("should list all tasks in the database") {
      val tasks = Seq(
        Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1)),
        Task(title = "Write blog on Slick", dueBy = LocalDateTime.now().plusDays(2)),
        Task(title = "Build a simple application using Slick", dueBy = LocalDateTime.now().plusDays(3))
      )
      Await.result(db.run(DataModel.insertTaskAction(tasks: _*)), 2 seconds)
      val result = Await.result(db.run(DataModel.listTasksAction), 2 seconds)
      result should have length 3
    }

  }


}


================================================
FILE: 05-slick/README.md
================================================
Slick 3: Functional Relational Mapping for Mere Mortals Part 2: Querying data
----

Last week we learnt the [basics of Slick](https://github.com/shekhargulati/52-technologies-in-2016/tree/master/04-slick) library. We started with a general introduction of Slick, then covered how to define a table definition, custom mappers, and perform insert queries. Today, we will learn how to perform `select` queries with Slick. Slick allows you to work with database tables in the same way as you work with Scala collections. This means that you can use methods like `map`, `filter`, `sort`, etc. to process data in your table.

> **In case you are new to Slick, please first read [part 1 of Slick tutorial](https://github.com/shekhargulati/52-technologies-in-2016/tree/master/04-slick). This blog is part of my year long blog series [52 Technologies in 2016](https://github.com/shekhargulati/52-technologies-in-2016)**

## Github repository

The code for today’s demo application is available on github: [tasky](./tasky).

## Let's (again) look at the data model

Before we start with querying data, let's again look at the data model. I have added one more field to the `TaskTable`. The field that we have added is an enum to store priority of the task. Enums are useful when a variable can have one of the small set of possible values. In our example application, `Priority` is an enum that can be either `HIGH`, `LOW`, or `MEDIUM`. To create a new enum, create an object that extends `scala.Enumeration` as shown below. We have created `Priority` enum in a new file `Priority.scala` inside the `datamodel` package.

```scala
package datamodel

object Priority extends Enumeration {
  type Priority = Value
  val HIGH = Value(3)
  val MEDIUM = Value(2)
  val LOW = Value(1)
}
```

As you can see above, we have provided int values to each enum constant.

After creating our new enum, we have to add its declaration in our `Task` case class as well as `TaskTable`.

```scala
import datamodel.columnDataMappers._
case class Task(
                 title: String,
                 description: String = "",
                 createdAt: LocalDateTime = LocalDateTime.now(),
                 dueBy: LocalDateTime,
                 tags: Set[String] = Set[String](),
                 priority: Priority = Priority.LOW,
                 id: Long = 0L)


class TaskTable(tag: Tag) extends Table[Task](tag, "tasks") {
  def title = column[String]("title")

  def description = column[String]("description")

  def createdAt = column[LocalDateTime]("createdAt")

  def dueBy = column[LocalDateTime]("dueBy")

  def tags = column[Set[String]]("tags")

  def priority = column[Priority]("priority")

  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

  override def * = (title, description, createdAt, dueBy, tags, priority, id) <>(Task.tupled, Task.unapply)
}
```

If we try to compile code now, it will not compile. We have to add column mapping to convert between `Priority` enum to `Int`. This is shown below.

```scala
implicit val priorityMapper = MappedColumnType.base[Priority, Int](
  p => p.id,
  v => Priority(v)
)
```

Compile and run the test cases using `sbt test` and everything should work fine.

## Select all the tasks in the database

Let's start with the simplest select query i.e. `select * from tasks`. We want to list all the tasks in our database. As discussed last week, we have to create an instance of `TableQuery` that will give us the handle to Slick Query DSL API. We already have instance of `TableQuery` created inside the `dataModels.scala`.

```scala
lazy val Tasks = TableQuery[TaskTable]
```

Create a new Scala object `queries` inside the `queries` package. This object will house all the queries.

```scala
package queries

import datamodel.columnDataMappers._
import datamodel.dataModel.Tasks
import slick.driver.H2Driver.api._

object queries {

}
```

As shown above, we have created a new Scala object `queries` and added the required imports.

1. `import datamodel.columnDataMappers._` is required so that Slick knows how to handle our custom data types like `LocalDateTime`, `Set[String]`, and `Priority`.

2. `import datamodel.dataModel.Tasks` is required so that we can work with the `Tasks` `TableQuery` object.

3. `import slick.driver.H2Driver.api._` is required to tell which Slick database API we will use in our application.

Before we will write query for listing all the tasks in the database let's write a test case. Create a new test specification `QueriesSpec` and populate it with following contents.

```scala
package queries

import java.time.LocalDateTime

import datamodel.dataModel.Task
import datamodel.{Priority, dataModel}
import org.scalatest.{BeforeAndAfterAll, FunSpec, Matchers}
import queries._
import slick.driver.H2Driver.api._

import scala.concurrent._
import scala.concurrent.duration._

class QueriesSpec extends FunSpec with Matchers with BeforeAndAfterAll {

  var db: Database = _
  var t1: Task = _
  var t2: Task = _
  var t3: Task = _
  var t4: Task = _
  var t5: Task = _
  var t6: Task = _
  var t7: Task = _

  override protected def beforeAll(): Unit = {
    db = Database.forConfig("taskydb")
    Await.result(db.run(dataModel.createTaskTableAction), 2 seconds)
    t1 = Task(title = "Write part 1 blog on Slick", dueBy = LocalDateTime.now().minusDays(7), tags = Set("blogging", "scala", "slick"), priority = Priority.HIGH)
    t2 = Task(title = "Give a Java 8 training", dueBy = LocalDateTime.now().minusDays(3), tags = Set("java", "training", "travel"), priority = Priority.LOW)
    t3 = Task(title = "Write part 2 blog on Slick queries", dueBy = LocalDateTime.now(), tags = Set("blogging", "scala", "slick"), priority = Priority.HIGH)
    t4 = Task(title = "Read Good to Great book", dueBy = LocalDateTime.now().plusDays(15), tags = Set("reading", "books", "startup"), priority = Priority.MEDIUM)
    t5 = Task(title = "Read Programming Scala book", dueBy = LocalDateTime.now().plusDays(30), tags = Set("reading", "books", "scala"), priority = Priority.HIGH)
    t6 = Task(title = "Go to Goa for holiday", dueBy = LocalDateTime.now().plusDays(60), tags = Set("travel"), priority = Priority.LOW)
    t7 = Task(title = "Build my dream application using Play framework and Slick", dueBy = LocalDateTime.now().plusMonths(3), tags = Set("application", "play", "startup"), priority = Priority.HIGH)
    val tasks = Seq(t1, t2, t3, t4, t5, t6, t7)
    performAction(dataModel.insertTaskAction(tasks: _*))
  }

  private def performAction[T](action: DBIO[T]): T = {
    Await.result(db.run(action), 2 seconds)
  }
}
```

In the code shown above, we have done the following:

1. We imported all the required classes and traits that are required by our test case.

2. We provided implementation of `beforeAll` method. This allows us to perform one time setup for this test case. We inserted seven tasks in the database using the `insertTaskAction` we discussed last week. In the task list shown above, there are two tasks that were due in past and 5 tasks which are due in future.

3. `performAction` is a method that will help us avoid writing boilerplate code of wrapping the future in an `Await`. We will just pass an action to `performAction` and it will take care of the rest. We will use this method in all our test cases.

Now, that we have setup our test data. We can write our first test case that will select all the tasks in the `tasks` table.

```scala
import queries._

it("should select all the tasks stored in the database") {
  val tasks = performAction(selectAllTasksQuery.result)
  tasks should have length 7
  tasks.head should have(
    'title (t1.title),
    'description (t1.description),
    'createdAt (t1.createdAt),
    'dueBy (t1.dueBy),
    'tags (t1.tags)
  )
}
```

In the code shown above, only thing that is of interest to us is the `selectAllTasksQuery`. This is imported from the `queries` object. `performAction` method discussed above needs an action. You can convert a query to an action by calling the `result` method on it. If you try to run the test case now, it will not work as we have not yet defined `selectAllTasksQuery`.

In the `queries` object, define `selectAllTasksQuery` as shown below.

```scala
object queries {
  val selectAllTasksQuery: Query[TaskTable, Task, Seq] = Tasks
}
```

Let's try to decipher one line of code that we have written above. In the code shown above, we have a defined a value `selectAllTasksQuery` that returns `Tasks` object. `Tasks` is an instance of `TableQuery` object we defined in `dataModels.scala`. `Tasks` i.e. `TableQuery` object is the gateway to the Slick query DSL API. When you return `Tasks` object then Slick uses the default `*` projection that we defined in the `TaskTable`.

The other interesting bit is the type of `selectAllTasksQuery`. You are not required to define the type here as Scala can infer the type. By understanding the type `Query[TaskTable, Task, Seq]`, you will understand how Slick determine what value should be returned by the query. `Query` takes three type parameters. The first type parameter is called the packed type i.e. the type of values you work against in the query DSL. The second type is called the unpacked type i.e. the type of values you get back when you run the query. The third type is the container type that collects the result.

Run the test case and it should pass.  You can look at the logs to confirm that Slick executed `select *` query.

```sql
select "title", "description", "createdAt", "dueBy", "tags", "priority", "id" from "tasks"
```

## Select all task titles

The first query that we saw above fetches all the columns of `tasks` table. Most of the time we only want to select few columns. Let's write our test case for this use case.

```scala
it("should select all task titles") {
  val taskTitles = performAction(selectAllTaskTitleQuery.result)
  taskTitles should have length 7
  taskTitles should be(List(t1.title, t2.title, t3.title, t4.title, t5.title, t6.title, t7.title))
}
```

As you can see above, we are executing `selectAllTaskTitleQuery`. This query is defined in `queries` object as shown below.

```scala
val selectAllTaskTitleQuery: Query[Rep[String], String, Seq] = Tasks.map(taskTable => taskTable.title)
```

In the code shown above, we have used map function on the `Tasks` table query object. `map` is a transformation function that take a lambda. The lambda function tells Slick that we only want to select title column. One thing to note here is that in the `map` function we are working on the `TaskTable` object. As `map` function only returns title so the type of `selectAllTaskTitleQuery` is `Query[Rep[String], String, Seq]`.

You can also use the shorthand `_` in the lambda as shown below.

```scala
val selectAllTaskTitleQuery: Query[Rep[String], String, Seq] = Tasks.map(_.title)
```

You can also select more than one columns in the map function as shown below.

```scala
val selectMultipleColumnsQuery: Query[(Rep[String], Rep[Priority], Rep[LocalDateTime]), (String, Priority, LocalDateTime), Seq] = Tasks.map(t => (t.title, t.priority, t.createdAt))
```

The query executed by Slick can be seen in the logs.

```sql
select "title", "priority", "createdAt" from "tasks"
```

## Select all the high priority task titles

So far we have selected all the data in our tasks table. There are times when we have to filter data as we have to do it this usecase. We have filter out all the high priority tasks and then select only title field. Let's write the test case first.

```scala
it("should select all the high priority task titles"){
  val highPriorityTasks = performAction(selectHighPriorityTasksQuery.result)
  highPriorityTasks should have length 4
  highPriorityTasks should be(List(t1.title, t3.title, t5.title, t7.title))
}
```

In the dataset that we created in `beforeAll` method, we have four high priority tasks.

The `selectHighPriorityTasksQuery` will use the `filter` and the `map` operation to get the job done. `filter` allows us to specify the `where` clauses.

```scala
val selectHighPriorityTasksQuery: Query[Rep[String], String, Seq] = Tasks.filter(_.priority === Priority.HIGH).map(_.title)
```

In the code shown above, we first filtered out all the high priority tasks and then selected only title column.

You can view the SQL query generated by Slick in the logs.

```sql
select "title" from "tasks" where "priority" = 3
```

## Paginate results

Slick allows you to paginate our the result by using the `drop` and `limit` methods of `TableQuery`. To skip first 3 elements and then limit the result to 2 records, you can write following Slick code.

```scala
Tasks.drop(3).take(2)
```

You can view the SQL query generated by Slick in the logs.

```sql
select "title", "description", "createdAt", "dueBy", "tags", "priority", "id" from "tasks" limit 3 offset 2
```

## Sort tasks in descending order of due date

A lot of times we have to work with data in some sorting order. Let's suppose, we want to work on the task that is due last. One way to sort would be to sort the data in your application code. You could also ask your database to return the data in sorted order by passing the `order by clause`. Let's write a test case to test this scenario.

```scala
it("should sort tasks in descending order of due date") {
  val tasks = performAction(selectTasksSortedByDueDateDescQuery.result)
  tasks.head should have(
    'title (t7.title),
    'description (t7.description),
    'createdAt (t7.createdAt),
    'dueBy (t7.dueBy),
    'tags (t7.tags)
  )
}
```

We have to define `selectTasksSortedByDueDateDescQuery` in the `queries` object as shown below.

```scala
val selectTasksSortedByDueDateDescQuery = Tasks.sortBy(_.dueBy.desc)
```

The reason `desc` is available on the `dueBy` is because for Slick it is a `Timestamp`. All the operations that work on `Timestamp` are available on the `dueBy` as well.

You can view the SQL query generated by Slick in the logs.

```sql
select "title", "description", "createdAt", "dueBy", "tags", "priority", "id" from "tasks" order by "dueBy" desc
```

## Select all tasks due today

To select all the tasks due today we can use `filter` operator as shown below. We are using `LocalDate` `asStartOfDay` method to define the time range of our where clause.

```scala
val selectAllTasksDueToday = Tasks
  .filter(t => t.dueBy > LocalDate.now().atStartOfDay() && t.dueBy < LocalDate.now().atStartOfDay().plusDays(1))
  .map(_.title)
```

You could have also used two filters instead of one as shown below.

```scala
val selectAllTasksDueToday = Tasks
  .filter(_.dueBy > LocalDate.now().atStartOfDay())
  .filter(_.dueBy < LocalDate.now().atStartOfDay().plusDays(1))
  .map(_.title)
```

You can view the SQL query generated by Slick in the logs.

```sql
select "title" from "tasks" where ("dueBy" > {ts '2016-01-31 00:00:00.0'}) and ("dueBy" < {ts '2016-02-01 00:00:00.0'})
```

## Select data with in a range

We can use the SQL `BETWEEN` operator to select data between two dates as shown below.

```scala
val selectTasksBetweenTodayAndSameDateNextMonthQuery = Tasks.filter(t => t.dueBy.between(LocalDateTime.now(), LocalDateTime.now().plusMonths(1)))
```

You can view the SQL query generated by Slick in the logs.

```sql
select "title", "description", "createdAt", "dueBy", "tags", "priority", "id" from "tasks" where "dueBy" between {ts '2016-01-31 21:44:40.643'} and {ts '2016-02-29 21:44:40.643'}
```

## Check if any high priority task is pending today

You can use SQL `exists` operator as shown below.

```scala
val selectAllTasksDueToday = Tasks
  .filter(_.dueBy > LocalDate.now().atStartOfDay())
  .filter(_.dueBy < LocalDate.now().atStartOfDay().plusDays(1))

val checkIfAnyHighPriorityTaskExistsToday = selectAllTasksDueToday.filter(_.priority === Priority.HIGH).exists
```

You can view the SQL query generated by Slick in the logs.

```sql
select exists(select "description", "createdAt", "priority", "tags", "dueBy", "id", "title" from "tasks" where (("dueBy" > {ts '2016-01-31 00:00:00.0'}) and ("dueBy" < {ts '2016-02-01 00:00:00.0'})) and ("priority" = 3))
```

There are many more aggregate functions like `max`, `min`, `average` that you can use.

## Conclusion

Today, we looked at how we can use the Slick library to query our data. If you have used Scala collections or Java 8 Streams you should feel home. We still haven't covered many other important Slick topics like joins, profiles, working with real databases like MySQL or PostgreSQL, etc.  I will write at least one more post about Slick so that we have good understanding of it.

That's all for this week. Please provide your valuable feedback by adding a comment to [https://github.com/shekhargulati/52-technologies-in-2016/issues/7](https://github.com/shekhargulati/52-technologies-in-2016/issues/7).

[![Analytics](https://ga-beacon.appspot.com/UA-59411913-2/shekhargulati/52-technologies-in-2016/05-slick)](https://github.com/igrigorik/ga-beacon)


================================================
FILE: 05-slick/tasky/.gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control

target/
lib_managed/
src_managed/
project/boot/
.history
.cache
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Scala template
*.class
*.log

# sbt specific
.cache
.history
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/

# Scala-IDE specific
.scala_dependencies
.worksheet



================================================
FILE: 05-slick/tasky/build.sbt
================================================
name := "tasky"

description := "A simple task manager for humans"

version := "0.1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.slick" %% "slick" % "3.1.1"
libraryDependencies += "com.h2database" % "h2" % "1.4.191"
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.1.3"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"

================================================
FILE: 05-slick/tasky/project/plugins.sbt
================================================
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.1")

================================================
FILE: 05-slick/tasky/src/main/scala/datamodel/Priority.scala
================================================
package datamodel

object Priority extends Enumeration {
  type Priority = Value
  val HIGH = Value(3)
  val MEDIUM = Value(2)
  val LOW = Value(1)
}

================================================
FILE: 05-slick/tasky/src/main/scala/datamodel/columnDataMappers.scala
================================================
package datamodel

import java.sql.Timestamp
import java.time.LocalDateTime

import datamodel.Priority._
import slick.driver.H2Driver.api._

object columnDataMappers {

  implicit val localDateTimeColumnType: BaseColumnType[LocalDateTime] = MappedColumnType.base[LocalDateTime, Timestamp](
    ldt => Timestamp.valueOf(ldt),
    t => t.toLocalDateTime
  )

  implicit val setStringColumnType: BaseColumnType[Set[String]] = MappedColumnType.base[Set[String], String](
    tags => tags.mkString(","),
    tagsString => tagsString.split(",").toSet
  )


  implicit val priorityMapper = MappedColumnType.base[Priority, Int](
    p => p.id,
    v => Priority(v)
  )
}


================================================
FILE: 05-slick/tasky/src/main/scala/datamodel/dataModel.scala
================================================
package datamodel

import java.time.LocalDateTime

import datamodel.Priority.Priority
import datamodel.columnDataMappers._
import slick.driver.H2Driver.api._

object dataModel {

  case class Task(
                   title: String,
                   description: String = "",
                   createdAt: LocalDateTime = LocalDateTime.now(),
                   dueBy: LocalDateTime,
                   tags: Set[String] = Set[String](),
                   priority: Priority = Priority.LOW,
                   id: Long = 0L)


  class TaskTable(tag: Tag) extends Table[Task](tag, "tasks") {
    def title = column[String]("title")

    def description = column[String]("description")

    def createdAt = column[LocalDateTime]("createdAt")

    def dueBy = column[LocalDateTime]("dueBy")

    def tags = column[Set[String]]("tags")

    def priority = column[Priority]("priority")

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

    override def * = (title, description, createdAt, dueBy, tags, priority, id) <>(Task.tupled, Task.unapply)
  }

  lazy val Tasks = TableQuery[TaskTable]

  val createTaskTableAction = Tasks.schema.create

  def insertTaskAction(tasks: Task*) = Tasks ++= tasks.toSeq

  val listAllTasksAction = Tasks.result
}






================================================
FILE: 05-slick/tasky/src/main/scala/queries/queries.scala
================================================
package queries

import java.time.{LocalDate, LocalDateTime}

import datamodel.Priority
import datamodel.Priority.Priority
import datamodel.columnDataMappers._
import datamodel.dataModel._
import slick.driver.H2Driver.api._

object queries {

  val selectAllTasksQuery: Query[TaskTable, Task, Seq] = Tasks

  //  val findAllTaskTitleQuery = Tasks.map(taskTable => taskTable.title)
  val selectAllTaskTitleQuery: Query[Rep[String], String, Seq] = Tasks.map(_.title)

  val selectMultipleColumnsQuery: Query[(Rep[String], Rep[Priority], Rep[LocalDateTime]), (String, Priority, LocalDateTime), Seq] = Tasks.map(t => (t.title, t.priority, t.createdAt))

  val selectHighPriorityTasksQuery: Query[Rep[String], String, Seq] = Tasks.filter(_.priority === Priority.HIGH).map(_.title)

  def findAllTasksPageQuery(skip: Int, limit: Int) = Tasks.drop(skip).take(limit)

  val selectTasksSortedByDueDateDescQuery = Tasks.sortBy(_.dueBy.desc)

  val findAllDueTasks = Tasks.filter(_.dueBy >= LocalDate.now().atStartOfDay())

  val selectAllTaskTitlesDueToday = Tasks
    .filter(_.dueBy > LocalDate.now().atStartOfDay())
    .filter(_.dueBy < LocalDate.now().atStartOfDay().plusDays(1))
    .map(_.title)

  val selectTasksBetweenTodayAndSameDateNextMonthQuery = Tasks.filter(t => t.dueBy.between(LocalDateTime.now(), LocalDateTime.now().plusMonths(1)))

  val selectAllTasksDueToday = Tasks
    .filter(_.dueBy > LocalDate.now().atStartOfDay())
    .filter(_.dueBy < LocalDate.now().atStartOfDay().plusDays(1))

  val checkIfAnyHighPriorityTaskExistsToday = selectAllTasksDueToday.filter(_.priority === Priority.HIGH).exists
}


================================================
FILE: 05-slick/tasky/src/test/resources/application.conf
================================================
taskydb = {
  connectionPool      = disabled
  url                 = "jdbc:h2:mem:taskydb"
  driver              = "org.h2.Driver"
  keepAliveConnection = true
}

================================================
FILE: 05-slick/tasky/src/test/scala/datamodel/DataModelSpec.scala
================================================
package datamodel

import java.time.LocalDateTime

import datamodel.dataModel.Task
import org.scalatest.{BeforeAndAfterEach, FunSpec, Matchers}
import slick.driver.H2Driver.api._

import scala.concurrent._
import scala.concurrent.duration._

class DataModelSpec extends FunSpec with Matchers with BeforeAndAfterEach {

  var db: Database = _

  override protected def beforeEach(): Unit = {
    db = Database.forConfig("taskydb")
    Await.result(db.run(dataModel.createTaskTableAction), 2 seconds)
  }

  override protected def afterEach(): Unit = db.shutdown

  describe("DataModel Spec") {

    it("should insert single task into database") {
      val result = Await.result(db.run(dataModel.insertTaskAction(Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1), priority = Priority.HIGH))), 2 seconds)
      result should be(Some(1))
    }

    it("should insert multiple tasks into database") {
      val tasks = Seq(
        Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1), priority = Priority.HIGH),
        Task(title = "Write blog on Slick", dueBy = LocalDateTime.now().plusDays(2), priority = Priority.HIGH),
        Task(title = "Build a simple application using Slick", dueBy = LocalDateTime.now().plusDays(3), priority = Priority.HIGH)
      )
      val result = Await.result(db.run(dataModel.insertTaskAction(tasks: _*)), 2 seconds)
      result should be(Some(3))
    }

    it("should list all tasks in the database") {
      val tasks = Seq(
        Task(title = "Learn Slick", dueBy = LocalDateTime.now().plusDays(1), priority = Priority.HIGH),
        Task(title = "Write blog on Slick", dueBy = LocalDateTime.now().plusDays(2), priority = Priority.HIGH),
        Task(title = "Build a simple application using Slick", dueBy = LocalDateTime.now().plusDays(3), priority = Priority.HIGH)
      )
      Await.result(db.run(dataModel.insertTaskAction(tasks: _*)), 2 seconds)
      val result = Await.result(db.run(dataModel.listAllTasksAction), 2 seconds)
      result should have length 3
    }

  }


}

================================================
FILE: 05-slick/tasky/src/test/scala/queries/QueriesSpec.scala
================================================
package queries

import java.time.LocalDateTime

import datamodel.dataModel.Task
import datamodel.{Priority, dataModel}
import org.scalatest.{BeforeAndAfterAll, FunSpec, Matchers}
import queries._
import slick.driver.H2Driver.api._

import scala.concurrent._
import scala.concurrent.duration._

class QueriesSpec extends FunSpec with Matchers with BeforeAndAfterAll {

  var db: Database = _
  var t1: Task = _
  var t2: Task = _
  var t3: Task = _
  var t4: Task = _
  var t5: Task = _
  var t6: Task = _
  var t7: Task = _

  override protected def beforeAll(): Unit = {
    db = Database.forConfig("taskydb")
    Await.result(db.run(dataModel.createTaskTableAction), 2 seconds)
    t1 = Task(title = "Write part 1 blog on Slick", dueBy = LocalDateTime.now().minusDays(7), tags = Set("blogging", "scala", "slick"), priority = Priority.HIGH)
    t2 = Task(title = "Give a Java 8 training", dueBy = LocalDateTime.now().minusDays(3), tags = Set("java", "training", "travel"), priority = Priority.LOW)
    t3 = Task(title = "Write part 2 blog on Slick queries", dueBy = LocalDateTime.now(), tags = Set("blogging", "scala", "slick"), priority = Priority.HIGH)
    t4 = Task(title = "Read Good to Great book", dueBy = LocalDateTime.now().plusDays(15), tags = Set("reading", "books", "startup"), priority = Priority.MEDIUM)
    t5 = Task(title = "Read Programming Scala book", dueBy = LocalDateTime.now().plusDays(30), tags = Set("reading", "books", "scala"), priority = Priority.HIGH)
    t6 = Task(title = "Go to Goa for holiday", dueBy = LocalDateTime.now().plusDays(60), tags = Set("travel"), priority = Priority.LOW)
    t7 = Task(title = "Build my dream application using Play framework and Slick", dueBy = LocalDateTime.now().plusMonths(3), tags = Set("application", "play", "startup"), priority = Priority.HIGH)
    val tasks = Seq(t1, t2, t3, t4, t5, t6, t7)
    performAction(dataModel.insertTaskAction(tasks: _*))
  }

  private def performAction[T](action: DBIO[T]): T = {
    Await.result(db.run(action), 2 seconds)
  }

  describe("Task Data Model Query Spec") {

    it("should select all the tasks stored in the database") {
      val tasks = performAction(selectAllTasksQuery.result)
      tasks should have length 7
      tasks.head should have(
        'title (t1.title),
        'description (t1.description),
        'createdAt (t1.createdAt),
        'dueBy (t1.dueBy),
        'tags (t1.tags)
      )
    }

    it("should select all task titles") {
      val taskTitles = performAction(selectAllTaskTitleQuery.result)
      taskTitles should have length 7
      taskTitles should be(List(t1.title, t2.title, t3.title, t4.title, t5.title, t6.title, t7.title))
    }

    it("should select task title, priority, and creation date for all tasks") {
      val taskTitles = performAction(selectMultipleColumnsQuery.result)
      taskTitles should have length 7
      taskTitles should be(List(
        (t1.title, t1.priority, t1.createdAt),
        (t2.title, t2.priority, t2.createdAt),
        (t3.title, t3.priority, t3.createdAt),
        (t4.title, t4.priority, t4.createdAt),
        (t5.title, t5.priority, t5.createdAt),
        (t6.title, t6.priority, t6.createdAt),
        (t7.title, t7.priority, t7.createdAt))
      )
    }

    it("should select all the high priority task titles"){
      val highPriorityTasks = performAction(selectHighPriorityTasksQuery.result)
      highPriorityTasks should have length 4
      highPriorityTasks should be(List(t1.title, t3.title, t5.title, t7.title))
    }

    it("should skip first 2 records and then limit result to 3") {
      val tasks = performAction(findAllTasksPageQuery(2, 3).result)
      tasks should have length 3
      tasks.head should have(
        'title (t3.title),
        'description (t3.description),
        'createdAt (t3.createdAt),
        'dueBy (t3.dueBy),
        'tags (t3.tags)
      )
    }

    it("should sort tasks in descending order of due date") {
      val tasks = performAction(selectTasksSortedByDueDateDescQuery.result)
      tasks.head should have(
        'title (t7.title),
        'description (t7.description),
        'createdAt (t7.createdAt),
        'dueBy (t7.dueBy),
        'tags (t7.tags)
      )
    }

    it("should find all due tasks") {
      val dueTasks = performAction(findAllDueTasks.result)
      dueTasks should have length 5
      dueTasks.map(_.title) should be(List(t3.title, t4.title, t5.title, t6.title, t7.title))
    }

    it("should find all tasks due today") {
      val dueTasks = performAction(selectAllTaskTitlesDueToday.result)
      dueTasks should have length 1
      dueTasks should be(List(t3.title))
    }


    it("select tasks between today and same date next month "){
      val tasks = performAction(selectTasksBetweenTodayAndSameDateNextMonthQuery.result)
      tasks should have length 1
    }

    it("check if any high priority task exists today"){
      val exists = performAction(checkIfAnyHighPriorityTaskExistsToday.result)
      exists should be(true)
    }


  }


}

================================================
FILE: 06-okhttp/README.md
================================================
Building A Lightweight Scala REST API Client with OkHttp
----

Welcome to the sixth blog of [52-technologies-in-2016](https://github.com/shekhargulati/52-technologies-in-2016) blog series. In this blog, we will learn how to write Scala REST API client for [Medium](https://medium.com/)'s REST API using [OkHttp](https://github.com/square/okhttp) library. [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) APIs have become a standard method of communication between two devices over a network. Most applications expose their REST API that developers can use to get work with an application programmatically. For example, if I have to build a realtime opinion mining application then I can use Twitter or Facebook REST APIs to get hold of their data and build my application. To work with an application REST APIs, you either can write your own client or you can use one of the language specific client provided by the application. Last few weeks, I have started using [Medium](https://medium.com/) for posting non-technical blogs. Medium is a blog publishing platform created by Twitter co-founder <a href="https://en.wikipedia.org/wiki/Evan_Williams_(Internet_entrepreneur)">Evan Williams</a>. Evan Williams is the same guy who earlier created <a href="https://en.wikipedia.org/wiki/Blogger_(service)">Blogger</a>, which was bought by Google in 2003.

Medium exposed their REST API to the external world last year. The API is simple and allows you to do operations like submitting a post, getting details of the authenticated user, getting publications for a user, etc. You can read about Medium API documentation in their [Github repository](https://github.com/Medium/medium-api-docs). Medium officially provides REST API clients for [Node.js](https://github.com/Medium/medium-sdk-nodejs), [Python](https://github.com/Medium/medium-sdk-python), and [Go](https://github.com/Medium/medium-sdk-go) programming languages. I couldn't find Scala client for Medium REST API so I decided to write my own client using OkHttp.

## What is OkHttp?

[OkHttp](https://square.github.io/okhttp/) is an open source Java HTTP client library focussed on efficiency. It is written by folks at [Square](https://squareup.com/). It supports [SPDY](https://developers.google.com/speed/spdy/), [HTTP/2](https://http2.github.io/), and [WebSocket](https://tools.ietf.org/html/rfc6455) protocols.

OkHttp API is very easy to use. You just have to add its dependency to your classpath and then you can start using it to build your clients.

According to [OkHttp documentation](https://square.github.io/okhttp/),

> OkHttp is an HTTP client that’s efficient by default:
* HTTP/2 support allows all requests to the same host to share a socket.
* Connection pooling reduces request latency (if HTTP/2 isn’t available).
* Transparent GZIP shrinks download sizes.
* Response caching avoids the network completely for repeat requests.

## Why are you using a Java library?

I know you must be thinking why I am using a Java library to build a Scala REST API client. Like most Scala developers, I thought of using a Scala library instead. But, as I started looking into which Scala library should I use I didn't find any single winner. If you search for "Scala REST client", you will land up on this [StackOverFlow question](https://stackoverflow.com/questions/12334476/simple-and-concise-http-client-library-for-scala). It suggests four libraries Dispatch, Scalaz http, spray-client, Play WS. Let's discuss why I didn't used them one by one.

1. [Dispatch](https://github.com/dispatch/reboot): It is a Scala wrapper around Ning's Async Http Client. The project doesn't look very active with last commit on [May 30, 2015](https://github.com/dispatch/reboot/commits/0.11.3). The [travis-ci build](https://travis-ci.org/dispatch/reboot) is also broken so I am not sure if this project is actively maintained.

2. [Scalaz](https://github.com/scalaz/scalaz/) http: Scalaz is an extension to the core Scala library for functional programming. They used to have an HTTP client. They [dropped http module from Scalaz in version 7](https://stackoverflow.com/questions/25482520/what-happened-to-the-scalaz-http-module).

3. [spray-client](http://spray.io/documentation/1.2.2/spray-client/):  It provides high-level HTTP client functionality by adding another logic layer on top of the relatively basic spray-can HTTP Client APIs. spray-client depends on many other spray projects and Akka. I didn't wanted to use a library that depends on so many other libraries.

4. [Play WS](https://www.playframework.com/documentation/2.5.x/ScalaWS): Play WS is part of the Scala's Play web framework. It can used in standalone mode but it also depends on [many other libraries](http://mvnrepository.com/artifact/com.typesafe.play/play-ws_2.11/2.4.6). It also looked very heavy weight for something simple. So, I decided not to use it as well.

## Why OkHttp?

My reasons for going with OkHttp are:

1. It has only one dependency Okio. [Okio](https://github.com/square/okio) is a library that complements java.io and java.nio to make it much easier to access, store, and process your data.
2. It has very good testing support. It provides [scriptable web server](https://github.com/square/okhttp/tree/master/mockwebserver) for testing HTTP client. This makes it easy to test whether your client is doing the right thing without depending on the network.
3. OkHttp is one of the few libraries that is designed up front for efficiency.
4. Stable and actively developed by Square. Last commit was 15 hours ago.
5. API is very simple and intuitive to use. It comes with good defaults and works like a charm.

Although, OkHttp is a Java library but it works great with Scala. I know it might not be the Scala way but sometimes we have to become pragmatic and choose the right tool for the job. There is also an OkHttp Scala wrapper called [Communicator](https://github.com/Taig/Communicator) that one can use.

## Github repository

The code for today’s application is available on github: [medium-scala-client](./medium-scala-client). In this blog, I will only cover couple of REST endpoints. You can view the full source of [medium-scala-sdk here](https://github.com/shekhargulati/medium-scala-sdk).

## Getting Started

Start by creating a new directory `medium-scala-client` at a convenient location on your filesystem. This directory will house the source code of our client.

```bash
$ mkdir medium-scala-client
```

Create a new file `build.sbt` inside the `medium-scala-client` directory. `build.sbt` is the sbt build script.
> **If you are new to sbt, then [please refer to my earlier post on it](../02-sbt/README.md).**

Populate `build.sbt` with following contents.

```scala
name := "medium-scala-client"

version := "1.0"

description := "Scala client for Medium.com REST API"

scalaVersion := "2.11.7"

libraryDependencies += "com.squareup.okhttp3" % "okhttp" % "3.0.1"

libraryDependencies += "io.spray" %% "spray-json" % "1.3.2"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"

libraryDependencies += "com.squareup.okhttp3" % "mockwebserver" % "3.0.1" % "test"
```

In the build script shown above, you can see that we have only added two compile time dependencies -- `okhttp` and `spray-json`. [spray-json](https://github.com/spray/spray-json) is a lightweight, clean, and efficient library to work with JSON in Scala. It has no dependencies. We will use it to convert our domain objects into JSON and vice-versa. `scalatest` and `mockwebserver` are added for testing.

Create a project layout for your Scala source and test files.

```bash
$ mkdir -p src/main/{scala,resources}
$ mkdir -p src/test/scala
```

## Getting the authenticated user's details

Let's start with implementing the REST endpoint to get details of an authenticated user. To get the details of a user, we have to make an HTTP GET request.

```
GET https://api.medium.com/v1/me
```

We will start with writing a test. Create a new package `medium` inside the `src/test/scala`. After creating the package, create a Scala class `MediumClientSpec`. Populate the `MediumClientSpec` with following contents.

```scala
package medium

import okhttp3.mockwebserver.MockWebServer
import org.scalatest.{BeforeAndAfterEach, FunSpec, Matchers}

class MediumClientSpec extends FunSpec with Matchers  with BeforeAndAfterEach{

  var server: MockWebServer = _

  override protected def beforeEach(): Unit = {
    server = new MockWebServer()
  }

  override protected def afterEach(): Unit = {
    server.shutdown()
  }
}
```

The code shown above does the following:

1. We created a new class `MediumClientSpec` that extended `FunSpec`, `Matchers`, and `BeforeAndAfterEach` traits. These are part of `scalatest` library.
2. We override two methods of `BeforeAndAfterEach` trait. `beforeEach` will make sure that `MockWebServer` instance is created before each test case is executed. `MockWebServer` is a scriptable web server. You can configure it to return mock responses for your requests. It works very similarly to any mocking framework. You first set your expectations, then run the application code, and finally verify that expected requests were made.
3. `afterEach` will make sure that server is shutdown after each test.

Add the following test case to the `MediumClientSpec`. This code should be added after the `afterEach` method.

```scala
describe("MediumClientSpec") {
  it("should get details of an authenticated user") {
    val json =
      """
        |{
        |  "data": {
        |    "id": "123",
        |    "username": "shekhargulati",
        |    "name": "Shekhar Gulati",
        |    "url": "https://medium.com/@shekhargulati",
        |    "imageUrl": "https://cdn-images-1.medium.com/fit/c/200/200/1*pC-eYQUV-iP2Y10_LgGvwA.jpeg"
        |  }
        |}
      """.stripMargin

    server.enqueue(new MockResponse()
      .setBody(json)
      .setHeader("Content-Type", "application/json")
      .setHeader("charset", "utf-8"))
    server.start()

    val medium = new MediumClient("test_client_id", "test_client_secret", Some("access_token")) {
      override val baseApiUrl = server.url("/v1/me")
    }
    val user = medium.getUser
    user should have(
      'id ("123"),
      'username ("shekhargulati"),
      'name ("Shekhar Gulati"),
      'url ("https://medium.com/@shekhargulati"),
      'imageUrl ("https://cdn-images-1.medium.com/fit/c/200/200/1*pC-eYQUV-iP2Y10_LgGvwA.jpeg")
    )
  }
}
```

Let's understand the code show above:

1. We created a json that will be returned by `MockWebServer` when GET request is made to `https://api.medium.com/v1/me`.
2. Then, we set up the server with a mock response. We set the body to the json created in step 1. Also, we added HTTP headers that will be passed in the response.
3. Next, we started the mock web server so that it can accept test requests.
4. Then, we created an instance of MediumClient(that we will create later in the blog). We have to set the URL returned by our server in the client so that it makes requests to the mock server instead of hitting the actual Medium API. This is the reason we have overridden `baseApiUrl` value of `MediumClient`.
5. Finally, we called the `getUser` method of `MediumClient` and asserted its response.

Now that we have written our test case we should start working on the implementation of MediumClient. Create a new package `medium` inside `src/main/scala`. Then, create a new Scala class `MediumClient` inside the `medium` package.

```scala
package medium

class MediumClient(clientId: String, clientSecret: String, var accessToken: Option[String] = None)

object MediumClient {
  def apply(clientId: String, clientSecret: String): MediumClient = new MediumClient(clientId, clientSecret)

  def apply(clientId: String, clientSecret: String, accessToken: String): MediumClient = new MediumClient(clientId, clientSecret, Some(accessToken))
}

case class MediumException(message: String, cause: Throwable = null) extends RuntimeException(message, cause)
```

The code shown above does the following:

1. We created a new Scala class `MediumClient`. The primary constructor of MediumClient takes three arguments -- `clientId`, `clientSecret`, and `accessToken`. The clientId and clientSecret are created for you when you create new Medium application [http://medium.com/me/applications](http://medium.com/me/applications). Using the clientId and clientSecret, users can generate `accessToken`. You have to pass `accessToken` in each request to the Medium API.
2. Then, we created a companion object to the `MediumClient`. It provides factory methods to easily construct `MediumClient` instances.
3. `MediumException` is a runtime exception that we will throw when client will not be able to process user requests.

Create an instance of `OkHttpClient` inside the `MediumClient` class as shown below. `OkHttpClient` is used to send HTTP requests and read HTTP responses. When you create the `OkHttpClient` instance using the default constructor then an `OkHttpClient` instance is created using the default values. You can also create an instance configured using other values by using the `OkHttpClient.Builder` API. We also created another value `baseApiUrl` of type `okhttp3.HttpUrl`. This will store the base URL of the Medium API i.e. `https://api.medium.com`.

```scala
import okhttp3.{HttpUrl, OkHttpClient}

class MediumClient(clientId: String, clientSecret: String, var accessToken: Option[String] = None) {
  val client = new OkHttpClient()

  val baseApiUrl: HttpUrl = new HttpUrl.Builder()
    .scheme("https")
    .host("api.medium.com")
    .build()

}
```

Now, we will write the `getUser` method that will make an HTTP GET request to the Medium API to fetch the user details. User is determined using the `accessToken`. If access token is not set then it will throw `MediumException`.

```scala
def getUser: User = accessToken match {
  case Some(at) => ???
  case _ => throw new MediumException("Please set access token")
}
```

> The Scala syntax `???` lets you write a not yet implemented method. This allows you to write code that compiles. But, if you run this code, then it will thrown an exception.

The code shown above needs `User` to compile. Create a new Scala object `domainObjects`. The `domainObjects.scala` will house all our domain objects like `User`, `Post`, etc. Create a case class for `User` inside it as shown below.

```scala
package medium

object domainObjects {

  case class User(id: String, username: String, name: String, url: String, imageUrl: String)

}
```

As shown above, we created a User case class with five fields inside the `domainObjects` Scala object.

After creating the `domainObjects` Scala object, add its import in the `MediumClient` so that code can compile.

```scala
import domainObjects._

class MediumClient(clientId: String, clientSecret: String, var accessToken: Option[String] = None)
```

Now, let's write code to replace `???` with actual implementation inside the `getUser` method.

```scala
def getUser: User = accessToken match {
  case Some(at) =>
    val request = new Request.Builder()
      .header("Content-Type", "application/json")
      .header("Accept", "application/json")
      .header("Accept-Charset", "utf-8")
      .header("Authorization", s"Bearer $at")
      .url(baseApiUrl.resolve("/v1/me"))
      .get()
      .build()
    makeRequest[User](request)
  case _ => throw new MediumException("Please set access token")
}

private def makeRequest[T](request: Request)(implicit p: JsonReader[T]): T = ???
```

In the code shown above:

1. We created request using the OkHttp `Request.Builder` API. We set the required headers in the request and set url of the request to `/v1/me`. `HttpUrl.resolve` method resolves the url against the baseApiUrl. So, the full url will become `https://api.medium.com/v1/me`. OkHttp understands which HTTP method to use by looking at the request. As you can see above, we called the `get` method of the request builder. This constructs an immutable `okhttp3.Request` object.
2. Once request is created, we passed the request to `makeRequest` method. This method will process any request be it GET or POST or DELETE and return the domain object.

Now, we will implement `makeRequest` method. `makeRequest` method makes use of `OkHttpClient` instance to create a new `Call`. To make the HTTP call, we first called `newCall` method on `OkHttpClient` instance. The `newCall` returns `okhttp3.Call` object. OkHttp uses `Call` to model the task of satisfying your request through however many intermediate requests and responses are necessary. Calls can be executed in synchronous or asynchronous manner. In the code shown below, we called the `execute` method to make a synchronous HTTP GET call.

```scala
private def makeRequest[T](request: Request)(implicit p: JsonReader[T]): T= {
  val response = client.newCall(request).execute()
  val responseJson = response.body().string()
  println(s"Received response $responseJson")
  ???
}
```
If you run the test method now, it will render the `json` response we have set in the test.

```javascript
Received response
{
  "data": {
    "id": "123",
    "username": "shekhargulati",
    "name": "Shekhar Gulati",
    "url": "https://medium.com/@shekhargulati",
    "imageUrl": "https://cdn-images-1.medium.com/fit/c/200/200/1*pC-eYQUV-iP2Y10_LgGvwA.jpeg"
  }
}
```

Now, let's take a look at the last bit of code required to convert json into `User` object. To convert json into User object, we will make use of `spray-json` library.

To use `spray-json`, we have to first add few imports so that relevant elements are added in the scope of  our `MediumClient`.

```scala
import spray.json._
import DefaultJsonProtocol._
```

After adding the imports, you can convert the json string into User object as shown below.

```scala
private def makeRequest[T](request: Request)(implicit p: JsonReader[T]): T= {
  val response = client.newCall(request).execute()
  val responseJson = response.body().string()
  println(s"Received response $responseJson")
  response match {
    case r if r.isSuccessful =>
      val jsValue: JsValue = responseJson.parseJson
      jsValue.asJsObject.getFields("data").headOption match {
        case Some(data) => data.convertTo[T]
        case _ => throw new MediumException(s"Received unexpected JSON response $responseJson")
      }
    case _ => throw new MediumException(s"Received HTTP error response code ${response.code()}")
  }
}
```

The code shown above will not compile as you have to bring implicit values in scope that provide `JsonFormat[User]` instances for User.

Create a new object `MediumApiProtocol` that will define a `JsonFormat` to convert `User` into JSON.

```scala
package medium

import medium.domainObjects.User
import spray.json.DefaultJsonProtocol

object MediumApiProtocol extends DefaultJsonProtocol{

  implicit val userFormat = jsonFormat5(User)

}
```

Now, code will compile and test case will pass.

## Posting a blog on Medium

Let's now implement method that will create a post on Medium. To create a post, we have to use HTTP POST method as we are creating a resource on the server. Let's write a test method, that will test the post creation.

```scala
it("should publish a new post") {
  val responsJson =
    """
      |{
      | "data": {
      |   "id": "e6f36a",
      |   "title": "Liverpool FC",
      |   "authorId": "5303d74c64f66366f00cb9b2a94f3251bf5",
      |   "tags": ["football", "sport", "Liverpool"],
      |   "url": "https://medium.com/@majelbstoat/liverpool-fc-e6f36a",
      |   "canonicalUrl": "http://jamietalbot.com/posts/liverpool-fc",
      |   "publishStatus": "public",
      |   "publishedAt": 1442286338435,
      |   "license": "all-rights-reserved",
      |   "licenseUrl": "https://medium.com/policy/9db0094a1e0f"
      | }
      |}
    """.stripMargin

  server.enqueue(new MockResponse()
    .setBody(responsJson)
    .setHeader("Content-Type", "application/json")
    .setHeader("charset", "utf-8"))
  server.start()
  val medium = new MediumClient("test_client_id", "test_client_secret", Some("access_token")) {
    override val baseApiUrl = server.url("/v1/users/123/posts")
  }

  val content =
    """
      |# Hello World
      |Hello how are you?
      |## What's up today?
      |Writing REST client for Medium API
    """.stripMargin
  val post = medium.createPost("123", PostRequest("Liverpool FC", "html", content))

  post.id should be("e6f36a")
}
```

Next, add `PostRequest` and `Post` case classes to `domainObjects.scala`.

```scala
package medium

object domainObjects {

  case class User(id: String, username: String, name: String, url: String, imageUrl: String)

  case class PostRequest(title: String, contentFormat: String, content: String, tags: Array[String] = Array(), canonicalUrl: Option[String] = None, publishStatus: String = "public", license: String = "all-rights-reserved")

  case class Post(id: String, publicationId: Option[String] = None, title: String, authorId: String, tags: Array[String], url: String, canonicalUrl: String, publishStatus: String, publishedAt: Long, license: String, licenseUrl: String)

}
```

Write the JSON formatter in `MediumApiProtocol` as shown below.

```scala
package medium

import medium.domainObjects._
import spray.json._

object MediumApiProtocol extends DefaultJsonProtocol{

  implicit val userFormat = jsonFormat5(User)

  implicit val postRequestFormat = jsonFormat7(PostRequest)

  implicit val postFormat = jsonFormat11(Post)

}
```

Now, we will write `createPost` that will create a Medium post.

```scala
def createPost(authorId: String, postRequest: PostRequest): Post = accessToken match {
  case Some(at) =>
    val httpUrl = baseApiUrl.resolve(s"/v1/users/$authorId/posts")
    val request = new Request.Builder()
      .header("Content-Type", "application/json")
      .header("Accept", "application/json")
      .header("Accept-Charset", "utf-8")
      .header("Authorization", s"Bearer $at")
      .url(httpUrl)
      .post(RequestBody.create(MediaType.parse("application/json"), postRequest.toJson.prettyPrint))
      .build()
    makeRequest[Post](request)
  case _ => throw new MediumException("Please set access token")
}
```

Now, compile the code and run the test case. Both test cases will pass now.

## Conclusion

This week we learnt how to write REST API using OkHttp library. We covered how to make HTTP GET and POST requests using OkHttp. OkHttp supports all HTTP methods like head, delete, put, etc. You can also use OKHttp to [make asynchronous calls](https://github.com/square/okhttp/wiki/Recipes#asynchronous-get). You can refer to [OkHttp documentation](https://github.com/square/okhttp/wiki) for more details.


That's all for this week. Please provide your valuable feedback by adding a comment to [https://github.com/shekhargulati/52-technologies-in-2016/issues/8](https://github.com/shekhargulati/52-technologies-in-2016/issues/8).

[![Analytics](https://ga-beacon.appspot.com/UA-59411913-2/shekhargulati/52-technologies-in-2016/06-okhttp)](https://github.com/igrigorik/ga-beacon)


================================================
FILE: 06-okhttp/medium-scala-client/.gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control

target/
lib_managed/
src_managed/
project/boot/
.history
.cache
### Scala template
*.class
*.log

# sbt specific
.cache
.history
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/

# Scala-IDE specific
.scala_dependencies
.worksheet



================================================
FILE: 06-okhttp/medium-scala-client/build.sbt
================================================
name := "medium-scala-client"

version := "1.0"

description := "Scala client for Medium.com REST API"

scalaVersion := "2.11.7"

libraryDependencies += "com.squareup.okhttp3" % "okhttp" % "3.0.1"

libraryDependencies += "io.spray" %% "spray-json" % "1.3.2"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" % "test"

libraryDependencies += "com.squareup.okhttp3" % "mockwebserver" % "3.0.1" % "test"


================================================
FILE: 06-okhttp/medium-scala-client/src/main/scala/medium/MediumApiProtocol.scala
================================================
package medium

import medium.domainObjects._
import spray.json._

object MediumApiProtocol extends DefaultJsonProtocol{

  implicit val userFormat = jsonFormat5(User)

  implicit val postRequestFormat = jsonFormat7(PostRequest)

  implicit val postFormat = jsonFormat11(Post)

}


================================================
FILE: 06-okhttp/medium-scala-client/src/main/scala/medium/MediumClient.scala
================================================
package medium

import medium.MediumApiProtocol._
import medium.domainObjects._
import okhttp3._
import spray.json._

class MediumClient(clientId: String, clientSecret: String, var accessToken: Option[String] = None) {
  val client = new OkHttpClient()

  val baseApiUrl: HttpUrl = new HttpUrl.Builder()
    .scheme("https")
    .host("api.medium.com")
    .build()

  def getUser: User = accessToken match {
    case Some(at) =>
      val request = new Request.Builder()
        .header("Content-Type", "application/json")
        .header("Accept", "application/json")
        .header("Accept-Charset", "utf-8")
        .header("Authorization", s"Bearer $at")
        .url(baseApiUrl.resolve("/v1/me"))
        .get()
        .build()
      makeRequest[User](request)
    case _ => throw new MediumException("Please set access token")
  }

  def createPost(authorId: String, postRequest: PostRequest): Post = accessToken match {
    case Some(at) =>
      val httpUrl = baseApiUrl.resolve(s"/v1/users/$authorId/posts")
      val request = new Request.Builder()
        .header("Content-Type", "application/json")
        .header("Accept", "application/json")
        .header("Accept-Charset", "utf-8")
        .header("Authorization", s"Bearer $at")
        .url(httpUrl)
        .post(RequestBody.create(MediaType.parse("application/json"), postRequest.toJson.prettyPrint))
        .build()
      makeRequest[Post](request)
    case _ => throw new MediumException("Please set access token")
  }


  private def makeRequest[T](request: Request)(implicit p: JsonReader[T]): T= {
    val response = client.newCall(request).execute()
    val responseJson = response.body().string()
    println(s"Received response $responseJson")
    response match {
      case r if r.isSuccessful =>
        val jsValue: JsValue = responseJson.parseJson
        jsValue.asJsObject.getFields("data").headOption match {
          case Some(data) => data.convertTo[T]
          case _ => throw new MediumException(s"Received unexpected JSON response $responseJson")
        }
      case _ => throw new MediumException(s"Received HTTP error response code ${response.code()}")
    }
  }



}

object MediumClient {
  def apply(clientId: String, clientSecret: String): MediumClient = new MediumClient(clientId, clientSecret)

  def apply(clientId: String, clientSecret: String, accessToken: String): MediumClient = new MediumClient(clientId, clientSecret, Some(accessToken))
}

case class MediumException(message: String, cause: Throwable = null) extends RuntimeException(message, cause)



================================================
FILE: 06-okhttp/medium-scala-client/src/main/scala/medium/domainObjects.scala
================================================
package medium

object domainObjects {

  case class User(id: String, username: String, name: String, url: String, imageUrl: String)

  case class PostRequest(title: String, contentFormat: String, content: String, tags: Array[String] = Array(), canonicalUrl: Option[String] = None, publishStatus: String = "public", license: String = "all-rights-reserved")

  case class Post(id: String, publicationId: Option[String] = None, title: String, authorId: String, tags: Array[String], url: String, canonicalUrl: String, publishStatus: String, publishedAt: Long, license: String, licenseUrl: String)

}


================================================
FILE: 06-okhttp/medium-scala-client/src/test/scala/medium/MediumClientSpec.scala
================================================
package medium

import medium.domainObjects.PostRequest
import okhttp3.mockwebserver.{MockResponse, MockWebServer}
import org.scalatest.{BeforeAndAfterEach, FunSpec, Matchers}

class MediumClientSpec extends FunSpec with Matchers with BeforeAndAfterEach {

  var server: MockWebServer = _

  override protected def beforeEach(): Unit = {
    server = new MockWebServer()
  }

  override protected def afterEach(): Unit = {
    server.shutdown()
  }

  describe("MediumClientSpec") {

    it("should get details of an authenticated user") {
      val json =
        """
          |{
          |  "data": {
          |    "id": "123",
          |    "username": "shekhargulati",
          |    "name": "Shekhar Gulati",
          |    "url": "https://medium.com/@shekhargulati",
          |    "imageUrl": "https://cdn-images-1.medium.com/fit/c/200/200/1*pC-eYQUV-iP2Y10_LgGvwA.jpeg"
          |  }
          |}
        """.stripMargin

      server.enqueue(new MockResponse()
        .setBody(json)
        .setHeader("Content-Type", "application/json")
        .setHeader("charset", "utf-8"))
      server.start()

      val medium = new MediumClient("test_client_id", "test_client_secret", Some("access_token")) {
        override val baseApiUrl = server.url("/v1/me")
      }
      val user = medium.getUser
      user should have(
        'id ("123"),
        'username ("shekhargulati"),
        'name ("Shekhar Gulati"),
        'url ("https://medium.com/@shekhargulati"),
        'imageUrl ("https://cdn-images-1.medium.com/fit/c/200/200/1*pC-eYQUV-iP2Y10_LgGvwA.jpeg")
      )
    }

    it("should publish a new post") {
      val responsJson =
        """
          |{
          | "data": {
          |   "id": "e6f36a",
          |   "title": "Liverpool FC",
          |   "authorId": "5303d74c64f66366f00cb9b2a94f3251bf5",
          |   "tags": ["football", "sport", "Liverpool"],
          |   "url": "https://medium.com/@majelbstoat/liverpool-fc-e6f36a",
          |   "canonicalUrl": "http://jamietalbot.com/posts/liverpool-fc",
          |   "publishStatus": "public",
          |   "publishedAt": 1442286338435,
          |   "license": "all-rights-reserved",
          |   "licenseUrl": "https://medium.com/policy/9db0094a1e0f"
          | }
          |}
        """.stripMargin

      server.enqueue(new MockResponse()
        .setBody(responsJson)
        .setHeader("Content-Type", "application/json")
        .setHeader("charset", "utf-8"))
      server.start()
      val medium = new MediumClient("test_client_id", "test_client_secret", Some("access_token")) {
        override val baseApiUrl = server.url("/v1/users/123/posts")
      }

      val content =
        """
          |# Hello World
          |Hello how are you?
          |## What's up today?
          |Writing REST client for Medium API
        """.stripMargin
      val post = medium.createPost("123", PostRequest("Liverpool FC", "html", content))

      post.id should be("e6f36a")
    }

  }

}

================================================
FILE: 07-hugo/README.md
================================================
Hugo: A Modern WebSite Engine That Just Works
----

This week I decided to take a break from Scala and scratch my own itch my building an online bookshelf using Hugo. **[Hugo](https://gohugo.io/)** is a static site generator written in Go programming language. You can use it for building modern static websites. Static site generator takes your content files written in a markup language like [Markdown](https://en.wikipedia.org/wiki/Markdown), apply layouts you have defined, and generate static HTML files that can be delivered to the user. Static websites are nothing new, they date back to the [first ever website](http://info.cern.ch/hypertext/WWW/TheProject.html) in human history. We started with static websites, then moved to dynamic websites, and finally we are moving back to static websites for use-cases where it make sense. Most common use-cases for static websites are blogs, product documentation, help guides, tutorials, online portfolio or resume.

> **This blog is part of my year long blog series [52 Technologies in 2016](https://github.com/shekhargulati/52-technologies-in-2016)**

Static generators again came into limelight after the introduction of [Jekyll](https://jekyllrb.com/) in 2008. Jekyll is a static website generator written in Ruby. It was created by Github co-founder Tom Preston-Werner. Because Jekyll was created by Github co-founder, it has very good integration with Github. It was very easy to get your website running on Github pages.

Another reason static site generators are back in popularity has to do with a lot of advantages they offer. In my opinion, static generators offer following advantages:

1. You don't need a database to store content
2. Forces you to use version control system to store content
3. Fast and cacheable
4. Less maintenance overhead
5. Works well for a lot of use-cases like blogs, documentation, etc.
6. Low barrier to entry
7. Runs out of the box on many platforms like Github pages, Amazon S3, or any web server like Nginx
8. Good developer workflow using Git

I would recommend that you read [good post by David Walsh on advantages and disadvantages of static site generators](https://davidwalsh.name/introduction-static-site-generators). If you look at Google trends, you will notice a steep rise in interest for static site generators. As you can see below, after 2011 more and more people are searching about `static site generator`.

<img src="images/google-trends.png" width="600">

There are many Open-source static site generators options available to the users. You can choose from more than [400 static site generators](https://staticsitegenerators.net/). The most popular ones are [Jekyll](https://github.com/jekyll/jekyll), [Hugo](https://github.com/spf13/hugo), [Middleman](https://github.com/middleman/middleman), [Harp](https://github.com/sintaxi/harp).

> **I have personally used Jekyll and Hugo. I migrated my company blog to Jekyll and it didn't turned out to be a good decision. The two main drawback of Jekyll are 1) you need Ruby runtime 2) it is very slow for bigger projects. To get someone running Jekyll on a machine is a pain. This leads to a barrier in adoption.**

## Why Hugo?

As mentioned above, I had a bad experience with Jekyll so I was looking for an alternative that didn't have same limitations. The reasons I prefer Hugo are:

1. It is not dependent on any programming language runtime
2. It provides binaries for all modern operating system
3. It is Fast
4. It provides quick feedback by live reloading of the content
5. It comes with good defaults and follows convention over configuration philosophy

## Why an online bookshelf?

Some of you might be wondering why I wanted to create an online bookshelf. One promise that I made this year to myself is to read at least one non-technology each month in 2016. Last many years, I am upset with myself for not giving time to read non-technology books. This year I have to change this so I decided to build a bookshelf that will keep track of all the books I read. I got inspired to build my online bookshelf after visiting [Bill Gates blog](https://www.gatesnotes.com/). Bill Gates is maintaining an [awesome bookshelf](https://www.gatesnotes.com/Books) where he shares which his recently read books and their reviews. A screenshot of his bookshelf is shown below.

<img src="images/bill_gate_bookshelf.png" width="600">

-----

Building our bookshelf
---

Now, that we know about static site generators and Hugo let's start building our bookshelf step by step. By the end of this tutorial, we will have our bookshelf hosted on Github pages and mapped to a domain.

## Github repository

The code for today’s demo application is available on github: [bookshelf](./bookshelf).

## Step 1: Getting started with Hugo

Go to [https://github.com/spf13/hugo/releases](https://github.com/spf13/hugo/releases) and download Hugo for your operating system. If you are on Mac, the you can install using `brew` package manager as well.

```bash
$ brew update && brew install hugo
```

Once `hugo` is installed, make sure to run the `help` command to verify `hugo` installation. Below I am only showing part of the output of the `help` command for brevity.

```bash
$ hugo help
```
```
hugo is the main command, used to build your Hugo site.

Hugo is a Fast and Flexible Static Site Generator
built with love by spf13 and friends in Go.

Complete documentation is available at http://gohugo.io/.
```

You can check `hugo` version using the command shown below.

```bash
$ hugo version
```
```
Hugo Static Site Generator v0.15 BuildDate: 2015-11-26T11:59:00+05:30
```

> **In this post, we will use the latest version of hugo i.e. version 0.15**

## Step 2: Scaffold bookshelf hugo site

Hugo has commands that allows us to quickly scaffold a Hugo managed website. Navigate to a convenient location on your filesystem and create a new Hugo site `bookshelf` by executing the following command.

```bash
$ hugo new site bookshelf
```

Change directory to `bookshelf` and you will see the following directory layout.

```bash
$ tree -a
```
```
.
|-- archetypes
|-- config.toml
|-- content
|-- data
|-- layouts
`-- static

5 directories, 1 file
```

As mentioned in the command output, `bookshelf` directory has 5 sub-directories and 1 file. Let's look at each of them one by one.

* **archetypes**: You can create new content files in Hugo using the `hugo new` command. When you run that command, it adds few configuration properties to the post like date and title. [Archetype](https://gohugo.io/content/archetypes/) allows you to define your own configuration properties that will be added to the post front matter whenever `hugo new` command is used.

* **config.toml**: Every website should have a configuration file at the root. By default, the configuration file uses `TOML` format but you can also use `YAML` or `JSON` formats as well. [TOML](https://github.com/toml-lang/toml) is minimal configuration file format that's easy to read due to obvious semantics. The configuration settings mentioned in the `config.toml` are applied to the full site. These configuration settings include `baseurl` and `title` of the website.

* **content**: This is where you will store content of the website. Inside content, you will create sub-directories for different sections. Let's suppose your website has three actions -- `blog`, `article`, and `tutorial` then you will have three different directories for each of them inside the `content` directory. The name of the section i.e. `blog`, `article`, or `tutorial` will be used by Hugo to apply a specific layout applicable to that section.

* **data**: This directory is used to store configuration YAML, JSON,or TOML files that can be used by Hugo when generating your website.

* **layouts**: The content inside this directory is used to specify how your content will be converted into the static website.

* **static**: This directory is used to store all the static content that your website will need like images, CSS, JavaScript or other static content.

## Step 3: Add content

Let's now add a post to our `bookshelf`. We will use the `hugo new` command to add a post. In January, I read [Good To Great](http://www.amazon.com/Good-Great-Some-Companies-Others/dp/0066620996/) book so we will start with creating a post for it. **Make sure you are inside the `bookshelf` directory.**

```bash
$ hugo new post/good-to-great.md
```
```
/Users/shekhargulati/bookshelf/content/post/good-to-great.md created
```

The above command will create a new directory `post` inside the `content` directory and create `good-to-great.md` file inside it.

```bash
$ tree -a content
```
```
content
`-- post
    `-- good-to-great.md

1 directory, 1 file
```

The content inside the `good-to-great.md` looks like as shown below.

```
+++
date = "2016-02-14T16:11:58+05:30"
draft = true
title = "good to great"

+++
```

The content inside `+++` is the TOML configuration for the post. This configuration is called **front matter**. It enables you to define about the post along with the content. Every post has three configuration properties shown above.

* **date** specifies the date and time at which post was created.
* **draft** specifies that post is not ready for publication yet so it will not be in the generated site
* **title** specifies title for the post

Let's add a small review for **Good to Great** book.

```
+++
date = "2016-02-14T16:11:58+05:30"
draft = true
title = "Good to Great Book Review"

+++

I read **Good to Great in January 2016**. An awesome read sharing detailed analysis on how good companies became great. Although this book is about how companies became great but we could apply a lot of the learnings on ourselves. Concepts like level 5 leader, hedgehog concept, the stockdale paradox are equally applicable to individuals.
```

## Step 4: Serve content

Hugo has inbuilt server that can serve content so that you can preview it. You can also use the inbuilt Hugo server in production as well. To serve content, execute the following command.

```bash
$ hugo server
```
```
0 of 1 draft rendered
0 future content
0 pages created
0 paginator pages created
0 tags created
0 categories created
in 9 ms
Watching for changes in /Users/shekhargulati/bookshelf/{data,content,layouts,static}
Serving pages from memory
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
```

This will start the server on port `1313`. You can view your blog at http://localhost:1313/. When you will go to the link, you will see nothing. There are couple of reasons for that:

1. As you can see in the `hugo server` command output, Hugo didn't rendered the draft. Hugo will only render drafts if you pass `buildDrafts` flag to the `hugo server` command.
2. We have not specified how Markdown content should be rendered. We have to specify a theme that Hugo can use. We will do that in next step.

To render drafts, re-run the server with command shown below.

```bash
$ hugo server --buildDrafts
```
```
1 of 1 draft rendered
0 future content
1 pages created
0 paginator pages created
0 tags created
0 categories created
in 6 ms
Watching for changes in /Users/shekhargulati/bookshelf/{data,content,layouts,static}
Serving pages from memory
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
```

If you go to [http://localhost:1313/](http://localhost:1313/), you will still not view anything as we have not specified theme that Hugo should use.

## Step 5: Add theme

Themes provide the layout and templates that will be used by Hugo to render your website. There are a lot of Open-source themes available at [https://themes.gohugo.io/](https://themes.gohugo.io/) that you can use. From the [Hugo docs](https://gohugo.io/themes/overview/),

> **Hugo currently doesn’t ship with a `default` theme, allowing the user to pick whichever theme best suits their project.**

Themes should be added in the `themes` directory inside the website root. Create new directory themes and change directory to it.

```bash
$ mkdir themes && cd themes
```
Now, you clone one or more themes inside the `themes` directory. We will use robust theme.

```bash
$ git clone git@github.com:dim0627/hugo_theme_robust.git
```

Start the server again

```bash
$ hugo server --theme=hugo_theme_robust --buildDrafts
```
```
1 of 1 draft rendered
0 future content
1 pages created
2 paginator pages created
0 tags created
0 categories created
in 10 ms
Watching for changes in /Users/shekhargulati/bookshelf/{data,content,layouts,static,themes}
Serving pages from memory
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
```

> ** If Hugo will not find a specific theme in the `themes` directory then it will throw an exception as shown below.**
```
FATAL: 2016/02/14 Unable to find theme Directory: /Users/shekhargulati/bookshelf/themes/robust
```

To view your website, you can go to http://localhost:1313/. You will see as shown below.

<img src="images/bookshelf-robust-theme.png" width="600">

Let's understand the layout of a theme. A theme consists of following:

* **theme.toml** is the theme configuration file that gives information about the theme like name and description of theme, author details, theme license.

* **images** directory contains two images -- `screenshot.png` and `tn.png`. `screenshot.png` is the image of the list view and `tn.png` is the single post view.

* **layouts** directory contains different views for different content types. Every content type should have two files single.html and list.html. single.html is used for rendering single piece of content. list.html is used to view a list of content items for example all posts with `programming` tag.

* **static** directory stores all the static assets used by the template. This could JavaScript libraries like jQuery or CSS styles or images or any other static content. This directory will be copied into the final site when rendered.

## Step 6: Use multiple themes

You can very easy test different layouts by switching between different themes. Let's suppose we want to try out `bleak` theme. We clone `bleak` theme inside the `themes` directory.

```bash
$ git clone git@github.com:Zenithar/hugo-theme-bleak.git
```


Restart the server using `hugo-theme-bleak`.

```bash
$ hugo server --theme=hugo-theme-bleak --buildDrafts
```

Now, website will use `bleak` theme and will be rendered differently as shown below.

<img src="images/bookshelf-bleak-theme.png" width="600">

## Step 7: Update config.toml and live reloading in action

Restart the server with `robust` theme as we will use it in this blog.

```bash
$ hugo server --theme=hugo_theme_robust --buildDrafts
```

The website uses the dummy values specified in the `config.toml`. Let's update the configuration.

```toml
baseurl = "http://replace-this-with-your-hugo-site.com/"
languageCode = "en-us"
title = "Shekhar Gulati Book Reviews"

[Params]
  Author = "Shekhar Gulati"
```

Hugo has inbuilt support for live reloading. So, as soon as you save your changes it will apply the change and reload the web page. You will see changes as shown below.

<img src="images/bookshelf-updated-config.png" width="600">

The same is reflected in the Hugo server logs as well. As soon as the configuration is changed, it applied the changes.

```
Config file changed: /Users/shekhargulati/bookshelf/config.toml
1 of 1 draft rendered
0 future content
1 pages created
2 paginator pages created
0 tags created
0 categories created
in 11 ms
```

## Step 8: Customize robust theme

Robust theme is a good start towards our online bookshelf but we to customize it a bit to meet the look and feel required for the bookshelf. Hugo makes it very easy to customize themes. You can also create your themes but we will not do that today. If you want to create your own theme, then you should refer to the [Hugo documentation](https://gohugo.io/themes/creation/).

The first change that we have to make is to use a different default image instead of the one used in the theme. The default image used in both the list and single view page resides inside the `themes/hugo_theme_robust/static/images/default.jpg`. We can easily replace it by creating a simple directory structure inside the `static` directory inside the `bookshelf` directory.

Create images directory inside the static directory and copy an image with name `default.jpg` inside it. We will use the default image shown below.

<img src="images/default.jpg" width="600">

Hugo will sync the changes and reload the website to use new image as shown below.

<img src="images/bookshelf-new-default-image.png" width="600">

Now, we need to change the layout of the index page so that only images are shown instead of the text. The index.html inside the layouts directory of the theme refer to partial `li` that renders the list view shown below.

```html
<article class="li">
  <a href="{{ .Permalink }}" class="clearfix">
    <div class="image" style="background-image: url({{ $.Site.BaseURL }}images/{{ with .Params.image }}{{ . }}{{ else }}default.jpg{{ end }});"></div>
    <div class="detail">
      <time>{{ with .Site.Params.DateForm }}{{ $.Date.Format . }}{{ else }}{{ $.Date.Format "Mon, Jan 2, 2006" }}{{ end }}</time>
      <h2 class="title">{{ .Title }}</h2>
      <div class="summary">{{ .Summary }}</div>
    </div>
  </a>
</article>
```

Create a new file li.html inside the `bookshelf/layouts/_default` directory. Copy the content shown below into the li.html. We have removed details of the book so that only image is shown.

```html
<article class="li">
  <a href="{{ .Permalink }}" class="clearfix">
    <div class="image" style="background-image: url({{ $.Site.BaseURL }}images/{{ with .Params.image }}{{ . }}{{ else }}default.jpg{{ end }});"></div>
  </a>
</article>
```

Now, the website will be rendered as shown below.

<img src="images/bookshelf-only-picture.png" width="600">


Next, we want to remove information related to theme from the footer. So, create a new file inside the `partials/default_foot.html` with the content copied from the theme `partials/default_foot.html`. Replace the footer section with the one shown below.

```html
<footer class="site">
  <p>{{ with .Site.Copyright | safeHTML }}{{ . }}{{ else }}&copy; {{ $.Site.LastChange.Year }} {{ if isset $.Site.Params "Author" }}{{ $.Site.Params.Author }}{{ else }}{{ .Site.Title }}{{ end }}{{ end }}</p>
  <p>Powered by <a href="http://gohugo.io" target="_blank">Hugo</a>,</p>
</footer>
```

We also have to remove the sidebar on the right. Copy the index.html from the themes layout directory to the bookshelf layouts directory. Remove the section related to sidebar from the html.

```html
<div class="col-sm-3">
  {{ partial "sidebar.html" . }}
</div>
```

So far we are using the default image but we would like to use the book image so that we can relate to the book. Every book review will define a configuration setting in its front matter. Update the `good-to-great.md` as shown below.


```
+++
date = "2016-02-14T16:11:58+05:30"
draft = true
title = "Good to Great Book Review"
image = "good-to-great.jpg"
+++

I read **Good to Great in January 2016**. An awesome read sharing detailed analysis on how good companies became great. Although this book is about how companies became great but we could apply a lot of the learnings on ourselves. Concepts like level 5 leader, hedgehog concept, the stockdale paradox are equally applicable to individuals.
```

After adding few more books to our shelf, the shelf looks like as shown below. These are few books that I have read within last one year.

<img src="images/bookshelf.png" width="600">


## Step 9: Make posts public

So far all the posts that we have written are in draft status. To make a draft public, you can either run a command or manually change the draft status in the post to True.

```bash
$ hugo undraft content/post/good-to-great.md
```

Now, you can start the server without `buildDrafts` option.

```
$ hugo server --theme=hugo_theme_robust
```

## Step 10: Integrate Disqus

Disqus allows you to integrate comments in your static blog. To enable Disqus, you just have to set `disqusShortname`  in the config.toml as shown below.

```
[Params]
  Author = "Shekhar Gulati"
  disqusShortname = "shekhargulati"
```

Now, commenting will be enabled in your blog.

<img src="images/bookshelf-disqus.png" width="600">

## Step 11: Generate website

To generate Hugo website code that you can use to deploy your website, type the following command.

```bash
$ hugo --theme=hugo_theme_robust
0 draft content
0 future content
5 pages created
2 paginator pages created
0 tags created
0 categories created
in 17 ms
```

> **Make sure to change the baseurl. For my bookshelf on Github pages, url is [https://shekhargulati.github.io/bookshelf](https://shekhargulati.github.io/bookshelf)**

After you run the hugo command, a public directory will be created with the generated website source.


## Step 12: Deploy bookshelf on Github pages

Create a new repository with name `bookshelf` on Github. Once created, create a new Git repo on local system and add remote.

```bash
$ mkdir bookshelf-public
$ cd bookshelf-public
$ git init
$ git remote add origin git@github.com:shekhargulati/bookshelf.git
```

Copy the content of the `public` directory to the `bookshelf-public` directory. Run this command from with in the `bookshelf-public` directory.

```bash
$ cp -r ../bookshelf/public/ .
```

Create new branch `gh-pages` and checkout it.

```bash
$ git checkout -b gh-pages
Switched to a new branch 'gh-pages'
```

Add all the files to the index, commit them, and push the changes to Github.

```bash
$ git add --all
$ git commit -am "bookshelf added"
$ git push origin gh-pages
```

In couple of minutes, your website will be live https://shekhargulati.github.io/bookshelf/.

----

That's all for this week. Please provide your valuable feedback by adding a comment to [https://github.com/shekhargulati/52-technologies-in-2016/issues/10](https://github.com/shekhargulati/52-technologies-in-2016/issues/10).

[![Analytics](https://ga-beacon.appspot.com/UA-59411913-2/shekhargulati/52-technologies-in-2016/07-hugo)](https://github.com/igrigorik/ga-beacon)


================================================
FILE: 07-hugo/bookshelf/config.toml
================================================
baseurl = "http://example.com"
languageCode = "en-us"
title = "Shekhar Gulati Bookshelf"

[Params]
  Author = "Shekhar Gulati"
  disqusShortname = "shekhargulati"
  GoogleAnalyticsUserID = "UA-73784822-1"


================================================
FILE: 07-hugo/bookshelf/content/post/art-of-thinking-clearly.md
================================================
+++
date = "2016-02-14T19:10:29+05:30"
draft = false
title = "art of thinking clearly"
image = "art-of-thinking-clearly.jpg"
+++


===============
Download .txt
gitextract_yi40xssl/

├── .gitignore
├── 01-finatra/
│   ├── README.md
│   └── fitman/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   ├── build.properties
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   ├── resources/
│           │   │   └── logback.xml
│           │   └── scala/
│           │       └── com/
│           │           └── shekhargulati/
│           │               └── fitman/
│           │                   ├── FitmanApp.scala
│           │                   └── api/
│           │                       └── WeightResource.scala
│           └── test/
│               └── scala/
│                   └── com/
│                       └── shekhargulati/
│                           └── fitman/
│                               ├── HelloControllerFeatureTest.scala
│                               └── api/
│                                   └── WeightResourceFeatureTest.scala
├── 02-sbt/
│   ├── README.md
│   └── tasky/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       ├── scalastyle-config.xml
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       ├── HelloSbt.scala
│           │       ├── datamodels.scala
│           │       └── taskmanager.scala
│           └── test/
│               └── scala/
│                   └── TaskManagerSpec.scala
├── 03-stanford-corenlp/
│   ├── README.md
│   └── sentiment-analyzer/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── com/
│           │           └── shekhargulati/
│           │               └── sentiment_analyzer/
│           │                   └── SentimentAnalyzer.scala
│           └── test/
│               └── scala/
│                   └── com/
│                       └── shekhargulati/
│                           └── sentiment_analyzer/
│                               └── SentimentAnalyzerSpec.scala
├── 04-slick/
│   ├── README.md
│   └── tasky/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── datamodel/
│           │           └── DataModel.scala
│           └── test/
│               ├── resources/
│               │   └── application.conf
│               └── scala/
│                   └── datamodel/
│                       └── DataModelSpec.scala
├── 05-slick/
│   ├── README.md
│   └── tasky/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       ├── datamodel/
│           │       │   ├── Priority.scala
│           │       │   ├── columnDataMappers.scala
│           │       │   └── dataModel.scala
│           │       └── queries/
│           │           └── queries.scala
│           └── test/
│               ├── resources/
│               │   └── application.conf
│               └── scala/
│                   ├── datamodel/
│                   │   └── DataModelSpec.scala
│                   └── queries/
│                       └── QueriesSpec.scala
├── 06-okhttp/
│   ├── README.md
│   └── medium-scala-client/
│       ├── .gitignore
│       ├── build.sbt
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── medium/
│           │           ├── MediumApiProtocol.scala
│           │           ├── MediumClient.scala
│           │           └── domainObjects.scala
│           └── test/
│               └── scala/
│                   └── medium/
│                       └── MediumClientSpec.scala
├── 07-hugo/
│   ├── README.md
│   ├── bookshelf/
│   │   ├── config.toml
│   │   ├── content/
│   │   │   └── post/
│   │   │       ├── art-of-thinking-clearly.md
│   │   │       ├── confessions-of-a-public-speaker.md
│   │   │       ├── good-to-great.md
│   │   │       ├── hen-who-dreamed-she-could-fly.md
│   │   │       └── seven-habbits-of-highly-effective-people.md
│   │   ├── layouts/
│   │   │   ├── _default/
│   │   │   │   └── li.html
│   │   │   ├── index.html
│   │   │   └── partials/
│   │   │       ├── default_foot.html
│   │   │       └── default_head.html
│   │   └── themes/
│   │       └── hugo_theme_robust/
│   │           ├── .gitignore
│   │           ├── LICENSE.md
│   │           ├── README.md
│   │           ├── config.yaml
│   │           ├── layouts/
│   │           │   ├── _default/
│   │           │   │   ├── li.html
│   │           │   │   ├── list.html
│   │           │   │   ├── single.html
│   │           │   │   └── terms.html
│   │           │   ├── index.html
│   │           │   ├── partials/
│   │           │   │   ├── default_foot.html
│   │           │   │   ├── default_head.html
│   │           │   │   ├── pagination.html
│   │           │   │   └── sidebar.html
│   │           │   └── rss.xml
│   │           ├── static/
│   │           │   └── css/
│   │           │       ├── custom.css
│   │           │       └── styles.css
│   │           └── theme.toml
│   └── bookshelf-public/
│       ├── 404.html
│       ├── categories/
│       │   └── index.html
│       ├── css/
│       │   ├── custom.css
│       │   └── styles.css
│       ├── index.html
│       ├── index.xml
│       ├── page/
│       │   └── 1/
│       │       └── index.html
│       ├── post/
│       │   ├── art-of-thinking-clearly/
│       │   │   └── index.html
│       │   ├── confessions-of-a-public-speaker/
│       │   │   └── index.html
│       │   ├── good-to-great/
│       │   │   └── index.html
│       │   ├── hen-who-dreamed-she-could-fly/
│       │   │   └── index.html
│       │   ├── index.html
│       │   ├── index.xml
│       │   ├── page/
│       │   │   └── 1/
│       │   │       └── index.html
│       │   └── seven-habbits-of-highly-effective-people/
│       │       └── index.html
│       ├── sitemap.xml
│       └── tags/
│           └── index.html
├── 08-coreos/
│   └── README.md
├── 09-cloudvision/
│   ├── README.md
│   └── people-counter/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   └── java/
│           │       └── com/
│           │           └── shekhargulati/
│           │               └── peoplecounter/
│           │                   ├── FaceDetector.java
│           │                   ├── GoogleVisionServiceFactory.java
│           │                   ├── ImagePeopleCount.java
│           │                   ├── ImageWriter.java
│           │                   ├── PeopleCounter.java
│           │                   ├── PeopleCounterApp.java
│           │                   └── TweetObservable.java
│           └── test/
│               ├── java/
│               │   └── com/
│               │       └── shekhargulati/
│               │           └── peoplecounter/
│               │               ├── FaceDetectorTest.java
│               │               └── PeopleCounterTest.java
│               └── resources/
│                   └── response.json
├── 10-gatling/
│   ├── README.md
│   └── blog/
│       ├── .gitignore
│       ├── build.sbt
│       ├── project/
│       │   └── plugins.sbt
│       └── src/
│           └── test/
│               └── scala/
│                   └── loadtests/
│                       └── AccessHomePageSimulation.scala
├── 11-textblob/
│   ├── README.md
│   └── sentiment-analyzer/
│       ├── .gitignore
│       ├── .openshift/
│       │   ├── action_hooks/
│       │   │   ├── README.md
│       │   │   └── deploy
│       │   ├── cron/
│       │   │   ├── README.cron
│       │   │   ├── daily/
│       │   │   │   └── .gitignore
│       │   │   ├── hourly/
│       │   │   │   └── .gitignore
│       │   │   ├── minutely/
│       │   │   │   └── .gitignore
│       │   │   ├── monthly/
│       │   │   │   └── .gitignore
│       │   │   └── weekly/
│       │   │       ├── README
│       │   │       ├── chronograph
│       │   │       ├── jobs.allow
│       │   │       └── jobs.deny
│       │   └── markers/
│       │       └── .gitkeep
│       ├── requirements.txt
│       ├── sentimentanalyzer.py
│       ├── static/
│       │   ├── css/
│       │   │   ├── bootstrap-theme.css
│       │   │   └── bootstrap.css
│       │   └── js/
│       │       └── jquery.js
│       ├── templates/
│       │   └── index.html
│       └── wsgi.py
├── 11-tweet-deduplication/
│   └── README.md
├── 12-play/
│   └── .gitkeep
├── 13-arangodb/
│   ├── README.md
│   └── localjobs/
│       └── jobs.json
├── 14-kafka/
│   └── README.md
├── 15-huginn/
│   └── README.md
├── 16-newspaper/
│   └── README.md
├── 17-typescript/
│   ├── README.md
│   └── code/
│       ├── getting-started.ts
│       ├── js/
│       │   └── getting-started.js
│       └── tsconfig.json
├── 18-mesos/
│   ├── README.md
│   └── images/
│       └── diagrams.key
├── 19-bees/
│   └── README.md
├── 20-json/
│   ├── README.md
│   └── code/
│       └── db.json
├── 21-strman/
│   └── README.md
├── 22-regex/
│   ├── README.md
│   └── code/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   └── java/
│           │       └── week22/
│           │           └── regex/
│           │               └── GaddafiSpellingMatcher.java
│           └── test/
│               └── java/
│                   └── week22/
│                       └── regex/
│                           └── GaddafiSpellingMatcherTest.java
├── 23-android-part1/
│   └── README.md
├── 24-jekyll-to-wordpress/
│   └── README.md
├── 25-angular-dragula/
│   ├── README.md
│   └── trello/
│       ├── .bowerrc
│       ├── .editorconfig
│       ├── .eslintrc
│       ├── .gitignore
│       ├── .yo-rc.json
│       ├── bower.json
│       ├── e2e/
│       │   ├── .eslintrc
│       │   ├── main.po.js
│       │   └── main.spec.js
│       ├── gulp/
│       │   ├── .eslintrc
│       │   ├── build.js
│       │   ├── conf.js
│       │   ├── e2e-tests.js
│       │   ├── inject.js
│       │   ├── scripts.js
│       │   ├── server.js
│       │   ├── unit-tests.js
│       │   └── watch.js
│       ├── gulpfile.js
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── src/
│           ├── app/
│           │   ├── index.config.js
│           │   ├── index.css
│           │   ├── index.module.js
│           │   ├── index.run.js
│           │   └── main/
│           │       ├── main.controller.js
│           │       └── main.controller.spec.js
│           └── index.html
├── 26-android-part2/
│   └── README.md
├── 27-learn-golang-for-great-good/
│   ├── README.md
│   └── programs/
│       ├── closestpair.go
│       ├── concatenator.go
│       ├── dedup.go
│       ├── duplicate-cli.go
│       ├── duplicate.go
│       ├── equalornotequal.go
│       ├── fizzbuzz.go
│       ├── greeter.go
│       ├── import_examples.go
│       ├── reverse.go
│       └── wordcount.go
├── 28-ionic/
│   ├── README.md
│   └── dailyreads/
│       ├── backend/
│       │   ├── .gitignore
│       │   ├── app.py
│       │   ├── requirements.txt
│       │   ├── static/
│       │   │   ├── css/
│       │   │   │   ├── 1-col-portfolio.css
│       │   │   │   └── bootstrap.css
│       │   │   └── js/
│       │   │       ├── bootstrap.js
│       │   │       └── jquery.js
│       │   └── templates/
│       │       └── index.html
│       └── mobileapp/
│           ├── .bowerrc
│           ├── .editorconfig
│           ├── .gitignore
│           ├── README.md
│           ├── bower.json
│           ├── config.xml
│           ├── gulpfile.js
│           ├── hooks/
│           │   ├── README.md
│           │   └── after_prepare/
│           │       └── 010_add_platform_class.js
│           ├── ionic.project
│           ├── package.json
│           ├── scss/
│           │   └── ionic.app.scss
│           ├── src/
│           │   ├── js/
│           │   │   └── app.js
│           │   └── views/
│           │       └── home/
│           │           └── home.html
│           └── www/
│               ├── README.md
│               ├── css/
│               │   ├── ionic.app.css
│               │   └── style.css
│               ├── index.html
│               └── lib/
│                   ├── angular/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── angular-csp.css
│                   │   ├── angular.js
│                   │   ├── angular.min.js.gzip
│                   │   ├── bower.json
│                   │   ├── index.js
│                   │   └── package.json
│                   ├── angular-animate/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── angular-animate.js
│                   │   ├── bower.json
│                   │   ├── index.js
│                   │   └── package.json
│                   ├── angular-sanitize/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── angular-sanitize.js
│                   │   ├── bower.json
│                   │   ├── index.js
│                   │   └── package.json
│                   ├── angular-ui-router/
│                   │   ├── .bower.json
│                   │   ├── CHANGELOG.md
│                   │   ├── CONTRIBUTING.md
│                   │   ├── LICENSE
│                   │   ├── README.md
│                   │   ├── api/
│                   │   │   └── angular-ui-router.d.ts
│                   │   ├── bower.json
│                   │   ├── release/
│                   │   │   └── angular-ui-router.js
│                   │   └── src/
│                   │       ├── common.js
│                   │       ├── resolve.js
│                   │       ├── state.js
│                   │       ├── stateDirectives.js
│                   │       ├── stateFilters.js
│                   │       ├── templateFactory.js
│                   │       ├── urlMatcherFactory.js
│                   │       ├── urlRouter.js
│                   │       ├── view.js
│                   │       ├── viewDirective.js
│                   │       └── viewScroll.js
│                   ├── babel-polyfill/
│                   │   ├── .bower.json
│                   │   ├── README.md
│                   │   ├── bower.json
│                   │   └── browser-polyfill.js
│                   └── ionic/
│                       ├── css/
│                       │   └── ionic.css
│                       ├── js/
│                       │   ├── angular/
│                       │   │   ├── angular-animate.js
│                       │   │   ├── angular-resource.js
│                       │   │   ├── angular-sanitize.js
│                       │   │   └── angular.js
│                       │   ├── angular-ui/
│                       │   │   └── angular-ui-router.js
│                       │   ├── ionic-angular.js
│                       │   ├── ionic.bundle.js
│                       │   └── ionic.js
│                       ├── scss/
│                       │   ├── _action-sheet.scss
│                       │   ├── _animations.scss
│                       │   ├── _backdrop.scss
│                       │   ├── _badge.scss
│                       │   ├── _bar.scss
│                       │   ├── _button-bar.scss
│                       │   ├── _button.scss
│                       │   ├── _checkbox.scss
│                       │   ├── _form.scss
│                       │   ├── _grid.scss
│                       │   ├── _items.scss
│                       │   ├── _list.scss
│                       │   ├── _loading.scss
│                       │   ├── _menu.scss
│                       │   ├── _mixins.scss
│                       │   ├── _modal.scss
│                       │   ├── _platform.scss
│                       │   ├── _popover.scss
│                       │   ├── _popup.scss
│                       │   ├── _progress.scss
│                       │   ├── _radio.scss
│                       │   ├── _range.scss
│                       │   ├── _refresher.scss
│                       │   ├── _reset.scss
│                       │   ├── _scaffolding.scss
│                       │   ├── _select.scss
│                       │   ├── _slide-box.scss
│                       │   ├── _slides.scss
│                       │   ├── _spinner.scss
│                       │   ├── _tabs.scss
│                       │   ├── _toggle.scss
│                       │   ├── _transitions.scss
│                       │   ├── _type.scss
│                       │   ├── _util.scss
│                       │   ├── _variables.scss
│                       │   ├── ionic.scss
│                       │   └── ionicons/
│                       │       ├── _ionicons-font.scss
│                       │       ├── _ionicons-icons.scss
│                       │       ├── _ionicons-variables.scss
│                       │       └── ionicons.scss
│                       └── version.json
├── 29-go-unit-testing/
│   ├── .gitignore
│   └── README.md
├── 29-golang-github-slacknotification/
│   ├── README.md
│   └── programs/
│       └── main.go
├── 30-dropwizard/
│   ├── README.md
│   └── blog/
│       ├── .gitignore
│       ├── build.gradle
│       ├── configuration.yml
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           └── main/
│               └── java/
│                   └── blog/
│                       ├── AppConfiguration.java
│                       ├── BlogApp.java
│                       ├── model/
│                       │   └── Blog.java
│                       ├── mongo/
│                       │   ├── MongoHealthCheck.java
│                       │   └── MongoManaged.java
│                       └── resources/
│                           ├── BlogResource.java
│                           └── IndexResource.java
├── 31-gradle-tips/
│   └── README.md
├── 32-groovy-ast-transformations/
│   ├── README.md
│   └── sha1-ast/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   ├── groovy/
│           │   │   └── playground/
│           │   │       └── Book.groovy
│           │   ├── java/
│           │   │   └── playground/
│           │   │       ├── Hash.java
│           │   │       └── ToHashAdderAstTransformation.java
│           │   └── resources/
│           │       └── hash.java.txt
│           └── test/
│               └── groovy/
│                   └── playground/
│                       ├── BookTest.groovy
│                       └── HashAstTransformationTests.groovy
├── 34-aws-lambda/
│   ├── README.md
│   └── code/
│       ├── .gitignore
│       ├── json_parsing.py
│       └── tweet_sender.py
├── 36-webpack/
│   ├── README.md
│   └── code/
│       ├── .gitignore
│       ├── css/
│       │   └── style.css
│       ├── dist/
│       │   └── bundle.js
│       ├── index.html
│       ├── js/
│       │   ├── profile.js
│       │   └── timeline.js
│       ├── package.json
│       └── webpack.config.js
├── 37-spring-boot-scala/
│   ├── README.md
│   └── gs-rest-service/
│       ├── .gitignore
│       ├── build.gradle
│       ├── gradle/
│       │   └── wrapper/
│       │       ├── gradle-wrapper.jar
│       │       └── gradle-wrapper.properties
│       ├── gradlew
│       ├── gradlew.bat
│       ├── settings.gradle
│       └── src/
│           ├── main/
│           │   └── scala/
│           │       └── hello/
│           │           ├── Application.scala
│           │           ├── GreetingController.scala
│           │           └── PingController.scala
│           └── test/
│               └── scala/
│                   └── hello/
│                       └── GreetingControllerTest.scala
├── 38-akka/
│   └── README.md
├── 39-docker/
│   └── README.md
├── 40-docker-cron/
│   ├── README.md
│   └── cron-example/
│       ├── .dockerignore
│       ├── .gitignore
│       ├── Dockerfile
│       ├── app.py
│       ├── crontab
│       ├── requirements.txt
│       └── run-crond.sh
├── 41-akka-dispatcher/
│   └── README.md
├── 42-docker-compose/
│   ├── README.md
│   └── code/
│       ├── with-waitforit/
│       │   ├── Dockerfile
│       │   ├── docker-compose.yml
│       │   ├── taskman.jar
│       │   └── wait-for-it.sh
│       └── without-waitforit/
│           ├── Dockerfile
│           ├── docker-compose.yml
│           └── taskman.jar
├── 43-graphql/
│   └── README.md
├── LICENSE
├── README.md
├── authors.md
└── who-is-talking-about.md
Download .txt
SYMBOL INDEX (2194 symbols across 76 files)

FILE: 09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/FaceDetector.java
  class FaceDetector (line 12) | public class FaceDetector {
    method FaceDetector (line 16) | public FaceDetector(Vision vision) {
    method detectFaces (line 20) | public List<FaceAnnotation> detectFaces(Path image) throws IOException {
    method detectFaces (line 24) | public List<FaceAnnotation> detectFaces(byte[] image) throws IOExcepti...

FILE: 09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/GoogleVisionServiceFactory.java
  class GoogleVisionServiceFactory (line 13) | public abstract class GoogleVisionServiceFactory {
    method getVisionServiceInstance (line 15) | public static Vision getVisionServiceInstance(final String application...

FILE: 09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/ImagePeopleCount.java
  class ImagePeopleCount (line 5) | public class ImagePeopleCount {
    method ImagePeopleCount (line 10) | public ImagePeopleCount(String image, int count) {
    method getImage (line 15) | public String getImage() {
    method getCount (line 19) | public int getCount() {
    method equals (line 23) | @Override
    method hashCode (line 32) | @Override
    method toString (line 37) | @Override

FILE: 09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/ImageWriter.java
  class ImageWriter (line 18) | public class ImageWriter {
    method writeWithFaces1 (line 20) | public static Path writeWithFaces1(String urlOfImage, Path outputPath,...
    method writeWithFaces (line 29) | public static Path writeWithFaces(Path inputPath, Path outputPath, Lis...
    method annotateWithFaces (line 37) | private static void annotateWithFaces(BufferedImage img, List<FaceAnno...
    method annotateWithFace (line 43) | private static void annotateWithFace(BufferedImage img, FaceAnnotation...

FILE: 09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/PeopleCounter.java
  class PeopleCounter (line 16) | public class PeopleCounter {
    method PeopleCounter (line 20) | public PeopleCounter(Vision vision) {
    method count (line 24) | public ImagePeopleCount count(String imageUrl) {
    method count (line 38) | public ImagePeopleCount count(Path image) throws IOException {
    method urlToByteArray (line 47) | private byte[] urlToByteArray(String urlOfImage) {

FILE: 09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/PeopleCounterApp.java
  class PeopleCounterApp (line 9) | public class PeopleCounterApp {
    method main (line 11) | public static void main(String[] args) throws Exception {

FILE: 09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/TweetObservable.java
  class TweetObservable (line 6) | public final class TweetObservable {
    method of (line 8) | public static Observable<Status> of(final String... searchKeywords) {

FILE: 09-cloudvision/people-counter/src/test/java/com/shekhargulati/peoplecounter/FaceDetectorTest.java
  class FaceDetectorTest (line 13) | public class FaceDetectorTest {
    method shouldReturnFaceAnnotationsAllThreePeople (line 16) | public void shouldReturnFaceAnnotationsAllThreePeople() throws Excepti...
    method shouldReturnFaceAnnotationForManOnly (line 25) | @Test

FILE: 09-cloudvision/people-counter/src/test/java/com/shekhargulati/peoplecounter/PeopleCounterTest.java
  class PeopleCounterTest (line 11) | public class PeopleCounterTest {
    method shouldReturnPeopleCountInAnImage (line 13) | @Test
    method shouldReturnOnlyPeopleCountInAnImage (line 21) | @Test

FILE: 11-textblob/sentiment-analyzer/sentimentanalyzer.py
  function index (line 8) | def index():
  function sentiment (line 12) | def sentiment():

FILE: 11-textblob/sentiment-analyzer/static/js/jquery.js
  function isArraylike (line 848) | function isArraylike( obj ) {
  function Sizzle (line 1048) | function Sizzle( selector, context, results, seed ) {
  function createCache (line 1163) | function createCache() {
  function markFunction (line 1181) | function markFunction( fn ) {
  function assert (line 1190) | function assert( fn ) {
  function addHandle (line 1212) | function addHandle( attrs, handler ) {
  function siblingCheck (line 1227) | function siblingCheck( a, b ) {
  function createInputPseudo (line 1254) | function createInputPseudo( type ) {
  function createButtonPseudo (line 1265) | function createButtonPseudo( type ) {
  function createPositionalPseudo (line 1276) | function createPositionalPseudo( fn ) {
  function setFilters (line 2259) | function setFilters() {}
  function tokenize (line 2263) | function tokenize( selector, parseOnly ) {
  function toSelector (line 2330) | function toSelector( tokens ) {
  function addCombinator (line 2340) | function addCombinator( matcher, combinator, base ) {
  function elementMatcher (line 2390) | function elementMatcher( matchers ) {
  function condense (line 2404) | function condense( unmatched, map, filter, context, xml ) {
  function setMatcher (line 2425) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
  function matcherFromTokens (line 2518) | function matcherFromTokens( tokens ) {
  function matcherFromGroupMatchers (line 2573) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
  function multipleContexts (line 2701) | function multipleContexts( selector, contexts, results ) {
  function select (line 2710) | function select( selector, context, results, seed ) {
  function createOptions (line 2850) | function createOptions( options ) {
  function Data (line 3312) | function Data() {
  function dataAttr (line 3625) | function dataAttr( elem, key, data ) {
  function returnTrue (line 4305) | function returnTrue() {
  function returnFalse (line 4309) | function returnFalse() {
  function safeActiveElement (line 4313) | function safeActiveElement() {
  function sibling (line 5268) | function sibling( cur, dir ) {
  function winnow (line 5384) | function winnow( elements, qualifier, not ) {
  function manipulationTarget (line 5893) | function manipulationTarget( elem, content ) {
  function disableScript (line 5903) | function disableScript( elem ) {
  function restoreScript (line 5907) | function restoreScript( elem ) {
  function setGlobalEval (line 5920) | function setGlobalEval( elems, refElements ) {
  function cloneCopyEvent (line 5931) | function cloneCopyEvent( src, dest ) {
  function getAll (line 5966) | function getAll( context, tag ) {
  function fixInput (line 5977) | function fixInput( src, dest ) {
  function vendorPropName (line 6078) | function vendorPropName( style, name ) {
  function isHidden (line 6100) | function isHidden( elem, el ) {
  function getStyles (line 6109) | function getStyles( elem ) {
  function showHide (line 6113) | function showHide( elements, show ) {
  function setPositiveNumber (line 6384) | function setPositiveNumber( elem, value, subtract ) {
  function augmentWidthOrHeight (line 6392) | function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
  function getWidthOrHeight (line 6431) | function getWidthOrHeight( elem, name, extra ) {
  function css_defaultDisplay (line 6475) | function css_defaultDisplay( nodeName ) {
  function actualDisplay (line 6507) | function actualDisplay( name, doc ) {
  function buildParams (line 6693) | function buildParams( prefix, obj, traditional, add ) {
  function addToPrefiltersOrTransports (line 6809) | function addToPrefiltersOrTransports( structure ) {
  function inspectPrefiltersOrTransports (line 6841) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
  function ajaxExtend (line 6868) | function ajaxExtend( target, src ) {
  function done (line 7314) | function done( status, nativeStatusText, responses, headers ) {
  function ajaxHandleResponses (line 7461) | function ajaxHandleResponses( s, jqXHR, responses ) {
  function ajaxConvert (line 7517) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
  function createFxNow (line 7912) | function createFxNow() {
  function createTween (line 7919) | function createTween( value, prop, animation ) {
  function Animation (line 7933) | function Animation( elem, properties, options ) {
  function propFilter (line 8037) | function propFilter( props, specialEasing ) {
  function defaultPrefilter (line 8104) | function defaultPrefilter( elem, props, opts ) {
  function Tween (line 8227) | function Tween( elem, options, prop, end, easing ) {
  function genFx (line 8451) | function genFx( type, includeWidth ) {
  function getWindow (line 8749) | function getWindow( elem ) {

FILE: 17-typescript/code/getting-started.ts
  type StoryType (line 26) | enum StoryType {Video, Article, Tutorial}
  function addStory (line 45) | function addStory(title: string, tags: string[]): void {
  function storySummary (line 76) | function storySummary(title:string, description: string = "") {
  type Story (line 86) | interface Story {
  type StoryExtractor (line 100) | interface StoryExtractor {
  class TextStory (line 106) | class TextStory implements Story{
    method storyWithNoTags (line 110) | static storyWithNoTags(title:string): TextStory {
    method constructor (line 113) | constructor(title:string, ...tags){
    method summary (line 118) | summary (){
  class TutorialStory (line 128) | class TutorialStory extends TextStory {
    method constructor (line 129) | constructor(title:string, ...tags){
    method summary (line 133) | summary(){
  method process (line 139) | public process(url: string): Story {
  class StoryManager (line 158) | class StoryManager{
    method addStory (line 159) | addStory(){}
    method removeStory (line 160) | removeStory(){}
  type HasLength (line 172) | interface HasLength{
  function addLengths (line 176) | function addLengths<T extends HasLength>(t1: T, t2: T):number {
  type Textable (line 183) | interface Textable{
  type Message (line 187) | interface Message<T extends Textable>{
  class Pair (line 193) | class Pair<T>{

FILE: 17-typescript/code/js/getting-started.js
  function __ (line 3) | function __() { this.constructor = d; }
  function addStory (line 42) | function addStory(title, tags) {
  function storySummary (line 62) | function storySummary(title, description) {
  function TextStory (line 74) | function TextStory(title) {
  function TutorialStory (line 93) | function TutorialStory(title) {
  function StoryProcessorTemplate (line 106) | function StoryProcessorTemplate() {
  function StoryManager (line 122) | function StoryManager() {
  function addLengths (line 135) | function addLengths(t1, t2) {
  function Pair (line 141) | function Pair() {

FILE: 22-regex/code/src/main/java/week22/regex/GaddafiSpellingMatcher.java
  class GaddafiSpellingMatcher (line 5) | public class GaddafiSpellingMatcher {
    method match (line 7) | public static boolean match(final String spelling) {

FILE: 22-regex/code/src/test/java/week22/regex/GaddafiSpellingMatcherTest.java
  class GaddafiSpellingMatcherTest (line 8) | public class GaddafiSpellingMatcherTest {
    method shouldMatchGadaffi (line 10) | @Test
    method shouldMatchGadafi (line 15) | @Test
    method shouldMatchGadafy (line 20) | @Test
    method shouldMatchGaddafiAndGaddafy (line 25) | @Test
    method shouldMatchGaddhafiAndGadhafi (line 31) | @Test
    method shouldMatchGathafi (line 37) | @Test
    method shouldMatchGhadaffi_Ghadafi_Ghaddafi_Ghaddafy (line 42) | @Test
    method shouldMatchGheddafi (line 50) | @Test
    method shouldMatchSpellingsStartingWithK (line 55) | @Test
    method shouldMatchKazzafi (line 66) | @Test
    method shouldMatchStartingWithQ (line 71) | @Test
    method shouldMatchQadhdhafi (line 80) | @Test
    method shouldMatchQuathafi (line 85) | @Test
    method shouldMatchQudhafi (line 90) | @Test
    method shouldMatchKadafiWithSingleQoute (line 95) | @Test

FILE: 25-angular-dragula/trello/gulp/e2e-tests.js
  function runProtractor (line 16) | function runProtractor (done) {

FILE: 25-angular-dragula/trello/gulp/scripts.js
  function webpackWrapper (line 13) | function webpackWrapper(watch, test, callback) {

FILE: 25-angular-dragula/trello/gulp/server.js
  function browserSyncInit (line 14) | function browserSyncInit(baseDir, browser) {

FILE: 25-angular-dragula/trello/gulp/unit-tests.js
  function runTests (line 17) | function runTests (singleRun, done) {

FILE: 25-angular-dragula/trello/gulp/watch.js
  function isOnlyChange (line 9) | function isOnlyChange(event) {

FILE: 25-angular-dragula/trello/karma.conf.js
  function listFiles (line 13) | function listFiles() {

FILE: 25-angular-dragula/trello/src/app/index.config.js
  function config (line 1) | function config ($logProvider, toastrConfig) {

FILE: 25-angular-dragula/trello/src/app/index.run.js
  function runBlock (line 1) | function runBlock ($log) {

FILE: 25-angular-dragula/trello/src/app/main/main.controller.js
  class MainController (line 1) | class MainController {
    method constructor (line 2) | constructor($scope) {

FILE: 27-learn-golang-for-great-good/programs/closestpair.go
  function closestPair (line 9) | func closestPair(numbers []int) (int, int) {
  function main (line 25) | func main() {

FILE: 27-learn-golang-for-great-good/programs/concatenator.go
  function main (line 5) | func main() {

FILE: 27-learn-golang-for-great-good/programs/dedup.go
  function dedup (line 8) | func dedup(numbers []int) []int {
  function main (line 20) | func main() {

FILE: 27-learn-golang-for-great-good/programs/duplicate-cli.go
  function duplicate (line 9) | func duplicate(arr []int, n int) []int {
  function main (line 22) | func main() {

FILE: 27-learn-golang-for-great-good/programs/duplicate.go
  function duplicate (line 5) | func duplicate(numbers []int, n int) []int  {
  function main (line 18) | func main() {

FILE: 27-learn-golang-for-great-good/programs/equalornotequal.go
  function equalOrNotEqual (line 5) | func equalOrNotEqual(first, second, third int) bool {
  function main (line 13) | func main() {

FILE: 27-learn-golang-for-great-good/programs/fizzbuzz.go
  function main (line 5) | func main() {

FILE: 27-learn-golang-for-great-good/programs/greeter.go
  function main (line 5) | func main() {

FILE: 27-learn-golang-for-great-good/programs/import_examples.go
  function main (line 5) | func main() {

FILE: 27-learn-golang-for-great-good/programs/reverse.go
  function main (line 5) | func main() {

FILE: 27-learn-golang-for-great-good/programs/wordcount.go
  function wordCount (line 5) | func wordCount(words []string) map[string]int {
  function main (line 17) | func main() {

FILE: 28-ionic/dailyreads/backend/app.py
  function bookmarks (line 16) | def bookmarks():
  function index (line 20) | def index():
  class LikedTweetsListener (line 31) | class LikedTweetsListener(StreamListener):
    method on_data (line 32) | def on_data(self, data):
    method on_error (line 46) | def on_error(self, status):
  function extract_url (line 50) | def extract_url(liked_tweet):
  function extract_article (line 60) | def extract_article(story_url):

FILE: 28-ionic/dailyreads/backend/static/js/bootstrap.js
  function transitionEnd (line 34) | function transitionEnd() {
  function removeElement (line 126) | function removeElement() {
  function Plugin (line 142) | function Plugin(option) {
  function Plugin (line 251) | function Plugin(option) {
  function Plugin (line 470) | function Plugin(option) {
  function getTargetFromTrigger (line 689) | function getTargetFromTrigger($trigger) {
  function Plugin (line 701) | function Plugin(option) {
  function getParent (line 768) | function getParent($this) {
  function clearMenus (line 781) | function clearMenus(e) {
  function Plugin (line 874) | function Plugin(option) {
  function Plugin (line 1200) | function Plugin(option, _relatedTarget) {
  function complete (line 1566) | function complete() {
  function Plugin (line 1736) | function Plugin(option) {
  function Plugin (line 1845) | function Plugin(option) {
  function ScrollSpy (line 1888) | function ScrollSpy(element, options) {
  function Plugin (line 2008) | function Plugin(option) {
  function next (line 2117) | function next() {
  function Plugin (line 2163) | function Plugin(option) {
  function Plugin (line 2320) | function Plugin(option) {

FILE: 28-ionic/dailyreads/backend/static/js/jquery.js
  function r (line 2) | function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindo...
  function fb (line 2) | function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)...
  function gb (line 2) | function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLengt...
  function hb (line 2) | function hb(a){return a[u]=!0,a}
  function ib (line 2) | function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){re...
  function jb (line 2) | function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[...
  function kb (line 2) | function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sou...
  function lb (line 2) | function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"...
  function mb (line 2) | function mb(a){return function(b){var c=b.nodeName.toLowerCase();return(...
  function nb (line 2) | function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,...
  function ob (line 2) | function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}
  function pb (line 2) | function pb(){}
  function qb (line 2) | function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}
  function rb (line 2) | function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.firs...
  function sb (line 2) | function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e-...
  function tb (line 2) | function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}
  function ub (line 2) | function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(...
  function vb (line 2) | function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)...
  function wb (line 2) | function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.r...
  function xb (line 2) | function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var...
  function w (line 2) | function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){retur...
  function D (line 2) | function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}
  function G (line 2) | function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b...
  function I (line 2) | function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded...
  function J (line 2) | function J(){(y.addEventListener||"load"===event.type||"complete"===y.re...
  function O (line 2) | function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace...
  function P (line 2) | function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&...
  function Q (line 2) | function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType...
  function R (line 3) | function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a...
  function ab (line 3) | function ab(){return!0}
  function bb (line 3) | function bb(){return!1}
  function cb (line 3) | function cb(){try{return y.activeElement}catch(a){}}
  function db (line 3) | function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.cre...
  function ub (line 3) | function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getEl...
  function vb (line 3) | function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}
  function wb (line 3) | function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeTyp...
  function xb (line 3) | function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}
  function yb (line 3) | function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttrib...
  function zb (line 3) | function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval"...
  function Ab (line 3) | function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a)...
  function Bb (line 3) | function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCas...
  function Eb (line 3) | function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getD...
  function Fb (line 3) | function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(...
  function Lb (line 3) | function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?voi...
  function i (line 3) | function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&...
  function Ub (line 3) | function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.sl...
  function Vb (line 3) | function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.styl...
  function Wb (line 3) | function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[...
  function Xb (line 3) | function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===...
  function Yb (line 3) | function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f...
  function Zb (line 3) | function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}
  function fc (line 4) | function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}
  function gc (line 4) | function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d[...
  function hc (line 4) | function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.lengt...
  function ic (line 4) | function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeTyp...
  function jc (line 4) | function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a...
  function kc (line 4) | function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(functio...
  function Lc (line 4) | function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var ...
  function Mc (line 4) | function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!...
  function Nc (line 4) | function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)voi...
  function Oc (line 4) | function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[...
  function Pc (line 4) | function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])fo...
  function x (line 4) | function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=v...
  function Vc (line 4) | function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc....
  function Zc (line 4) | function Zc(){try{return new a.XMLHttpRequest}catch(b){}}
  function $c (line 4) | function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(...
  function dd (line 4) | function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.pa...

FILE: 28-ionic/dailyreads/mobileapp/hooks/after_prepare/010_add_platform_class.js
  function addPlatformBodyTag (line 16) | function addPlatformBodyTag(indexPath, platform) {
  function findBodyTag (line 54) | function findBodyTag(html) {
  function findClassAttr (line 61) | function findClassAttr(bodyTag) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-animate/angular-animate.js
  function assertArg (line 82) | function assertArg(arg, name, reason) {
  function mergeClasses (line 89) | function mergeClasses(a,b) {
  function packageStyles (line 98) | function packageStyles(options) {
  function pendClasses (line 107) | function pendClasses(classes, fix, isPrefix) {
  function removeFromArray (line 124) | function removeFromArray(arr, val) {
  function stripCommentsFromElement (line 131) | function stripCommentsFromElement(element) {
  function extractElementNode (line 158) | function extractElementNode(element) {
  function $$addClass (line 168) | function $$addClass($$jqLite, element, className) {
  function $$removeClass (line 174) | function $$removeClass($$jqLite, element, className) {
  function applyAnimationClassesFactory (line 180) | function applyAnimationClassesFactory($$jqLite) {
  function prepareAnimationOptions (line 193) | function prepareAnimationOptions(options) {
  function applyAnimationStyles (line 207) | function applyAnimationStyles(element, options) {
  function applyAnimationFromStyles (line 212) | function applyAnimationFromStyles(element, options) {
  function applyAnimationToStyles (line 219) | function applyAnimationToStyles(element, options) {
  function mergeAnimationDetails (line 226) | function mergeAnimationDetails(element, oldAnimation, newAnimation) {
  function resolveElementClasses (line 267) | function resolveElementClasses(existing, toAdd, toRemove) {
  function getDomNode (line 325) | function getDomNode(element) {
  function applyGeneratedPreparationClasses (line 329) | function applyGeneratedPreparationClasses(element, event, options) {
  function clearGeneratedClasses (line 346) | function clearGeneratedClasses(element, options) {
  function blockTransitions (line 357) | function blockTransitions(node, duration) {
  function blockKeyframeAnimations (line 366) | function blockKeyframeAnimations(node, applyBlock) {
  function applyInlineStyle (line 373) | function applyInlineStyle(node, styleTuple) {
  function concatWithSpace (line 379) | function concatWithSpace(a,b) {
  function scheduler (line 388) | function scheduler(tasks) {
  function nextTick (line 418) | function nextTick() {
  function setData (line 525) | function setData(value) {
  function getCssKeyframeDurationStyle (line 771) | function getCssKeyframeDurationStyle(duration) {
  function getCssDelayStyle (line 775) | function getCssDelayStyle(delay, isKeyframeAnimation) {
  function computeCssStyles (line 780) | function computeCssStyles($window, element, properties) {
  function parseMaxTime (line 806) | function parseMaxTime(str) {
  function truthyTimingValue (line 821) | function truthyTimingValue(val) {
  function getCssTransitionDurationStyle (line 825) | function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
  function createLocalCacheLookup (line 836) | function createLocalCacheLookup() {
  function registerRestorableStyles (line 872) | function registerRestorableStyles(backup, node, properties) {
  function gcsHashFn (line 892) | function gcsHashFn(node, extraClasses) {
  function computeCachedCssStyles (line 899) | function computeCachedCssStyles(node, className, cacheKey, properties) {
  function computeCachedCssStaggerStyles (line 915) | function computeCachedCssStaggerStyles(node, className, cacheKey, proper...
  function waitUntilQuiet (line 946) | function waitUntilQuiet(callback) {
  function computeTimings (line 965) | function computeTimings(node, className, cacheKey) {
  function endFn (line 1235) | function endFn() {
  function cancelFn (line 1239) | function cancelFn() {
  function close (line 1243) | function close(rejected) { // jshint ignore:line
  function applyBlocking (line 1302) | function applyBlocking(duration) {
  function closeAndReturnNoopAnimator (line 1312) | function closeAndReturnNoopAnimator() {
  function onAnimationProgress (line 1331) | function onAnimationProgress(event) {
  function start (line 1358) | function start() {
  function isDocumentFragment (line 1544) | function isDocumentFragment(node) {
  function filterCssClasses (line 1575) | function filterCssClasses(classes) {
  function getUniqueValues (line 1580) | function getUniqueValues(a, b) {
  function prepareAnchoredAnimation (line 1588) | function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {
  function prepareFromToAnchorAnimation (line 1715) | function prepareFromToAnchorAnimation(from, to, classes, anchors) {
  function prepareRegularAnimation (line 1768) | function prepareRegularAnimation(animationDetails) {
  function applyOptions (line 1863) | function applyOptions() {
  function close (line 1868) | function close() {
  function onComplete (line 1930) | function onComplete(success) {
  function endAnimations (line 1935) | function endAnimations(cancelled) {
  function executeAnimationFn (line 1944) | function executeAnimationFn(fn, element, event, options, onDone) {
  function groupEventedAnimations (line 1987) | function groupEventedAnimations(element, event, options, animations, fnN...
  function packageAnimations (line 2028) | function packageAnimations(element, event, options, animations, fnName) {
  function lookupAnimations (line 2070) | function lookupAnimations(classes) {
  function endFnFactory (line 2116) | function endFnFactory() {
  function done (line 2125) | function done(status) {
  function prepareAnimation (line 2135) | function prepareAnimation(animationDetails) {
  function makeTruthyCssClassMap (line 2159) | function makeTruthyCssClassMap(classString) {
  function hasMatchingClasses (line 2173) | function hasMatchingClasses(newClassString, currentClassString) {
  function isAllowed (line 2182) | function isAllowed(ruleType, element, currentAnimation, previousAnimatio...
  function hasAnimationClasses (line 2188) | function hasAnimationClasses(animation, and) {
  function postDigestTaskFactory (line 2255) | function postDigestTaskFactory() {
  function normalizeAnimationDetails (line 2315) | function normalizeAnimationDetails(element, animation) {
  function findCallbacks (line 2326) | function findCallbacks(parent, element, event) {
  function filterFromRegistry (line 2368) | function filterFromRegistry(list, matchContainer, matchCallback) {
  function queueAnimation (line 2427) | function queueAnimation(element, event, initialOptions) {
  function closeChildAnimations (line 2699) | function closeChildAnimations(element) {
  function clearElementAnimationState (line 2718) | function clearElementAnimationState(element) {
  function isMatchingElement (line 2724) | function isMatchingElement(nodeOrElmA, nodeOrElmB) {
  function areAnimationsAllowed (line 2735) | function areAnimationsAllowed(element, parentElement, event) {
  function markElementAnimationState (line 2820) | function markElementAnimationState(element, state, details) {
  function setRunner (line 2843) | function setRunner(element, runner) {
  function removeRunner (line 2847) | function removeRunner(element) {
  function getRunner (line 2851) | function getRunner(element) {
  function sortAnimations (line 2861) | function sortAnimations(animations) {
  function getAnchorNodes (line 3061) | function getAnchorNodes(node) {
  function groupAnimations (line 3076) | function groupAnimations(animations) {
  function cssClassesIntersection (line 3159) | function cssClassesIntersection(a,b) {
  function invokeFirstDriver (line 3179) | function invokeFirstDriver(animationDetails) {
  function beforeStart (line 3194) | function beforeStart() {
  function updateAnimationRunners (line 3205) | function updateAnimationRunners(animation, newRunner) {
  function handleDestroyedElement (line 3218) | function handleDestroyedElement() {
  function close (line 3225) | function close(rejected) { // jshint ignore:line

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-sanitize/angular-sanitize.js
  function $SanitizeProvider (line 147) | function $SanitizeProvider() {
  function sanitizeText (line 204) | function sanitizeText(chars) {
  function toMap (line 293) | function toMap(str, lowercaseKeys) {
  function htmlParser (line 335) | function htmlParser(html, handler) {
  function attrToMap (line 395) | function attrToMap(attrs) {
  function encodeEntities (line 412) | function encodeEntities(value) {
  function htmlSanitizeWriter (line 437) | function htmlSanitizeWriter(buf, uriValidator) {
  function stripCustomNsAttrs (line 491) | function stripCustomNsAttrs(node) {
  function addText (line 682) | function addText(text) {
  function addLink (line 689) | function addLink(url, text) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/api/angular-ui-router.d.ts
  type IState (line 8) | interface IState {
  type ITypedState (line 27) | interface ITypedState<T> extends IState {
  type IStateProvider (line 31) | interface IStateProvider extends IServiceProvider {
  type IUrlMatcher (line 37) | interface IUrlMatcher {
  type IUrlMatcherFactory (line 44) | interface IUrlMatcherFactory {
  type IUrlRouterProvider (line 49) | interface IUrlRouterProvider extends IServiceProvider {
  type IStateOptions (line 66) | interface IStateOptions {
  type IHrefOptions (line 74) | interface IHrefOptions {
  type IStateService (line 81) | interface IStateService {
  type IStateParamsService (line 97) | interface IStateParamsService {
  type IStateParams (line 101) | interface IStateParams {
  type IUrlRouterService (line 105) | interface IUrlRouterService {
  type IUiViewScrollProvider (line 119) | interface IUiViewScrollProvider {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/release/angular-ui-router.js
  function inherit (line 27) | function inherit(parent, extra) {
  function merge (line 31) | function merge(dst) {
  function ancestors (line 49) | function ancestors(first, second) {
  function objectKeys (line 65) | function objectKeys(object) {
  function indexOf (line 84) | function indexOf(array, value) {
  function inheritParams (line 108) | function inheritParams(currentParams, newParams, $current, $to) {
  function equalForKeys (line 134) | function equalForKeys(a, b, keys) {
  function filterByKeys (line 154) | function filterByKeys(keys, values) {
  function indexBy (line 165) | function indexBy(array, propName) {
  function pick (line 175) | function pick(obj) {
  function omit (line 186) | function omit(obj) {
  function pluck (line 195) | function pluck(collection, key) {
  function filter (line 204) | function filter(collection, callback) {
  function map (line 215) | function map(collection, callback) {
  function $Resolve (line 318) | function $Resolve(  $q,    $injector) {
  function $TemplateFactory (line 572) | function $TemplateFactory(  $http,   $templateCache,   $injector) {
  function UrlMatcher (line 738) | function UrlMatcher(pattern, config, parentMatcher) {
  function decodePathArray (line 913) | function decodePathArray(string) {
  function encodeDashes (line 999) | function encodeDashes(str) { // Replace dashes with encoded "\-"
  function Type (line 1069) | function Type(config) {
  function ArrayType (line 1168) | function ArrayType(type, mode) {
  function $UrlMatcherFactory (line 1229) | function $UrlMatcherFactory() {
  function $UrlRouterProvider (line 1725) | function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
  function $StateProvider (line 2144) | function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
  function $ViewProvider (line 3498) | function $ViewProvider() {
  function $ViewScrollProvider (line 3575) | function $ViewScrollProvider() {
  function $ViewDirective (line 3735) | function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $inte...
  function $ViewDirectiveFill (line 3875) | function $ViewDirectiveFill (  $compile,   $controller,   $state,   $int...
  function getUiViewName (line 3915) | function getUiViewName(scope, attrs, element, $interpolate) {
  function parseStateRef (line 3924) | function parseStateRef(ref, current) {
  function stateContext (line 3932) | function stateContext(el) {
  function $StateRefDirective (line 4003) | function $StateRefDirective($state, $timeout) {
  function $StateRefActiveDirective (line 4148) | function $StateRefActiveDirective($state, $stateParams, $interpolate) {
  function $IsStateFilter (line 4203) | function $IsStateFilter($state) {
  function $IncludedByStateFilter (line 4221) | function $IncludedByStateFilter($state) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/common.js
  function inherit (line 14) | function inherit(parent, extra) {
  function merge (line 18) | function merge(dst) {
  function ancestors (line 36) | function ancestors(first, second) {
  function objectKeys (line 52) | function objectKeys(object) {
  function indexOf (line 71) | function indexOf(array, value) {
  function inheritParams (line 95) | function inheritParams(currentParams, newParams, $current, $to) {
  function equalForKeys (line 121) | function equalForKeys(a, b, keys) {
  function filterByKeys (line 141) | function filterByKeys(keys, values) {
  function indexBy (line 152) | function indexBy(array, propName) {
  function pick (line 162) | function pick(obj) {
  function omit (line 173) | function omit(obj) {
  function pluck (line 182) | function pluck(collection, key) {
  function filter (line 191) | function filter(collection, callback) {
  function map (line 202) | function map(collection, callback) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/resolve.js
  function $Resolve (line 12) | function $Resolve(  $q,    $injector) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/state.js
  function $StateProvider (line 23) | function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/stateDirectives.js
  function parseStateRef (line 1) | function parseStateRef(ref, current) {
  function stateContext (line 9) | function stateContext(el) {
  function $StateRefDirective (line 80) | function $StateRefDirective($state, $timeout) {
  function $StateRefActiveDirective (line 225) | function $StateRefActiveDirective($state, $stateParams, $interpolate) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/stateFilters.js
  function $IsStateFilter (line 11) | function $IsStateFilter($state) {
  function $IncludedByStateFilter (line 29) | function $IncludedByStateFilter($state) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/templateFactory.js
  function $TemplateFactory (line 13) | function $TemplateFactory(  $http,   $templateCache,   $injector) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/urlMatcherFactory.js
  function UrlMatcher (line 68) | function UrlMatcher(pattern, config, parentMatcher) {
  function decodePathArray (line 243) | function decodePathArray(string) {
  function encodeDashes (line 329) | function encodeDashes(str) { // Replace dashes with encoded "\-"
  function Type (line 399) | function Type(config) {
  function ArrayType (line 498) | function ArrayType(type, mode) {
  function $UrlMatcherFactory (line 559) | function $UrlMatcherFactory() {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/urlRouter.js
  function $UrlRouterProvider (line 18) | function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/view.js
  function $ViewProvider (line 3) | function $ViewProvider() {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/viewDirective.js
  function $ViewDirective (line 115) | function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $inte...
  function $ViewDirectiveFill (line 255) | function $ViewDirectiveFill (  $compile,   $controller,   $state,   $int...
  function getUiViewName (line 295) | function getUiViewName(scope, attrs, element, $interpolate) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular-ui-router/src/viewScroll.js
  function $ViewScrollProvider (line 8) | function $ViewScrollProvider() {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/angular/angular.js
  function minErr (line 38) | function minErr(module, ErrorConstructor) {
  function isArrayLike (line 249) | function isArrayLike(obj) {
  function forEach (line 306) | function forEach(obj, iterator, context) {
  function forEachSorted (line 350) | function forEachSorted(obj, iterator, context) {
  function reverseParams (line 364) | function reverseParams(iteratorFn) {
  function nextUid (line 378) | function nextUid() {
  function setHashKey (line 388) | function setHashKey(obj, h) {
  function baseExtend (line 397) | function baseExtend(dst, objs, deep) {
  function extend (line 449) | function extend(dst) {
  function merge (line 472) | function merge(dst) {
  function toInt (line 478) | function toInt(str) {
  function inherit (line 483) | function inherit(parent, extra) {
  function noop (line 503) | function noop() {}
  function identity (line 525) | function identity($) {return $;}
  function valueFn (line 529) | function valueFn(value) {return function valueRef() {return value;};}
  function hasCustomToString (line 531) | function hasCustomToString(obj) {
  function isUndefined (line 548) | function isUndefined(value) {return typeof value === 'undefined';}
  function isDefined (line 563) | function isDefined(value) {return typeof value !== 'undefined';}
  function isObject (line 579) | function isObject(value) {
  function isBlankObject (line 590) | function isBlankObject(value) {
  function isString (line 607) | function isString(value) {return typeof value === 'string';}
  function isNumber (line 628) | function isNumber(value) {return typeof value === 'number';}
  function isDate (line 643) | function isDate(value) {
  function isFunction (line 674) | function isFunction(value) {return typeof value === 'function';}
  function isRegExp (line 684) | function isRegExp(value) {
  function isWindow (line 696) | function isWindow(obj) {
  function isScope (line 701) | function isScope(obj) {
  function isFile (line 706) | function isFile(obj) {
  function isFormData (line 711) | function isFormData(obj) {
  function isBlob (line 716) | function isBlob(obj) {
  function isBoolean (line 721) | function isBoolean(value) {
  function isPromiseLike (line 726) | function isPromiseLike(obj) {
  function isTypedArray (line 732) | function isTypedArray(value) {
  function isArrayBuffer (line 736) | function isArrayBuffer(obj) {
  function isElement (line 766) | function isElement(node) {
  function makeMap (line 776) | function makeMap(str) {
  function nodeName_ (line 785) | function nodeName_(element) {
  function includes (line 789) | function includes(array, obj) {
  function arrayRemove (line 793) | function arrayRemove(array, value) {
  function copy (line 859) | function copy(source, destination) {
  function shallowCopy (line 1001) | function shallowCopy(src, dst) {
  function equals (line 1051) | function equals(o1, o2) {
  function noUnsafeEval (line 1116) | function noUnsafeEval() {
  function concat (line 1181) | function concat(array1, array2, index) {
  function sliceArgs (line 1185) | function sliceArgs(args, startIndex) {
  function bind (line 1209) | function bind(self, fn) {
  function toJsonReplacer (line 1230) | function toJsonReplacer(key, value) {
  function toJson (line 1262) | function toJson(obj, pretty) {
  function fromJson (line 1283) | function fromJson(json) {
  function timezoneToOffset (line 1291) | function timezoneToOffset(timezone, fallback) {
  function addDateMinutes (line 1299) | function addDateMinutes(date, minutes) {
  function convertTimezoneToLocal (line 1306) | function convertTimezoneToLocal(date, timezone, reverse) {
  function startingTag (line 1317) | function startingTag(element) {
  function tryDecodeURIComponent (line 1347) | function tryDecodeURIComponent(value) {
  function parseKeyValue (line 1360) | function parseKeyValue(/**string*/keyValue) {
  function toKeyValue (line 1387) | function toKeyValue(obj) {
  function encodeUriSegment (line 1415) | function encodeUriSegment(val) {
  function encodeUriQuery (line 1434) | function encodeUriQuery(val, pctEncodeSpaces) {
  function getNgAttribute (line 1446) | function getNgAttribute(element, ngAttr) {
  function angularInit (line 1591) | function angularInit(element, bootstrap) {
  function bootstrap (line 1679) | function bootstrap(element, modules, config) {
  function reloadWithDebugInfo (line 1757) | function reloadWithDebugInfo() {
  function getTestability (line 1770) | function getTestability(rootElement) {
  function snake_case (line 1780) | function snake_case(name, separator) {
  function bindJQuery (line 1788) | function bindJQuery() {
  function assertArg (line 1842) | function assertArg(arg, name, reason) {
  function assertArgFn (line 1849) | function assertArgFn(arg, name, acceptArrayAnnotation) {
  function assertNotHasOwnProperty (line 1864) | function assertNotHasOwnProperty(name, context) {
  function getter (line 1878) | function getter(obj, path, bindFnToScope) {
  function getBlockNodes (line 1902) | function getBlockNodes(nodes) {
  function createMap (line 1932) | function createMap() {
  function setupModuleLoader (line 1952) | function setupModuleLoader(window) {
  function serializeObject (line 2307) | function serializeObject(obj) {
  function toDebugString (line 2322) | function toDebugString(obj) {
  function publishExternalAPI (line 2454) | function publishExternalAPI(angular) {
  function jqNextId (line 2730) | function jqNextId() { return ++jqId; }
  function camelCase (line 2743) | function camelCase(name) {
  function jqLiteIsTextNode (line 2771) | function jqLiteIsTextNode(html) {
  function jqLiteAcceptsData (line 2775) | function jqLiteAcceptsData(node) {
  function jqLiteHasData (line 2782) | function jqLiteHasData(node) {
  function jqLiteCleanData (line 2789) | function jqLiteCleanData(nodes) {
  function jqLiteBuildFragment (line 2795) | function jqLiteBuildFragment(html, context) {
  function jqLiteParseHTML (line 2832) | function jqLiteParseHTML(html, context) {
  function jqLiteWrapNode (line 2847) | function jqLiteWrapNode(node, wrapper) {
  function JQLite (line 2866) | function JQLite(element) {
  function jqLiteClone (line 2891) | function jqLiteClone(element) {
  function jqLiteDealoc (line 2895) | function jqLiteDealoc(element, onlyDescendants) {
  function jqLiteOff (line 2906) | function jqLiteOff(element, type, fn, unsupported) {
  function jqLiteRemoveData (line 2944) | function jqLiteRemoveData(element, name) {
  function jqLiteExpandoStore (line 2966) | function jqLiteExpandoStore(element, createIfNecessary) {
  function jqLiteData (line 2979) | function jqLiteData(element, key, value) {
  function jqLiteHasClass (line 3005) | function jqLiteHasClass(element, selector) {
  function jqLiteRemoveClass (line 3011) | function jqLiteRemoveClass(element, cssClasses) {
  function jqLiteAddClass (line 3023) | function jqLiteAddClass(element, cssClasses) {
  function jqLiteAddNodes (line 3040) | function jqLiteAddNodes(root, elements) {
  function jqLiteController (line 3066) | function jqLiteController(element, name) {
  function jqLiteInheritedData (line 3070) | function jqLiteInheritedData(element, name, value) {
  function jqLiteEmpty (line 3090) | function jqLiteEmpty(element) {
  function jqLiteRemove (line 3097) | function jqLiteRemove(element, keepData) {
  function jqLiteDocumentLoaded (line 3104) | function jqLiteDocumentLoaded(action, win) {
  function trigger (line 3124) | function trigger() {
  function getBooleanAttrName (line 3178) | function getBooleanAttrName(element, name) {
  function getAliasedAttrName (line 3186) | function getAliasedAttrName(name) {
  function getText (line 3279) | function getText(element, value) {
  function createEventHandler (line 3364) | function createEventHandler(element, events) {
  function defaultHandlerWrapper (line 3416) | function defaultHandlerWrapper(element, event, handler) {
  function specialMouseHandlerWrapper (line 3420) | function specialMouseHandlerWrapper(target, event, handler) {
  function $$jqLiteProvider (line 3667) | function $$jqLiteProvider() {
  function hashKey (line 3698) | function hashKey(obj, nextUidFn) {
  function HashMap (line 3721) | function HashMap(array, isolatedUid) {
  function extractArgs (line 3834) | function extractArgs(fn) {
  function anonFn (line 3840) | function anonFn(fn) {
  function annotate (line 3850) | function annotate(fn, strictDi, name) {
  function createInjector (line 4397) | function createInjector(modulesToLoad, strictDi) {
  function $AnchorScrollProvider (line 4666) | function $AnchorScrollProvider() {
  function mergeClasses (line 4933) | function mergeClasses(a,b) {
  function extractElementNode (line 4942) | function extractElementNode(element) {
  function splitClasses (line 4951) | function splitClasses(classes) {
  function prepareAnimateOptions (line 4976) | function prepareAnimateOptions(options) {
  function updateData (line 5021) | function updateData(data, classes, value) {
  function handleCSSClassChanges (line 5036) | function handleCSSClassChanges() {
  function addRemoveClassesPostDigest (line 5065) | function addRemoveClassesPostDigest(element, add, remove) {
  function domInsert (line 5179) | function domInsert(element, parentElement, afterElement) {
  function waitForTick (line 5526) | function waitForTick(fn) {
  function next (line 5561) | function next() {
  function onProgress (line 5585) | function onProgress(response) {
  function AnimateRunner (line 5593) | function AnimateRunner(host) {
  function run (line 5749) | function run() {
  function applyAnimationContents (line 5760) | function applyAnimationContents() {
  function Browser (line 5801) | function Browser(window, document, $log, $sniffer) {
  function $BrowserProvider (line 6129) | function $BrowserProvider() {
  function $CacheFactoryProvider (line 6217) | function $CacheFactoryProvider() {
  function $TemplateCacheProvider (line 6532) | function $TemplateCacheProvider() {
  function $CompileProvider (line 7391) | function $CompileProvider($provide, $$sanitizeUriProvider) {
  function directiveNormalize (line 9786) | function directiveNormalize(name) {
  function nodesetLinkingFn (line 9835) | function nodesetLinkingFn(
  function directiveLinkingFn (line 9842) | function directiveLinkingFn(
  function tokenDifference (line 9850) | function tokenDifference(str1, str2) {
  function removeComments (line 9866) | function removeComments(jqNodes) {
  function identifierForController (line 9887) | function identifierForController(controller, ident) {
  function $ControllerProvider (line 9906) | function $ControllerProvider() {
  function $DocumentProvider (line 10088) | function $DocumentProvider() {
  function $ExceptionHandlerProvider (line 10134) | function $ExceptionHandlerProvider() {
  function serializeValue (line 10180) | function serializeValue(v) {
  function $HttpParamSerializerProvider (line 10188) | function $HttpParamSerializerProvider() {
  function $HttpParamSerializerJQLikeProvider (line 10225) | function $HttpParamSerializerJQLikeProvider() {
  function defaultHttpResponseTransform (line 10297) | function defaultHttpResponseTransform(data, headers) {
  function isJsonLike (line 10313) | function isJsonLike(str) {
  function parseHeaders (line 10324) | function parseHeaders(headers) {
  function headersGetter (line 10360) | function headersGetter(headers) {
  function transformData (line 10390) | function transformData(data, headers, status, fns) {
  function isSuccess (line 10403) | function isSuccess(status) {
  function $HttpProvider (line 10414) | function $HttpProvider() {
  function $xhrFactoryProvider (line 11516) | function $xhrFactoryProvider() {
  function $HttpBackendProvider (line 11541) | function $HttpBackendProvider() {
  function createHttpBackend (line 11547) | function createHttpBackend($browser, createXhr, $browserDefer, callbacks...
  function $InterpolateProvider (line 11749) | function $InterpolateProvider() {
  function $IntervalProvider (line 12074) | function $IntervalProvider() {
  function encodePath (line 12295) | function encodePath(path) {
  function parseAbsoluteUrl (line 12306) | function parseAbsoluteUrl(absoluteUrl, locationObj) {
  function parseAppUrl (line 12315) | function parseAppUrl(relativeUrl, locationObj) {
  function beginsWith (line 12340) | function beginsWith(begin, whole) {
  function stripHash (line 12347) | function stripHash(url) {
  function trimEmptyHash (line 12352) | function trimEmptyHash(url) {
  function stripFile (line 12357) | function stripFile(url) {
  function serverBase (line 12362) | function serverBase(url) {
  function LocationHtml5Url (line 12376) | function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
  function LocationHashbangUrl (line 12455) | function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
  function LocationHashbangInHtml5Url (line 12567) | function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
  function locationGetter (line 12931) | function locationGetter(property) {
  function locationGetterSetter (line 12938) | function locationGetterSetter(property, preprocess) {
  function $LocationProvider (line 12984) | function $LocationProvider() {
  function $LogProvider (line 13318) | function $LogProvider() {
  function ensureSafeMemberName (line 13474) | function ensureSafeMemberName(name, fullExpression) {
  function getStringValue (line 13485) | function getStringValue(name) {
  function ensureSafeObject (line 13503) | function ensureSafeObject(obj, fullExpression) {
  function ensureSafeFunction (line 13534) | function ensureSafeFunction(obj, fullExpression) {
  function ensureSafeAssignContext (line 13548) | function ensureSafeAssignContext(obj, fullExpression) {
  function ifDefined (line 14061) | function ifDefined(v, d) {
  function plusFn (line 14065) | function plusFn(l, r) {
  function isStateless (line 14071) | function isStateless($filter, filterName) {
  function findConstantAndWatchExpressions (line 14076) | function findConstantAndWatchExpressions(ast, $filter) {
  function getInputs (line 14184) | function getInputs(body) {
  function isAssignable (line 14192) | function isAssignable(ast) {
  function assignableAST (line 14196) | function assignableAST(ast) {
  function isLiteral (line 14202) | function isLiteral(ast) {
  function isConstant (line 14210) | function isConstant(ast) {
  function ASTCompiler (line 14214) | function ASTCompiler(astBuilder, $filter) {
  function ASTInterpreter (line 14713) | function ASTInterpreter(astBuilder, $filter) {
  function isPossiblyDangerousMemberName (line 15113) | function isPossiblyDangerousMemberName(name) {
  function getValueOf (line 15119) | function getValueOf(value) {
  function $ParseProvider (line 15174) | function $ParseProvider() {
  function $QProvider (line 15661) | function $QProvider() {
  function $$QProvider (line 15670) | function $$QProvider() {
  function qFactory (line 15686) | function qFactory(nextTick, exceptionHandler) {
  function $$RAFProvider (line 16039) | function $$RAFProvider() { //rAF
  function $RootScopeProvider (line 16136) | function $RootScopeProvider() {
  function $$SanitizeUriProvider (line 17455) | function $$SanitizeUriProvider() {
  function adjustMatcher (line 17546) | function adjustMatcher(matcher) {
  function adjustMatchers (line 17574) | function adjustMatchers(matchers) {
  function $SceDelegateProvider (line 17652) | function $SceDelegateProvider() {
  function $SceProvider (line 18189) | function $SceProvider() {
  function $SnifferProvider (line 18601) | function $SnifferProvider() {
  function $TemplateRequestProvider (line 18689) | function $TemplateRequestProvider() {
  function $$TestabilityProvider (line 18787) | function $$TestabilityProvider() {
  function $TimeoutProvider (line 18902) | function $TimeoutProvider() {
  function urlResolve (line 19053) | function urlResolve(url) {
  function urlIsSameOrigin (line 19087) | function urlIsSameOrigin(requestUrl) {
  function $WindowProvider (line 19134) | function $WindowProvider() {
  function $$CookieReader (line 19147) | function $$CookieReader($document) {
  function $$CookieReaderProvider (line 19189) | function $$CookieReaderProvider() {
  function $FilterProvider (line 19293) | function $FilterProvider($provide) {
  function filterFilter (line 19483) | function filterFilter() {
  function createPredicateFn (line 19520) | function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
  function deepCompare (line 19557) | function deepCompare(actual, expected, comparator, matchAgainstAnyProp, ...
  function getTypeForFilter (line 19607) | function getTypeForFilter(val) {
  function currencyFilter (line 19668) | function currencyFilter($locale) {
  function numberFilter (line 19740) | function numberFilter($locale) {
  function parse (line 19765) | function parse(numStr) {
  function roundNumber (line 19820) | function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
  function formatNumber (line 19895) | function formatNumber(number, pattern, groupSep, decimalSep, fractionSiz...
  function padNumber (line 19961) | function padNumber(num, digits, trim, negWrap) {
  function dateGetter (line 19980) | function dateGetter(name, size, offset, trim, negWrap) {
  function dateStrGetter (line 19992) | function dateStrGetter(name, shortForm, standAlone) {
  function timeZoneGetter (line 20002) | function timeZoneGetter(date, formats, offset) {
  function getFirstThursdayOfYear (line 20012) | function getFirstThursdayOfYear(year) {
  function getThursdayThisWeek (line 20020) | function getThursdayThisWeek(datetime) {
  function weekGetter (line 20026) | function weekGetter(size) {
  function ampmGetter (line 20038) | function ampmGetter(date, formats) {
  function eraGetter (line 20042) | function eraGetter(date, formats) {
  function longEraGetter (line 20046) | function longEraGetter(date, formats) {
  function dateFilter (line 20182) | function dateFilter($locale) {
  function jsonFilter (line 20289) | function jsonFilter() {
  function limitToFilter (line 20418) | function limitToFilter() {
  function orderByFilter (line 20640) | function orderByFilter($parse) {
  function ngDirective (line 20761) | function ngDirective(directive) {
  function defaultLinkFn (line 21140) | function defaultLinkFn(scope, element, attr) {
  function nullFormRenameControl (line 21242) | function nullFormRenameControl(control, name) {
  function FormController (line 21290) | function FormController(element, attrs, $scope, $animate, $interpolate) {
  function getSetter (line 21764) | function getSetter(expression) {
  function stringBasedInputType (line 22871) | function stringBasedInputType(ctrl) {
  function textInputType (line 22877) | function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function baseInputType (line 22882) | function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function weekParser (line 22992) | function weekParser(isoWeek, existingDate) {
  function createDateParser (line 23024) | function createDateParser(regexp, mapping) {
  function createDateInputType (line 23074) | function createDateInputType(type, regexp, parseDate, format) {
  function badInputChecker (line 23146) | function badInputChecker(scope, element, attr, ctrl) {
  function numberInputType (line 23157) | function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function urlInputType (line 23211) | function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function emailInputType (line 23224) | function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function radioInputType (line 23237) | function radioInputType(scope, element, attr, ctrl) {
  function parseConstantExpr (line 23259) | function parseConstantExpr($parse, context, name, expression, fallback) {
  function checkboxInputType (line 23272) | function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browse...
  function classDirective (line 23855) | function classDirective(name, selector) {
  function processParseErrors (line 26455) | function processParseErrors() {
  function processSyncValidators (line 26475) | function processSyncValidators() {
  function processAsyncValidators (line 26491) | function processAsyncValidators() {
  function setValidity (line 26517) | function setValidity(name, isValid) {
  function validationDone (line 26523) | function validationDone(allValid) {
  function writeToModelIfNeeded (line 26603) | function writeToModelIfNeeded() {
  function addSetValidityMethod (line 27179) | function addSetValidityMethod(context) {
  function isObjectEmpty (line 27273) | function isObjectEmpty(obj) {
  function parseOptionsExpression (line 27565) | function parseOptionsExpression(optionsExp, selectElement, scope) {
  function ngOptionsPostLink (line 27727) | function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
  function updateElementText (line 28315) | function updateElementText(newText) {
  function ngTranscludeCloneAttachFn (line 29641) | function ngTranscludeCloneAttachFn(clone) {
  function chromeHack (line 29713) | function chromeHack(optionElement) {
  function selectPreLink (line 30075) | function selectPreLink(scope, element, attr, ctrls) {
  function selectPostLink (line 30139) | function selectPostLink(scope, element, attrs, ctrls) {
  function getDecimals (line 30567) | function getDecimals(n) {
  function getVF (line 30573) | function getVF(n, opt_precision) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/babel-polyfill/browser-polyfill.js
  function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
  function assert (line 1) | function assert(condition,msg1,msg2){if(!condition)throw TypeError(msg2?...
  function cof (line 1) | function cof(it){return toString.call(it).slice(8,-1)}
  function fastKey (line 1) | function fastKey(it,create){if(!isObject(it))return(typeof it=="string"?...
  function getEntry (line 1) | function getEntry(that,key){var index=fastKey(key),entry;if(index!="F")r...
  function C (line 1) | function C(iterable){var that=assert.inst(this,C,NAME);set(that,O1,$.cre...
  function findFrozen (line 1) | function findFrozen(store,key){return find.call(store.array,function(it)...
  function leakStore (line 1) | function leakStore(that){return that[LEAK]||hide(that,LEAK,{array:[],get...
  function C (line 1) | function C(iterable){$.set(assert.inst(this,C,NAME),ID,id++);if(iterable...
  function fixMethod (line 1) | function fixMethod(KEY,CHAIN){var method=proto[KEY];if($.FW)proto[KEY]=f...
  function ctx (line 1) | function ctx(fn,that){return function(){return fn.apply(that,arguments)}}
  function $def (line 1) | function $def(type,name,source){var key,own,out,exp,isGlobal=type&$def.G...
  function setIterator (line 1) | function setIterator(O,value){$.hide(O,SYMBOL_ITERATOR,value);if(FF_ITER...
  function defineIterator (line 1) | function defineIterator(Constructor,NAME,value,DEFAULT){var proto=Constr...
  function getIterator (line 1) | function getIterator(it){var Symbol=$.g.Symbol,ext=it[Symbol&&Symbol.ite...
  function closeIterator (line 1) | function closeIterator(iterator){var ret=iterator["return"];if(ret!==und...
  function stepCall (line 1) | function stepCall(iterator,fn,value,entries){try{return entries?fn(asser...
  function createIter (line 1) | function createIter(kind){return function(){return new Constructor(this,...
  function toInteger (line 1) | function toInteger(it){return isNaN(it=+it)?0:(it>0?floor:ceil)(it)}
  function desc (line 1) | function desc(bitmap,value){return{enumerable:!(bitmap&1),configurable:!...
  function simpleSet (line 1) | function simpleSet(object,key,value){object[key]=value;return object}
  function createDefiner (line 1) | function createDefiner(bitmap){return DESC?function(object,key,value){re...
  function isObject (line 1) | function isObject(it){return it!==null&&(typeof it=="object"||typeof it=...
  function isFunction (line 1) | function isFunction(it){return typeof it=="function"}
  function assertDefined (line 1) | function assertDefined(it){if(it==undefined)throw TypeError("Can't call ...
  function check (line 1) | function check(O,proto){assert.obj(O);assert(proto===null||$.isObject(pr...
  function run (line 1) | function run(){var id=+this;if($.has(queue,id)){var fn=queue[id];delete ...
  function listner (line 1) | function listner(event){run.call(event.data)}
  function uid (line 1) | function uid(key){return"Symbol("+key+")_"+(++sid+Math.random()).toStrin...
  function createGetKeys (line 1) | function createGetKeys(names,length){return function(object){var O=toObj...
  function isPrimitive (line 1) | function isPrimitive(it){return!$.isObject(it)}
  function Empty (line 1) | function Empty(){}
  function bound (line 1) | function bound(){var args=partArgs.concat(slice.call(arguments));return ...
  function arrayMethodFix (line 1) | function arrayMethodFix(fn){return function(){return fn.apply($.ES5Objec...
  function createArrayReduce (line 1) | function createArrayReduce(isRight){return function(callbackfn,memo){ass...
  function lz (line 1) | function lz(num){return num>9?num:"0"+num}
  function roundTiesToEven (line 1) | function roundTiesToEven(n){return n+1/EPSILON-1/EPSILON}
  function sign (line 1) | function sign(x){return(x=+x)==0||x!=x?x:x<0?-1:1;
  function asinh (line 3) | function asinh(x){return!isFinite(x=+x)||x==0?x:x<0?-asinh(-x):log(x+sqr...
  function expm1 (line 3) | function expm1(x){return(x=+x)==0?x:x>-1e-6&&x<1e-6?x+x*x/2:exp(x)-1}
  function toPrimitive (line 3) | function toPrimitive(it){var fn,val;if(isFunction(fn=it.valueOf)&&!isObj...
  function toNumber (line 3) | function toNumber(it){if(isObject(it))it=toPrimitive(it);if(typeof it=="...
  function isInteger (line 3) | function isInteger(it){return!$.isObject(it)&&_isFinite(it)&&floor(it)==...
  function wrapObjectMethod (line 3) | function wrapObjectMethod(METHOD,MODE){var fn=($.core.Object||{})[METHOD...
  function getConstructor (line 3) | function getConstructor(C){var S=assertObject(C)[SPECIES];return S!=unde...
  function isThenable (line 3) | function isThenable(it){var then;if(isObject(it))then=it.then;return isF...
  function isUnhandled (line 3) | function isUnhandled(promise){var record=promise[RECORD],chain=record.c,...
  function notify (line 3) | function notify(record,isReject){var chain=record.c;if(isReject||chain.l...
  function $reject (line 3) | function $reject(value){var record=this;if(record.d)return;record.d=true...
  function $resolve (line 3) | function $resolve(value){var record=this,then,wrapper;if(record.d)return...
  function Enumerate (line 3) | function Enumerate(iterated){var keys=[],key;for(key in iterated)keys.pu...
  function wrap (line 3) | function wrap(fn){return function(it){assertObject(it);try{fn.apply(unde...
  function get (line 3) | function get(target,propertyKey){var receiver=arguments.length<3?target:...
  function set (line 3) | function set(target,propertyKey,V){var receiver=arguments.length<4?targe...
  function wrap (line 3) | function wrap(tag){var sym=AllSymbols[tag]=$.set($.create(Symbol.prototy...
  function createObjectToArray (line 3) | function createObjectToArray(isEntries){return function(object){var O=$....
  function setStatics (line 3) | function setStatics(keys,length){$.each.call(keys.split(","),function(ke...
  function wrap (line 3) | function wrap(set){return MSIE?function(fn,time){return set(invoke(parti...
  function wrap (line 3) | function wrap(innerFn,outerFn,self,tryLocsList){var generator=Object.cre...
  function tryCatch (line 3) | function tryCatch(fn,obj,arg){try{return{type:"normal",arg:fn.call(obj,a...
  function Generator (line 3) | function Generator(){}
  function GeneratorFunction (line 3) | function GeneratorFunction(){}
  function GeneratorFunctionPrototype (line 3) | function GeneratorFunctionPrototype(){}
  function step (line 3) | function step(method,arg){var record=tryCatch(generator[method],generato...
  function makeInvokeMethod (line 3) | function makeInvokeMethod(innerFn,self,context){var state=GenStateSuspen...
  function defineGeneratorMethod (line 3) | function defineGeneratorMethod(method){Gp[method]=function(arg){return t...
  function pushTryEntry (line 3) | function pushTryEntry(locs){var entry={tryLoc:locs[0]};if(1 in locs){ent...
  function resetTryEntry (line 3) | function resetTryEntry(entry){var record=entry.completion||{};record.typ...
  function Context (line 3) | function Context(tryLocsList){this.tryEntries=[{tryLoc:"root"}];tryLocsL...
  function values (line 3) | function values(iterable){if(iterable){var iteratorMethod=iterable[itera...
  function doneResult (line 3) | function doneResult(){return{value:undefined,done:true}}
  function handle (line 5) | function handle(loc,caught){record.type="throw";record.arg=exception;con...

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/angular-ui/angular-ui-router.js
  function inherit (line 27) | function inherit(parent, extra) {
  function merge (line 31) | function merge(dst) {
  function ancestors (line 49) | function ancestors(first, second) {
  function objectKeys (line 65) | function objectKeys(object) {
  function indexOf (line 84) | function indexOf(array, value) {
  function inheritParams (line 108) | function inheritParams(currentParams, newParams, $current, $to) {
  function equalForKeys (line 134) | function equalForKeys(a, b, keys) {
  function filterByKeys (line 154) | function filterByKeys(keys, values) {
  function indexBy (line 165) | function indexBy(array, propName) {
  function pick (line 175) | function pick(obj) {
  function omit (line 186) | function omit(obj) {
  function pluck (line 195) | function pluck(collection, key) {
  function filter (line 204) | function filter(collection, callback) {
  function map (line 215) | function map(collection, callback) {
  function $Resolve (line 318) | function $Resolve(  $q,    $injector) {
  function $TemplateFactory (line 572) | function $TemplateFactory(  $http,   $templateCache,   $injector) {
  function UrlMatcher (line 738) | function UrlMatcher(pattern, config, parentMatcher) {
  function decodePathArray (line 913) | function decodePathArray(string) {
  function encodeDashes (line 999) | function encodeDashes(str) { // Replace dashes with encoded "\-"
  function Type (line 1069) | function Type(config) {
  function ArrayType (line 1168) | function ArrayType(type, mode) {
  function $UrlMatcherFactory (line 1229) | function $UrlMatcherFactory() {
  function $UrlRouterProvider (line 1725) | function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
  function $StateProvider (line 2144) | function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
  function $ViewProvider (line 3498) | function $ViewProvider() {
  function $ViewScrollProvider (line 3575) | function $ViewScrollProvider() {
  function $ViewDirective (line 3735) | function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $inte...
  function $ViewDirectiveFill (line 3875) | function $ViewDirectiveFill (  $compile,   $controller,   $state,   $int...
  function getUiViewName (line 3915) | function getUiViewName(scope, attrs, element, $interpolate) {
  function parseStateRef (line 3924) | function parseStateRef(ref, current) {
  function stateContext (line 3932) | function stateContext(el) {
  function $StateRefDirective (line 4003) | function $StateRefDirective($state, $timeout) {
  function $StateRefActiveDirective (line 4148) | function $StateRefActiveDirective($state, $stateParams, $interpolate) {
  function $IsStateFilter (line 4203) | function $IsStateFilter($state) {
  function $IncludedByStateFilter (line 4221) | function $IncludedByStateFilter($state) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/angular/angular-animate.js
  function assertArg (line 82) | function assertArg(arg, name, reason) {
  function mergeClasses (line 89) | function mergeClasses(a,b) {
  function packageStyles (line 98) | function packageStyles(options) {
  function pendClasses (line 107) | function pendClasses(classes, fix, isPrefix) {
  function removeFromArray (line 124) | function removeFromArray(arr, val) {
  function stripCommentsFromElement (line 131) | function stripCommentsFromElement(element) {
  function extractElementNode (line 158) | function extractElementNode(element) {
  function $$addClass (line 168) | function $$addClass($$jqLite, element, className) {
  function $$removeClass (line 174) | function $$removeClass($$jqLite, element, className) {
  function applyAnimationClassesFactory (line 180) | function applyAnimationClassesFactory($$jqLite) {
  function prepareAnimationOptions (line 193) | function prepareAnimationOptions(options) {
  function applyAnimationStyles (line 207) | function applyAnimationStyles(element, options) {
  function applyAnimationFromStyles (line 212) | function applyAnimationFromStyles(element, options) {
  function applyAnimationToStyles (line 219) | function applyAnimationToStyles(element, options) {
  function mergeAnimationDetails (line 226) | function mergeAnimationDetails(element, oldAnimation, newAnimation) {
  function resolveElementClasses (line 267) | function resolveElementClasses(existing, toAdd, toRemove) {
  function getDomNode (line 325) | function getDomNode(element) {
  function applyGeneratedPreparationClasses (line 329) | function applyGeneratedPreparationClasses(element, event, options) {
  function clearGeneratedClasses (line 346) | function clearGeneratedClasses(element, options) {
  function blockTransitions (line 357) | function blockTransitions(node, duration) {
  function blockKeyframeAnimations (line 366) | function blockKeyframeAnimations(node, applyBlock) {
  function applyInlineStyle (line 373) | function applyInlineStyle(node, styleTuple) {
  function concatWithSpace (line 379) | function concatWithSpace(a,b) {
  function scheduler (line 388) | function scheduler(tasks) {
  function nextTick (line 418) | function nextTick() {
  function setData (line 525) | function setData(value) {
  function getCssKeyframeDurationStyle (line 771) | function getCssKeyframeDurationStyle(duration) {
  function getCssDelayStyle (line 775) | function getCssDelayStyle(delay, isKeyframeAnimation) {
  function computeCssStyles (line 780) | function computeCssStyles($window, element, properties) {
  function parseMaxTime (line 806) | function parseMaxTime(str) {
  function truthyTimingValue (line 821) | function truthyTimingValue(val) {
  function getCssTransitionDurationStyle (line 825) | function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
  function createLocalCacheLookup (line 836) | function createLocalCacheLookup() {
  function registerRestorableStyles (line 872) | function registerRestorableStyles(backup, node, properties) {
  function gcsHashFn (line 892) | function gcsHashFn(node, extraClasses) {
  function computeCachedCssStyles (line 899) | function computeCachedCssStyles(node, className, cacheKey, properties) {
  function computeCachedCssStaggerStyles (line 915) | function computeCachedCssStaggerStyles(node, className, cacheKey, proper...
  function waitUntilQuiet (line 946) | function waitUntilQuiet(callback) {
  function computeTimings (line 965) | function computeTimings(node, className, cacheKey) {
  function endFn (line 1235) | function endFn() {
  function cancelFn (line 1239) | function cancelFn() {
  function close (line 1243) | function close(rejected) { // jshint ignore:line
  function applyBlocking (line 1302) | function applyBlocking(duration) {
  function closeAndReturnNoopAnimator (line 1312) | function closeAndReturnNoopAnimator() {
  function onAnimationProgress (line 1331) | function onAnimationProgress(event) {
  function start (line 1358) | function start() {
  function isDocumentFragment (line 1544) | function isDocumentFragment(node) {
  function filterCssClasses (line 1575) | function filterCssClasses(classes) {
  function getUniqueValues (line 1580) | function getUniqueValues(a, b) {
  function prepareAnchoredAnimation (line 1588) | function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {
  function prepareFromToAnchorAnimation (line 1715) | function prepareFromToAnchorAnimation(from, to, classes, anchors) {
  function prepareRegularAnimation (line 1768) | function prepareRegularAnimation(animationDetails) {
  function applyOptions (line 1863) | function applyOptions() {
  function close (line 1868) | function close() {
  function onComplete (line 1930) | function onComplete(success) {
  function endAnimations (line 1935) | function endAnimations(cancelled) {
  function executeAnimationFn (line 1944) | function executeAnimationFn(fn, element, event, options, onDone) {
  function groupEventedAnimations (line 1987) | function groupEventedAnimations(element, event, options, animations, fnN...
  function packageAnimations (line 2028) | function packageAnimations(element, event, options, animations, fnName) {
  function lookupAnimations (line 2070) | function lookupAnimations(classes) {
  function endFnFactory (line 2116) | function endFnFactory() {
  function done (line 2125) | function done(status) {
  function prepareAnimation (line 2135) | function prepareAnimation(animationDetails) {
  function makeTruthyCssClassMap (line 2159) | function makeTruthyCssClassMap(classString) {
  function hasMatchingClasses (line 2173) | function hasMatchingClasses(newClassString, currentClassString) {
  function isAllowed (line 2182) | function isAllowed(ruleType, element, currentAnimation, previousAnimatio...
  function hasAnimationClasses (line 2188) | function hasAnimationClasses(animation, and) {
  function postDigestTaskFactory (line 2255) | function postDigestTaskFactory() {
  function normalizeAnimationDetails (line 2315) | function normalizeAnimationDetails(element, animation) {
  function findCallbacks (line 2326) | function findCallbacks(parent, element, event) {
  function filterFromRegistry (line 2368) | function filterFromRegistry(list, matchContainer, matchCallback) {
  function queueAnimation (line 2427) | function queueAnimation(element, event, initialOptions) {
  function closeChildAnimations (line 2699) | function closeChildAnimations(element) {
  function clearElementAnimationState (line 2718) | function clearElementAnimationState(element) {
  function isMatchingElement (line 2724) | function isMatchingElement(nodeOrElmA, nodeOrElmB) {
  function areAnimationsAllowed (line 2735) | function areAnimationsAllowed(element, parentElement, event) {
  function markElementAnimationState (line 2820) | function markElementAnimationState(element, state, details) {
  function setRunner (line 2843) | function setRunner(element, runner) {
  function removeRunner (line 2847) | function removeRunner(element) {
  function getRunner (line 2851) | function getRunner(element) {
  function sortAnimations (line 2861) | function sortAnimations(animations) {
  function getAnchorNodes (line 3061) | function getAnchorNodes(node) {
  function groupAnimations (line 3076) | function groupAnimations(animations) {
  function cssClassesIntersection (line 3159) | function cssClassesIntersection(a,b) {
  function invokeFirstDriver (line 3179) | function invokeFirstDriver(animationDetails) {
  function beforeStart (line 3194) | function beforeStart() {
  function updateAnimationRunners (line 3205) | function updateAnimationRunners(animation, newRunner) {
  function handleDestroyedElement (line 3218) | function handleDestroyedElement() {
  function close (line 3225) | function close(rejected) { // jshint ignore:line

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/angular/angular-resource.js
  function isValidDottedPath (line 15) | function isValidDottedPath(path) {
  function lookupDottedPath (line 20) | function lookupDottedPath(obj, path) {
  function shallowClearAndCopy (line 35) | function shallowClearAndCopy(src, dst) {
  function encodeUriSegment (line 443) | function encodeUriSegment(val) {
  function encodeUriQuery (line 462) | function encodeUriQuery(val, pctEncodeSpaces) {
  function Route (line 471) | function Route(template, defaults) {
  function resourceFactory (line 550) | function resourceFactory(url, paramDefaults, actions, options) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/angular/angular-sanitize.js
  function $SanitizeProvider (line 147) | function $SanitizeProvider() {
  function sanitizeText (line 204) | function sanitizeText(chars) {
  function toMap (line 293) | function toMap(str, lowercaseKeys) {
  function htmlParser (line 335) | function htmlParser(html, handler) {
  function attrToMap (line 395) | function attrToMap(attrs) {
  function encodeEntities (line 412) | function encodeEntities(value) {
  function htmlSanitizeWriter (line 437) | function htmlSanitizeWriter(buf, uriValidator) {
  function stripCustomNsAttrs (line 491) | function stripCustomNsAttrs(node) {
  function addText (line 682) | function addText(text) {
  function addLink (line 689) | function addLink(url, text) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/angular/angular.js
  function minErr (line 38) | function minErr(module, ErrorConstructor) {
  function isArrayLike (line 249) | function isArrayLike(obj) {
  function forEach (line 306) | function forEach(obj, iterator, context) {
  function forEachSorted (line 350) | function forEachSorted(obj, iterator, context) {
  function reverseParams (line 364) | function reverseParams(iteratorFn) {
  function nextUid (line 378) | function nextUid() {
  function setHashKey (line 388) | function setHashKey(obj, h) {
  function baseExtend (line 397) | function baseExtend(dst, objs, deep) {
  function extend (line 449) | function extend(dst) {
  function merge (line 472) | function merge(dst) {
  function toInt (line 478) | function toInt(str) {
  function inherit (line 483) | function inherit(parent, extra) {
  function noop (line 503) | function noop() {}
  function identity (line 525) | function identity($) {return $;}
  function valueFn (line 529) | function valueFn(value) {return function valueRef() {return value;};}
  function hasCustomToString (line 531) | function hasCustomToString(obj) {
  function isUndefined (line 548) | function isUndefined(value) {return typeof value === 'undefined';}
  function isDefined (line 563) | function isDefined(value) {return typeof value !== 'undefined';}
  function isObject (line 579) | function isObject(value) {
  function isBlankObject (line 590) | function isBlankObject(value) {
  function isString (line 607) | function isString(value) {return typeof value === 'string';}
  function isNumber (line 628) | function isNumber(value) {return typeof value === 'number';}
  function isDate (line 643) | function isDate(value) {
  function isFunction (line 674) | function isFunction(value) {return typeof value === 'function';}
  function isRegExp (line 684) | function isRegExp(value) {
  function isWindow (line 696) | function isWindow(obj) {
  function isScope (line 701) | function isScope(obj) {
  function isFile (line 706) | function isFile(obj) {
  function isFormData (line 711) | function isFormData(obj) {
  function isBlob (line 716) | function isBlob(obj) {
  function isBoolean (line 721) | function isBoolean(value) {
  function isPromiseLike (line 726) | function isPromiseLike(obj) {
  function isTypedArray (line 732) | function isTypedArray(value) {
  function isArrayBuffer (line 736) | function isArrayBuffer(obj) {
  function isElement (line 766) | function isElement(node) {
  function makeMap (line 776) | function makeMap(str) {
  function nodeName_ (line 785) | function nodeName_(element) {
  function includes (line 789) | function includes(array, obj) {
  function arrayRemove (line 793) | function arrayRemove(array, value) {
  function copy (line 859) | function copy(source, destination) {
  function shallowCopy (line 1001) | function shallowCopy(src, dst) {
  function equals (line 1051) | function equals(o1, o2) {
  function noUnsafeEval (line 1116) | function noUnsafeEval() {
  function concat (line 1181) | function concat(array1, array2, index) {
  function sliceArgs (line 1185) | function sliceArgs(args, startIndex) {
  function bind (line 1209) | function bind(self, fn) {
  function toJsonReplacer (line 1230) | function toJsonReplacer(key, value) {
  function toJson (line 1262) | function toJson(obj, pretty) {
  function fromJson (line 1283) | function fromJson(json) {
  function timezoneToOffset (line 1291) | function timezoneToOffset(timezone, fallback) {
  function addDateMinutes (line 1299) | function addDateMinutes(date, minutes) {
  function convertTimezoneToLocal (line 1306) | function convertTimezoneToLocal(date, timezone, reverse) {
  function startingTag (line 1317) | function startingTag(element) {
  function tryDecodeURIComponent (line 1347) | function tryDecodeURIComponent(value) {
  function parseKeyValue (line 1360) | function parseKeyValue(/**string*/keyValue) {
  function toKeyValue (line 1387) | function toKeyValue(obj) {
  function encodeUriSegment (line 1415) | function encodeUriSegment(val) {
  function encodeUriQuery (line 1434) | function encodeUriQuery(val, pctEncodeSpaces) {
  function getNgAttribute (line 1446) | function getNgAttribute(element, ngAttr) {
  function angularInit (line 1591) | function angularInit(element, bootstrap) {
  function bootstrap (line 1679) | function bootstrap(element, modules, config) {
  function reloadWithDebugInfo (line 1757) | function reloadWithDebugInfo() {
  function getTestability (line 1770) | function getTestability(rootElement) {
  function snake_case (line 1780) | function snake_case(name, separator) {
  function bindJQuery (line 1788) | function bindJQuery() {
  function assertArg (line 1842) | function assertArg(arg, name, reason) {
  function assertArgFn (line 1849) | function assertArgFn(arg, name, acceptArrayAnnotation) {
  function assertNotHasOwnProperty (line 1864) | function assertNotHasOwnProperty(name, context) {
  function getter (line 1878) | function getter(obj, path, bindFnToScope) {
  function getBlockNodes (line 1902) | function getBlockNodes(nodes) {
  function createMap (line 1932) | function createMap() {
  function setupModuleLoader (line 1952) | function setupModuleLoader(window) {
  function serializeObject (line 2307) | function serializeObject(obj) {
  function toDebugString (line 2322) | function toDebugString(obj) {
  function publishExternalAPI (line 2454) | function publishExternalAPI(angular) {
  function jqNextId (line 2730) | function jqNextId() { return ++jqId; }
  function camelCase (line 2743) | function camelCase(name) {
  function jqLiteIsTextNode (line 2771) | function jqLiteIsTextNode(html) {
  function jqLiteAcceptsData (line 2775) | function jqLiteAcceptsData(node) {
  function jqLiteHasData (line 2782) | function jqLiteHasData(node) {
  function jqLiteCleanData (line 2789) | function jqLiteCleanData(nodes) {
  function jqLiteBuildFragment (line 2795) | function jqLiteBuildFragment(html, context) {
  function jqLiteParseHTML (line 2832) | function jqLiteParseHTML(html, context) {
  function jqLiteWrapNode (line 2847) | function jqLiteWrapNode(node, wrapper) {
  function JQLite (line 2866) | function JQLite(element) {
  function jqLiteClone (line 2891) | function jqLiteClone(element) {
  function jqLiteDealoc (line 2895) | function jqLiteDealoc(element, onlyDescendants) {
  function jqLiteOff (line 2906) | function jqLiteOff(element, type, fn, unsupported) {
  function jqLiteRemoveData (line 2944) | function jqLiteRemoveData(element, name) {
  function jqLiteExpandoStore (line 2966) | function jqLiteExpandoStore(element, createIfNecessary) {
  function jqLiteData (line 2979) | function jqLiteData(element, key, value) {
  function jqLiteHasClass (line 3005) | function jqLiteHasClass(element, selector) {
  function jqLiteRemoveClass (line 3011) | function jqLiteRemoveClass(element, cssClasses) {
  function jqLiteAddClass (line 3023) | function jqLiteAddClass(element, cssClasses) {
  function jqLiteAddNodes (line 3040) | function jqLiteAddNodes(root, elements) {
  function jqLiteController (line 3066) | function jqLiteController(element, name) {
  function jqLiteInheritedData (line 3070) | function jqLiteInheritedData(element, name, value) {
  function jqLiteEmpty (line 3090) | function jqLiteEmpty(element) {
  function jqLiteRemove (line 3097) | function jqLiteRemove(element, keepData) {
  function jqLiteDocumentLoaded (line 3104) | function jqLiteDocumentLoaded(action, win) {
  function trigger (line 3124) | function trigger() {
  function getBooleanAttrName (line 3178) | function getBooleanAttrName(element, name) {
  function getAliasedAttrName (line 3186) | function getAliasedAttrName(name) {
  function getText (line 3279) | function getText(element, value) {
  function createEventHandler (line 3364) | function createEventHandler(element, events) {
  function defaultHandlerWrapper (line 3416) | function defaultHandlerWrapper(element, event, handler) {
  function specialMouseHandlerWrapper (line 3420) | function specialMouseHandlerWrapper(target, event, handler) {
  function $$jqLiteProvider (line 3667) | function $$jqLiteProvider() {
  function hashKey (line 3698) | function hashKey(obj, nextUidFn) {
  function HashMap (line 3721) | function HashMap(array, isolatedUid) {
  function extractArgs (line 3834) | function extractArgs(fn) {
  function anonFn (line 3840) | function anonFn(fn) {
  function annotate (line 3850) | function annotate(fn, strictDi, name) {
  function createInjector (line 4397) | function createInjector(modulesToLoad, strictDi) {
  function $AnchorScrollProvider (line 4666) | function $AnchorScrollProvider() {
  function mergeClasses (line 4933) | function mergeClasses(a,b) {
  function extractElementNode (line 4942) | function extractElementNode(element) {
  function splitClasses (line 4951) | function splitClasses(classes) {
  function prepareAnimateOptions (line 4976) | function prepareAnimateOptions(options) {
  function updateData (line 5021) | function updateData(data, classes, value) {
  function handleCSSClassChanges (line 5036) | function handleCSSClassChanges() {
  function addRemoveClassesPostDigest (line 5065) | function addRemoveClassesPostDigest(element, add, remove) {
  function domInsert (line 5179) | function domInsert(element, parentElement, afterElement) {
  function waitForTick (line 5526) | function waitForTick(fn) {
  function next (line 5561) | function next() {
  function onProgress (line 5585) | function onProgress(response) {
  function AnimateRunner (line 5593) | function AnimateRunner(host) {
  function run (line 5749) | function run() {
  function applyAnimationContents (line 5760) | function applyAnimationContents() {
  function Browser (line 5801) | function Browser(window, document, $log, $sniffer) {
  function $BrowserProvider (line 6129) | function $BrowserProvider() {
  function $CacheFactoryProvider (line 6217) | function $CacheFactoryProvider() {
  function $TemplateCacheProvider (line 6532) | function $TemplateCacheProvider() {
  function $CompileProvider (line 7391) | function $CompileProvider($provide, $$sanitizeUriProvider) {
  function directiveNormalize (line 9786) | function directiveNormalize(name) {
  function nodesetLinkingFn (line 9835) | function nodesetLinkingFn(
  function directiveLinkingFn (line 9842) | function directiveLinkingFn(
  function tokenDifference (line 9850) | function tokenDifference(str1, str2) {
  function removeComments (line 9866) | function removeComments(jqNodes) {
  function identifierForController (line 9887) | function identifierForController(controller, ident) {
  function $ControllerProvider (line 9906) | function $ControllerProvider() {
  function $DocumentProvider (line 10088) | function $DocumentProvider() {
  function $ExceptionHandlerProvider (line 10134) | function $ExceptionHandlerProvider() {
  function serializeValue (line 10180) | function serializeValue(v) {
  function $HttpParamSerializerProvider (line 10188) | function $HttpParamSerializerProvider() {
  function $HttpParamSerializerJQLikeProvider (line 10225) | function $HttpParamSerializerJQLikeProvider() {
  function defaultHttpResponseTransform (line 10297) | function defaultHttpResponseTransform(data, headers) {
  function isJsonLike (line 10313) | function isJsonLike(str) {
  function parseHeaders (line 10324) | function parseHeaders(headers) {
  function headersGetter (line 10360) | function headersGetter(headers) {
  function transformData (line 10390) | function transformData(data, headers, status, fns) {
  function isSuccess (line 10403) | function isSuccess(status) {
  function $HttpProvider (line 10414) | function $HttpProvider() {
  function $xhrFactoryProvider (line 11516) | function $xhrFactoryProvider() {
  function $HttpBackendProvider (line 11541) | function $HttpBackendProvider() {
  function createHttpBackend (line 11547) | function createHttpBackend($browser, createXhr, $browserDefer, callbacks...
  function $InterpolateProvider (line 11749) | function $InterpolateProvider() {
  function $IntervalProvider (line 12074) | function $IntervalProvider() {
  function encodePath (line 12295) | function encodePath(path) {
  function parseAbsoluteUrl (line 12306) | function parseAbsoluteUrl(absoluteUrl, locationObj) {
  function parseAppUrl (line 12315) | function parseAppUrl(relativeUrl, locationObj) {
  function beginsWith (line 12340) | function beginsWith(begin, whole) {
  function stripHash (line 12347) | function stripHash(url) {
  function trimEmptyHash (line 12352) | function trimEmptyHash(url) {
  function stripFile (line 12357) | function stripFile(url) {
  function serverBase (line 12362) | function serverBase(url) {
  function LocationHtml5Url (line 12376) | function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
  function LocationHashbangUrl (line 12455) | function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
  function LocationHashbangInHtml5Url (line 12567) | function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
  function locationGetter (line 12931) | function locationGetter(property) {
  function locationGetterSetter (line 12938) | function locationGetterSetter(property, preprocess) {
  function $LocationProvider (line 12984) | function $LocationProvider() {
  function $LogProvider (line 13318) | function $LogProvider() {
  function ensureSafeMemberName (line 13474) | function ensureSafeMemberName(name, fullExpression) {
  function getStringValue (line 13485) | function getStringValue(name) {
  function ensureSafeObject (line 13503) | function ensureSafeObject(obj, fullExpression) {
  function ensureSafeFunction (line 13534) | function ensureSafeFunction(obj, fullExpression) {
  function ensureSafeAssignContext (line 13548) | function ensureSafeAssignContext(obj, fullExpression) {
  function ifDefined (line 14061) | function ifDefined(v, d) {
  function plusFn (line 14065) | function plusFn(l, r) {
  function isStateless (line 14071) | function isStateless($filter, filterName) {
  function findConstantAndWatchExpressions (line 14076) | function findConstantAndWatchExpressions(ast, $filter) {
  function getInputs (line 14184) | function getInputs(body) {
  function isAssignable (line 14192) | function isAssignable(ast) {
  function assignableAST (line 14196) | function assignableAST(ast) {
  function isLiteral (line 14202) | function isLiteral(ast) {
  function isConstant (line 14210) | function isConstant(ast) {
  function ASTCompiler (line 14214) | function ASTCompiler(astBuilder, $filter) {
  function ASTInterpreter (line 14713) | function ASTInterpreter(astBuilder, $filter) {
  function isPossiblyDangerousMemberName (line 15113) | function isPossiblyDangerousMemberName(name) {
  function getValueOf (line 15119) | function getValueOf(value) {
  function $ParseProvider (line 15174) | function $ParseProvider() {
  function $QProvider (line 15661) | function $QProvider() {
  function $$QProvider (line 15670) | function $$QProvider() {
  function qFactory (line 15686) | function qFactory(nextTick, exceptionHandler) {
  function $$RAFProvider (line 16039) | function $$RAFProvider() { //rAF
  function $RootScopeProvider (line 16136) | function $RootScopeProvider() {
  function $$SanitizeUriProvider (line 17455) | function $$SanitizeUriProvider() {
  function adjustMatcher (line 17546) | function adjustMatcher(matcher) {
  function adjustMatchers (line 17574) | function adjustMatchers(matchers) {
  function $SceDelegateProvider (line 17652) | function $SceDelegateProvider() {
  function $SceProvider (line 18189) | function $SceProvider() {
  function $SnifferProvider (line 18601) | function $SnifferProvider() {
  function $TemplateRequestProvider (line 18689) | function $TemplateRequestProvider() {
  function $$TestabilityProvider (line 18787) | function $$TestabilityProvider() {
  function $TimeoutProvider (line 18902) | function $TimeoutProvider() {
  function urlResolve (line 19053) | function urlResolve(url) {
  function urlIsSameOrigin (line 19087) | function urlIsSameOrigin(requestUrl) {
  function $WindowProvider (line 19134) | function $WindowProvider() {
  function $$CookieReader (line 19147) | function $$CookieReader($document) {
  function $$CookieReaderProvider (line 19189) | function $$CookieReaderProvider() {
  function $FilterProvider (line 19293) | function $FilterProvider($provide) {
  function filterFilter (line 19483) | function filterFilter() {
  function createPredicateFn (line 19520) | function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
  function deepCompare (line 19557) | function deepCompare(actual, expected, comparator, matchAgainstAnyProp, ...
  function getTypeForFilter (line 19607) | function getTypeForFilter(val) {
  function currencyFilter (line 19668) | function currencyFilter($locale) {
  function numberFilter (line 19740) | function numberFilter($locale) {
  function parse (line 19765) | function parse(numStr) {
  function roundNumber (line 19820) | function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
  function formatNumber (line 19895) | function formatNumber(number, pattern, groupSep, decimalSep, fractionSiz...
  function padNumber (line 19961) | function padNumber(num, digits, trim, negWrap) {
  function dateGetter (line 19980) | function dateGetter(name, size, offset, trim, negWrap) {
  function dateStrGetter (line 19992) | function dateStrGetter(name, shortForm, standAlone) {
  function timeZoneGetter (line 20002) | function timeZoneGetter(date, formats, offset) {
  function getFirstThursdayOfYear (line 20012) | function getFirstThursdayOfYear(year) {
  function getThursdayThisWeek (line 20020) | function getThursdayThisWeek(datetime) {
  function weekGetter (line 20026) | function weekGetter(size) {
  function ampmGetter (line 20038) | function ampmGetter(date, formats) {
  function eraGetter (line 20042) | function eraGetter(date, formats) {
  function longEraGetter (line 20046) | function longEraGetter(date, formats) {
  function dateFilter (line 20182) | function dateFilter($locale) {
  function jsonFilter (line 20289) | function jsonFilter() {
  function limitToFilter (line 20418) | function limitToFilter() {
  function orderByFilter (line 20640) | function orderByFilter($parse) {
  function ngDirective (line 20761) | function ngDirective(directive) {
  function defaultLinkFn (line 21140) | function defaultLinkFn(scope, element, attr) {
  function nullFormRenameControl (line 21242) | function nullFormRenameControl(control, name) {
  function FormController (line 21290) | function FormController(element, attrs, $scope, $animate, $interpolate) {
  function getSetter (line 21764) | function getSetter(expression) {
  function stringBasedInputType (line 22871) | function stringBasedInputType(ctrl) {
  function textInputType (line 22877) | function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function baseInputType (line 22882) | function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function weekParser (line 22992) | function weekParser(isoWeek, existingDate) {
  function createDateParser (line 23024) | function createDateParser(regexp, mapping) {
  function createDateInputType (line 23074) | function createDateInputType(type, regexp, parseDate, format) {
  function badInputChecker (line 23146) | function badInputChecker(scope, element, attr, ctrl) {
  function numberInputType (line 23157) | function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function urlInputType (line 23211) | function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function emailInputType (line 23224) | function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function radioInputType (line 23237) | function radioInputType(scope, element, attr, ctrl) {
  function parseConstantExpr (line 23259) | function parseConstantExpr($parse, context, name, expression, fallback) {
  function checkboxInputType (line 23272) | function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browse...
  function classDirective (line 23855) | function classDirective(name, selector) {
  function processParseErrors (line 26455) | function processParseErrors() {
  function processSyncValidators (line 26475) | function processSyncValidators() {
  function processAsyncValidators (line 26491) | function processAsyncValidators() {
  function setValidity (line 26517) | function setValidity(name, isValid) {
  function validationDone (line 26523) | function validationDone(allValid) {
  function writeToModelIfNeeded (line 26603) | function writeToModelIfNeeded() {
  function addSetValidityMethod (line 27179) | function addSetValidityMethod(context) {
  function isObjectEmpty (line 27273) | function isObjectEmpty(obj) {
  function parseOptionsExpression (line 27565) | function parseOptionsExpression(optionsExp, selectElement, scope) {
  function ngOptionsPostLink (line 27727) | function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
  function updateElementText (line 28315) | function updateElementText(newText) {
  function ngTranscludeCloneAttachFn (line 29641) | function ngTranscludeCloneAttachFn(clone) {
  function chromeHack (line 29713) | function chromeHack(optionElement) {
  function selectPreLink (line 30075) | function selectPreLink(scope, element, attr, ctrls) {
  function selectPostLink (line 30139) | function selectPostLink(scope, element, attrs, ctrls) {
  function getDecimals (line 30567) | function getDecimals(n) {
  function getVF (line 30573) | function getVF(n, opt_precision) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/ionic-angular.js
  function actionSheet (line 120) | function actionSheet(opts) {
  function retain (line 368) | function retain() {
  function release (line 379) | function release() {
  function getElement (line 391) | function getElement() {
  function preventClick (line 544) | function preventClick(ev) {
  function addClickBlock (line 549) | function addClickBlock() {
  function removeClickBlock (line 564) | function removeClickBlock() {
  function getViewById (line 710) | function getViewById(viewId) {
  function getBackView (line 714) | function getBackView(view) {
  function getForwardView (line 718) | function getForwardView(view) {
  function getHistoryById (line 722) | function getHistoryById(historyId) {
  function getHistory (line 726) | function getHistory(scope) {
  function getParentHistoryObj (line 742) | function getParentHistoryObj(scope) {
  function setNavViews (line 756) | function setNavViews(viewId) {
  function getCurrentStateId (line 762) | function getCurrentStateId() {
  function getCurrentStateParams (line 779) | function getCurrentStateParams() {
  function isAbstractTag (line 1417) | function isAbstractTag(ele) {
  function canSwipeBack (line 1421) | function canSwipeBack(ele, viewLocals) {
  function onHardwareBackButton (line 1496) | function onHardwareBackButton(e) {
  function setStyles (line 1924) | function setStyles(ele, opacity, x, boxShadowOpacity) {
  function setStyles (line 1959) | function setStyles(ctrl, opacity, titleX, backTextX) {
  function enter (line 1975) | function enter(ctrlA, ctrlB, step) {
  function leave (line 1982) | function leave(ctrlA, ctrlB, step) {
  function setStyles (line 2014) | function setStyles(ele, x, opacity) {
  function setStyles (line 2046) | function setStyles(ctrl, opacity) {
  function setPlatformConfig (line 2092) | function setPlatformConfig(platformName, platformConfigs) {
  function addConfig (line 2103) | function addConfig(configObj, platformObj) {
  function createConfig (line 2121) | function createConfig(configObj, providerObj, platformPath) {
  function stringObj (line 2152) | function stringObj(obj, str) {
  function getLoader (line 2300) | function getLoader() {
  function showLoader (line 2387) | function showLoader(options) {
  function hideLoader (line 2408) | function hideLoader() {
  function positionView (line 3155) | function positionView(target, popoverEle) {
  function createPopup (line 3572) | function createPopup(options) {
  function onHardwareBackButton (line 3656) | function onHardwareBackButton() {
  function showPopup (line 3661) | function showPopup(options) {
  function focusInput (line 3725) | function focusInput(element) {
  function showAlert (line 3732) | function showAlert(opts) {
  function showConfirm (line 3744) | function showConfirm(opts) {
  function showPrompt (line 3758) | function showPrompt(opts) {
  function getStyle (line 3807) | function getStyle(el, cssprop) {
  function isStaticPositioned (line 3821) | function isStaticPositioned(element) {
  function $ionicTemplateCache (line 4413) | function $ionicTemplateCache(templates) {
  function run (line 4429) | function run() {
  function fetchTemplate (line 4499) | function fetchTemplate(url) {
  function loadAndCompile (line 4506) | function loadAndCompile(options) {
  function warn (line 4558) | function warn(oldMethod, newMethod) {
  function onReflow (line 4811) | function onReflow() {
  function completeOnTransitionEnd (line 4832) | function completeOnTransitionEnd(ev) {
  function transitionComplete (line 4836) | function transitionComplete() {
  function cancelOnTransitionEnd (line 4870) | function cancelOnTransitionEnd(ev) {
  function cancelTransition (line 4874) | function cancelTransition() {
  function getViewElementIdentifier (line 5057) | function getViewElementIdentifier(locals, view) {
  function viewState (line 5063) | function viewState(locals) {
  function getTransitionData (line 5067) | function getTransitionData(viewLocals, enteringEle, direction, view) {
  function getViewData (line 5089) | function getViewData(view) {
  function navViewAttr (line 5100) | function navViewAttr(ele, value) {
  function destroyViewEle (line 5108) | function destroyViewEle(ele) {
  function compareStatePrefixes (line 5121) | function compareStatePrefixes(enteringStateName, exitingStateName) {
  function getScopeForElement (line 5136) | function getScopeForElement(element, stateData) {
  function aggregateNavViewChildren (line 5170) | function aggregateNavViewChildren(element) {
  function isIOS9UIWebView (line 5233) | function isIOS9UIWebView(userAgent) {
  function applyIOS9Shim (line 5237) | function applyIOS9Shim(browser) {
  function $LocationDecorator (line 5284) | function $LocationDecorator($location, $timeout) {
  function getEle (line 5678) | function getEle(className) {
  function onInfinite (line 5724) | function onInfinite() {
  function finishInfiniteScroll (line 5732) | function finishInfiniteScroll() {
  function checkInfiniteBounds (line 5748) | function checkInfiniteBounds() {
  function calculateMaxValue (line 5808) | function calculateMaxValue(maximum) {
  function positionItem (line 6095) | function positionItem(ele, itemType) {
  function transitionEnd (line 6262) | function transitionEnd() {
  function deprecatedWarning (line 6402) | function deprecatedWarning(oldMethod, newMethod) {
  function createNavElement (line 6409) | function createNavElement(type) {
  function getOnScreenHeaderBar (line 6416) | function getOnScreenHeaderBar() {
  function getOffScreenHeaderBar (line 6423) | function getOffScreenHeaderBar() {
  function navBarAttr (line 6430) | function navBarAttr(ctrl, val) {
  function navSwipeAttr (line 6434) | function navSwipeAttr(val) {
  function onTabsLeave (line 6651) | function onTabsLeave(ev, data) {
  function onDragStart (line 6808) | function onDragStart(ev) {
  function onDrag (line 6848) | function onDrag(ev) {
  function onRelease (line 6869) | function onRelease(ev) {
  function getDragX (line 6917) | function getDragX(ev) {
  function getSwipeCompletion (line 6921) | function getSwipeCompletion(dragX) {
  function navSwipeAttr (line 6936) | function navSwipeAttr(val) {
  function onTabsTop (line 6941) | function onTabsTop(ev, isTabsTop) {
  function onBarSubheader (line 6946) | function onBarSubheader(ev, isBarSubheader) {
  function getAssociatedNavBarCtrl (line 6951) | function getAssociatedNavBarCtrl() {
  function handleMousedown (line 7005) | function handleMousedown(e) {
  function handleTouchstart (line 7014) | function handleTouchstart(e) {
  function handleTouchend (line 7023) | function handleTouchend() {
  function handleTouchmove (line 7052) | function handleTouchmove(e) {
  function handleScroll (line 7130) | function handleScroll(e) {
  function overscroll (line 7135) | function overscroll(val) {
  function nativescroll (line 7140) | function nativescroll(target, newScrollTop) {
  function setScrollLock (line 7149) | function setScrollLock(enabled) {
  function scrollTo (line 7189) | function scrollTo(Y, duration, callback) {
  function destroy (line 7271) | function destroy() {
  function activate (line 7300) | function activate() {
  function deactivate (line 7305) | function deactivate() {
  function start (line 7314) | function start() {
  function show (line 7326) | function show() {
  function hide (line 7331) | function hide() {
  function tail (line 7336) | function tail() {
  function createSvgElement (line 8053) | function createSvgElement(tagName, data, parent, spinnerName) {
  function setSvgAttribute (line 8078) | function setSvgAttribute(ele, k, v) {
  function animationValues (line 8082) | function animationValues(strValues, i) {
  function run (line 8365) | function run() {
  function easeInOutCubic (line 8410) | function easeInOutCubic(t, c) {
  function afterEnter (line 8685) | function afterEnter() {
  function titleUpdate (line 8708) | function titleUpdate(newTitle) {
  function deregisterFns (line 8716) | function deregisterFns() {
  function generateNavBarItem (line 8725) | function generateNavBarItem(html) {
  function attrTrue (line 8733) | function attrTrue(key) {
  function CollectionRepeatDirective (line 8942) | function CollectionRepeatDirective($ionicCollectionManager, $parse, $win...
  function RepeatManagerFactory (line 9248) | function RepeatManagerFactory($rootScope, $window, $$rAF) {
  function prelink (line 9923) | function prelink($scope, $element, $attr) {
  function checkAsideExpose (line 10088) | function checkAsideExpose() {
  function onResize (line 10094) | function onResize() {
  function gestureDirective (line 10388) | function gestureDirective(directiveName) {
  function tapScrollToTopDirective (line 10491) | function tapScrollToTopDirective() { //eslint-disable-line no-unused-vars
  function headerFooterBarDirective (line 10531) | function headerFooterBarDirective(isHeader) {
  function stopPropagation (line 10954) | function stopPropagation(ev) {
  function init (line 10980) | function init() {
  function stopPropagation (line 11059) | function stopPropagation(e) {
  function onShow (line 11215) | function onShow(e) {
  function onHide (line 11229) | function onHide() {
  function keyboardAttachGetClientHeight (line 11251) | function keyboardAttachGetClientHeight(element) {
  function init (line 11360) | function init() {
  function hasIconClass (line 11701) | function hasIconClass(ele) {
  function updateView (line 12207) | function updateView(firstTime) {
  function eventStopPropagation (line 12273) | function eventStopPropagation(e) {
  function prelink (line 12574) | function prelink($scope, $element, $attr) {
  function prelink (line 12741) | function prelink($scope, $element, $attr, sideMenuCtrl) {
  function prelink (line 13027) | function prelink($scope, $element, $attrs, ctrl) {
  function freezeAllScrolls (line 13151) | function freezeAllScrolls(shouldFreeze) {
  function getPager (line 13228) | function getPager() {
  function attrStr (line 13735) | function attrStr(k, v) {
  function selectIfMatchesState (line 13823) | function selectIfMatchesState() {
  function tabSelected (line 13835) | function tabSelected(isSelected) {
  function destroyTab (line 13870) | function destroyTab() {
  function prelink (line 14034) | function prelink($scope, $element, $attr, tabsCtrl) {
  function postLink (line 14076) | function postLink($scope, $element, $attr, tabsCtrl) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/ionic.bundle.js
  function trueFn (line 38) | function trueFn() { return true; }
  function DelegateInstance (line 58) | function DelegateInstance(instances, handle) {
  function DelegateService (line 72) | function DelegateService() {
  function instanceMethodCaller (line 95) | function instanceMethodCaller(methodName) {
  function domReady (line 138) | function domReady() {
  function setup (line 698) | function setup() {
  function getParameterByName (line 2040) | function getParameterByName(name) {
  function verifyPlatformReady (line 2460) | function verifyPlatformReady() {
  function onWindowLoad (line 2469) | function onWindowLoad() {
  function onPlatformReady (line 2490) | function onPlatformReady() {
  function update (line 2583) | function update(fn) {
  function tapEventListener (line 2924) | function tapEventListener(type, enable, useCapture) {
  function tapClick (line 2932) | function tapClick(e) {
  function triggerMouseEvent (line 2948) | function triggerMouseEvent(type, ele, x, y) {
  function tapClickGateKeeper (line 2956) | function tapClickGateKeeper(e) {
  function tapMouseDown (line 2978) | function tapMouseDown(e) {
  function tapMouseUp (line 3007) | function tapMouseUp(e) {
  function tapMouseMove (line 3025) | function tapMouseMove(e) {
  function tapTouchStart (line 3036) | function tapTouchStart(e) {
  function tapTouchEnd (line 3063) | function tapTouchEnd(e) {
  function tapTouchMove (line 3080) | function tapTouchMove(e) {
  function tapTouchCancel (line 3089) | function tapTouchCancel() {
  function tapEnableTouchEvents (line 3095) | function tapEnableTouchEvents() {
  function tapIgnoreEvent (line 3103) | function tapIgnoreEvent(e) {
  function tapHandleFocus (line 3117) | function tapHandleFocus(ele) {
  function tapFocusOutActive (line 3152) | function tapFocusOutActive() {
  function tapFocusIn (line 3161) | function tapFocusIn(e) {
  function tapFocusOut (line 3183) | function tapFocusOut() {
  function tapActiveElement (line 3188) | function tapActiveElement(ele) {
  function tapHasPointerMoved (line 3195) | function tapHasPointerMoved(endEvent) {
  function tapContainingElement (line 3211) | function tapContainingElement(ele, allowSelf) {
  function tapTargetElement (line 3221) | function tapTargetElement(ele) {
  function isSelectOrOption (line 3234) | function isSelectOrOption(tagName){
  function clear (line 3310) | function clear() {
  function activateElements (line 3318) | function activateElements() {
  function deactivateElements (line 3329) | function deactivateElements() {
  function keyboardInit (line 3813) | function keyboardInit() {
  function keyboardNativeShow (line 3843) | function keyboardNativeShow(e) {
  function keyboardFocusIn (line 3872) | function keyboardFocusIn(e) {
  function keyboardFocusOut (line 3940) | function keyboardFocusOut() {
  function keyboardOrientationChange (line 3981) | function keyboardOrientationChange() {
  function keyboardOnKeyDown (line 4013) | function keyboardOnKeyDown(e) {
  function keyboardPreventDefault (line 4023) | function keyboardPreventDefault(e) {
  function keyboardWaitForResize (line 4047) | function keyboardWaitForResize(callback, isOpening) {
  function keyboardHide (line 4111) | function keyboardHide() {
  function keyboardShow (line 4152) | function keyboardShow() {
  function keyboardGetHeight (line 4192) | function keyboardGetHeight() {
  function isPortraitViewportHeight (line 4231) | function isPortraitViewportHeight(viewportHeight) {
  function isLandscapeViewportHeight (line 4237) | function isLandscapeViewportHeight(viewportHeight) {
  function keyboardUpdateViewportHeight (line 4243) | function keyboardUpdateViewportHeight() {
  function keyboardInitViewportHeight (line 4267) | function keyboardInitViewportHeight() {
  function getViewportHeight (line 4285) | function getViewportHeight() {
  function keyboardHasPlugin (line 4302) | function keyboardHasPlugin() {
  function viewportLoadTag (line 4360) | function viewportLoadTag() {
  function viewportUpdate (line 4383) | function viewportUpdate() {
  function viewportTagUpdate (line 4467) | function viewportTagUpdate() {
  function getEventTouches (line 5274) | function getEventTouches(e) {
  function animateScroll (line 7221) | function animateScroll(Y, X) {
  function makeInvisible (line 7669) | function makeInvisible() {
  function setup (line 8353) | function setup() {
  function prev (line 8411) | function prev(slideSpeed) {
  function next (line 8418) | function next(slideSpeed) {
  function circle (line 8425) | function circle(index) {
  function slide (line 8432) | function slide(to, slideSpeed) {
  function move (line 8480) | function move(index, dist, speed) {
  function translate (line 8487) | function translate(index, dist, speed) {
  function animate (line 8507) | function animate(from, to, speed) {
  function begin (line 8546) | function begin() {
  function stop (line 8552) | function stop() {
  function isH (line 9330) | function isH() {
  function round (line 9392) | function round(a) {
  function onReady (line 9412) | function onReady () {
  function _onReady (line 9436) | function _onReady() {
  function autoplay (line 9455) | function autoplay() {
  function forceSetTranslate (line 9920) | function forceSetTranslate() {
  function findElementInEvent (line 10109) | function findElementInEvent(e, selector) {
  function initObserver (line 10963) | function initObserver(target, options) {
  function setControlledTranslate (line 11749) | function setControlledTranslate(c) {
  function setControlledTransition (line 11789) | function setControlledTransition(c) {
  function handleKeyboard (line 11844) | function handleKeyboard(e) {
  function handleMousewheel (line 11939) | function handleMousewheel(e) {
  function setParallaxTransform (line 12048) | function setParallaxTransform(el, progress) {
  function normalizeEventName (line 12132) | function normalizeEventName (eventName) {
  function handleLiveEvent (line 12661) | function handleLiveEvent(e) {
  function proxy (line 12729) | function proxy(e) {
  function fireCallBack (line 12753) | function fireCallBack(e) {
  function addLibraryPlugin (line 13162) | function addLibraryPlugin(lib) {
  function fireCallBack (line 13178) | function fireCallBack(e) {
  function minErr (line 13408) | function minErr(module, ErrorConstructor) {
  function isArrayLike (line 13619) | function isArrayLike(obj) {
  function forEach (line 13676) | function forEach(obj, iterator, context) {
  function forEachSorted (line 13720) | function forEachSorted(obj, iterator, context) {
  function reverseParams (line 13734) | function reverseParams(iteratorFn) {
  function nextUid (line 13748) | function nextUid() {
  function setHashKey (line 13758) | function setHashKey(obj, h) {
  function baseExtend (line 13767) | function baseExtend(dst, objs, deep) {
  function extend (line 13819) | function extend(dst) {
  function merge (line 13842) | function merge(dst) {
  function toInt (line 13848) | function toInt(str) {
  function inherit (line 13853) | function inherit(parent, extra) {
  function noop (line 13873) | function noop() {}
  function identity (line 13895) | function identity($) {return $;}
  function valueFn (line 13899) | function valueFn(value) {return function valueRef() {return value;};}
  function hasCustomToString (line 13901) | function hasCustomToString(obj) {
  function isUndefined (line 13918) | function isUndefined(value) {return typeof value === 'undefined';}
  function isDefined (line 13933) | function isDefined(value) {return typeof value !== 'undefined';}
  function isObject (line 13949) | function isObject(value) {
  function isBlankObject (line 13960) | function isBlankObject(value) {
  function isString (line 13977) | function isString(value) {return typeof value === 'string';}
  function isNumber (line 13998) | function isNumber(value) {return typeof value === 'number';}
  function isDate (line 14013) | function isDate(value) {
  function isFunction (line 14044) | function isFunction(value) {return typeof value === 'function';}
  function isRegExp (line 14054) | function isRegExp(value) {
  function isWindow (line 14066) | function isWindow(obj) {
  function isScope (line 14071) | function isScope(obj) {
  function isFile (line 14076) | function isFile(obj) {
  function isFormData (line 14081) | function isFormData(obj) {
  function isBlob (line 14086) | function isBlob(obj) {
  function isBoolean (line 14091) | function isBoolean(value) {
  function isPromiseLike (line 14096) | function isPromiseLike(obj) {
  function isTypedArray (line 14102) | function isTypedArray(value) {
  function isArrayBuffer (line 14106) | function isArrayBuffer(obj) {
  function isElement (line 14136) | function isElement(node) {
  function makeMap (line 14146) | function makeMap(str) {
  function nodeName_ (line 14155) | function nodeName_(element) {
  function includes (line 14159) | function includes(array, obj) {
  function arrayRemove (line 14163) | function arrayRemove(array, value) {
  function copy (line 14229) | function copy(source, destination) {
  function shallowCopy (line 14371) | function shallowCopy(src, dst) {
  function equals (line 14421) | function equals(o1, o2) {
  function noUnsafeEval (line 14486) | function noUnsafeEval() {
  function concat (line 14551) | function concat(array1, array2, index) {
  function sliceArgs (line 14555) | function sliceArgs(args, startIndex) {
  function bind (line 14579) | function bind(self, fn) {
  function toJsonReplacer (line 14600) | function toJsonReplacer(key, value) {
  function toJson (line 14632) | function toJson(obj, pretty) {
  function fromJson (line 14653) | function fromJson(json) {
  function timezoneToOffset (line 14661) | function timezoneToOffset(timezone, fallback) {
  function addDateMinutes (line 14669) | function addDateMinutes(date, minutes) {
  function convertTimezoneToLocal (line 14676) | function convertTimezoneToLocal(date, timezone, reverse) {
  function startingTag (line 14687) | function startingTag(element) {
  function tryDecodeURIComponent (line 14717) | function tryDecodeURIComponent(value) {
  function parseKeyValue (line 14730) | function parseKeyValue(/**string*/keyValue) {
  function toKeyValue (line 14757) | function toKeyValue(obj) {
  function encodeUriSegment (line 14785) | function encodeUriSegment(val) {
  function encodeUriQuery (line 14804) | function encodeUriQuery(val, pctEncodeSpaces) {
  function getNgAttribute (line 14816) | function getNgAttribute(element, ngAttr) {
  function angularInit (line 14961) | function angularInit(element, bootstrap) {
  function bootstrap (line 15049) | function bootstrap(element, modules, config) {
  function reloadWithDebugInfo (line 15127) | function reloadWithDebugInfo() {
  function getTestability (line 15140) | function getTestability(rootElement) {
  function snake_case (line 15150) | function snake_case(name, separator) {
  function bindJQuery (line 15158) | function bindJQuery() {
  function assertArg (line 15212) | function assertArg(arg, name, reason) {
  function assertArgFn (line 15219) | function assertArgFn(arg, name, acceptArrayAnnotation) {
  function assertNotHasOwnProperty (line 15234) | function assertNotHasOwnProperty(name, context) {
  function getter (line 15248) | function getter(obj, path, bindFnToScope) {
  function getBlockNodes (line 15272) | function getBlockNodes(nodes) {
  function createMap (line 15302) | function createMap() {
  function setupModuleLoader (line 15322) | function setupModuleLoader(window) {
  function serializeObject (line 15677) | function serializeObject(obj) {
  function toDebugString (line 15692) | function toDebugString(obj) {
  function publishExternalAPI (line 15824) | function publishExternalAPI(angular) {
  function jqNextId (line 16100) | function jqNextId() { return ++jqId; }
  function camelCase (line 16113) | function camelCase(name) {
  function jqLiteIsTextNode (line 16141) | function jqLiteIsTextNode(html) {
  function jqLiteAcceptsData (line 16145) | function jqLiteAcceptsData(node) {
  function jqLiteHasData (line 16152) | function jqLiteHasData(node) {
  function jqLiteCleanData (line 16159) | function jqLiteCleanData(nodes) {
  function jqLiteBuildFragment (line 16165) | function jqLiteBuildFragment(html, context) {
  function jqLiteParseHTML (line 16202) | function jqLiteParseHTML(html, context) {
  function jqLiteWrapNode (line 16217) | function jqLiteWrapNode(node, wrapper) {
  function JQLite (line 16236) | function JQLite(element) {
  function jqLiteClone (line 16261) | function jqLiteClone(element) {
  function jqLiteDealoc (line 16265) | function jqLiteDealoc(element, onlyDescendants) {
  function jqLiteOff (line 16276) | function jqLiteOff(element, type, fn, unsupported) {
  function jqLiteRemoveData (line 16314) | function jqLiteRemoveData(element, name) {
  function jqLiteExpandoStore (line 16336) | function jqLiteExpandoStore(element, createIfNecessary) {
  function jqLiteData (line 16349) | function jqLiteData(element, key, value) {
  function jqLiteHasClass (line 16375) | function jqLiteHasClass(element, selector) {
  function jqLiteRemoveClass (line 16381) | function jqLiteRemoveClass(element, cssClasses) {
  function jqLiteAddClass (line 16393) | function jqLiteAddClass(element, cssClasses) {
  function jqLiteAddNodes (line 16410) | function jqLiteAddNodes(root, elements) {
  function jqLiteController (line 16436) | function jqLiteController(element, name) {
  function jqLiteInheritedData (line 16440) | function jqLiteInheritedData(element, name, value) {
  function jqLiteEmpty (line 16460) | function jqLiteEmpty(element) {
  function jqLiteRemove (line 16467) | function jqLiteRemove(element, keepData) {
  function jqLiteDocumentLoaded (line 16474) | function jqLiteDocumentLoaded(action, win) {
  function trigger (line 16494) | function trigger() {
  function getBooleanAttrName (line 16548) | function getBooleanAttrName(element, name) {
  function getAliasedAttrName (line 16556) | function getAliasedAttrName(name) {
  function getText (line 16649) | function getText(element, value) {
  function createEventHandler (line 16734) | function createEventHandler(element, events) {
  function defaultHandlerWrapper (line 16786) | function defaultHandlerWrapper(element, event, handler) {
  function specialMouseHandlerWrapper (line 16790) | function specialMouseHandlerWrapper(target, event, handler) {
  function $$jqLiteProvider (line 17037) | function $$jqLiteProvider() {
  function hashKey (line 17068) | function hashKey(obj, nextUidFn) {
  function HashMap (line 17091) | function HashMap(array, isolatedUid) {
  function extractArgs (line 17204) | function extractArgs(fn) {
  function anonFn (line 17210) | function anonFn(fn) {
  function annotate (line 17220) | function annotate(fn, strictDi, name) {
  function createInjector (line 17767) | function createInjector(modulesToLoad, strictDi) {
  function $AnchorScrollProvider (line 18036) | function $AnchorScrollProvider() {
  function mergeClasses (line 18303) | function mergeClasses(a,b) {
  function extractElementNode (line 18312) | function extractElementNode(element) {
  function splitClasses (line 18321) | function splitClasses(classes) {
  function prepareAnimateOptions (line 18346) | function prepareAnimateOptions(options) {
  function updateData (line 18391) | function updateData(data, classes, value) {
  function handleCSSClassChanges (line 18406) | function handleCSSClassChanges() {
  function addRemoveClassesPostDigest (line 18435) | function addRemoveClassesPostDigest(element, add, remove) {
  function domInsert (line 18549) | function domInsert(element, parentElement, afterElement) {
  function waitForTick (line 18896) | function waitForTick(fn) {
  function next (line 18931) | function next() {
  function onProgress (line 18955) | function onProgress(response) {
  function AnimateRunner (line 18963) | function AnimateRunner(host) {
  function run (line 19119) | function run() {
  function applyAnimationContents (line 19130) | function applyAnimationContents() {
  function Browser (line 19171) | function Browser(window, document, $log, $sniffer) {
  function $BrowserProvider (line 19499) | function $BrowserProvider() {
  function $CacheFactoryProvider (line 19587) | function $CacheFactoryProvider() {
  function $TemplateCacheProvider (line 19902) | function $TemplateCacheProvider() {
  function $CompileProvider (line 20761) | function $CompileProvider($provide, $$sanitizeUriProvider) {
  function directiveNormalize (line 23156) | function directiveNormalize(name) {
  function nodesetLinkingFn (line 23205) | function nodesetLinkingFn(
  function directiveLinkingFn (line 23212) | function directiveLinkingFn(
  function tokenDifference (line 23220) | function tokenDifference(str1, str2) {
  function removeComments (line 23236) | function removeComments(jqNodes) {
  function identifierForController (line 23257) | function identifierForController(controller, ident) {
  function $ControllerProvider (line 23276) | function $ControllerProvider() {
  function $DocumentProvider (line 23458) | function $DocumentProvider() {
  function $ExceptionHandlerProvider (line 23504) | function $ExceptionHandlerProvider() {
  function serializeValue (line 23550) | function serializeValue(v) {
  function $HttpParamSerializerProvider (line 23558) | function $HttpParamSerializerProvider() {
  function $HttpParamSerializerJQLikeProvider (line 23595) | function $HttpParamSerializerJQLikeProvider() {
  function defaultHttpResponseTransform (line 23667) | function defaultHttpResponseTransform(data, headers) {
  function isJsonLike (line 23683) | function isJsonLike(str) {
  function parseHeaders (line 23694) | function parseHeaders(headers) {
  function headersGetter (line 23730) | function headersGetter(headers) {
  function transformData (line 23760) | function transformData(data, headers, status, fns) {
  function isSuccess (line 23773) | function isSuccess(status) {
  function $HttpProvider (line 23784) | function $HttpProvider() {
  function $xhrFactoryProvider (line 24886) | function $xhrFactoryProvider() {
  function $HttpBackendProvider (line 24911) | function $HttpBackendProvider() {
  function createHttpBackend (line 24917) | function createHttpBackend($browser, createXhr, $browserDefer, callbacks...
  function $InterpolateProvider (line 25119) | function $InterpolateProvider() {
  function $IntervalProvider (line 25444) | function $IntervalProvider() {
  function encodePath (line 25665) | function encodePath(path) {
  function parseAbsoluteUrl (line 25676) | function parseAbsoluteUrl(absoluteUrl, locationObj) {
  function parseAppUrl (line 25685) | function parseAppUrl(relativeUrl, locationObj) {
  function beginsWith (line 25710) | function beginsWith(begin, whole) {
  function stripHash (line 25717) | function stripHash(url) {
  function trimEmptyHash (line 25722) | function trimEmptyHash(url) {
  function stripFile (line 25727) | function stripFile(url) {
  function serverBase (line 25732) | function serverBase(url) {
  function LocationHtml5Url (line 25746) | function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
  function LocationHashbangUrl (line 25825) | function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
  function LocationHashbangInHtml5Url (line 25937) | function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
  function locationGetter (line 26301) | function locationGetter(property) {
  function locationGetterSetter (line 26308) | function locationGetterSetter(property, preprocess) {
  function $LocationProvider (line 26354) | function $LocationProvider() {
  function $LogProvider (line 26688) | function $LogProvider() {
  function ensureSafeMemberName (line 26844) | function ensureSafeMemberName(name, fullExpression) {
  function getStringValue (line 26855) | function getStringValue(name) {
  function ensureSafeObject (line 26873) | function ensureSafeObject(obj, fullExpression) {
  function ensureSafeFunction (line 26904) | function ensureSafeFunction(obj, fullExpression) {
  function ensureSafeAssignContext (line 26918) | function ensureSafeAssignContext(obj, fullExpression) {
  function ifDefined (line 27431) | function ifDefined(v, d) {
  function plusFn (line 27435) | function plusFn(l, r) {
  function isStateless (line 27441) | function isStateless($filter, filterName) {
  function findConstantAndWatchExpressions (line 27446) | function findConstantAndWatchExpressions(ast, $filter) {
  function getInputs (line 27554) | function getInputs(body) {
  function isAssignable (line 27562) | function isAssignable(ast) {
  function assignableAST (line 27566) | function assignableAST(ast) {
  function isLiteral (line 27572) | function isLiteral(ast) {
  function isConstant (line 27580) | function isConstant(ast) {
  function ASTCompiler (line 27584) | function ASTCompiler(astBuilder, $filter) {
  function ASTInterpreter (line 28083) | function ASTInterpreter(astBuilder, $filter) {
  function isPossiblyDangerousMemberName (line 28483) | function isPossiblyDangerousMemberName(name) {
  function getValueOf (line 28489) | function getValueOf(value) {
  function $ParseProvider (line 28544) | function $ParseProvider() {
  function $QProvider (line 29031) | function $QProvider() {
  function $$QProvider (line 29040) | function $$QProvider() {
  function qFactory (line 29056) | function qFactory(nextTick, exceptionHandler) {
  function $$RAFProvider (line 29409) | function $$RAFProvider() { //rAF
  function $RootScopeProvider (line 29506) | function $RootScopeProvider() {
  function $$SanitizeUriProvider (line 30825) | function $$SanitizeUriProvider() {
  function adjustMatcher (line 30916) | function adjustMatcher(matcher) {
  function adjustMatchers (line 30944) | function adjustMatchers(matchers) {
  function $SceDelegateProvider (line 31022) | function $SceDelegateProvider() {
  function $SceProvider (line 31559) | function $SceProvider() {
  function $SnifferProvider (line 31971) | function $SnifferProvider() {
  function $TemplateRequestProvider (line 32059) | function $TemplateRequestProvider() {
  function $$TestabilityProvider (line 32157) | function $$TestabilityProvider() {
  function $TimeoutProvider (line 32272) | function $TimeoutProvider() {
  function urlResolve (line 32423) | function urlResolve(url) {
  function urlIsSameOrigin (line 32457) | function urlIsSameOrigin(requestUrl) {
  function $WindowProvider (line 32504) | function $WindowProvider() {
  function $$CookieReader (line 32517) | function $$CookieReader($document) {
  function $$CookieReaderProvider (line 32559) | function $$CookieReaderProvider() {
  function $FilterProvider (line 32663) | function $FilterProvider($provide) {
  function filterFilter (line 32853) | function filterFilter() {
  function createPredicateFn (line 32890) | function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
  function deepCompare (line 32927) | function deepCompare(actual, expected, comparator, matchAgainstAnyProp, ...
  function getTypeForFilter (line 32977) | function getTypeForFilter(val) {
  function currencyFilter (line 33038) | function currencyFilter($locale) {
  function numberFilter (line 33110) | function numberFilter($locale) {
  function parse (line 33135) | function parse(numStr) {
  function roundNumber (line 33190) | function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
  function formatNumber (line 33265) | function formatNumber(number, pattern, groupSep, decimalSep, fractionSiz...
  function padNumber (line 33331) | function padNumber(num, digits, trim, negWrap) {
  function dateGetter (line 33350) | function dateGetter(name, size, offset, trim, negWrap) {
  function dateStrGetter (line 33362) | function dateStrGetter(name, shortForm, standAlone) {
  function timeZoneGetter (line 33372) | function timeZoneGetter(date, formats, offset) {
  function getFirstThursdayOfYear (line 33382) | function getFirstThursdayOfYear(year) {
  function getThursdayThisWeek (line 33390) | function getThursdayThisWeek(datetime) {
  function weekGetter (line 33396) | function weekGetter(size) {
  function ampmGetter (line 33408) | function ampmGetter(date, formats) {
  function eraGetter (line 33412) | function eraGetter(date, formats) {
  function longEraGetter (line 33416) | function longEraGetter(date, formats) {
  function dateFilter (line 33552) | function dateFilter($locale) {
  function jsonFilter (line 33659) | function jsonFilter() {
  function limitToFilter (line 33788) | function limitToFilter() {
  function orderByFilter (line 34010) | function orderByFilter($parse) {
  function ngDirective (line 34131) | function ngDirective(directive) {
  function defaultLinkFn (line 34510) | function defaultLinkFn(scope, element, attr) {
  function nullFormRenameControl (line 34612) | function nullFormRenameControl(control, name) {
  function FormController (line 34660) | function FormController(element, attrs, $scope, $animate, $interpolate) {
  function getSetter (line 35134) | function getSetter(expression) {
  function stringBasedInputType (line 36241) | function stringBasedInputType(ctrl) {
  function textInputType (line 36247) | function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function baseInputType (line 36252) | function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function weekParser (line 36362) | function weekParser(isoWeek, existingDate) {
  function createDateParser (line 36394) | function createDateParser(regexp, mapping) {
  function createDateInputType (line 36444) | function createDateInputType(type, regexp, parseDate, format) {
  function badInputChecker (line 36516) | function badInputChecker(scope, element, attr, ctrl) {
  function numberInputType (line 36527) | function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function urlInputType (line 36581) | function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function emailInputType (line 36594) | function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
  function radioInputType (line 36607) | function radioInputType(scope, element, attr, ctrl) {
  function parseConstantExpr (line 36629) | function parseConstantExpr($parse, context, name, expression, fallback) {
  function checkboxInputType (line 36642) | function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browse...
  function classDirective (line 37225) | function classDirective(name, selector) {
  function processParseErrors (line 39825) | function processParseErrors() {
  function processSyncValidators (line 39845) | function processSyncValidators() {
  function processAsyncValidators (line 39861) | function processAsyncValidators() {
  function setValidity (line 39887) | function setValidity(name, isValid) {
  function validationDone (line 39893) | function validationDone(allValid) {
  function writeToModelIfNeeded (line 39973) | function writeToModelIfNeeded() {
  function addSetValidityMethod (line 40549) | function addSetValidityMethod(context) {
  function isObjectEmpty (line 40643) | function isObjectEmpty(obj) {
  function parseOptionsExpression (line 40935) | function parseOptionsExpression(optionsExp, selectElement, scope) {
  function ngOptionsPostLink (line 41097) | function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
  function updateElementText (line 41685) | function updateElementText(newText) {
  function ngTranscludeCloneAttachFn (line 43011) | function ngTranscludeCloneAttachFn(clone) {
  function chromeHack (line 43083) | function chromeHack(optionElement) {
  function selectPreLink (line 43445) | function selectPreLink(scope, element, attr, ctrls) {
  function selectPostLink (line 43509) | function selectPostLink(scope, element, attrs, ctrls) {
  function getDecimals (line 43937) | function getDecimals(n) {
  function getVF (line 43943) | function getVF(n, opt_precision) {
  function assertArg (line 44173) | function assertArg(arg, name, reason) {
  function mergeClasses (line 44180) | function mergeClasses(a,b) {
  function packageStyles (line 44189) | function packageStyles(options) {
  function pendClasses (line 44198) | function pendClasses(classes, fix, isPrefix) {
  function removeFromArray (line 44215) | function removeFromArray(arr, val) {
  function stripCommentsFromElement (line 44222) | function stripCommentsFromElement(element) {
  function extractElementNode (line 44249) | function extractElementNode(element) {
  function $$addClass (line 44259) | function $$addClass($$jqLite, element, className) {
  function $$removeClass (line 44265) | function $$removeClass($$jqLite, element, className) {
  function applyAnimationClassesFactory (line 44271) | function applyAnimationClassesFactory($$jqLite) {
  function prepareAnimationOptions (line 44284) | function prepareAnimationOptions(options) {
  function applyAnimationStyles (line 44298) | function applyAnimationStyles(element, options) {
  function applyAnimationFromStyles (line 44303) | function applyAnimationFromStyles(element, options) {
  function applyAnimationToStyles (line 44310) | function applyAnimationToStyles(element, options) {
  function mergeAnimationDetails (line 44317) | function mergeAnimationDetails(element, oldAnimation, newAnimation) {
  function resolveElementClasses (line 44358) | function resolveElementClasses(existing, toAdd, toRemove) {
  function getDomNode (line 44416) | function getDomNode(element) {
  function applyGeneratedPreparationClasses (line 44420) | function applyGeneratedPreparationClasses(element, event, options) {
  function clearGeneratedClasses (line 44437) | function clearGeneratedClasses(element, options) {
  function blockTransitions (line 44448) | function blockTransitions(node, duration) {
  function blockKeyframeAnimations (line 44457) | function blockKeyframeAnimations(node, applyBlock) {
  function applyInlineStyle (line 44464) | function applyInlineStyle(node, styleTuple) {
  function concatWithSpace (line 44470) | function concatWithSpace(a,b) {
  function scheduler (line 44479) | function scheduler(tasks) {
  function nextTick (line 44509) | function nextTick() {
  function setData (line 44616) | function setData(value) {
  function getCssKeyframeDurationStyle (line 44862) | function getCssKeyframeDurationStyle(duration) {
  function getCssDelayStyle (line 44866) | function getCssDelayStyle(delay, isKeyframeAnimation) {
  function computeCssStyles (line 44871) | function computeCssStyles($window, element, properties) {
  function parseMaxTime (line 44897) | function parseMaxTime(str) {
  function truthyTimingValue (line 44912) | function truthyTimingValue(val) {
  function getCssTransitionDurationStyle (line 44916) | function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
  function createLocalCacheLookup (line 44927) | function createLocalCacheLookup() {
  function registerRestorableStyles (line 44963) | function registerRestorableStyles(backup, node, properties) {
  function gcsHashFn (line 44983) | function gcsHashFn(node, extraClasses) {
  function computeCachedCssStyles (line 44990) | function computeCachedCssStyles(node, className, cacheKey, properties) {
  function computeCachedCssStaggerStyles (line 45006) | function computeCachedCssStaggerStyles(node, className, cacheKey, proper...
  function waitUntilQuiet (line 45037) | function waitUntilQuiet(callback) {
  function computeTimings (line 45056) | function computeTimings(node, className, cacheKey) {
  function endFn (line 45326) | function endFn() {
  function cancelFn (line 45330) | function cancelFn() {
  function close (line 45334) | function close(rejected) { // jshint ignore:line
  function applyBlocking (line 45393) | function applyBlocking(duration) {
  function closeAndReturnNoopAnimator (line 45403) | function closeAndReturnNoopAnimator() {
  function onAnimationProgress (line 45422) | function onAnimationProgress(event) {
  function start (line 45449) | function start() {
  function isDocumentFragment (line 45635) | function isDocumentFragment(node) {
  function filterCssClasses (line 45666) | function filterCssClasses(classes) {
  function getUniqueValues (line 45671) | function getUniqueValues(a, b) {
  function prepareAnchoredAnimation (line 45679) | function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {
  function prepareFromToAnchorAnimation (line 45806) | function prepareFromToAnchorAnimation(from, to, classes, anchors) {
  function prepareRegularAnimation (line 45859) | function prepareRegularAnimation(animationDetails) {
  function applyOptions (line 45954) | function applyOptions() {
  function close (line 45959) | function close() {
  function onComplete (line 46021) | function onComplete(success) {
  function endAnimations (line 46026) | function endAnimations(cancelled) {
  function executeAnimationFn (line 46035) | function executeAnimationFn(fn, element, event, options, onDone) {
  function groupEventedAnimations (line 46078) | function groupEventedAnimations(element, event, options, animations, fnN...
  function packageAnimations (line 46119) | function packageAnimations(element, event, options, animations, fnName) {
  function lookupAnimations (line 46161) | function lookupAnimations(classes) {
  function endFnFactory (line 46207) | function endFnFactory() {
  function done (line 46216) | function done(status) {
  function prepareAnimation (line 46226) | function prepareAnimation(animationDetails) {
  function makeTruthyCssClassMap (line 46250) | function makeTruthyCssClassMap(classString) {
  function hasMatchingClasses (line 46264) | function hasMatchingClasses(newClassString, currentClassString) {
  function isAllowed (line 46273) | function isAllowed(ruleType, element, currentAnimation, previousAnimatio...
  function hasAnimationClasses (line 46279) | function hasAnimationClasses(animation, and) {
  function postDigestTaskFactory (line 46346) | function postDigestTaskFactory() {
  function normalizeAnimationDetails (line 46406) | function normalizeAnimationDetails(element, animation) {
  function findCallbacks (line 46417) | function findCallbacks(parent, element, event) {
  function filterFromRegistry (line 46459) | function filterFromRegistry(list, matchContainer, matchCallback) {
  function queueAnimation (line 46518) | function queueAnimation(element, event, initialOptions) {
  function closeChildAnimations (line 46790) | function closeChildAnimations(element) {
  function clearElementAnimationState (line 46809) | function clearElementAnimationState(element) {
  function isMatchingElement (line 46815) | function isMatchingElement(nodeOrElmA, nodeOrElmB) {
  function areAnimationsAllowed (line 46826) | function areAnimationsAllowed(element, parentElement, event) {
  function markElementAnimationState (line 46911) | function markElementAnimationState(element, state, details) {
  function setRunner (line 46934) | function setRunner(element, runner) {
  function removeRunner (line 46938) | function removeRunner(element) {
  function getRunner (line 46942) | function getRunner(element) {
  function sortAnimations (line 46952) | function sortAnimations(animations) {
  function getAnchorNodes (line 47152) | function getAnchorNodes(node) {
  function groupAnimations (line 47167) | function groupAnimations(animations) {
  function cssClassesIntersection (line 47250) | function cssClassesIntersection(a,b) {
  function invokeFirstDriver (line 47270) | function invokeFirstDriver(animationDetails) {
  function beforeStart (line 47285) | function beforeStart() {
  function updateAnimationRunners (line 47296) | function updateAnimationRunners(animation, newRunner) {
  function handleDestroyedElement (line 47309) | function handleDestroyedElement() {
  function close (line 47316) | function close(rejected) { // jshint ignore:line
  function $SanitizeProvider (line 48361) | function $SanitizeProvider() {
  function sanitizeText (line 48418) | function sanitizeText(chars) {
  function toMap (line 48507) | function toMap(str, lowercaseKeys) {
  function htmlParser (line 48549) | function htmlParser(html, handler) {
  function attrToMap (line 48609) | function attrToMap(attrs) {
  function encodeEntities (line 48626) | function encodeEntities(value) {
  function htmlSanitizeWriter (line 48651) | function htmlSanitizeWriter(buf, uriValidator) {
  function stripCustomNsAttrs (line 48705) | function stripCustomNsAttrs(node) {
  function addText (line 48896) | function addText(text) {
  function addLink (line 48903) | function addLink(url, text) {
  function inherit (line 48966) | function inherit(parent, extra) {
  function merge (line 48970) | function merge(dst) {
  function ancestors (line 48988) | function ancestors(first, second) {
  function objectKeys (line 49004) | function objectKeys(object) {
  function indexOf (line 49023) | function indexOf(array, value) {
  function inheritParams (line 49047) | function inheritParams(currentParams, newParams, $current, $to) {
  function equalForKeys (line 49073) | function equalForKeys(a, b, keys) {
  function filterByKeys (line 49093) | function filterByKeys(keys, values) {
  function indexBy (line 49104) | function indexBy(array, propName) {
  function pick (line 49114) | function pick(obj) {
  function omit (line 49125) | function omit(obj) {
  function pluck (line 49134) | function pluck(collection, key) {
  function filter (line 49143) | function filter(collection, callback) {
  function map (line 49154) | function map(collection, callback) {
  function $Resolve (line 49257) | function $Resolve(  $q,    $injector) {
  function $TemplateFactory (line 49511) | function $TemplateFactory(  $http,   $templateCache,   $injector) {
  function UrlMatcher (line 49677) | function UrlMatcher(pattern, config, parentMatcher) {
  function decodePathArray (line 49852) | function decodePathArray(string) {
  function encodeDashes (line 49938) | function encodeDashes(str) { // Replace dashes with encoded "\-"
  function Type (line 50008) | function Type(config) {
  function ArrayType (line 50107) | function ArrayType(type, mode) {
  function $UrlMatcherFactory (line 50168) | function $UrlMatcherFactory() {
  function $UrlRouterProvider (line 50664) | function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
  function $StateProvider (line 51083) | function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
  function $ViewProvider (line 52437) | function $ViewProvider() {
  function $ViewScrollProvider (line 52514) | function $ViewScrollProvider() {
  function $ViewDirective (line 52674) | function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $inte...
  function $ViewDirectiveFill (line 52814) | function $ViewDirectiveFill (  $compile,   $controller,   $state,   $int...
  function getUiViewName (line 52854) | function getUiViewName(scope, attrs, element, $interpolate) {
  function parseStateRef (line 52863) | function parseStateRef(ref, current) {
  function stateContext (line 52871) | function stateContext(el) {
  function $StateRefDirective (line 52942) | function $StateRefDirective($state, $timeout) {
  function $StateRefActiveDirective (line 53087) | function $StateRefActiveDirective($state, $stateParams, $interpolate) {
  function $IsStateFilter (line 53142) | function $IsStateFilter($state) {
  function $IncludedByStateFilter (line 53160) | function $IncludedByStateFilter($state) {
  function actionSheet (line 53298) | function actionSheet(opts) {
  function retain (line 53546) | function retain() {
  function release (line 53557) | function release() {
  function getElement (line 53569) | function getElement() {
  function preventClick (line 53722) | function preventClick(ev) {
  function addClickBlock (line 53727) | function addClickBlock() {
  function removeClickBlock (line 53742) | function removeClickBlock() {
  function getViewById (line 53888) | function getViewById(viewId) {
  function getBackView (line 53892) | function getBackView(view) {
  function getForwardView (line 53896) | function getForwardView(view) {
  function getHistoryById (line 53900) | function getHistoryById(historyId) {
  function getHistory (line 53904) | function getHistory(scope) {
  function getParentHistoryObj (line 53920) | function getParentHistoryObj(scope) {
  function setNavViews (line 53934) | function setNavViews(viewId) {
  function getCurrentStateId (line 53940) | function getCurrentStateId() {
  function getCurrentStateParams (line 53957) | function getCurrentStateParams() {
  function isAbstractTag (line 54595) | function isAbstractTag(ele) {
  function canSwipeBack (line 54599) | function canSwipeBack(ele, viewLocals) {
  function onHardwareBackButton (line 54674) | function onHardwareBackButton(e) {
  function setStyles (line 55102) | function setStyles(ele, opacity, x, boxShadowOpacity) {
  function setStyles (line 55137) | function setStyles(ctrl, opacity, titleX, backTextX) {
  function enter (line 55153) | function enter(ctrlA, ctrlB, step) {
  function leave (line 55160) | function leave(ctrlA, ctrlB, step) {
  function setStyles (line 55192) | function setStyles(ele, x, opacity) {
  function setStyles (line 55224) | function setStyles(ctrl, opacity) {
  function setPlatformConfig (line 55270) | function setPlatformConfig(platformName, platformConfigs) {
  function addConfig (line 55281) | function addConfig(configObj, platformObj) {
  function createConfig (line 55299) | function createConfig(configObj, providerObj, platformPath) {
  function stringObj (line 55330) | function stringObj(obj, str) {
  function getLoader (line 55478) | function getLoader() {
  function showLoader (line 55565) | function showLoader(options) {
  function hideLoader (line 55586) | function hideLoader() {
  function positionView (line 56333) | function positionView(target, popoverEle) {
  function createPopup (line 56750) | function createPopup(options) {
  function onHardwareBackButton (line 56834) | function onHardwareBackButton() {
  function showPopup (line 56839) | function showPopup(options) {
  function focusInput (line 56903) | function focusInput(element) {
  function showAlert (line 56910) | function showAlert(opts) {
  function showConfirm (line 56922) | function showConfirm(opts) {
  function showPrompt (line 56936) | function showPrompt(opts) {
  function getStyle (line 56985) | function getStyle(el, cssprop) {
  function isStaticPositioned (line 56999) | function isStaticPositioned(element) {
  function $ionicTemplateCache (line 57591) | function $ionicTemplateCache(templates) {
  function run (line 57607) | function run() {
  function fetchTemplate (line 57677) | function fetchTemplate(url) {
  function loadAndCompile (line 57684) | function loadAndCompile(options) {
  function warn (line 57736) | function warn(oldMethod, newMethod) {
  function onReflow (line 57989) | function onReflow() {
  function completeOnTransitionEnd (line 58010) | function completeOnTransitionEnd(ev) {
  function transitionComplete (line 58014) | function transitionComplete() {
  function cancelOnTransitionEnd (line 58048) | function cancelOnTransitionEnd(ev) {
  function cancelTransition (line 58052) | function cancelTransition() {
  function getViewElementIdentifier (line 58235) | function getViewElementIdentifier(locals, view) {
  function viewState (line 58241) | function viewState(locals) {
  function getTransitionData (line 58245) | function getTransitionData(viewLocals, enteringEle, direction, view) {
  function getViewData (line 58267) | function getViewData(view) {
  function navViewAttr (line 58278) | function navViewAttr(ele, value) {
  function destroyViewEle (line 58286) | function destroyViewEle(ele) {
  function compareStatePrefixes (line 58299) | function compareStatePrefixes(enteringStateName, exitingStateName) {
  function getScopeForElement (line 58314) | function getScopeForElement(element, stateData) {
  function aggregateNavViewChildren (line 58348) | function aggregateNavViewChildren(element) {
  function isIOS9UIWebView (line 58411) | function isIOS9UIWebView(userAgent) {
  function applyIOS9Shim (line 58415) | function applyIOS9Shim(browser) {
  function $LocationDecorator (line 58462) | function $LocationDecorator($location, $timeout) {
  function getEle (line 58856) | function getEle(className) {
  function onInfinite (line 58902) | function onInfinite() {
  function finishInfiniteScroll (line 58910) | function finishInfiniteScroll() {
  function checkInfiniteBounds (line 58926) | function checkInfiniteBounds() {
  function calculateMaxValue (line 58986) | function calculateMaxValue(maximum) {
  function positionItem (line 59273) | function positionItem(ele, itemType) {
  function transitionEnd (line 59440) | function transitionEnd() {
  function deprecatedWarning (line 59580) | function deprecatedWarning(oldMethod, newMethod) {
  function createNavElement (line 59587) | function createNavElement(type) {
  function getOnScreenHeaderBar (line 59594) | function getOnScreenHeaderBar() {
  function getOffScreenHeaderBar (line 59601) | function getOffScreenHeaderBar() {
  function navBarAttr (line 59608) | function navBarAttr(ctrl, val) {
  function navSwipeAttr (line 59612) | function navSwipeAttr(val) {
  function onTabsLeave (line 59829) | function onTabsLeave(ev, data) {
  function onDragStart (line 59986) | function onDragStart(ev) {
  function onDrag (line 60026) | function onDrag(ev) {
  function onRelease (line 60047) | function onRelease(ev) {
  function getDragX (line 60095) | function getDragX(ev) {
  function getSwipeCompletion (line 60099) | function getSwipeCompletion(dragX) {
  function navSwipeAttr (line 60114) | function navSwipeAttr(val) {
  function onTabsTop (line 60119) | function onTabsTop(ev, isTabsTop) {
  function onBarSubheader (line 60124) | function onBarSubheader(ev, isBarSubheader) {
  function getAssociatedNavBarCtrl (line 60129) | function getAssociatedNavBarCtrl() {
  function handleMousedown (line 60183) | function handleMousedown(e) {
  function handleTouchstart (line 60192) | function handleTouchstart(e) {
  function handleTouchend (line 60201) | function handleTouchend() {
  function handleTouchmove (line 60230) | function handleTouchmove(e) {
  function handleScroll (line 60308) | function handleScroll(e) {
  function overscroll (line 60313) | function overscroll(val) {
  function nativescroll (line 60318) | function nativescroll(target, newScrollTop) {
  function setScrollLock (line 60327) | function setScrollLock(enabled) {
  function scrollTo (line 60367) | function scrollTo(Y, duration, callback) {
  function destroy (line 60449) | function destroy() {
  function activate (line 60478) | function activate() {
  function deactivate (line 60483) | function deactivate() {
  function start (line 60492) | function start() {
  function show (line 60504) | function show() {
  function hide (line 60509) | function hide() {
  function tail (line 60514) | function tail() {
  function createSvgElement (line 61231) | function createSvgElement(tagName, data, parent, spinnerName) {
  function setSvgAttribute (line 61256) | function setSvgAttribute(ele, k, v) {
  function animationValues (line 61260) | function animationValues(strValues, i) {
  function run (line 61543) | function run() {
  function easeInOutCubic (line 61588) | function easeInOutCubic(t, c) {
  function afterEnter (line 61863) | function afterEnter() {
  function titleUpdate (line 61886) | function titleUpdate(newTitle) {
  function deregisterFns (line 61894) | function deregisterFns() {
  function generateNavBarItem (line 61903) | function generateNavBarItem(html) {
  function attrTrue (line 61911) | function attrTrue(key) {
  function CollectionRepeatDirective (line 62120) | function CollectionRepeatDirective($ionicCollectionManager, $parse, $win...
  function RepeatManagerFactory (line 62426) | function RepeatManagerFactory($rootScope, $window, $$rAF) {
  function prelink (line 63101) | function prelink($scope, $element, $attr) {
  function checkAsideExpose (line 63266) | function checkAsideExpose() {
  function onResize (line 63272) | function onResize() {
  function gestureDirective (line 63566) | function gestureDirective(directiveName) {
  function tapScrollToTopDirective (line 63669) | function tapScrollToTopDirective() { //eslint-disable-line no-unused-vars
  function headerFooterBarDirective (line 63709) | function headerFooterBarDirective(isHeader) {
  function stopPropagation (line 64132) | function stopPropagation(ev) {
  function init (line 64158) | function init() {
  function stopPropagation (line 64237) | function stopPropagation(e) {
  function onShow (line 64393) | function onShow(e) {
  function onHide (line 64407) | function onHide() {
  function keyboardAttachGetClientHeight (line 64429) | function keyboardAttachGetClientHeight(element) {
  function init (line 64538) | function init() {
  function hasIconClass (line 64879) | function hasIconClass(ele) {
  function updateView (line 65385) | function updateView(firstTime) {
  function eventStopPropagation (line 65451) | function eventStopPropagation(e) {
  function prelink (line 65752) | function prelink($scope, $element, $attr) {
  function prelink (line 65919) | function prelink($scope, $element, $attr, sideMenuCtrl) {
  function prelink (line 66205) | function prelink($scope, $element, $attrs, ctrl) {
  function freezeAllScrolls (line 66329) | function freezeAllScrolls(shouldFreeze) {
  function getPager (line 66406) | function getPager() {
  function attrStr (line 66913) | function attrStr(k, v) {
  function selectIfMatchesState (line 67001) | function selectIfMatchesState() {
  function tabSelected (line 67013) | function tabSelected(isSelected) {
  function destroyTab (line 67048) | function destroyTab() {
  function prelink (line 67212) | function prelink($scope, $element, $attr, tabsCtrl) {
  function postLink (line 67254) | function postLink($scope, $element, $attr, tabsCtrl) {

FILE: 28-ionic/dailyreads/mobileapp/www/lib/ionic/js/ionic.js
  function trueFn (line 31) | function trueFn() { return true; }
  function DelegateInstance (line 51) | function DelegateInstance(instances, handle) {
  function DelegateService (line 65) | function DelegateService() {
  function instanceMethodCaller (line 88) | function instanceMethodCaller(methodName) {
  function domReady (line 131) | function domReady() {
  function setup (line 691) | function setup() {
  function getParameterByName (line 2033) | function getParameterByName(name) {
  function verifyPlatformReady (line 2453) | function verifyPlatformReady() {
  function onWindowLoad (line 2462) | function onWindowLoad() {
  function onPlatformReady (line 2483) | function onPlatformReady() {
  function update (line 2576) | function update(fn) {
  function tapEventListener (line 2917) | function tapEventListener(type, enable, useCapture) {
  function tapClick (line 2925) | function tapClick(e) {
  function triggerMouseEvent (line 2941) | function triggerMouseEvent(type, ele, x, y) {
  function tapClickGateKeeper (line 2949) | function tapClickGateKeeper(e) {
  function tapMouseDown (line 2971) | function tapMouseDown(e) {
  function tapMouseUp (line 3000) | function tapMouseUp(e) {
  function tapMouseMove (line 3018) | function tapMouseMove(e) {
  function tapTouchStart (line 3029) | function tapTouchStart(e) {
  function tapTouchEnd (line 3056) | function tapTouchEnd(e) {
  function tapTouchMove (line 3073) | function tapTouchMove(e) {
  function tapTouchCancel (line 3082) | function tapTouchCancel() {
  function tapEnableTouchEvents (line 3088) | function tapEnableTouchEvents() {
  function tapIgnoreEvent (line 3096) | function tapIgnoreEvent(e) {
  function tapHandleFocus (line 3110) | function tapHandleFocus(ele) {
  function tapFocusOutActive (line 3145) | function tapFocusOutActive() {
  function tapFocusIn (line 3154) | function tapFocusIn(e) {
  function tapFocusOut (line 3176) | function tapFocusOut() {
  function tapActiveElement (line 3181) | function tapActiveElement(ele) {
  function tapHasPointerMoved (line 3188) | function tapHasPointerMoved(endEvent) {
  function tapContainingElement (line 3204) | function tapContainingElement(ele, allowSelf) {
  function tapTargetElement (line 3214) | function tapTargetElement(ele) {
  function isSelectOrOption (line 3227) | function isSelectOrOption(tagName){
  function clear (line 3303) | function clear() {
  function activateElements (line 3311) | function activateElements() {
  function deactivateElements (line 3322) | function deactivateElements() {
  function keyboardInit (line 3806) | function keyboardInit() {
  function keyboardNativeShow (line 3836) | function keyboardNativeShow(e) {
  function keyboardFocusIn (line 3865) | function keyboardFocusIn(e) {
  function keyboardFocusOut (line 3933) | function keyboardFocusOut() {
  function keyboardOrientationChange (line 3974) | function keyboardOrientationChange() {
  function keyboardOnKeyDown (line 4006) | function keyboardOnKeyDown(e) {
  function keyboardPreventDefault (line 4016) | function keyboardPreventDefault(e) {
  function keyboardWaitForResize (line 4040) | function keyboardWaitForResize(callback, isOpening) {
  function keyboardHide (line 4104) | function keyboardHide() {
  function keyboardShow (line 4145) | function keyboardShow() {
  function keyboardGetHeight (line 4185) | function keyboardGetHeight() {
  function isPortraitViewportHeight (line 4224) | function isPortraitViewportHeight(viewportHeight) {
  function isLandscapeViewportHeight (line 4230) | function isLandscapeViewportHeight(viewportHeight) {
  function keyboardUpdateViewportHeight (line 4236) | function keyboardUpdateViewportHeight() {
  function keyboardInitViewportHeight (line 4260) | function keyboardInitViewportHeight() {
  function getViewportHeight (line 4278) | function getViewportHeight() {
  function keyboardHasPlugin (line 4295) | function keyboardHasPlugin() {
  function viewportLoadTag (line 4353) | function viewportLoadTag() {
  function viewportUpdate (line 4376) | function viewportUpdate() {
  function viewportTagUpdate (line 4460) | function viewportTagUpdate() {
  function getEventTouches (line 5267) | function getEventTouches(e) {
  function animateScroll (line 7214) | function animateScroll(Y, X) {
  function makeInvisible (line 7662) | function makeInvisible() {
  function setup (line 8346) | function setup() {
  function prev (line 8404) | function prev(slideSpeed) {
  function next (line 8411) | function next(slideSpeed) {
  function circle (line 8418) | function circle(index) {
  function slide (line 8425) | function slide(to, slideSpeed) {
  function move (line 8473) | function move(index, dist, speed) {
  function translate (line 8480) | function translate(index, dist, speed) {
  function animate (line 8500) | function animate(from, to, speed) {
  function begin (line 8539) | function begin() {
  function stop (line 8545) | function stop() {
  function isH (line 9323) | function isH() {
  function round (line 9385) | function round(a) {
  function onReady (line 9405) | function onReady () {
  function _onReady (line 9429) | function _onReady() {
  function autoplay (line 9448) | function autoplay() {
  function forceSetTranslate (line 9913) | function forceSetTranslate() {
  function findElementInEvent (line 10102) | function findElementInEvent(e, selector) {
  function initObserver (line 10956) | function initObserver(target, options) {
  function setControlledTranslate (line 11742) | function setControlledTranslate(c) {
  function setControlledTransition (line 11782) | function setControlledTransition(c) {
  function handleKeyboard (line 11837) | function handleKeyboard(e) {
  function handleMousewheel (line 11932) | function handleMousewheel(e) {
  function setParallaxTransform (line 12041) | function setParallaxTransform(el, progress) {
  function normalizeEventName (line 12125) | function normalizeEventName (eventName) {
  function handleLiveEvent (line 12654) | function handleLiveEvent(e) {
  function proxy (line 12722) | function proxy(e) {
  function fireCallBack (line 12746) | function fireCallBack(e) {
  function addLibraryPlugin (line 13155) | function addLibraryPlugin(lib) {
  function fireCallBack (line 13171) | function fireCallBack(e) {

FILE: 29-golang-github-slacknotification/programs/main.go
  constant GitHubStatusAPIURL (line 15) | GitHubStatusAPIURL      = "https://status.github.com/api/last-message.json"
  constant SlackIncomingWebhookURL (line 16) | SlackIncomingWebhookURL = "YOUR_SLACK_INCOMING_WEBHOOK_URL"
  function check (line 19) | func check(e error) {
  type GitHubStatusLastMessage (line 26) | type GitHubStatusLastMessage struct
  type SlackMessage (line 35) | type SlackMessage struct
  function main (line 39) | func main() {
  function raiseSlackNotification (line 60) | func raiseSlackNotification(status GitHubStatusLastMessage) {

FILE: 30-dropwizard/blog/src/main/java/blog/AppConfiguration.java
  class AppConfiguration (line 10) | public class AppConfiguration extends Configuration {

FILE: 30-dropwizard/blog/src/main/java/blog/BlogApp.java
  class BlogApp (line 15) | public class BlogApp extends Application<AppConfiguration> {
    method main (line 18) | public static void main(String[] args) throws Exception {
    method run (line 23) | @Override
    method initialize (line 45) | @Override

FILE: 30-dropwizard/blog/src/main/java/blog/model/Blog.java
  class Blog (line 9) | public class Blog {
    method Blog (line 24) | public Blog() {
    method Blog (line 27) | public Blog(String title, String url) {
    method getId (line 33) | public String getId() {
    method getTitle (line 37) | public String getTitle() {
    method getUrl (line 41) | public String getUrl() {
    method getPublishedOn (line 45) | public Date getPublishedOn() {
    method get_id (line 49) | public String get_id() {

FILE: 30-dropwizard/blog/src/main/java/blog/mongo/MongoHealthCheck.java
  class MongoHealthCheck (line 6) | public class MongoHealthCheck extends HealthCheck {
    method MongoHealthCheck (line 10) | public MongoHealthCheck(Mongo mongo) {
    method check (line 14) | @Override

FILE: 30-dropwizard/blog/src/main/java/blog/mongo/MongoManaged.java
  class MongoManaged (line 6) | public class MongoManaged implements Managed {
    method MongoManaged (line 10) | public MongoManaged(Mongo mongo) {
    method start (line 14) | @Override
    method stop (line 18) | @Override

FILE: 30-dropwizard/blog/src/main/java/blog/resources/BlogResource.java
  class BlogResource (line 16) | @Path("/blogs")
    method BlogResource (line 23) | public BlogResource(JacksonDBCollection<Blog, String> blogs) {
    method publishNewBlog (line 27) | @POST

FILE: 30-dropwizard/blog/src/main/java/blog/resources/IndexResource.java
  class IndexResource (line 17) | @Path("/")
    method IndexResource (line 22) | public IndexResource(JacksonDBCollection<Blog, String> blogs) {
    method index (line 26) | @GET

FILE: 32-groovy-ast-transformations/sha1-ast/src/main/java/playground/ToHashAdderAstTransformation.java
  class ToHashAdderAstTransformation (line 16) | @GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
    method visit (line 19) | @Override

FILE: 34-aws-lambda/code/tweet_sender.py
  function tweet_handler (line 9) | def tweet_handler(event, context):

FILE: 36-webpack/code/dist/bundle.js
  function __webpack_require__ (line 6) | function __webpack_require__(moduleId) {
  function addStylesToDom (line 225) | function addStylesToDom(styles, options) {
  function listToStyles (line 247) | function listToStyles(list) {
  function insertStyleElement (line 265) | function insertStyleElement(options, styleElement) {
  function removeStyleElement (line 284) | function removeStyleElement(styleElement) {
  function createStyleElement (line 292) | function createStyleElement(options) {
  function createLinkElement (line 299) | function createLinkElement(options) {
  function addStyle (line 306) | function addStyle(obj, options) {
  function applyToSingletonTag (line 357) | function applyToSingletonTag(styleElement, index, remove, obj) {
  function applyToTag (line 374) | function applyToTag(styleElement, obj) {
  function updateLink (line 392) | function updateLink(linkElement, obj) {
  function timeline (line 419) | function timeline(user){
  function DOMEval (line 515) | function DOMEval( code, doc ) {
  function isArrayLike (line 966) | function isArrayLike( obj ) {
  function Sizzle (line 1198) | function Sizzle( selector, context, results, seed ) {
  function createCache (line 1337) | function createCache() {
  function markFunction (line 1355) | function markFunction( fn ) {
  function assert (line 1364) | function assert( fn ) {
  function addHandle (line 1386) | function addHandle( attrs, handler ) {
  function siblingCheck (line 1401) | function siblingCheck( a, b ) {
  function createInputPseudo (line 1427) | function createInputPseudo( type ) {
  function createButtonPseudo (line 1438) | function createButtonPseudo( type ) {
  function createDisabledPseudo (line 1449) | function createDisabledPseudo( disabled ) {
  function createPositionalPseudo (line 1477) | function createPositionalPseudo( fn ) {
  function testContext (line 1500) | function testContext( context ) {
  function setFilters (line 2556) | function setFilters() {}
  function toSelector (line 2627) | function toSelector( tokens ) {
  function addCombinator (line 2637) | function addCombinator( matcher, combinator, base ) {
  function elementMatcher (line 2699) | function elementMatcher( matchers ) {
  function multipleContexts (line 2713) | function multipleContexts( selector, contexts, results ) {
  function condense (line 2722) | function condense( unmatched, map, filter, context, xml ) {
  function setMatcher (line 2743) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
  function matcherFromTokens (line 2836) | function matcherFromTokens( tokens ) {
  function matcherFromGroupMatchers (line 2894) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
  function winnow (line 3236) | function winnow( elements, qualifier, not ) {
  function sibling (line 3532) | function sibling( cur, dir ) {
  function createOptions (line 3608) | function createOptions( options ) {
  function Identity (line 3833) | function Identity( v ) {
  function Thrower (line 3836) | function Thrower( ex ) {
  function adoptValue (line 3840) | function adoptValue( value, resolve, reject ) {
  function resolve (line 3932) | function resolve( depth, deferred, handler, special ) {
  function completed (line 4298) | function completed() {
  function Data (line 4397) | function Data() {
  function dataAttr (line 4566) | function dataAttr( elem, key, data ) {
  function adjustCSS (line 4886) | function adjustCSS( elem, prop, valueParts, tween ) {
  function getDefaultDisplay (line 4951) | function getDefaultDisplay( elem ) {
  function showHide (line 4974) | function showHide( elements, show ) {
  function getAll (line 5075) | function getAll( context, tag ) {
  function setGlobalEval (line 5092) | function setGlobalEval( elems, refElements ) {
  function buildFragment (line 5108) | function buildFragment( elems, context, scripts, selection, ignored ) {
  function returnTrue (line 5231) | function returnTrue() {
  function returnFalse (line 5235) | function returnFalse() {
  function safeActiveElement (line 5241) | function safeActiveElement() {
  function on (line 5247) | function on( elem, types, selector, data, fn, one ) {
  function manipulationTarget (line 5956) | function manipulationTarget( elem, content ) {
  function disableScript (line 5967) | function disableScript( elem ) {
  function restoreScript (line 5971) | function restoreScript( elem ) {
  function cloneCopyEvent (line 5983) | function cloneCopyEvent( src, dest ) {
  function fixInput (line 6018) | function fixInput( src, dest ) {
  function domManip (line 6031) | function domManip( collection, args, callback, ignored ) {
  function remove (line 6121) | function remove( elem, selector, keepData ) {
  function computeStyleTests (line 6414) | function computeStyleTests() {
  function curCSS (line 6488) | function curCSS( elem, name, computed ) {
  function addGetHookIf (line 6535) | function addGetHookIf( conditionFn, hookFn ) {
  function vendorPropName (line 6571) | function vendorPropName( name ) {
  function setPositiveNumber (line 6590) | function setPositiveNumber( elem, value, subtract ) {
  function augmentWidthOrHeight (line 6602) | function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
  function getWidthOrHeight (line 6646) | function getWidthOrHeight( elem, name, extra ) {
  function Tween (line 6954) | function Tween( elem, options, prop, end, easing ) {
  function raf (line 7077) | function raf() {
  function createFxNow (line 7085) | function createFxNow() {
  function genFx (line 7093) | function genFx( type, includeWidth ) {
  function createTween (line 7113) | function createTween( value, prop, animation ) {
  function defaultPrefilter (line 7127) | function defaultPrefilter( elem, props, opts ) {
  function propFilter (line 7298) | function propFilter( props, specialEasing ) {
  function Animation (line 7335) | function Animation( elem, properties, options ) {
  function getClass (line 8026) | function getClass( elem ) {
  function buildParams (line 8651) | function buildParams( prefix, obj, traditional, add ) {
  function addToPrefiltersOrTransports (line 8797) | function addToPrefiltersOrTransports( structure ) {
  function inspectPrefiltersOrTransports (line 8831) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
  function ajaxExtend (line 8860) | function ajaxExtend( target, src ) {
  function ajaxHandleResponses (line 8880) | function ajaxHandleResponses( s, jqXHR, responses ) {
  function ajaxConvert (line 8938) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
  function done (line 9451) | function done( status, nativeStatusText, responses, headers ) {
  function getWindow (line 10176) | function getWindow( elem ) {
  function createReduce (line 10696) | function createReduce(dir) {
  function createPredicateIndexFinder (line 11132) | function createPredicateIndexFinder(dir) {
  function createIndexFinder (line 11162) | function createIndexFinder(dir, predicateFind, sortedIndex) {
  function collectNonEnumProps (line 11427) | function collectNonEnumProps(obj, keys) {

FILE: 36-webpack/code/js/timeline.js
  function timeline (line 4) | function timeline(user){
Condensed preview — 416 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (9,911K chars).
[
  {
    "path": ".gitignore",
    "chars": 10,
    "preview": ".DS_Store\n"
  },
  {
    "path": "01-finatra/README.md",
    "chars": 19479,
    "preview": "Week 1: Finatra Tutorial -- Build Beautiful REST API The Twitter Way\n-------\n\n[Finatra](https://github.com/twitter/finat"
  },
  {
    "path": "01-finatra/fitman/.gitignore",
    "chars": 1274,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### Scala template\n*.class\n*.log\n\n# sbt specific\n.cache\n.history\n.lib/\ndi"
  },
  {
    "path": "01-finatra/fitman/build.sbt",
    "chars": 2220,
    "preview": "name := \"fitman\"\n\nversion := \"1.0\"\n\nscalaVersion := \"2.11.7\"\n\nlazy val versions = new {\n  val finatra = \"2.1.2\"\n  val lo"
  },
  {
    "path": "01-finatra/fitman/project/build.properties",
    "chars": 20,
    "preview": "sbt.version = 0.13.8"
  },
  {
    "path": "01-finatra/fitman/project/plugins.sbt",
    "chars": 22,
    "preview": "logLevel := Level.Warn"
  },
  {
    "path": "01-finatra/fitman/src/main/resources/logback.xml",
    "chars": 446,
    "preview": "<configuration>\n    <!-- Console Appender -->\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n "
  },
  {
    "path": "01-finatra/fitman/src/main/scala/com/shekhargulati/fitman/FitmanApp.scala",
    "chars": 655,
    "preview": "package com.shekhargulati.fitman\n\nimport com.shekhargulati.fitman.api.WeightResource\nimport com.twitter.finagle.http.Req"
  },
  {
    "path": "01-finatra/fitman/src/main/scala/com/shekhargulati/fitman/api/WeightResource.scala",
    "chars": 1290,
    "preview": "package com.shekhargulati.fitman.api\n\nimport com.twitter.finagle.http.Request\nimport com.twitter.finatra.http.Controller"
  },
  {
    "path": "01-finatra/fitman/src/test/scala/com/shekhargulati/fitman/HelloControllerFeatureTest.scala",
    "chars": 477,
    "preview": "package com.shekhargulati.fitman\n\nimport com.twitter.finagle.http.Status\nimport com.twitter.finatra.http.test.EmbeddedHt"
  },
  {
    "path": "01-finatra/fitman/src/test/scala/com/shekhargulati/fitman/api/WeightResourceFeatureTest.scala",
    "chars": 2296,
    "preview": "package com.shekhargulati.fitman.api\n\nimport com.shekhargulati.fitman.FitmanServer\nimport com.twitter.finagle.http.Statu"
  },
  {
    "path": "02-sbt/README.md",
    "chars": 33907,
    "preview": "SBT: The Missing Tutorial [![TimeToRead](http://ttr.myapis.xyz/ttr.svg?pageUrl=https://github.com/shekhargulati/52-techn"
  },
  {
    "path": "02-sbt/tasky/.gitignore",
    "chars": 1274,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### Scala template\n*.class\n*.log\n\n# sbt specific\n.cache\n.history\n.lib/\ndi"
  },
  {
    "path": "02-sbt/tasky/build.sbt",
    "chars": 491,
    "preview": "name := \"tasky\"\nversion := \"0.1.0\"\nscalaVersion := \"2.11.6\"\n\nlibraryDependencies += \"org.scalatest\" % \"scalatest_2.11\" %"
  },
  {
    "path": "02-sbt/tasky/project/plugins.sbt",
    "chars": 68,
    "preview": "addSbtPlugin(\"org.scalastyle\" %% \"scalastyle-sbt-plugin\" % \"0.8.0\")\n"
  },
  {
    "path": "02-sbt/tasky/scalastyle-config.xml",
    "chars": 5972,
    "preview": "<scalastyle>\n <name>Scalastyle standard configuration</name>\n <check level=\"warning\" class=\"org.scalastyle.file.FileTabC"
  },
  {
    "path": "02-sbt/tasky/src/main/scala/HelloSbt.scala",
    "chars": 62,
    "preview": "object HelloSbt extends App {\n  println(\"Sbt says Hello!!\")\n}\n"
  },
  {
    "path": "02-sbt/tasky/src/main/scala/datamodels.scala",
    "chars": 131,
    "preview": "import java.time.LocalDate\n\ncase class Task(title: String, dueOn: LocalDate, tags: Seq[String] = Seq(), finished: Boolea"
  },
  {
    "path": "02-sbt/tasky/src/main/scala/taskmanager.scala",
    "chars": 159,
    "preview": "import java.time.LocalDate\n\nobject TaskManager {\n\n  def allTasksDueToday(tasks: List[Task]): List[Task] = tasks.filter(t"
  },
  {
    "path": "02-sbt/tasky/src/test/scala/TaskManagerSpec.scala",
    "chars": 630,
    "preview": "import org.scalatest._\nimport java.time.LocalDate\n\nclass TaskManagerSpec extends FlatSpec with Matchers {\n\n  \"An empty t"
  },
  {
    "path": "03-stanford-corenlp/README.md",
    "chars": 9303,
    "preview": "Sentiment Analysis in Scala with Stanford CoreNLP\n-----\n\nSo far in this [series](https://github.com/shekhargulati/52-tec"
  },
  {
    "path": "03-stanford-corenlp/sentiment-analyzer/.gitignore",
    "chars": 1338,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### Scala template\n*.class\n*.log\n\n# sbt specific\n.cache\n.history\n.lib/\ndi"
  },
  {
    "path": "03-stanford-corenlp/sentiment-analyzer/build.sbt",
    "chars": 403,
    "preview": "name := \"sentiment-analyzer\"\ndescription := \"A demo application to showcase sentiment analysis using Stanford CoreNLP an"
  },
  {
    "path": "03-stanford-corenlp/sentiment-analyzer/project/plugins.sbt",
    "chars": 67,
    "preview": "addSbtPlugin(\"net.virtual-void\" % \"sbt-dependency-graph\" % \"0.8.1\")"
  },
  {
    "path": "03-stanford-corenlp/sentiment-analyzer/src/main/scala/com/shekhargulati/sentiment_analyzer/SentimentAnalyzer.scala",
    "chars": 2162,
    "preview": "package com.shekhargulati.sentiment_analyzer\n\nimport java.util.Properties\n\nimport com.shekhargulati.sentiment_analyzer.S"
  },
  {
    "path": "03-stanford-corenlp/sentiment-analyzer/src/test/scala/com/shekhargulati/sentiment_analyzer/SentimentAnalyzerSpec.scala",
    "chars": 902,
    "preview": "package com.shekhargulati.sentiment_analyzer\n\nimport org.scalatest.{FunSpec, Matchers}\n\nclass SentimentAnalyzerSpec exte"
  },
  {
    "path": "04-slick/README.md",
    "chars": 17599,
    "preview": "Slick 3: Functional Relational Mapping for Mere Mortals Part 1\n----\n\nWelcome to the fourth blog of [52-technologies-in-2"
  },
  {
    "path": "04-slick/tasky/.gitignore",
    "chars": 1338,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### SBT template\n# Simple Build Tool\n# http://www.scala-sbt.org/release/d"
  },
  {
    "path": "04-slick/tasky/build.sbt",
    "chars": 381,
    "preview": "name := \"tasky\"\n\ndescription := \"A simple task manager for humans\"\n\nversion := \"0.1.0\"\n\nscalaVersion := \"2.11.7\"\n\nlibrar"
  },
  {
    "path": "04-slick/tasky/project/plugins.sbt",
    "chars": 67,
    "preview": "addSbtPlugin(\"net.virtual-void\" % \"sbt-dependency-graph\" % \"0.8.1\")"
  },
  {
    "path": "04-slick/tasky/src/main/scala/datamodel/DataModel.scala",
    "chars": 1550,
    "preview": "package datamodel\n\nimport java.sql.Timestamp\nimport java.time.LocalDateTime\n\nimport datamodel.ColumnDataMapper._\nimport "
  },
  {
    "path": "04-slick/tasky/src/test/resources/application.conf",
    "chars": 161,
    "preview": "taskydb = {\n  connectionPool      = disabled\n  url                 = \"jdbc:h2:mem:taskydb\"\n  driver              = \"org."
  },
  {
    "path": "04-slick/tasky/src/test/scala/datamodel/DataModelSpec.scala",
    "chars": 1895,
    "preview": "package datamodel\n\nimport java.time.LocalDateTime\n\nimport datamodel.DataModel.Task\nimport org.scalatest.{BeforeAndAfterE"
  },
  {
    "path": "05-slick/README.md",
    "chars": 16958,
    "preview": "Slick 3: Functional Relational Mapping for Mere Mortals Part 2: Querying data\n----\n\nLast week we learnt the [basics of S"
  },
  {
    "path": "05-slick/tasky/.gitignore",
    "chars": 1338,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### SBT template\n# Simple Build Tool\n# http://www.scala-sbt.org/release/d"
  },
  {
    "path": "05-slick/tasky/build.sbt",
    "chars": 381,
    "preview": "name := \"tasky\"\n\ndescription := \"A simple task manager for humans\"\n\nversion := \"0.1.0\"\n\nscalaVersion := \"2.11.7\"\n\nlibrar"
  },
  {
    "path": "05-slick/tasky/project/plugins.sbt",
    "chars": 67,
    "preview": "addSbtPlugin(\"net.virtual-void\" % \"sbt-dependency-graph\" % \"0.8.1\")"
  },
  {
    "path": "05-slick/tasky/src/main/scala/datamodel/Priority.scala",
    "chars": 149,
    "preview": "package datamodel\n\nobject Priority extends Enumeration {\n  type Priority = Value\n  val HIGH = Value(3)\n  val MEDIUM = Va"
  },
  {
    "path": "05-slick/tasky/src/main/scala/datamodel/columnDataMappers.scala",
    "chars": 663,
    "preview": "package datamodel\n\nimport java.sql.Timestamp\nimport java.time.LocalDateTime\n\nimport datamodel.Priority._\nimport slick.dr"
  },
  {
    "path": "05-slick/tasky/src/main/scala/datamodel/dataModel.scala",
    "chars": 1258,
    "preview": "package datamodel\n\nimport java.time.LocalDateTime\n\nimport datamodel.Priority.Priority\nimport datamodel.columnDataMappers"
  },
  {
    "path": "05-slick/tasky/src/main/scala/queries/queries.scala",
    "chars": 1616,
    "preview": "package queries\n\nimport java.time.{LocalDate, LocalDateTime}\n\nimport datamodel.Priority\nimport datamodel.Priority.Priori"
  },
  {
    "path": "05-slick/tasky/src/test/resources/application.conf",
    "chars": 161,
    "preview": "taskydb = {\n  connectionPool      = disabled\n  url                 = \"jdbc:h2:mem:taskydb\"\n  driver              = \"org."
  },
  {
    "path": "05-slick/tasky/src/test/scala/datamodel/DataModelSpec.scala",
    "chars": 2054,
    "preview": "package datamodel\n\nimport java.time.LocalDateTime\n\nimport datamodel.dataModel.Task\nimport org.scalatest.{BeforeAndAfterE"
  },
  {
    "path": "05-slick/tasky/src/test/scala/queries/QueriesSpec.scala",
    "chars": 5030,
    "preview": "package queries\n\nimport java.time.LocalDateTime\n\nimport datamodel.dataModel.Task\nimport datamodel.{Priority, dataModel}\n"
  },
  {
    "path": "06-okhttp/README.md",
    "chars": 23147,
    "preview": "Building A Lightweight Scala REST API Client with OkHttp\n----\n\nWelcome to the sixth blog of [52-technologies-in-2016](ht"
  },
  {
    "path": "06-okhttp/medium-scala-client/.gitignore",
    "chars": 1338,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### JetBrains template\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpSt"
  },
  {
    "path": "06-okhttp/medium-scala-client/build.sbt",
    "chars": 416,
    "preview": "name := \"medium-scala-client\"\n\nversion := \"1.0\"\n\ndescription := \"Scala client for Medium.com REST API\"\n\nscalaVersion := "
  },
  {
    "path": "06-okhttp/medium-scala-client/src/main/scala/medium/MediumApiProtocol.scala",
    "chars": 280,
    "preview": "package medium\n\nimport medium.domainObjects._\nimport spray.json._\n\nobject MediumApiProtocol extends DefaultJsonProtocol{"
  },
  {
    "path": "06-okhttp/medium-scala-client/src/main/scala/medium/MediumClient.scala",
    "chars": 2566,
    "preview": "package medium\n\nimport medium.MediumApiProtocol._\nimport medium.domainObjects._\nimport okhttp3._\nimport spray.json._\n\ncl"
  },
  {
    "path": "06-okhttp/medium-scala-client/src/main/scala/medium/domainObjects.scala",
    "chars": 597,
    "preview": "package medium\n\nobject domainObjects {\n\n  case class User(id: String, username: String, name: String, url: String, image"
  },
  {
    "path": "06-okhttp/medium-scala-client/src/test/scala/medium/MediumClientSpec.scala",
    "chars": 2984,
    "preview": "package medium\n\nimport medium.domainObjects.PostRequest\nimport okhttp3.mockwebserver.{MockResponse, MockWebServer}\nimpor"
  },
  {
    "path": "07-hugo/README.md",
    "chars": 22304,
    "preview": "Hugo: A Modern WebSite Engine That Just Works\n----\n\nThis week I decided to take a break from Scala and scratch my own it"
  },
  {
    "path": "07-hugo/bookshelf/config.toml",
    "chars": 205,
    "preview": "baseurl = \"http://example.com\"\nlanguageCode = \"en-us\"\ntitle = \"Shekhar Gulati Bookshelf\"\n\n[Params]\n  Author = \"Shekhar G"
  },
  {
    "path": "07-hugo/bookshelf/content/post/art-of-thinking-clearly.md",
    "chars": 129,
    "preview": "+++\ndate = \"2016-02-14T19:10:29+05:30\"\ndraft = false\ntitle = \"art of thinking clearly\"\nimage = \"art-of-thinking-clearly."
  },
  {
    "path": "07-hugo/bookshelf/content/post/confessions-of-a-public-speaker.md",
    "chars": 145,
    "preview": "+++\ndate = \"2016-02-14T19:10:48+05:30\"\ndraft = false\ntitle = \"confessions of a public speaker\"\nimage = \"confessions-of-a"
  },
  {
    "path": "07-hugo/bookshelf/content/post/good-to-great.md",
    "chars": 462,
    "preview": "+++\ndate = \"2016-02-14T19:23:40+05:30\"\ndraft = false\ntitle = \"Good to Great Book Review\"\nimage = \"good-to-great.jpg\"\n+++"
  },
  {
    "path": "07-hugo/bookshelf/content/post/hen-who-dreamed-she-could-fly.md",
    "chars": 523,
    "preview": "+++\ndate = \"2016-02-14T19:06:04+05:30\"\ndraft = false\ntitle = \"Hen Who Dreamed She Could Fly\"\nimage = \"hen-who-dreamed.jp"
  },
  {
    "path": "07-hugo/bookshelf/content/post/seven-habbits-of-highly-effective-people.md",
    "chars": 162,
    "preview": "+++\ndate = \"2016-02-14T19:11:05+05:30\"\ndraft = false\ntitle = \"seven habbits of highly effective people\"\nimage = \"seven-h"
  },
  {
    "path": "07-hugo/bookshelf/layouts/_default/li.html",
    "chars": 237,
    "preview": "<article class=\"li\">\n  <a href=\"{{ .Permalink }}\" class=\"clearfix\">\n    <div class=\"image\" style=\"background-image: url("
  },
  {
    "path": "07-hugo/bookshelf/layouts/index.html",
    "chars": 491,
    "preview": "<div class=\"index\">\n  {{ partial \"default_head.html\" . }}\n  <div class=\"row\">\n    <div class=\"col-sm-9\">\n\n      <div cla"
  },
  {
    "path": "07-hugo/bookshelf/layouts/partials/default_foot.html",
    "chars": 1260,
    "preview": "    </div>\n\n    <footer class=\"site\">\n      <p>{{ with .Site.Copyright | safeHTML }}{{ . }}{{ else }}&copy; {{ $.Site.La"
  },
  {
    "path": "07-hugo/bookshelf/layouts/partials/default_head.html",
    "chars": 1760,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    {{ .Hugo.Generator }}\n\n    <meta name=\"viewport\" content="
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/.gitignore",
    "chars": 8,
    "preview": "public/\n"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/LICENSE.md",
    "chars": 1083,
    "preview": "# The MIT License (MIT)\n\nCopyright (c) 2015 Daisuke Tsuji.\n\nPermission is hereby granted, free of charge, to any person "
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/README.md",
    "chars": 1207,
    "preview": "![screenshot](https://raw.githubusercontent.com/dim0627/hugo_theme_aglaus/master/images/screenshot.png)\n\n# Features\n\n* G"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/config.yaml",
    "chars": 512,
    "preview": "BaseURL: \"http://example.com\"\nLanguageCode: \"en-us\"\nTitle: \"Robust\"\n\nParams:\n  Author: \"Your name.\"\n  Birth: \"Sun, Feb 2"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/_default/li.html",
    "chars": 493,
    "preview": "<article class=\"li\">\n  <a href=\"{{ .Permalink }}\" class=\"clearfix\">\n    <div class=\"image\" style=\"background-image: url("
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/_default/list.html",
    "chars": 585,
    "preview": "<div class=\"list\">\n  {{ partial \"default_head.html\" . }}\n  <div class=\"row\">\n    <div class=\"col-sm-9\">\n\n      <header c"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/_default/single.html",
    "chars": 4407,
    "preview": "<div class=\"single\">\n  {{ partial \"default_head.html\" . }}\n\n  <div class=\"row\">\n    <div class=\"col-sm-9\">\n\n      <artic"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/_default/terms.html",
    "chars": 473,
    "preview": "<div class=\"list\">\n  {{ partial \"default_head.html\" . }}\n  <div class=\"row\">\n    <div class=\"col-sm-9\">\n\n      <header c"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/index.html",
    "chars": 567,
    "preview": "<div class=\"index\">\n  {{ partial \"default_head.html\" . }}\n  <div class=\"row\">\n    <div class=\"col-sm-9\">\n\n      <div cla"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/partials/default_foot.html",
    "chars": 1443,
    "preview": "    </div>\n\n    <footer class=\"site\">\n      <p>{{ with .Site.Copyright | safeHTML }}{{ . }}{{ else }}&copy; {{ $.Site.La"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/partials/default_head.html",
    "chars": 1762,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    {{ .Hugo.Generator }}\n\n    <meta name=\"viewport\" content="
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/partials/pagination.html",
    "chars": 364,
    "preview": "{{ if or (.Paginator.HasPrev) (.Paginator.HasNext) }}\n<nav class=\"paging\">\n  {{ if .Paginator.HasPrev }}\n  <a href=\"{{ ."
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/partials/sidebar.html",
    "chars": 1376,
    "preview": "<aside class=\"site\">\n\n  {{ if and .IsPage (ne .Params.toc false) }}\n  <div class=\"section\">\n    <header><div class=\"titl"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/layouts/rss.xml",
    "chars": 762,
    "preview": "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n  <channel>\n    <title>{{ .Site.Title }} </title>\n    <link"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/static/css/custom.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/static/css/styles.css",
    "chars": 7577,
    "preview": "html {\n  font-size: 16px;\n}\n\nbody {\n  font-family: 'Open Sans', \"ヒラギノ角ゴシック Pro\", \"Hiragino Kaku Gothic Pro\", メイリオ, Meiry"
  },
  {
    "path": "07-hugo/bookshelf/themes/hugo_theme_robust/theme.toml",
    "chars": 300,
    "preview": "name = \"Robust\"\nlicense = \"MIT\"\nlicenselink = \"https://github.com/dim0627/hugo_theme_robust/blob/master/LICENSE.md\"\ndesc"
  },
  {
    "path": "07-hugo/bookshelf-public/404.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "07-hugo/bookshelf-public/categories/index.html",
    "chars": 4802,
    "preview": "<div class=\"list\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hugo"
  },
  {
    "path": "07-hugo/bookshelf-public/css/custom.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "07-hugo/bookshelf-public/css/styles.css",
    "chars": 7577,
    "preview": "html {\n  font-size: 16px;\n}\n\nbody {\n  font-family: 'Open Sans', \"ヒラギノ角ゴシック Pro\", \"Hiragino Kaku Gothic Pro\", メイリオ, Meiry"
  },
  {
    "path": "07-hugo/bookshelf-public/index.html",
    "chars": 4482,
    "preview": "<div class=\"index\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hug"
  },
  {
    "path": "07-hugo/bookshelf-public/index.xml",
    "chars": 3453,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n  "
  },
  {
    "path": "07-hugo/bookshelf-public/page/1/index.html",
    "chars": 271,
    "preview": "<!DOCTYPE html><html><head><link rel=\"canonical\" href=\"https://shekhargulati.github.io/bookshelf/\"/><meta http-equiv=\"co"
  },
  {
    "path": "07-hugo/bookshelf-public/post/art-of-thinking-clearly/index.html",
    "chars": 9186,
    "preview": "<div class=\"single\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hu"
  },
  {
    "path": "07-hugo/bookshelf-public/post/confessions-of-a-public-speaker/index.html",
    "chars": 9320,
    "preview": "<div class=\"single\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hu"
  },
  {
    "path": "07-hugo/bookshelf-public/post/good-to-great/index.html",
    "chars": 9811,
    "preview": "<div class=\"single\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hu"
  },
  {
    "path": "07-hugo/bookshelf-public/post/hen-who-dreamed-she-could-fly/index.html",
    "chars": 10054,
    "preview": "<div class=\"single\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hu"
  },
  {
    "path": "07-hugo/bookshelf-public/post/index.html",
    "chars": 6674,
    "preview": "<div class=\"list\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hugo"
  },
  {
    "path": "07-hugo/bookshelf-public/post/index.xml",
    "chars": 3458,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n  "
  },
  {
    "path": "07-hugo/bookshelf-public/post/page/1/index.html",
    "chars": 281,
    "preview": "<!DOCTYPE html><html><head><link rel=\"canonical\" href=\"https://shekhargulati.github.io/bookshelf/post/\"/><meta http-equi"
  },
  {
    "path": "07-hugo/bookshelf-public/post/seven-habbits-of-highly-effective-people/index.html",
    "chars": 9467,
    "preview": "<div class=\"single\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hu"
  },
  {
    "path": "07-hugo/bookshelf-public/sitemap.xml",
    "chars": 1085,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n  "
  },
  {
    "path": "07-hugo/bookshelf-public/tags/index.html",
    "chars": 4778,
    "preview": "<div class=\"list\">\n  <!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"generator\" content=\"Hugo"
  },
  {
    "path": "08-coreos/README.md",
    "chars": 17098,
    "preview": "CoreOS for Application Developers\n----\n\nWelcome to eighth week of [52 Technologies in 2016](https://github.com/shekhargu"
  },
  {
    "path": "09-cloudvision/README.md",
    "chars": 21218,
    "preview": "Realtime People Counter with Google's Cloud Vision API and RxJava\n-----\n\nWelcome to the ninth blog of [52 Technologies i"
  },
  {
    "path": "09-cloudvision/people-counter/.gitignore",
    "chars": 1315,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### Gradle template\n.gradle\nbuild/\n\n# Ignore Gradle GUI config\ngradle-app"
  },
  {
    "path": "09-cloudvision/people-counter/build.gradle",
    "chars": 387,
    "preview": "group 'com.shekhargulati.52tech'\nversion '1.0-SNAPSHOT'\n\napply plugin: 'java'\n\nsourceCompatibility = 1.8\n\nrepositories {"
  },
  {
    "path": "09-cloudvision/people-counter/gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Sun Feb 28 16:45:51 IST 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "09-cloudvision/people-counter/gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "09-cloudvision/people-counter/gradlew.bat",
    "chars": 2314,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "09-cloudvision/people-counter/settings.gradle",
    "chars": 37,
    "preview": "rootProject.name = 'people-counter'\n\n"
  },
  {
    "path": "09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/FaceDetector.java",
    "chars": 1620,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport com.google.api.services.vision.v1.Vision;\nimport com.google.api.service"
  },
  {
    "path": "09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/GoogleVisionServiceFactory.java",
    "chars": 1043,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport com.google.api.client.googleapis.auth.oauth2.GoogleCredential;\nimport c"
  },
  {
    "path": "09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/ImagePeopleCount.java",
    "chars": 987,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport java.util.Objects;\n\npublic class ImagePeopleCount {\n\n    private final "
  },
  {
    "path": "09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/ImageWriter.java",
    "chars": 2411,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport com.google.api.services.vision.v1.model.FaceAnnotation;\nimport com.goog"
  },
  {
    "path": "09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/PeopleCounter.java",
    "chars": 2298,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport com.google.api.services.vision.v1.Vision;\nimport com.google.api.service"
  },
  {
    "path": "09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/PeopleCounterApp.java",
    "chars": 1193,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport rx.Observable;\nimport twitter4j.MediaEntity;\nimport twitter4j.Status;\n\n"
  },
  {
    "path": "09-cloudvision/people-counter/src/main/java/com/shekhargulati/peoplecounter/TweetObservable.java",
    "chars": 838,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport rx.Observable;\nimport twitter4j.*;\n\npublic final class TweetObservable "
  },
  {
    "path": "09-cloudvision/people-counter/src/test/java/com/shekhargulati/peoplecounter/FaceDetectorTest.java",
    "chars": 1618,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport com.google.api.services.vision.v1.model.FaceAnnotation;\nimport org.juni"
  },
  {
    "path": "09-cloudvision/people-counter/src/test/java/com/shekhargulati/peoplecounter/PeopleCounterTest.java",
    "chars": 1198,
    "preview": "package com.shekhargulati.peoplecounter;\n\nimport org.junit.Test;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;"
  },
  {
    "path": "09-cloudvision/people-counter/src/test/resources/response.json",
    "chars": 5967,
    "preview": "{\n  \"angerLikelihood\": \"VERY_UNLIKELY\",\n  \"blurredLikelihood\": \"VERY_UNLIKELY\",\n  \"boundingPoly\": {\n    \"vertices\": [\n  "
  },
  {
    "path": "10-gatling/README.md",
    "chars": 11662,
    "preview": "Gatling: The Ultimate Load Testing Tools for Programmers\n--------\n\nWelcome to the tenth blog of [52 Technologies in 2016"
  },
  {
    "path": "10-gatling/blog/.gitignore",
    "chars": 1338,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### JetBrains template\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpSt"
  },
  {
    "path": "10-gatling/blog/build.sbt",
    "chars": 281,
    "preview": "name := \"blog-load-tests\"\nversion := \"0.1.0\"\n\nscalaVersion := \"2.11.7\"\n\nenablePlugins(GatlingPlugin)\n\nlibraryDependencie"
  },
  {
    "path": "10-gatling/blog/project/plugins.sbt",
    "chars": 53,
    "preview": "addSbtPlugin(\"io.gatling\" % \"gatling-sbt\" % \"2.1.5\")\n"
  },
  {
    "path": "10-gatling/blog/src/test/scala/loadtests/AccessHomePageSimulation.scala",
    "chars": 718,
    "preview": "package loadtests\n\nimport io.gatling.core.Predef._\nimport io.gatling.http.Predef._\nimport scala.concurrent.duration._\n\nc"
  },
  {
    "path": "11-textblob/README.md",
    "chars": 9934,
    "preview": "Sentiment Analysis in Python with TextBlob\n-----\n\nWelcome to the eleventh blog of [52 Technologies in 2016](https://gith"
  },
  {
    "path": "11-textblob/sentiment-analyzer/.gitignore",
    "chars": 6,
    "preview": "venv/\n"
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/action_hooks/README.md",
    "chars": 171,
    "preview": "For information about action hooks supported by OpenShift, consult the documentation:\n\nhttp://openshift.github.io/docume"
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/action_hooks/deploy",
    "chars": 190,
    "preview": "#!/bin/bash\nset -x\nexport NLTK_DATA=$OPENSHIFT_DATA_DIR\necho \"YOU ARE IN PRE_START HOOK\"\necho $NLTK_DATA\n. $VIRTUAL_ENV/"
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/README.cron",
    "chars": 1217,
    "preview": "Run scripts or jobs on a periodic basis\n=======================================\nAny scripts or jobs added to the minutel"
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/daily/.gitignore",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/hourly/.gitignore",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/minutely/.gitignore",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/monthly/.gitignore",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/weekly/README",
    "chars": 744,
    "preview": "Run scripts or jobs on a weekly basis\n=====================================\nAny scripts or jobs added to this directory "
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/weekly/chronograph",
    "chars": 63,
    "preview": "#!/bin/bash\n\necho \"`date`: `cat $(dirname \\\"$0\\\")/chrono.dat`\"\n"
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/weekly/jobs.allow",
    "chars": 404,
    "preview": "#\n#  Script or job files listed in here (one entry per line) will be\n#  executed on a weekly-basis.\n#\n#  Example: The ch"
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/cron/weekly/jobs.deny",
    "chars": 132,
    "preview": "#\n#  Any script or job files listed in here (one entry per line) will NOT be\n#  executed (read as ignored by run-parts)."
  },
  {
    "path": "11-textblob/sentiment-analyzer/.openshift/markers/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "11-textblob/sentiment-analyzer/requirements.txt",
    "chars": 98,
    "preview": "Flask==0.10.1\nJinja2==2.7.2\nMarkupSafe==0.23\nWerkzeug==0.9.4\ndistribute==0.7.3\nitsdangerous==0.24\n"
  },
  {
    "path": "11-textblob/sentiment-analyzer/sentimentanalyzer.py",
    "chars": 461,
    "preview": "from flask import Flask , jsonify, render_template, request\nfrom textblob import TextBlob\n\napp = Flask(__name__)\n\n@app.r"
  },
  {
    "path": "11-textblob/sentiment-analyzer/static/css/bootstrap-theme.css",
    "chars": 19791,
    "preview": "/*!\n * Bootstrap v3.0.1 by @fat and @mdo\n * Copyright 2013 Twitter, Inc.\n * Licensed under http://www.apache.org/license"
  },
  {
    "path": "11-textblob/sentiment-analyzer/static/css/bootstrap.css",
    "chars": 125975,
    "preview": "/*!\n * Bootstrap v3.0.1 by @fat and @mdo\n * Copyright 2013 Twitter, Inc.\n * Licensed under http://www.apache.org/license"
  },
  {
    "path": "11-textblob/sentiment-analyzer/static/js/jquery.js",
    "chars": 242142,
    "preview": "/*!\n * jQuery JavaScript Library v2.0.3\n * http://jquery.com/\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n *\n * Cop"
  },
  {
    "path": "11-textblob/sentiment-analyzer/templates/index.html",
    "chars": 3293,
    "preview": "<html>\n<head>\n\t<title>Sentiment Analyzer: Built using Text Blob</title>\n\t<meta name=\"viewport\" content=\"width=device-wid"
  },
  {
    "path": "11-textblob/sentiment-analyzer/wsgi.py",
    "chars": 71,
    "preview": "#!/usr/bin/env python\nfrom sentimentanalyzer import app as application\n"
  },
  {
    "path": "11-tweet-deduplication/README.md",
    "chars": 6848,
    "preview": "Tweet Deduplication\n---\n\nWelcome to the eleventh blog of [52 Technologies in 2016](https://github.com/shekhargulati/52-t"
  },
  {
    "path": "12-play/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "13-arangodb/README.md",
    "chars": 18780,
    "preview": "ArangoDB: Polyglot Persistence Without Cost\n------\n\nWelcome to thirteenth week of [52 Technologies in 2016](https://gith"
  },
  {
    "path": "13-arangodb/localjobs/jobs.json",
    "chars": 39133,
    "preview": "{\"company\":{\"id\":\"21836\",\"name\":\"CyberCoders\"},\"title\":\"Ruby on Rails Engineer - PostgreSQL, MongoDB, Redis, HTML, CSS\","
  },
  {
    "path": "14-kafka/README.md",
    "chars": 429,
    "preview": "Apache Kafka\n---\n\nI will write on Apache Kafka this weekend so stay tuned.\n\n----\nPlease provide your valuable feedback b"
  },
  {
    "path": "15-huginn/README.md",
    "chars": 17861,
    "preview": "Airline Bot Platform with Huginn\n-----\n\nWelcome to fifteenth week of [52 Technologies in 2016](https://github.com/shekha"
  },
  {
    "path": "16-newspaper/README.md",
    "chars": 13244,
    "preview": "Building `Read It Later` App with Python Newspaper Library\n-----\n\nWelcome to sixteenth week of [52 Technologies in 2016]"
  },
  {
    "path": "17-typescript/README.md",
    "chars": 15027,
    "preview": "Let's Learn TypeScript\n-------\n\nWelcome to seventeenth week of [52 Technologies in 2016](https://github.com/shekhargulat"
  },
  {
    "path": "17-typescript/code/getting-started.ts",
    "chars": 4035,
    "preview": "// Basic Types\nvar votes = 10;\nvotes++;\nconsole.log(votes);\n\nvar visible: boolean = true;\nlet storyExists:boolean = fals"
  },
  {
    "path": "17-typescript/code/js/getting-started.js",
    "chars": 4409,
    "preview": "var __extends = (this && this.__extends) || function (d, b) {\n    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];"
  },
  {
    "path": "17-typescript/code/tsconfig.json",
    "chars": 105,
    "preview": "{\n    \"compilerOptions\": {\n        \"outDir\": \"js\",\n        \"target\": \"es5\",\n        \"watch\": true\n    }\n}"
  },
  {
    "path": "18-mesos/README.md",
    "chars": 12408,
    "preview": "Getting Started with Apache Mesos\n----\n\nWelcome to eighteenth week of [52 Technologies in 2016](https://github.com/shekh"
  },
  {
    "path": "19-bees/README.md",
    "chars": 5260,
    "preview": "Load testing with bees\n---\n\nWelcome to the nineteenth blog of [52-technologies-in-2016](https://github.com/shekhargulati"
  },
  {
    "path": "20-json/README.md",
    "chars": 6821,
    "preview": "5 open source projects that will make working with JSON awesome and fun\n-------\n\nWelcome to twentieth week of [52 Techno"
  },
  {
    "path": "20-json/code/db.json",
    "chars": 488,
    "preview": "{\n  \"tasklists\": [\n    {\n      \"id\": 1,\n      \"name\": \"Todo\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"In Progress\"\n   "
  },
  {
    "path": "21-strman/README.md",
    "chars": 9551,
    "preview": "strman - A Java 8 String manipulation library\n------\n\nA Java 8 library for working with String. It is inspired by [dleit"
  },
  {
    "path": "22-regex/README.md",
    "chars": 10670,
    "preview": "Making Sense of Regular Expressions\n-----\n\nWelcome to the twenty second blog of [52-technologies-in-2016](https://github"
  },
  {
    "path": "22-regex/code/.gitignore",
    "chars": 1315,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### JetBrains template\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpSt"
  },
  {
    "path": "22-regex/code/build.gradle",
    "chars": 296,
    "preview": "group 'com.52tech'\nversion '1.0-SNAPSHOT'\n\napply plugin: 'java'\napply plugin: 'idea'\n\nsourceCompatibility = 1.8\n\nreposit"
  },
  {
    "path": "22-regex/code/gradle/wrapper/gradle-wrapper.properties",
    "chars": 230,
    "preview": "#Sat Jun 04 15:59:37 IST 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "22-regex/code/gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "22-regex/code/gradlew.bat",
    "chars": 2314,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
  },
  {
    "path": "22-regex/code/settings.gradle",
    "chars": 35,
    "preview": "rootProject.name = 'week22-regex'\n\n"
  },
  {
    "path": "22-regex/code/src/main/java/week22/regex/GaddafiSpellingMatcher.java",
    "chars": 741,
    "preview": "package week22.regex;\n\nimport ru.lanwen.verbalregex.VerbalExpression;\n\npublic class GaddafiSpellingMatcher {\n\n    public"
  },
  {
    "path": "22-regex/code/src/test/java/week22/regex/GaddafiSpellingMatcherTest.java",
    "chars": 2518,
    "preview": "package week22.regex;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertTrue;\nimport static week22.regex.Gad"
  },
  {
    "path": "23-android-part1/README.md",
    "chars": 27569,
    "preview": "Building An Android Application To Track Missing Kids Part 1\n-----\n\nYesterday, I read an article which talked about an a"
  },
  {
    "path": "24-jekyll-to-wordpress/README.md",
    "chars": 15758,
    "preview": "Saved My Ass: Moving back to WordPress from Jekyll\n----------\n\nWelcome to week 24 of [52-technology-in-2016](https://git"
  },
  {
    "path": "25-angular-dragula/README.md",
    "chars": 12601,
    "preview": "Trello Clone with Angular Dragula\n----\n\nWelcome to twenty-fifth blog of [52-technologies-in-2016](https://github.com/she"
  },
  {
    "path": "25-angular-dragula/trello/.bowerrc",
    "chars": 38,
    "preview": "{\n  \"directory\": \"bower_components\"\n}\n"
  },
  {
    "path": "25-angular-dragula/trello/.editorconfig",
    "chars": 214,
    "preview": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_tr"
  },
  {
    "path": "25-angular-dragula/trello/.eslintrc",
    "chars": 263,
    "preview": "{\n  \"extends\": \"eslint:recommended\",\n  \"plugins\": [\"angular\"],\n  \"env\": {\n    \"es6\": true,\n    \"browser\": true,\n    \"jas"
  },
  {
    "path": "25-angular-dragula/trello/.gitignore",
    "chars": 74,
    "preview": "node_modules/\nbower_components/\ncoverage/\n.sass-cache/\n.idea/\n.tmp/\ndist/\n"
  },
  {
    "path": "25-angular-dragula/trello/.yo-rc.json",
    "chars": 1563,
    "preview": "{\n  \"generator-gulp-angular\": {\n    \"version\": \"1.1.0\",\n    \"props\": {\n      \"angularVersion\": \"~1.5.3\",\n      \"angularM"
  },
  {
    "path": "25-angular-dragula/trello/bower.json",
    "chars": 965,
    "preview": "{\n  \"name\": \"trello\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {\n    \"angular-animate\": \"~1.5.3\",\n    \"angular-cookies\": "
  },
  {
    "path": "25-angular-dragula/trello/e2e/.eslintrc",
    "chars": 116,
    "preview": "{\n  \"globals\": {\n    \"browser\": false,\n    \"element\": false,\n    \"by\": false,\n    \"$\": false,\n    \"$$\": false\n  }\n}\n"
  },
  {
    "path": "25-angular-dragula/trello/e2e/main.po.js",
    "chars": 501,
    "preview": "/**\n * This file uses the Page Object pattern to define the main page for tests\n * https://docs.google.com/presentation/"
  },
  {
    "path": "25-angular-dragula/trello/e2e/main.spec.js",
    "chars": 572,
    "preview": "'use strict';\n\ndescribe('The main view', function () {\n  var page;\n\n  beforeEach(function () {\n    browser.get('/index.h"
  },
  {
    "path": "25-angular-dragula/trello/gulp/.eslintrc",
    "chars": 36,
    "preview": "{\n  \"env\": {\n    \"node\": true\n  }\n}\n"
  },
  {
    "path": "25-angular-dragula/trello/gulp/build.js",
    "chars": 2975,
    "preview": "'use strict';\n\nvar path = require('path');\nvar gulp = require('gulp');\nvar conf = require('./conf');\n\nvar $ = require('g"
  },
  {
    "path": "25-angular-dragula/trello/gulp/conf.js",
    "chars": 974,
    "preview": "/**\n *  This file contains the variables used in other gulp files\n *  which defines tasks\n *  By design, we only put the"
  },
  {
    "path": "25-angular-dragula/trello/gulp/e2e-tests.js",
    "chars": 1074,
    "preview": "'use strict';\n\nvar path = require('path');\nvar gulp = require('gulp');\nvar conf = require('./conf');\n\nvar browserSync = "
  },
  {
    "path": "25-angular-dragula/trello/gulp/inject.js",
    "chars": 993,
    "preview": "'use strict';\n\nvar path = require('path');\nvar gulp = require('gulp');\nvar conf = require('./conf');\n\nvar $ = require('g"
  },
  {
    "path": "25-angular-dragula/trello/gulp/scripts.js",
    "chars": 1725,
    "preview": "'use strict';\n\nvar path = require('path');\nvar gulp = require('gulp');\nvar conf = require('./conf');\n\nvar browserSync = "
  },
  {
    "path": "25-angular-dragula/trello/gulp/server.js",
    "chars": 1766,
    "preview": "'use strict';\n\nvar path = require('path');\nvar gulp = require('gulp');\nvar conf = require('./conf');\n\nvar browserSync = "
  },
  {
    "path": "25-angular-dragula/trello/gulp/unit-tests.js",
    "chars": 1150,
    "preview": "'use strict';\n\nvar path = require('path');\nvar gulp = require('gulp');\nvar conf = require('./conf');\n\nvar karma = requir"
  },
  {
    "path": "25-angular-dragula/trello/gulp/watch.js",
    "chars": 693,
    "preview": "'use strict';\n\nvar path = require('path');\nvar gulp = require('gulp');\nvar conf = require('./conf');\n\nvar browserSync = "
  },
  {
    "path": "25-angular-dragula/trello/gulpfile.js",
    "chars": 676,
    "preview": "/**\n *  Welcome to your gulpfile!\n *  The gulp tasks are split into several files in the gulp directory\n *  because putt"
  },
  {
    "path": "25-angular-dragula/trello/karma.conf.js",
    "chars": 2400,
    "preview": "'use strict';\n\nvar path = require('path');\nvar conf = require('./gulp/conf');\n\nvar _ = require('lodash');\nvar wiredep = "
  },
  {
    "path": "25-angular-dragula/trello/package.json",
    "chars": 1590,
    "preview": "{\n  \"name\": \"trello\",\n  \"version\": \"0.0.0\",\n  \"dependencies\": {},\n  \"scripts\": {\n    \"test\": \"gulp test\"\n  },\n  \"devDepe"
  },
  {
    "path": "25-angular-dragula/trello/protractor.conf.js",
    "chars": 746,
    "preview": "'use strict';\n\nvar paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths;\n\n// An example configuration "
  },
  {
    "path": "25-angular-dragula/trello/src/app/index.config.js",
    "chars": 345,
    "preview": "export function config ($logProvider, toastrConfig) {\n  'ngInject';\n  // Enable log\n  $logProvider.debugEnabled(true);\n\n"
  },
  {
    "path": "25-angular-dragula/trello/src/app/index.css",
    "chars": 170,
    "preview": ".browsehappy {\n  margin: 0.2em 0;\n  background: #ccc;\n  color: #000;\n  padding: 0.2em 0;\n}\n\n.thumbnail {\n  height: 200px"
  },
  {
    "path": "25-angular-dragula/trello/src/app/index.module.js",
    "chars": 402,
    "preview": "/* global malarkey:false, moment:false */\n\nimport { config } from './index.config';\nimport { runBlock } from './index.ru"
  },
  {
    "path": "25-angular-dragula/trello/src/app/index.run.js",
    "chars": 80,
    "preview": "export function runBlock ($log) {\n  'ngInject';\n  $log.debug('runBlock end');\n}\n"
  },
  {
    "path": "25-angular-dragula/trello/src/app/main/main.controller.js",
    "chars": 1099,
    "preview": "export class MainController {\n  constructor($scope) {\n    'ngInject';\n\n    $scope.$on('bag.drop', function(e, el, target"
  },
  {
    "path": "25-angular-dragula/trello/src/app/main/main.controller.spec.js",
    "chars": 989,
    "preview": "describe('controllers', () => {\n  let vm;\n\n  beforeEach(angular.mock.module('trello'));\n\n  beforeEach(inject(($controlle"
  },
  {
    "path": "25-angular-dragula/trello/src/index.html",
    "chars": 2097,
    "preview": "<!doctype html>\n<html ng-app=\"trello\" ng-strict-di>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>trello</title>\n    <m"
  },
  {
    "path": "26-android-part2/README.md",
    "chars": 20042,
    "preview": "Building An Android Application Part 2\n---\n\nWelcome to twenty sixth post of [52-technologies-in-2016](https://github.com"
  },
  {
    "path": "27-learn-golang-for-great-good/README.md",
    "chars": 26161,
    "preview": "Learn GoLang For Great Good -- Part 1\n---------\n\nWelcome to twenty seventh post of [52-technologies-in-2016](https://git"
  },
  {
    "path": "27-learn-golang-for-great-good/programs/closestpair.go",
    "chars": 440,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"sort\"\n)\n\nfunc closestPair(numbers []int) (int, int) {\n\tsort.Ints(numbers)\n\tvar n"
  }
]

// ... and 216 more files (download for full content)

About this extraction

This page contains the full source code of the shekhargulati/52-technologies-in-2016 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 416 files (65.0 MB), approximately 2.4M tokens, and a symbol index with 2194 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!