Full Code of zhangxin840/tomato5 for AI

master 8dfba19fb619 cached
56 files
267.0 KB
81.0k tokens
13 symbols
1 requests
Download .txt
Showing preview only (283K chars total). Download the full file or copy to clipboard to get everything.
Repository: zhangxin840/tomato5
Branch: master
Commit: 8dfba19fb619
Files: 56
Total size: 267.0 KB

Directory structure:
gitextract_6_u3udeh/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .firebaserc
├── .gitignore
├── LICENSE
├── README.md
├── build/
│   ├── build.js
│   ├── dev-client.js
│   ├── dev-server.js
│   ├── utils.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── config/
│   ├── dev.env.js
│   ├── index.js
│   ├── prod.env.js
│   └── test.env.js
├── dashboard.html
├── deploy.sh
├── firebase.json
├── index.html
├── package.json
├── src/
│   ├── App.vue
│   ├── auth.js
│   ├── base.scss
│   ├── common.scss
│   ├── components/
│   │   ├── Account.vue
│   │   ├── ActiveTask.vue
│   │   ├── Emotion.vue
│   │   ├── Member.vue
│   │   ├── Panel.vue
│   │   ├── Task.vue
│   │   ├── TeamPanel.vue
│   │   └── Usage.vue
│   ├── configs.js
│   ├── dashboard.js
│   ├── dataBase.js
│   ├── database.js
│   ├── index.js
│   ├── index.scss
│   ├── model.js
│   ├── timer.js
│   └── utils.js
├── static/
│   └── .gitkeep
├── test/
│   ├── e2e/
│   │   ├── custom-assertions/
│   │   │   └── elementCount.js
│   │   ├── nightwatch.conf.js
│   │   ├── runner.js
│   │   └── specs/
│   │       └── test.js
│   └── unit/
│       ├── .eslintrc
│       ├── index.js
│       ├── karma.conf.js
│       └── specs/
│           └── Hello.spec.js
└── vendors/
    ├── firebase-ui-auth.css
    └── firebase-ui-auth.js

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

================================================
FILE: .babelrc
================================================
{
  "presets": ["es2015", "stage-2"],
  "plugins": ["transform-runtime"],
  "comments": false
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true


================================================
FILE: .eslintignore
================================================
build/*.js
config/*.js
vendors/*.js


================================================
FILE: .eslintrc.js
================================================
module.exports = {
  root: true,
  parserOptions: {
    sourceType: 'module'
  },
  extends: 'airbnb-base',
  // required to lint *.vue files
  plugins: [
    'html'
  ],
  // add your custom rules here
  'rules': {
    'import/no-unresolved': 0,
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
  }
}


================================================
FILE: .firebaserc
================================================
{
  "projects": {
    "default": "tomato5-685bf"
  }
}


================================================
FILE: .gitignore
================================================
.DS_Store
node_modules/
dist/
npm-debug.log
selenium-debug.log
test/unit/coverage
test/e2e/reports
firebase-debug.log


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 Zhang Xin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
![logo](http://tomato5.io/static/icons/logo.png)

Tomato5 is a real-time collaboration tool.
It combines Pomodoro Technique with a team status share board.

Homepage: [http://tomato5.io](http://tomato5.io)

## Updates

### 1.3.0

- New feature: Show speech bubble in team board.

### 1.2.0

- New feature: Give thumbs up to team members.
- New feature: Headline for team board.

## Features

- Adapt to all kinds of screen sizes.
- Real-time data sync.
- Serverless architecture, powered by GCP and AWS.

## Responsive web design

![responsive](http://tomato5.io/static/promotions/responsive.gif)

## Serverless

We build Tomato5 as the 'Serverless' architecture.
All services run on the cloud, without any server of its own.
 
Front-end part of this system is just static files hosted on Google's CDN.
The web app talks directly to the Realtime Database of Firebase.

The whole account system, including login UI, is also provided by Firebase.

With the full support of cloud services, we can build real-world products at lowest development costs, and get free from most of the maintaining works.

## Tech Stack

- Responsive web design
- ES6
- Vue
- Webpack
- Firebase
- AWS Lambda

## The name Tomato5

- 5 minutes break after 25 minutes concentration
- 5 tomatoes a day
- 5 team members

## Real-time collaboration

We believe that it is important for the team to share everyone's status.

Traditional collaboration tools only tell the team what to achieve, without concern about individual's actual status.

By showing what we are doing and thinking in real-time, we can express ourselves more adequately, be more connected to the team, and get more feedbacks on time.

Real-time collaboration means to share the team status in real-time.

Key points:
- Concern about individuals
- Expressional
- Instant Feedbacks

## Develop

``` bash
# Install dependencies
npm install

# Serve with hot reload at localhost:8080
npm run dev

# Build for production
npm run build

# Deploy to Firebase
bash deploy.sh
```


================================================
FILE: build/build.js
================================================
// https://github.com/shelljs/shelljs
require('shelljs/global')
env.NODE_ENV = 'production'

var path = require('path')
var config = require('../config')
var ora = require('ora')
var webpack = require('webpack')
var webpackConfig = require('./webpack.prod.conf')

console.log(
  '  Tip:\n' +
  '  Built files are meant to be served over an HTTP server.\n' +
  '  Opening index.html over file:// won\'t work.\n'
)

var spinner = ora('building for production...')
spinner.start()

var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
rm('-rf', config.build.assetsRoot) // https://github.com/vuejs-templates/webpack/issues/362
mkdir('-p', assetsPath)
cp('-R', 'static/', assetsPath)

webpack(webpackConfig, function (err, stats) {
  spinner.stop()
  if (err) throw err
  process.stdout.write(stats.toString({
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }) + '\n')
})


================================================
FILE: build/dev-client.js
================================================
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')

hotClient.subscribe(function (event) {
  if (event.action === 'reload') {
    window.location.reload()
  }
})


================================================
FILE: build/dev-server.js
================================================
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var config = require('../config')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = process.env.NODE_ENV === 'testing'
  ? require('./webpack.prod.conf')
  : require('./webpack.dev.conf')

// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
})

var hotMiddleware = require('webpack-hot-middleware')(compiler)
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(context, options))
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
var staticPath = path.posix.join(config.build.assetsPublicPath, config.build.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }
  console.log('Listening at http://localhost:' + port + '\n')
})


================================================
FILE: build/utils.js
================================================
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')

exports.assetsPath = function (_path) {
  return path.posix.join(config.build.assetsSubDirectory, _path)
}

exports.cssLoaders = function (options) {
  options = options || {}
  // generate loader string to be used with extract text plugin
  function generateLoaders (loaders) {
    var sourceLoader = loaders.map(function (loader) {
      var extraParamChar
      if (/\?/.test(loader)) {
        loader = loader.replace(/\?/, '-loader?')
        extraParamChar = '&'
      } else {
        loader = loader + '-loader'
        extraParamChar = '?'
      }
      return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
    }).join('!')

    if (options.extract) {
      return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
    } else {
      return ['vue-style-loader', sourceLoader].join('!')
    }
  }

  // http://vuejs.github.io/vue-loader/configurations/extract-css.html
  return {
    css: generateLoaders(['css']),
    postcss: generateLoaders(['css']),
    less: generateLoaders(['css', 'less']),
    sass: generateLoaders(['css', 'sass?indentedSyntax']),
    scss: generateLoaders(['css', 'sass']),
    stylus: generateLoaders(['css', 'stylus']),
    styl: generateLoaders(['css', 'stylus'])
  }
}

// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
  var output = []
  var loaders = exports.cssLoaders(options)
  for (var extension in loaders) {
    var loader = loaders[extension]
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      loader: loader
    })
  }
  return output
}


================================================
FILE: build/webpack.base.conf.js
================================================
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')

module.exports = {
  entry: {
    dashboard: './src/dashboard.js',
    index: './src/index.js'
  },
  output: {
    path: config.build.assetsRoot,
    publicPath: config.build.assetsPublicPath,
    filename: '[name].js'
  },
  resolve: {
    extensions: ['', '.js', '.vue'],
    fallback: [path.join(__dirname, '../node_modules')],
    alias: {
      'src': path.resolve(__dirname, '../src'),
      'assets': path.resolve(__dirname, '../src/assets'),
      'components': path.resolve(__dirname, '../src/components')
    }
  },
  resolveLoader: {
    fallback: [path.join(__dirname, '../node_modules')]
  },
  module: {
    preLoaders: [
      {
        test: /\.vue$/,
        loader: 'eslint',
        include: projectRoot,
        exclude: /node_modules/
      },
      {
        test: /\.js$/,
        loader: 'eslint',
        include: projectRoot,
        exclude: /node_modules/
      }
    ],
    loaders: [
      {
        test: /\.vue$/,
        loader: 'vue'
      },
      {
        test: /\.js$/,
        loader: 'babel',
        include: projectRoot,
        exclude: /node_modules/
      },
      {
        test: /\.json$/,
        loader: 'json'
      },
      {
        test: /\.html$/,
        loader: 'vue-html'
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url',
        query: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url',
        query: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  eslint: {
    formatter: require('eslint-friendly-formatter')
  },
  vue: {
    loaders: utils.cssLoaders()
  }
}


================================================
FILE: build/webpack.dev.conf.js
================================================
var config = require('../config')
var webpack = require('webpack')
var merge = require('webpack-merge')
var utils = require('./utils')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')

// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})

module.exports = merge(baseWebpackConfig, {
  module: {
    loaders: utils.styleLoaders()
  },
  // eval-source-map is faster for development
  devtool: '#eval-source-map',
  plugins: [
    new webpack.DefinePlugin({
      'process.env': config.dev.env
    }),
    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    new HtmlWebpackPlugin({
      filename: 'dashboard.html',
      template: 'dashboard.html',
      inject: true
    })
  ]
})


================================================
FILE: build/webpack.prod.conf.js
================================================
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var env = process.env.NODE_ENV === 'testing'
  ? require('../config/test.env')
  : config.build.env

var webpackConfig = merge(baseWebpackConfig, {
  module: {
    loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true })
  },
  devtool: config.build.productionSourceMap ? '#source-map' : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  vue: {
    loaders: utils.cssLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true
    })
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    new webpack.optimize.OccurenceOrderPlugin(),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: process.env.NODE_ENV === 'testing'
        ? 'index.html'
        : config.build.index,
      template: 'index.html',
      inject: true,
      // The index chunk is self contained to speed up the first visit
      chunks: ['index'],
      // This will cause error if using img in teamplate
      // Html will still be minified by html loader
      // https://github.com/vuejs-templates/webpack/issues/361
      // minify: {
      //   removeComments: true,
      //   collapseWhitespace: true,
      //   removeAttributeQuotes: true
      //   // more options:
      //   // https://github.com/kangax/html-minifier#options-quick-reference
      // },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    new HtmlWebpackPlugin({
      filename: process.env.NODE_ENV === 'testing' ? 'dashboard.html' : config.build.dashboard,
      template: 'dashboard.html',
      inject: true,
      chunks: ['manifest', 'vendor', 'dashboard'],
      chunksSortMode: 'dependency'
    }),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      // Only extract vendors for the main app
      // The index chunk will keep its own vendors,
      // otherwise you will have to load a large vendor chunk for first visit
      chunks: ['dashboard'],
      minChunks: function (module, count) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          // Should not only extract common js files
          // https://github.com/vuejs-templates/webpack/issues/363
          // /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      chunks: ['vendor']
    }),
    // extract css into its own file
    new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
  ]
})

if (config.build.productionGzip) {
  var CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

module.exports = webpackConfig


================================================
FILE: config/dev.env.js
================================================
var merge = require('webpack-merge')
var prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
})


================================================
FILE: config/index.js
================================================
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')

module.exports = {
  build: {
    env: require('./prod.env'),
    index: path.resolve(__dirname, '../dist/index.html'),
    dashboard: path.resolve(__dirname, '../dist/dashboard.html'),
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '', // Default set to '/'
    productionSourceMap: true,
    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css']
  },
  dev: {
    env: require('./dev.env'),
    port: 8080,
    proxyTable: {}
  }
}


================================================
FILE: config/prod.env.js
================================================
module.exports = {
  NODE_ENV: '"production"'
}


================================================
FILE: config/test.env.js
================================================
var merge = require('webpack-merge')
var devEnv = require('./dev.env')

module.exports = merge(devEnv, {
  NODE_ENV: '"testing"'
})


================================================
FILE: dashboard.html
================================================
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name= "viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="keywords" content="pomodoro technique, pomodoro timer, tomato timer, tomato5">
    <meta name="description" content="Best online tool for pomodoro technique. Concentrate for work five times a day.">
    <title>Tomato5</title>
    <link rel="icon" href="http://tomato5.io/static/icons/tomato.png">
    <link href="https://fonts.googleapis.com/css?family=Short+Stack" rel="stylesheet">
  </head>
  <body>
    <app></app>
    <script src="https://www.gstatic.com/firebasejs/ui/live/0.4/firebase-ui-auth.js"></script>
    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-82894599-1', 'auto');
      ga('send', 'pageview');
    </script>
    <!-- built files will be auto injected -->
  </body>
</html>


================================================
FILE: deploy.sh
================================================
git checkout master
npm run build
firebase deploy


================================================
FILE: firebase.json
================================================
{
  "hosting": {
    "public": "dist"
  }
}


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name= "viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="keywords" content="pomodoro technique, pomodoro timer, tomato timer, tomato5, collaboration, real-time, productivity">
    <meta name="description" content="Tomato5 is a real-time collaboration tool. It combines Pomodoro Technique with a team status board.">
    <title>Tomato5</title>
    <link rel="icon" href="http://tomato5.io/static/icons/tomato.png">
    <link href="https://fonts.googleapis.com/css?family=Short+Stack" rel="stylesheet">
  </head>
  <body>
    <article id="welcome" class="welcome">
      <section class="brand">
        <div class="title">
          <img class="logo" src="src/assets/tomato.svg">
          <h1 class="">Tomato5</h1>
        </div>
        <h2 class="slogan ">Concentrate with your team</h2>
      </section>
      <section class="indexStart">
        <a href="/dashboard.html" id="startNow" class="">Start now</a>
      </section>
    </article>
    <article id="instruction" class="introduction">
      <section class="what">
        <h2>What is Tomato5</h2>
        <p>
          Tomato5 is a real-time collaboration tool
        </p>
        <p>
          It combines Pomodoro Technique with a team status share board
        </p>
      </section>
      <section class="features">
        <h2>Features</h2>
        <div class="feature timer clearfix">
          <div class="detail">
            Set Pomodoro Timer
          </div>
          <img src="src/assets/images/timer.png" alt="timer" />
        </div>
        <div class="feature emotions right clearfix">
          <div class="detail">
            Track energy level
          </div>
          <img src="src/assets/images/emotions.png" alt="emotions" />
        </div>
        <div class="feature team clearfix">
          <div class="detail">
            Share task status
          </div>
          <img src="src/assets/images/team.png" alt="team" />
        </div>
        <div class="feature flowers right clearfix">
          <div class="detail">
            Give thumbs up
          </div>
          <img src="src/assets/images/flowers.png" alt="flowers" />
        </div>
        <div class="feature bubble clearfix">
          <div class="detail">
            Show speech bubble
          </div>
          <img src="src/assets/images/bubble.png" alt="bubble" />
        </div>
        <div class="feature metric right clearfix">
          <div class="detail">
            Analyze key metrics
          </div>
          <img src="src/assets/images/metric.png" alt="metric" />
        </div>
      </section>
      <section class="why">
        <h2>Why Tomato5?</h2>
        <p>Traditional collaboration tools only tell the team <strong>what to achieve</strong>, without concern about <strong>individual's actual status</strong>.</p>
        <p>By sharing what we are doing、thinking、and feeling in real-time, we can express ourselves more adequately, feel more connected to the team, and get more feedbacks on time.</p>
        <!-- <p>
          Tomato5 is clean and comfortable. You can easily get started with your team, or just use it for yourself to improve productivity.
        </p> -->
      </section>
      <section class="tools">
        <h2>Add Tomato5 to your productivity toolkit</h2>
        <img src="src/assets/images/tools.png" alt="tools" />
      </section>
    </article>
    <!-- <p class="instruction"><span>Concentrate for work</span> <span>five times a day</span></p> -->
    <div class="tools">
    </div>
    <article class="indexFooter">
      <div>
        <a href="https://github.com/zhangxin840/tomato5" target="_blank">Star me on Github</a>
      </div>
      <!-- <p>
        <a href="https://www.focusboosterapp.com/the-pomodoro-technique
" target="_blank">The theory behind: Pomodoro Technique</a>
      </p> -->
      <!-- <p>
        <div>Icons made by <a href="http://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>
      </p> -->
    </article>
    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-82894599-1', 'auto');
      ga('send', 'pageview');
    </script>
    <!-- built files will be auto injected -->
  </body>
</html>


================================================
FILE: package.json
================================================
{
  "name": "tomato5",
  "version": "1.3.1",
  "description": "A real-time collaboration tool. Built with Vue and Google Firebase.",
  "author": "XinZhang <zhangxin840@gmail.com>",
  "private": true,
  "scripts": {
    "dev": "node build/dev-server.js",
    "build": "node build/build.js",
    "unit": "karma start test/unit/karma.conf.js --single-run",
    "e2e": "node test/e2e/runner.js",
    "test": "npm run unit && npm run e2e",
    "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
  },
  "dependencies": {},
  "devDependencies": {
    "animate.css": "^3.5.1",
    "babel-core": "^6.0.0",
    "babel-loader": "^6.0.0",
    "babel-plugin-transform-runtime": "^6.0.0",
    "babel-preset-es2015": "^6.0.0",
    "babel-preset-stage-2": "^6.0.0",
    "babel-runtime": "^6.0.0",
    "chai": "^3.5.0",
    "chart.js": "^2.2.1",
    "chromedriver": "^2.21.2",
    "connect-history-api-fallback": "^1.1.0",
    "cross-spawn": "^2.1.5",
    "css-loader": "^0.23.0",
    "eslint": "^2.10.2",
    "eslint-config-airbnb-base": "^3.0.1",
    "eslint-friendly-formatter": "^2.0.5",
    "eslint-loader": "^1.3.0",
    "eslint-plugin-html": "^1.3.0",
    "eslint-plugin-import": "^1.8.1",
    "eventsource-polyfill": "^0.9.6",
    "express": "^4.13.3",
    "extract-text-webpack-plugin": "^1.0.1",
    "fastclick": "^1.0.6",
    "file-loader": "^0.8.4",
    "firebase": "^3.1.0",
    "function-bind": "^1.0.2",
    "html-webpack-plugin": "^2.8.1",
    "http-proxy-middleware": "^0.12.0",
    "inject-loader": "^2.0.1",
    "isparta-loader": "^2.0.0",
    "json-loader": "^0.5.4",
    "karma": "^0.13.15",
    "karma-coverage": "^0.5.5",
    "karma-mocha": "^0.2.2",
    "karma-phantomjs-launcher": "^1.0.0",
    "karma-sinon-chai": "^1.2.0",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-spec-reporter": "0.0.24",
    "karma-webpack": "^1.7.0",
    "lolex": "^1.4.0",
    "mocha": "^2.4.5",
    "moment": "^2.14.1",
    "nightwatch": "^0.8.18",
    "node-sass": "^3.8.0",
    "normalize.css": "^4.2.0",
    "ora": "^0.2.0",
    "phantomjs-prebuilt": "^2.1.3",
    "sass-loader": "^4.0.0",
    "selenium-server": "2.53.0",
    "shelljs": "^0.6.0",
    "sinon": "^1.17.3",
    "sinon-chai": "^2.8.0",
    "url-loader": "^0.5.7",
    "vue": "^1.0.21",
    "vue-hot-reload-api": "^1.2.0",
    "vue-html-loader": "^1.0.0",
    "vue-loader": "^8.3.0",
    "vue-style-loader": "^1.0.0",
    "vue-validator": "^2.1.6",
    "webpack": "^1.12.2",
    "webpack-dev-middleware": "^1.4.0",
    "webpack-hot-middleware": "^2.6.0",
    "webpack-merge": "^0.8.3"
  }
}


================================================
FILE: src/App.vue
================================================
<template>
  <div id="app">
    <article class="main" v-bind:class="{'start': !user.uid}">
      <div class="title">
        <a href="/">
          <img class="logo" src="./assets/tomato.svg">
        </a>
        <h1 class="">Tomato5</h1>
      </div>
      <!-- <p class="instruction"><span>Concentrate for work</span> <span>five times a day</span></p> -->
      <panel transition="fade" v-if="user.uid"></panel>
      <usage v-if="user.uid"></usage>
      <account v-if="user.uid"></account>
      <section class="footer">
        <p>
          <a href="https://github.com/zhangxin840/tomato5" target="_blank">Star or open issues on Github</a>
        </p>
        <!-- <p>
          <div>Icons made by <a href="http://www.flaticon.com/authors/freepik" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>
        </p> -->
      </section>
      <div class="login" v-show="!user.uid">
        <div id="firebaseui-auth-container"></div>
      </div>
    </article>

  </div>
</template>

<script>
import normalizeCss from 'normalize.css'; /* eslint no-unused-vars: 0 */
import animateCss from 'animate.css'; /* eslint no-unused-vars: 0 */
import firebase from 'firebase';
import FastClick from 'fastclick';

import commonCss from './common.scss'; /* eslint no-unused-vars: 0 */
import Panel from './components/Panel';
import Account from './components/Account';
import Usage from './components/Usage';
import database from './database';
import auth from './auth';
import { firebaseConfig } from './configs';
import utils from './utils';

const user = auth.getUser();

const tabId = Math.round(1000 * Math.random());

const makeMonoTab = function makeMonoTab() {
  const checkTab = function checkTab() {
    const storedTabId = window.localStorage.getItem('tabId');
    if (storedTabId && storedTabId.length > 0 && storedTabId !== `${tabId}`) {
      // if (window.confirm('You already have Tomato5 running in another tab,'
      //   + ' we suggest to close this tab and use the previous one.')) {
      //   window.close();
      // } else {
      //   window.localStorage.setItem('tabId', tabId);
      // }
      utils.report('usage', 'multiTabs');
    } else {
      window.localStorage.setItem('tabId', tabId);
    }
  };

  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'visible') {
      checkTab();
      this.$broadcast('tabFocused');
    }
  });

  window.addEventListener('beforeunload', (e) => {
    window.localStorage.removeItem('tabId');
  });

  checkTab();
};

const onTaskDone = function onTaskDone() {
  this.$broadcast('recount');
};

const initApp = function initApp() {
  firebase.initializeApp(firebaseConfig);
  database.init();
  auth.init();

  if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', () => {
      FastClick.attach(document.body);
    }, false);
  }

  this.makeMonoTab();
  utils.report('workflow', 'initApp');
};

export default {
  data() {
    return { user };
  },
  methods: { makeMonoTab },
  created: initApp,
  events: {
    taskDone: onTaskDone,
  },
  components: {
    Panel, Account, Usage,
  },
};
</script>

<style lang="scss">
@import 'base';

.main {
  padding: 0 10px;
  max-width: 400px;
  margin: auto;
}

.main.start {
  .title {
    max-width: 280px;
    margin: 40px auto;
  }
  .login {
    // margin-top: 30%;
  }
  .footer {
    text-align: center;
    display: none;
  }
}

.login {
  margin: 50px 0 50px 0;

  .firebaseui-busy-indicator {
    top: 0;
  }
  .mdl-button {
    font-family: 'Short Stack', sans-serif;;
  }

  .firebaseui-idp-password,
  .firebaseui-idp-password:hover {
    background-color: $red;
  }
}

.instruction {
  // display: inline-block;
  text-align: center;
  margin: 20px 0;
  line-height: 1.5;

  span {
    display: inline-block;
  }

  &.team {
    margin-top: 80px;
  }
}

.footer {
  p {
    margin: 10px 0;
  }

  padding-bottom: 30px;
}

/* always present */
.fade-transition {
  transition: opacity 0.3s;
}

/* .fade-enter defines the starting state for entering */
/* .fade-leave defines the ending state for leaving */
.fade-enter, .fade-leave {
  opacity: 0;
}
</style>


================================================
FILE: src/auth.js
================================================
// import firebaseUi from '../vendors/firebase-ui-auth.js';
import firebase from 'firebase';
import firebaseUiAuthCss from '../vendors/firebase-ui-auth.css'; /* eslint no-unused-vars: 0 */

const user = {
  displayName: '',
  email: '',
  emailVerified: false,
  photoURL: '',
  uid: '',
};

const initAuthUI = function initAuthUI() {
  // firebaseui is imported by script tag
  const firebaseui = window.firebaseui;
  // FirebaseUI config.
  const uiConfig = {
    signInSuccessUrl: '',
    signInOptions: [
      // Leave the lines as is for the providers you want to offer your users.
      firebase.auth.GoogleAuthProvider.PROVIDER_ID,
      firebase.auth.EmailAuthProvider.PROVIDER_ID,
    ],
    // Terms of service url.
    tosUrl: '/',
  };

  // Initialize the FirebaseUI Widget using Firebase.
  const ui = new firebaseui.auth.AuthUI(firebase.auth());
  // The start method will wait until the DOM is loaded.
  ui.start('#firebaseui-auth-container', uiConfig);
};

const init = function init() {
  firebase.auth().onAuthStateChanged((theUser) => {
    if (theUser) {
      user.displayName = theUser.displayName;
      user.email = theUser.email;
      user.emailVerified = theUser.emailVerified;
      user.photoURL = theUser.photoURL;
      user.uid = theUser.uid;

      // Object.assign(user, theUser);
      // console.log(user.uid);
    } else {
      // User is signed out.
      // console.log('signed out');
      user.displayName = '';
      user.email = '';
      user.emailVerified = false;
      user.photoURL = '';
      user.uid = '';
      initAuthUI();
    }
  }, (error) => {
    // console.log(error);
  });
};

const getUser = function getUser() {
  return user;
};

const logout = function logout() {
  firebase.auth().signOut().then(() => {
    // Sign-out successful.
    window.location.reload();
  }, (error) => {
    // An error happened.
    window.location.reload();
  });
};

export default { init, getUser, logout };


================================================
FILE: src/base.scss
================================================
$red: #ff5959;
$green: #42b983;
$ink: #2c3e50;
$blue: #5fd2db;
$yellow: #fff082;
$gray: #8d97a1;
$light-gray: #f0f0f0;

$font-size-s: 15px;
$font-size-m: 18px;
$font-size-l: 20px;
$font-size-xl: 30px;

.clearfix {
  &:after,
  &:before {
    content: "";
    display: table;
    clear: both;
  }
}


================================================
FILE: src/common.scss
================================================
@import 'base';

html {
  height: 100%;
}

body {
  // font-family: 'Architects Daughter', cursive;
  // font-family: 'Patrick Hand', cursive;
  font-family: 'Short Stack', sans-serif;
  font-size: $font-size-s;
  color: $ink;

  height: 100%;

  a {
    color: $blue;
    text-decoration: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
    -webkit-tap-highlight-color: transparent; /* For some Androids */
    cursor: pointer;

    &:active{
        transform: scale3d(1.1, 1.1, 1);
    }
  }

  h2 {
    font-size: $font-size-m;
    font-weight: normal;
  }

  input[type=text], input[type=text]:focus {
    color: $ink;
    border: none;
    margin: 0;
    padding: 0;
    box-shadow: none;
    outline: initial;

    border-bottom: 1px solid $light-gray;
  }
}



.title{
  text-align: left;
  margin: 40px -10px 40px -10px;
  text-align: center;

  h1 {
    color: $green;
    font-size: 44px;
    line-height: 80px;
    text-align: center;
    display: inline-block;
    margin: 0;
    position: relative;
    top: 5px;
    font-weight: bold;
  }

  .logo {
    width: 80px;
    height: 80px;
    display: inline-block;
    vertical-align: top;

    &:active{
        transform: scale3d(1.05, 1.05, 1);
    }
  }

  @media (min-width: 400px) {
    h1 {
      font-size: 55px;
      line-height: 100px;
    }

    .logo {
      width: 100px;
      height: 100px;
    }
  }
}

.icon {
  display: inline-block;
  text-indent: -9999px;
  background-repeat: no-repeat;

  -webkit-tap-highlight-color: rgba(0,0,0,0);
  -webkit-tap-highlight-color: transparent; /* For some Androids */

  &:active{
    transform: scale(1.15, 1.15);
  }
  opacity: 1;
}

.tomato{
  width: 30px;
  height: 30px;
  background-image: url('assets/tomato.svg');
}


================================================
FILE: src/components/Account.vue
================================================
<template>
  <div class="account">
    User: {{ user.displayName }}
    <a class="name" v-on:click="logout">(Change)</a>
  </div>
</template>

<script>
import auth from '../auth';

const user = auth.getUser();
const logout = function logout() {
  auth.logout();
};

export default {
  data() {
    return { user };
  },
  methods: { logout },
};
</script>

<style lang="scss" scoped>
.account {
  margin: 100px 0 0 0;
  // text-align: right;
}
</style>


================================================
FILE: src/components/ActiveTask.vue
================================================
<template>
  <div class="activeTask">
    <p class="operations" transition="fade" v-show="task.status === taskStatus.active">
      <span class="done" v-on:click="done">Done</span>
      <span class="drop" v-on:click="drop">Drop</span>
    </p>
  </div>
</template>

<script>
const done = function done() {
  this.task.status = this.taskStatus.done;
  this.$dispatch('taskDone', this.task);
};

const drop = function drop() {
  this.task.status = this.taskStatus.idle;
  this.$dispatch('taskDropped', this.task);
};

export default {
  props: ['task', 'taskStatus'],
  data() {
    return { };
  },
  methods: { done, drop },
};
</script>

<style lang="scss" scoped>
.activeTask {
  .operations .span {
    cursor: pointer;
  }

  position: absolute;
}
</style>


================================================
FILE: src/components/Emotion.vue
================================================
<template>
  <div class="emotion">
    <span class="inner", v-bind:class="[emotionLevelClass]">
      {{level}}
    </span>
  </div>
</template>

<script>
export default {
  props: ['level'],
  data() {
    return { };
  },
  computed: {
    emotionLevelClass: function doneCount() {
      return `${this.level > 0 ? 'p' : 'n'}-${Math.abs(this.level)}`;
    },
  },
  methods: { },
};
</script>

<style lang="scss" scoped>
@import "../base";

.emotion {
  width: 50px;
  height: 50px;
  display: inline-block;
  background: $yellow;
  line-height: 50px;
  text-align: center;
  border-radius: 5px;
  cursor: pointer;

  .inner {
    display: inline-block;
    vertical-align: middle;
    width: 30px;
    height: 30px;
    position: relative;
    top: 0px;
    transition: background 0.6s, top 0.2s;
    text-indent: -9999px;

    &.p-1 {
      background: url('../assets/p-1.svg');
    }
    &.p-2 {
      background: url('../assets/p-2.svg');
    }
    &.p-3 {
      background: url('../assets/p-3.svg');
    }
    &.p-4 {
      background: url('../assets/p-4.svg');
    }

    &.n-1 {
      background: url('../assets/n-1.svg');
    }
    &.n-2 {
      background: url('../assets/n-2.svg');
    }
    &.n-3 {
      background: url('../assets/n-3.svg');
    }
    &.n-4 {
      background: url('../assets/n-4.svg');
    }
  }

  &:hover {
    .inner {
      top: -2px;
    }
  }

  &:active {
    transform: scale(1.1, 1.1);
  }
}

.emotion.small {
  width: 25px;
  height: 25px;
  line-height: 25px;
  cursor: default;

  .inner {
    width: 15px;
    height: 15px;
    top: 0;
  }
}

.icon-wrapper {
  .emotion {
    margin: 0 2px 0 3px;
  }
}
</style>


================================================
FILE: src/components/Member.vue
================================================
<template>
  <div class="member">
    <div class="speech" v-bind:class="{
      'show': speech.isShowSpeech && (authUser.uid === userInfo.uid || (speech.content && speech.content.length > 0)),
      'long': speech.content && speech.content.length > 15
    }">
      <div v-if="authUser.uid === userInfo.uid" class="content edit">
        <input v-model="speech.content" v-on:blur="onChangeSpeech" class="" type="text" name="" placeholder="How are you doing?" maxlength="100">
      </div>
      <div v-if="authUser.uid !== userInfo.uid" class="content show">
        <p>{{speech.content}}</p>
      </div>
    </div>
    <div class="memberStatus">
      <emotion class="small" v-on:click="onClickEmotion" v-bind:level="userStatus.emotion"></emotion>
      <span class="beacon"
            v-bind:class="{
              'busy': userStatus.availability === availabilities.busy,
              'idle': userStatus.availability === availabilities.idle,
              'resting': userStatus.availability === availabilities.resting,
              'active': userStatus.availability === availabilities.active,
              'offline': isOffline,
            }">
      </span>
      <span class="name">{{ userInfo.displayName || (userInfo.email && userInfo.email.match(/^([^@]*)@/)[1]) }}</span>
      <span class="time" transition="fade" v-if="taskTimeLable">{{ taskTimeLable }}</span>
      <div class="flowers"
           v-bind:class="{
             'disabled': isFlowersCooling || isSaving,
           }"
           v-on:click="onClickFlowers()">
        <span class="icon flower animated"></span>
        <span class="count animated"
              v-bind:class="{
                'jello': isFlowersCooling,
              }">{{ flowers.count }}</span>
      </div>
    </div>
    <div class="tasks">
      <span class="icon tomato animated"
          v-for="task in tasks"
          v-bind:title="task.note"
          v-bind:class="{
            'planned': (task.status === taskStatus.idle && task.note) || (isOffline && (task.status === (taskStatus.ongoing || taskStatus.active))),
            'ongoing pulse infinite': (task.status === taskStatus.ongoing) && !isOffline,
            'active': (task.status === taskStatus.active) && !isOffline,
            'done': task.status === taskStatus.done,
          }">
      </span>
    </div>
    <div class="note-container">
      <p class="note" transition="fade" v-if="activeTask">{{ activeTask.note }}</p>
    </div>
  </div>
</template>

<script>
import timer from '../timer';
import moment from 'moment';
import Emotion from './Emotion';
import { taskStatus, availabilities } from '../model';
import { offLineThreshold, checkMemberInterval } from '../configs';

const taskTimeLable = '';
let statusChecker = null;

const isOffline = true;
const isFlowersCooling = false;

const onClickFlowers = function onClickFlowers() {
  if (this.isFlowersCooling || this.isSaving) {
    return;
  }

  this.flowers.count++;
  this.isFlowersCooling = true;

  this.$dispatch('addFlower', this.userInfo.uid);

  setTimeout(() => {
    this.isFlowersCooling = false;
  }, 1000);
};

const onChangeSpeech = function onChangeSpeech() {
  if (this.userInfo.uid === this.authUser.uid) {
    this.$dispatch('updateSpeech', this.userInfo.uid);
  }
};

const onClickEmotion = function onClickEmotion() {
  this.speech.isShowSpeech = !this.speech.isShowSpeech;
  if (this.userInfo.uid === this.authUser.uid) {
    this.$dispatch('updateSpeech', this.userInfo.uid);
  }
};

const init = function init() {
  statusChecker = window.setInterval(() => {
    if (!this.updateTime) {
      this.isOffline = true;
    } else {
      // Restore from server
      this.updateTime = moment(this.updateTime);
      if (moment().diff(this.updateTime, 'minutes') > offLineThreshold) {
        this.isOffline = true;
      } else {
        this.isOffline = false;
      }
    }

    if (this.activeTask && this.activeTask.status === taskStatus.ongoing) {
      this.taskTimeLable = timer.getContdown(this.activeTask.startTime,
        'standard', 'minute');
    } else {
      this.taskTimeLable = '';
    }
  }, checkMemberInterval);
};

const destroy = function destroy() {
  window.clearInterval(this.statusChecker);
};

export default {
  props: [
    'tasks', 'userStatus', 'userInfo', 'updateTime',
    'flowers', 'speech', 'isSaving',
    'authUser',
  ],
  data() {
    return {
      taskStatus, availabilities,
      taskTimeLable, statusChecker,
      isOffline, isFlowersCooling,
    };
  },
  methods: { onClickFlowers, onClickEmotion, onChangeSpeech },
  components: { Emotion },
  created: init,
  beforeDestroy: destroy,
  computed: {
    activeTask: function activeTask() {
      let result = null;

      if (this.tasks) {
        for (let i = 0; i < this.tasks.length; i++) {
          if (this.tasks[i].status === taskStatus.ongoing) {
            result = this.tasks[i];
            break;
          }
        }
      }

      if (this.isOffline) {
        result = null;
      }

      return result;
    },
  },
};
</script>

<style lang="scss" scoped>
@import "../base";

.member {
  position: relative;
  padding-bottom: 50px;
  margin: 0 0 0 0;

  &:first-of-type {
    margin-top: 25px;
  }

  .speech {
    position: relative;
    opacity: 0;
    transition: opacify, 0.2s;

    &.show {
      opacity: 1;
    }

    &.long .content.edit {
      width: 100%;
    }

    .content {
      display: inline-block;
      border: 1px solid $light-gray;
      border-radius: 10px;
      padding: 0px 15px;
      line-height: 30px;
      min-height: 30px;
      box-sizing: border-box;
      max-width: 100%;
      margin: 0;
      min-width: 80px;
      color: $gray;
      transition: width 0.5s;

      &.edit {
        width: 200px;
        input {
          color: $gray;
          line-height: 28px;
          border: none;
          display: inline-block;
          box-sizing: border-box;
          background: none;
          width: 100%;
        }
      }

      &.show {
        p {
          margin: 0;
          display: inline-block;
          line-height: 28px;
          box-sizing: border-box;
        }
      }
    }

    .content:after {
      content: "";
      position: absolute;
      left: 35px;
      bottom: 0;
      width: 10px;
      height: 1px;
      background: white;
    }

    &:after {
      border-color: $light-gray;
      content: "";
      position: absolute;
      z-index: 10;
      bottom: -10px;
      left: 25px;
      width: 10px;
      height: 10px;
      border-style: solid;
      border-width: 0 1px 1px 0;
      background: transparent;
      border-bottom-right-radius: 10px;
      display: block;
    }


    &:before {
      border-color: $light-gray;
      content: "";
      position: absolute;
      z-index: 10;
      bottom: -10px;
      left: 25px;
      width: 20px;
      height: 10px;
      border-style: solid;
      border-width: 0 1px 1px 0;
      background: transparent;
      border-bottom-right-radius: 15px 10px;
      display: block;
    }
  }

  .memberStatus {
    line-height: 50px;
    user-select: none;
  }

  .emotion {
    margin-right: 10px;
    margin-left: 3px;
    cursor: pointer;
  }

  .beacon {
    color: $green;
    background-color: #eee;
    display: inline-block;
    width: 10px;
    height: 10px;
    vertical-align: middle;
    border-radius: 5px;
    text-indent: -9999px;
    margin-right: 5px;

    &.offline {
      background-color: #eee !important;
    }

    &.idle {
      background-color: $green;
    }

    &.active {
      background-color: $yellow;
    }

    &.busy {
      background-color: $red;
    }

    &.resting {
      background-color: $blue;
    }
  }

  .name {
    margin-right: 15px;
    // min-width: 80px;
    display: inline-block;
  }

  .time {
    // position: absolute;
    // right: 0;
    // color: $red;
  }

  .flowers {
    float: right;
    display: inline-block;
    cursor: pointer;
    user-select: none;
    // margin-right: 15px;
    min-width: 50px;

    &.disabled {
      cursor: default;
    }

    .count {
      transition: all .1s;
      text-align: left;
      display: inline-block;
      margin-left: -5px;
      padding: 0;
    }

    .flower{
      vertical-align: middle;
      margin-right: 0px;

      width: 25px;
      height: 25px;
      background-image: url('../assets/flower.svg');
      opacity: 1;
    }
  }

  .tomato {
    opacity: 0.1;
    margin: 0 10px 5px 0;

    &.done {
      opacity: 1;
    }

    &.ongoing, &.active, &.planned {
      opacity: 0.5;
    }
  }

  .note-container {
    height: 50px;
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;

    .note {
      margin: 3px 0 0 0;
    }
  }
}
</style>


================================================
FILE: src/components/Panel.vue
================================================
<template>
  <section class="panel"
            v-bind:class="{
              'resting': userStatus.availability === availabilities.resting,
              'idle': userStatus.availability === availabilities.idle,
              'busy': userStatus.availability === availabilities.busy,
            }">
    <div class="user">
      <div class="status">
        <div class="avator" v-on:click="toggleEmotions">
          <emotion v-bind:level="userStatus.emotion"></emotion>
        </div>
        <span class="timer animated "
              v-show="userStatus.availability >= 0"
              v-on:click="onTimerClicked"
              v-bind:class="{
                'rubberBand': (userStatus.availability === availabilities.busy)
                || (userStatus.availability === availabilities.resting)
                || ((userStatus.availability === availabilities.idle) && replay)
              }">
          {{ panelStatus.label }}
        </span>
      </div>
      <div class="emotions"
            v-bind:class="{
              'expanded': panelStatus.isShowEmotions
            }">
        <p>
          Select your energy level:
        </p>
        <div class="selections">
          <p>
            <emotion v-bind:level="1" v-on:click="changeEmotion(1)"></emotion>
            <emotion v-bind:level="2" v-on:click="changeEmotion(2)"></emotion>
            <emotion v-bind:level="3" v-on:click="changeEmotion(3)"></emotion>
            <emotion v-bind:level="4" v-on:click="changeEmotion(4)"></emotion>
          </p>
          <p>
            <emotion v-bind:level="-1" v-on:click="changeEmotion(-1)"></emotion>
            <emotion v-bind:level="-2" v-on:click="changeEmotion(-2)"></emotion>
            <emotion v-bind:level="-3" v-on:click="changeEmotion(-3)"></emotion>
            <emotion v-bind:level="-4" v-on:click="changeEmotion(-4)"></emotion>
          </p>
        </div>
      </div>
    </div>
    <p class="taskTips animated tada" transition="fade" v-if="doneCount >= 5">
      Congratulations! You have done all 5 tomatoes planned today
    </p>
    <div class="tasks-wrapper" v-bind:class="{'loading': !tasks}">
      <div class="tasks" transition="fade" v-if="tasks">
        <div class="list">
          <template v-for="(index, task) in tasks">
            <task v-bind:task="task"
                  v-bind:index="index"
                  v-bind:task-status="taskStatus"
                  v-bind:user-status="userStatus"
                  v-bind:availabilities="availabilities"
                  v-bind:disabled="(userStatus.availability === availabilities.resting)
                      && (task.status === taskStatus.idle)"
                  v-bind:hide="(userStatus.availability === availabilities.busy || userStatus.availability === availabilities.active)
                      && !(task.status === taskStatus.ongoing || task.status === taskStatus.active)">
            </task>
          </template>
        </div>
        <span class="icon add"
              transition="fade"
              v-show="(usedCount >= 5 || tasks.length < 5) && (userStatus.availability === availabilities.idle || userStatus.availability === availabilities.resting)"
              v-on:click="addTask">
              Add task
        </span>
      </div>
    </div>
    <team-panel
              v-if="tasks"
              v-bind:user-info="user"
              v-bind:user-status="userStatus"
              v-bind:tasks="tasks">
    </team-panel>
  </section>
</template>

<script>
import moment from 'moment';
import vue from 'vue';
import ActiveTask from './ActiveTask';
import Task from './Task';
import Emotion from './Emotion';
import TeamPanel from './TeamPanel';
import timer from '../timer';
import { taskStatus, availabilities, getDefaultTasks, userStatus } from '../model';
import database from '../database';
import auth from '../auth';
import utils from '../utils';

const user = auth.getUser();
const tasks = null;

const panelStatus = {
  label: '00:00',
  activeTask: null,
  isShowEmotions: false,
};

const Notification = window.Notification;

const replay = false;

const sendNotification = function sendNotification(title, message) {
  if (Notification) {
    const noti = new Notification(title, { /* eslint no-unused-vars: 0 */
      icon: 'http://tomato5.io/static/icons/tomato.png',
      body: message,
    });

    utils.report('task', 'notify');
  }
};

const getTasksPath = function getTasksPath(time) {
  return `tasks/${auth.getUser().uid}/${moment(time).format('YYYYMMDD')}`;
};

const saveTasks = function saveTasks() {
  const tasksToday = [];
  const tasksYesterday = [];

  this.tasks.forEach((task) => {
    const createDate = task.createTime && moment(task.createTime).format('YYYYMMDD');
    if (createDate === moment().format('YYYYMMDD')) {
      tasksToday.push(task);
    } else if (createDate === moment().subtract(1, 'd').format('YYYYMMDD')) {
      tasksYesterday.push(task);
    }
  });

  database.save(getTasksPath(), tasksToday);

  if (tasksYesterday[0] && tasksYesterday[0].createTime) {
    database.save(getTasksPath(tasksYesterday[0].createTime), tasksYesterday);
  }

  this.$broadcast('publish');
};

let restTimer = null;

const onRestTimeDue = function onRestTimeDue() {
  userStatus.availability = availabilities.idle;
  this.$broadcast('publish');
};

const updateTimer = function updateTimer(startTime, type) {
  const seconds = timer.getContdown(startTime, type, 'second');
  const minutes = timer.getContdown(startTime, type, 'minute');
  const tabLabel = (seconds > '00:01') ? `${minutes}` : 'Tomato5';
  panelStatus.label = seconds;
  document.title = `${tabLabel}`;
};

const clearTimer = function clearTimer() {
  panelStatus.label = '00:00';
  document.title = 'Tomato5';
};

const startRest = function startRest() {
  const restStartTime = moment();

  updateTimer(restStartTime, 'resting');
  userStatus.availability = availabilities.resting;

  restTimer = window.setInterval(() => {
    updateTimer(restStartTime, 'resting');
    if (timer.getRemaning(restStartTime, 'resting').asMilliseconds() <= 0) {
      window.clearInterval(restTimer);
      this.onRestTimeDue();
    }
  }, 1000);

  this.$broadcast('publish');
  utils.report('rest', 'start');
};

const stopResting = function stopResting() {
  clearTimer();
  this.userStatus.availability = availabilities.idle;

  if (restTimer) {
    window.clearInterval(restTimer);
  }

  this.$broadcast('publish');
  utils.report('rest', 'stop');
};

const replayAnimation = function replayAnimation() {
  this.replay = false;
  window.setTimeout(() => {
    this.replay = true;
  }, 1); // To clear class in DOM
  window.setTimeout(() => {
    this.replay = false;
  }, 1000); // Clear after length of animation
};

const onTimerClicked = function onTimerClicked() {
  if (this.userStatus.availability === this.availabilities.resting) {
    this.stopResting();
    this.replayAnimation();
  } else if (this.userStatus.availability === this.availabilities.busy) {
    this.$broadcast('clearTask');
    this.replayAnimation();
  }
  utils.report('timer', 'click');
};

const addTask = function addTask() {
  this.tasks.push({
    // note: `The ${this.tasks.length + 1}th task`,
    note: '',
    status: taskStatus.idle,
    startTime: null,
    createTime: moment(),
    emotion: 2,
  });

  this.saveTasks();
  utils.report('task', 'add');
};

const onTaskStarted = function onTaskStarted(task) {
  this.panelStatus.activeTask = task;
  updateTimer(task.startTime, 'standard');
  this.userStatus.availability = this.availabilities.busy;

  task.emotion = this.userStatus.emotion;

  this.saveTasks();
  utils.report('task', 'start');
};

const onTaskDone = function onTaskDone(task) {
  this.userStatus.availability = this.availabilities.idle;
  this.panelStatus.activeTask = null;

  task.emotion = this.userStatus.emotion;

  window.setTimeout(() => {
    this.startRest();
  }, 1);

  this.saveTasks();
  utils.report('task', 'done');

  return true; // Do not stop stop propagation
};

const onTaskDropped = function onTaskDropped() {
  this.userStatus.availability = this.availabilities.idle;
  this.panelStatus.activeTask = null;
  clearTimer();

  this.saveTasks();
  utils.report('task', 'drop');
};

const onTaskTimeDue = function onTaskTimeDue() {
  sendNotification('Tomato5', 'Tomato completed, nice job!');
  this.userStatus.availability = this.availabilities.active;

  this.saveTasks();
};

const onTaskTimerUpdated = function onTaskTimerUpdated(task) {
  updateTimer(task.startTime, 'standard');
};

const onTaskEdited = function onTaskEdited() {
  this.saveTasks();
  utils.report('task', 'edit');
};

const toggleEmotions = function toggleEmotions() {
  this.panelStatus.isShowEmotions = !this.panelStatus.isShowEmotions;
  utils.report('emotion', 'toggle');
};

const changeEmotion = function changeEmotion(level) {
  this.panelStatus.isShowEmotions = false;
  this.userStatus.emotion = level;

  if (this.panelStatus.activeTask && this.panelStatus.activeTask.status === taskStatus.ongoing) {
    this.panelStatus.activeTask = this.userStatus.emotion;
  }

  this.$broadcast('publish');
  utils.report('emotion', 'change');
};

const initTime = moment();

const init = function init() {
  this.getTasks();

  window.setInterval(() => {
    if ((initTime.dayOfYear() !== moment().dayOfYear())
          && (this.userStatus.availability === this.availabilities.idle)) {
      utils.report('usage', 'crossDay');
      window.location.reload(); // Reload if cross day
    }
  }, 100000);

  utils.report('workflow', 'initPanel');
};

const prepareTasks = function prepareTasks(theTasks) {
  theTasks.forEach((task) => { /* eslint no-param-reassign: 0 */
    task.createTime = task.createTime && moment(task.createTime);
    task.startTime = task.startTime && moment(task.startTime);
    task.emotion = task.emotion || 2;
    task.status = task.status || taskStatus.idle;
    task.note = task.note || '';

    // Rest task if not done
    if (task.status !== taskStatus.done) {
      task.startTime = null;
      task.status = taskStatus.idle;
    }
  });

  return theTasks;
};

const getTasks = function getTasks() {
  return database.get(getTasksPath(), getDefaultTasks()).then((data) => {
    this.tasks = prepareTasks(data);
    return this.tasks;
  });
};

export default {
  data() {
    return {
      user, tasks, panelStatus,
      userStatus,
      taskStatus, availabilities,
      replay,
    };
  },
  methods: {
    getTasks, addTask, saveTasks,
    startRest, onRestTimeDue, stopResting,
    toggleEmotions, changeEmotion,
    onTimerClicked, replayAnimation,
  },
  components: {
    ActiveTask, Task, Emotion, TeamPanel,
  },
  events: {
    taskDone: onTaskDone,
    taskDropped: onTaskDropped,
    taskStarted: onTaskStarted,
    taskTimeDue: onTaskTimeDue,
    taskTimerUpdated: onTaskTimerUpdated,
    taskEdited: onTaskEdited,
  },
  created: init,
  computed: {
    doneCount: function doneCount() {
      return this.tasks && this.tasks.reduce(
        (last, item) => last + (item.status === this.taskStatus.done ? 1 : 0)
      , 0);
    },
    usedCount: function doneCount() {
      return this.tasks && this.tasks.reduce(
        (last, item) => last + ((item.status === this.taskStatus.done || item.note) ? 1 : 0)
      , 0);
    },
    emotionClass: function emotionUrl() {
      const level = this.userStatus.emotion;
      return `${level > 0 ? 'p' : 'n'}-${Math.abs(level)}`;
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
@import "../base";

h1 {
  color: $green;
}

.panel {
  // display: flex;
  margin: 75px auto;

  &.resting {
    .timer {
      color: $green;
      cursor: pointer;
    }
  }

  &.busy {
    .timer {
      cursor: pointer;
    }
  }

  .taskTips {
    text-align: center;
    margin: 0px 0 30px 0;
  }

  .tasks-wrapper {
    min-height: 315px;

    &.loading {
      background: url('../assets/loading.svg') no-repeat center 30%;
      background-size: 80px 80px;
    }
  }

  .tasks {
    width: 100%;
    position: relative;
    margin: 10px 0;

    .list {
      padding-bottom: 15px;
      position: relative;
    }

    .add {
      width: 20px;
      height: 20px;
      background: url('../assets/add.svg');
      margin: 0px 7px;
      cursor: pointer;
      position: absolute;
      left: 0;
      bottom: 0;

      &:active{
        transform: scale(1.1, 1.1);
      }
    }
  }

  .user {
    user-select: none;
    position: relative;

    .status {
      width: 100%;
      text-align: center;
      display: flex;
      justify-content: center;
      padding: 30px 0;

      .avator {
        margin-right: 20px;
      }

      .timer{
        font-size: 24px;
        line-height: 50px;
        width: 80px;
        text-align: left;

        &:active{
          transform: scale(1.1, 1.1);
        }
      }
    }

    .emotions{
      overflow: hidden;
      transition: all 0.2s;
      width: 100%;
      background: white;
      z-index: 1;
      border: 1px solid #eee;
      padding: 10px 10px 10px 10px;
      border-radius: 10px;
      box-sizing: border-box;

      position: absolute;
      top: -60px;
      left: 50%;
      opacity: 0;
      transform: scaleY(0) translate(-50%, 0);

      p {
        margin: 10px 0;
      }

      &.expanded {
        // scaleY(1) will be compiled to scale(1), that will break the animation.
        transform: scaleY(0.999) translate(-50%, 0);
        opacity: 1;
      };

      .selections {
        text-align: center;

        .emotion {
          margin: 15px 2.5%;
        }

        p {
          margin: 0;
        }
      }
    }
  }

}
</style>


================================================
FILE: src/components/Task.vue
================================================
<template>
  <div class="task"
       v-bind:class="{
             'idle': task.status === taskStatus.idle,
             'ongoing': task.status === taskStatus.ongoing,
             'active': task.status === taskStatus.active,
             'done': task.status === taskStatus.done,
             'hide': this.hide,
             'disabled': this.disabled,
           }">
    <div class="icon-wrapper">
      <span class="icon tomato animated"
          v-bind:class="{
            'tada ': task.status === taskStatus.idle,
            'pulse infinite': task.status === taskStatus.ongoing,
            'swing infinite': task.status === taskStatus.active,
            'done': task.status === taskStatus.done,
          }"
          v-on:click="onClick(task)"
          v-show="task.status !== taskStatus.done">
          {{ task.note }}
      </span>
      <emotion class="small"
               v-bind:level="task.emotion"
               v-show="task.status === taskStatus.done">
      </emotion>
    </div>
    <div class="note-wrapper">
      <input class="note" type="text" autocomplete="off" maxlength="50"
            v-bind:placeholder="taskTips[index] || `the ${index + 1}th task.`"
            v-model="task.note"
            v-on:blur="onTaskEdited">
      <div class="activeTask">
        <p class="operations" transition="fade" v-show="task.status === taskStatus.active">
          <span class="done" v-on:click="done">Done</span>
          <span class="drop" v-on:click="drop">Drop</span>
        </p>
      </div>
    </div>
  </div>
</template>

<script>

import moment from 'moment';
import timer from '../timer';
import { taskTips } from '../model';
import Emotion from './Emotion';

const done = function done() {
  this.task.status = this.taskStatus.done;
  this.$dispatch('taskDone', this.task);
};

const drop = function drop() {
  this.task.status = this.taskStatus.idle;
  window.clearInterval(this.timeInterval);
  this.$dispatch('taskDropped', this.task);
};

const onClearTask = function onClearTask() {
  if (this.task.status === this.taskStatus.ongoing) {
    this.drop();
  }
};

const onTaskEdited = function onTaskEdited() {
  this.$dispatch('taskEdited', this.task);
};

const onTaskTimeDue = function onTaskTimeDue() {
  this.task.status = this.taskStatus.active;
  this.$dispatch('taskTimeDue', this.task);
};

let timeInterval;

const start = function start() {
  this.task.status = this.taskStatus.ongoing;
  this.task.startTime = moment();

  this.$dispatch('taskStarted', this.task);

  this.timeInterval = window.setInterval(() => {
    this.$dispatch('taskTimerUpdated', this.task);

    if (timer.getRemaning(this.task.startTime, 'standard').asMilliseconds() <= 0) {
      window.clearInterval(this.timeInterval);
      this.onTaskTimeDue();
    }
  }, 1000);

  if (Notification && Notification.permission !== 'granted') {
    Notification.requestPermission();
  }
};

const onClick = function onClick() {
  if (this.task.status === this.taskStatus.idle
      && this.userStatus.availability === this.availabilities.idle) {
    this.start();
  } else if (this.task.status === this.taskStatus.ongoing) {
    this.drop();
  }
};

export default {
  props: ['task', 'index', 'taskStatus', 'hide', 'disabled', 'userStatus', 'availabilities'],
  data() {
    return { timeInterval, taskTips };
  },
  methods: { onClick, onTaskTimeDue, onTaskEdited, start, done, drop },
  events: {
    clearTask: onClearTask,
  },
  components: { Emotion },
};
</script>

<style lang="scss" scoped>
@import '../base';

.task {
  margin: 0 0 30px 0;
  display: flex;
  line-height: 30px;
  width: 100%;

  .icon-wrapper {
    min-width: 35px;
    max-width: 35px;
    height: 35px;
    display: inline-block;
    margin-right: 10px;
    text-align: center;

    .tomato {
      width: 35px;
      height: 35px;
    }

    .emotion {
      margin: 0 auto;
      vertical-align: text-top;
    }
  }

  .note-wrapper {
    width: 100%;
    line-height: 35px
  }

  .note {
    line-height: 25px;
    position: relative;
    width: 100%;
    vertical-align: middle;
    top: -0px;
  }

  &.hide {
    opacity: 0.1;
  }

  &.disabled {
    .tomato {
      opacity: 0.1;
    }
  }

  &.active {
    .note {
      display: none;
      // opacity: 0.1;
    }
  }

  transition: all 0.5s;
  position: relative;
}

.panel.idle .idle .tomato,
.panel.busy .ongoing .tomato {
  cursor: pointer;
}

.activeTask {
  position: absolute;
  // left: 0;
  top: 0;
  background: rgba(255,255,255,1);

  // display: none;

  .operations {
    margin: 0;

    span {
      display: inline-block;
      width: 20px;
      height: 20px;
      text-indent: -9999px;
      vertical-align: middle;

      cursor: pointer;
      margin: 0 10px;
    }

    .done {
      color: $green;
      background: url('../assets/done.svg');
    }

    .drop {
      color: $red;
      width: 15px;
      height: 15px;
      background: url('../assets/delete.svg');
    }
  }

}
</style>


================================================
FILE: src/components/TeamPanel.vue
================================================
<template>
  <section class="teamPanel">
    <div class="teamPanel-wrapper">
      <h2 class="">
        Team
        <!-- <span>{{ teamData.info.name }}</span>     -->
        <span class="expander" v-on:click="showForm()">+</span>
      </h2>
      <div class="teamManager " transition="expand" v-show="teamForm.isShow">
        <p class="switch" v-show="false">
          <span class="selector" v-on:click="teamForm.isJoin = true" v-bind:class="{ 'selected': teamForm.isJoin }">Join Team</span>
          <span class="spacer">or</span>
          <span class="selector" v-on:click="teamForm.isJoin = false" v-bind:class="{ 'selected': !teamForm.isJoin }">My Team</span>
        </p>
        <validator name="validation">
        <form class="theForm" novalidate>
          <div class="join" transition="slide_left" v-show="teamForm.isJoin">
            <p class="field">
              <span>TeamID: </span>
              <input type="text" autocomplete="off" value=""
                    placeholder="6-24 characters"
                    maxlength="24"
                    v-model="teamForm.inviteCode"
                    v-validate:invite-code="{ minlength: 6 }">
            </p>
            <p class="tips">
              Share this TeamID to invite others to join your team
            </p>
            <p class="tips">
              Edit TeamID to join or create another team
            </p>
            <div class="status" >
              <div class="wrapper" v-bind:class="{
                'ready': teamForm.status === 'ready' && !$validation.invalid && teamForm.inviteCode !== userTeamData.currentTeam,
                'doing': teamForm.status === 'doing',
                'success': teamForm.status === 'success',
                'fail': teamForm.status === 'fail'
              }">
                <p class="ready">
                  <a v-on:click="joinTeam(teamForm.inviteCode)">
                     Join Team
                  </a>
                </p>
                <p class="doing">Joining...</p>
                <p class="success">Succeeded</p>
                <p class="fail">Failed</p>
              </div>
            </div>
          </div>
          <div class="create" transition="slide_right" v-show="!teamForm.isJoin">
            <p class="field">
              <span class="">Name: </span>
              <input type="text" value="" v-model="teamForm.name">
            </p>
            <p class="field">
              <span class="">Invite Code: </span>
              <input type="text" autocomplete="off" value="" placeholder="4-12 charactors" v-model="teamForm.inviteCode">
            </p>
            <p class="tips">
              Others will use this code to join your team.
            </p>
            <p>
              <a v-on:click="createTeam(teamForm.inviteCode, teamForm.name)">Save</a>
            </p>
          </div>
          <!-- <p class="info"></p> -->
        </form>
        </validator>
      </div>
      <div class="message clearfix" transition="fade" v-if="teamData">
        <input type="text" class="" placeholder="Headline" maxlength="200"
               v-model="teamData.common.messages[dateLabel].headline"
               v-on:blur="changeHeadline">
      </div>
      <div class="members" transition="fade" v-if="teamData">
        <member v-for="(uid, member) in teamData.members"
                v-bind:flowers="teamData.common.flowers[this.dateLabel][uid]"
                v-bind:speech="teamData.common.messages[this.dateLabel].speeches[uid]"
                v-bind:auth-user="authUser"
                v-bind:is-saving="isSaving"
                v-bind:tasks="member.tasks"
                v-bind:user-status="member.userStatus"
                v-bind:user-info="member.userInfo"
                v-bind:update-time="member.updateTime"
                v-if="!member.isHideMember">
        </member>
      </div>
    </div>
  </section>
</template>

<script>
import moment from 'moment';
import Member from './Member';
import database from '../database';
import auth from '../auth';
import { getDefaultTasks } from '../model';
import utils from '../utils';

const showMemberDays = 7;

const teamData = null;
const userTeamData = null;

const teamForm = {
  inviteCode: '',
  name: '',
  isShow: false,
  isJoin: true,
  status: 'ready',
};

const isSaving = false;

const showForm = function showForm() {
  this.teamForm.isShow = !this.teamForm.isShow;
  utils.report('team', 'showForm');
};

let watchRef = null;

const publishToTeam = function publishToTeam() {
  let promise;

  if (this.userTeamData && this.userTeamData.currentTeam) {
    const memberPath = `teams/${this.userTeamData.currentTeam}/members/${auth.getUser().uid}`;
    promise = database.save(memberPath, {
      rule: 'member',
      userInfo: this.userInfo,
      userStatus: this.userStatus,
      tasks: this.tasks,
      updateTime: moment(),
    }).then(() => {
      // console.log('published');
    });
  } else {
    promise = Promise.resolve();
  }

  return promise;
};

const addFlower = function addFlower(uid) {
  const path = `teams/${this.userTeamData.currentTeam}/common/flowers/${this.dateLabel}/${uid}`;
  // console.log(path);
  this.isSaving = true;
  database.save(path, this.teamData.common.flowers[this.dateLabel][uid]).then(() => {
    this.isSaving = false;
  }, () => {
    this.isSaving = false;
  });

  utils.report('team', 'addFlower');
};

const updateSpeech = function updateSpeech(uid) {
  const teamId = this.userTeamData.currentTeam;
  const path = `teams/${teamId}/common/messages/${this.dateLabel}/speeches/${uid}`;
  database.save(path, this.teamData.common.messages[this.dateLabel].speeches[uid]).then(() => {
  }, () => {
  });

  utils.report('team', 'updateSpeech');
};

const changeHeadline = function changeHeadline() {
  const path = `teams/${this.userTeamData.currentTeam}/common/messages/${this.dateLabel}/headline`;
  database.save(path, this.teamData.common.messages[this.dateLabel].headline).then(() => {
  }, () => {
  });

  utils.report('team', 'changeHeadline');
};

const joinTeam = function joinTeam(inviteCode) {
  const recoverTime = 2500;
  const animationTime = 1500;

  if (inviteCode && this.userTeamData) {
    this.userTeamData.currentTeam = inviteCode;
    if (watchRef) {
      watchRef.off();
    }

    this.teamForm.status = 'doing';

    this.publishToTeam().then(() => {
      this.watchTeam();

      this.teamForm.status = 'success';
      window.setTimeout(() => {
        this.teamForm.isShow = false;
        window.setTimeout(() => {
          this.teamForm.status = 'ready';
        }, animationTime / 2);
      }, recoverTime);
    }, () => {
      this.teamForm.status = 'fail';
      window.setTimeout(() => {
        this.teamForm.status = 'ready';
      }, recoverTime);
    });

    this.saveUserTeamData().then(() => {
    });

    utils.report('team', 'join');
  }
};

const createTeam = function createTeam(inviteCode, name) {
  if (inviteCode && name) {
    database.save(`teams/${inviteCode}`, {
      info: { inviteCode, name },
      members: [],
    }).then(() => this.joinTeam(inviteCode));
  }
};

const changeTeamName = function changeTeamName(inviteCode, name) {
  database.save(`teams/${inviteCode}/info`, { inviteCode, name }).then(() => {
    // console.log('Name changed');
  });
};

const processTeamData = function processTeamData(data) {
  if (!data) {
    return null;
  }

  /* eslint-disable no-param-reassign */
  // Init the common data structure
  data.common = data.common || {};
  data.common.flowers = data.common.flowers || {};
  data.common.messages = data.common.messages || {};

  const flowers = data.common.flowers;
  const messages = data.common.messages;

  flowers[this.dateLabel] = flowers[this.dateLabel] || {};
  Object.keys(data.members).forEach((uid) => {
    flowers[this.dateLabel][uid] = flowers[this.dateLabel][uid] || {
      count: 0,
    };
  });

  messages[this.dateLabel] = messages[this.dateLabel] || {};
  messages[this.dateLabel].headline = messages[this.dateLabel].headline || '';
  messages[this.dateLabel].speeches = messages[this.dateLabel].speeches || {};
  Object.keys(data.members).forEach((uid) => {
    messages[this.dateLabel].speeches[uid] = messages[this.dateLabel].speeches[uid] || {
      content: '',
      isShowSpeech: false,
    };
  });
  /* eslint-enable no-param-reassign */

  // Filter tasks and members
  Object.keys(data.members).forEach((uid) => {
    const member = data.members[uid];

    member.tasks = member.tasks.filter((task) => (task.createTime
        && moment(task.createTime).format('YYYYMMDD') === moment().add(0, 'd').format('YYYYMMDD'))
    );

    member.tasks = member.tasks.length > 0 ? member.tasks : getDefaultTasks();

    // Hide member if not updated
    if (!member.updateTime ||
      moment.duration(moment(member.updateTime) - moment()).days() > showMemberDays) {
      member.isHideMember = true;
    }
  });

  return data;
};

const watchTeam = function watchTeam() {
  watchRef = database.watch(`teams/${this.userTeamData.currentTeam}`, (snapshot) => {
    this.teamData = this.processTeamData(snapshot.val());
  });
};

const authUser = auth.getUser();

const init = function init() {
  this.getUserTeamData().then(() => {
    this.teamForm.inviteCode = this.userTeamData.currentTeam;
    this.joinTeam(this.userTeamData.currentTeam);
  });

  utils.report('workflow', 'initTeam');
};

const getUserTeamData = function getUserTeamData() {
  return database.get(`userData/${auth.getUser().uid}/teamData`, {
    currentTeam: null,
  }).then((data) => {
    const defaultTeam = `TEAM-${authUser.uid.slice(0, 3)}-${Math.random().toString().slice(2, 7)}`;

    this.userTeamData = data;
    this.userTeamData.currentTeam = this.userTeamData.currentTeam || defaultTeam;
  });
};

const saveUserTeamData = function saveUserTeamData() {
  return database.save(`userData/${auth.getUser().uid}/teamData`, this.userTeamData);
};

// Only get dateLabel when init, to avoid cross day issues.
const dateLabel = moment().format('YYYYMMDD');


export default {
  props: ['userInfo', 'userStatus', 'tasks'],
  data() {
    return { teamData, teamForm, userTeamData, dateLabel, isSaving, authUser };
  },
  events: {
    publish: publishToTeam,
    tabFocused: publishToTeam,
    updateSpeech,
    addFlower,
  },
  created: init,
  methods: {
    publishToTeam, watchTeam,
    getUserTeamData, saveUserTeamData,
    joinTeam, createTeam, changeTeamName,
    showForm,
    changeHeadline,
    processTeamData,
    updateSpeech,
  },
  components: { Member },
};
</script>

<style lang="scss" scoped>
@import "../base";

.teamPanel {
  margin: 85px 0 0px 0;;

  &:before, &:after {
    content: "";
    display: table;
    clear: both;
  }

  .expander {
    color: $blue;
    line-height: 30px;
    display: inline-block;
    padding: 0px 10px;
    cursor: pointer;
    user-select: none;
  }

  .message {
    input {
      display: block;
      width: 100%;
      text-align: center;
      line-height: 30px;
      margin: 35px 0 0px 0;
    }
  }

  .members {
    overflow: hidden; // Enable BFC to avoid merging margin, for smoother animation
  }

  .teamManager {
    .switch {
      margin: 0px 0 0px 0;
      user-select: none;
      .selector {
        padding: 5px 10px;
        border-radius: 5px;
        transition: all .3s;
        color: $blue;
        cursor: pointer;
      }
      .spacer {
        margin: 0 10px;
      }
      .selected {
        background-color: $green;
        color: white;
        cursor: default;
      }
    }

    .theForm {
      position: relative;

      .field {
        span {
          margin-right: 10px;
        }
      }

      .tips {
        color: $gray;
      }

      .status {
        position: relative;
        height: 30px;
        overflow: hidden;
        margin-top: 0px;

        .wrapper {
          transition: top 0.5s;
          position: relative;
          top: 30px;

          &.ready {
            top: -0px;
            p.ready {
              opacity: 1;
            }
          }
          &.doing {
            top: -30px;
            p.doing {
              opacity: 1;
            }
          }
          &.success {
            top: -60px;
            p.success {
              opacity: 1;
            }
          }
          &.fail {
            top: -90px;
            p.fail {
              opacity: 1;
            }
          }

          p {
            transition: opacity 1s;
            margin: 0px;
            line-height: 30px;
            opacity: 0;

            &.success {
              color: $green;
            }

            &.fail {
              color: $red;
            }
          }
        }
      }
    }
  }
}

.slide_left-transition {
  transition: all .5s;
  width: 100%;
  position: absolute;;
  overflow: hidden;
  top: 0;
  left: 0;
}

.slide_left-enter, .slide_left-leave {
  left: -500px;
  opacity: 0;
}

.slide_right-transition {
  transition: all .5s;
  width: 100%;
  position: absolute;
  overflow: hidden;
  left: 0;
  top: 0;
}

.slide_right-enter, .slide_right-leave {
  left: 700px;
  opacity: 0;
}

/* always present */
.expand-transition {
  transition: all .3s ease;
  height: 170px;
  overflow: hidden;
}

/* .expand-enter defines the starting state for entering */
/* .expand-leave defines the ending state for leaving */
.expand-enter, .expand-leave {
  height: 0;
  opacity: 0;
}
</style>


================================================
FILE: src/components/Usage.vue
================================================
<template>
  <article class="usage" transition="fade" v-if="loaded">
    <h2>Metrics</h2>
    <p class="chartTitle">
      Tasks completed
    </p>
    <canvas id="line-chart" height="200"></canvas>
    <p class="chartTitle">
      Peak hour
    </p>
    <canvas id="bar-chart" height="200"></canvas>
    <p class="streak">
      Usage streak: {{ usageData.streak }} day{{usageData.streak > 1 ? 's' : ''}}
    </p>
    <p class="total">
      Total completed: {{ usageData.totalCount }}
    </p>
  </article>
</template>

<script>

import Chart from 'chart.js';
import moment from 'moment';
import database from '../database';
import auth from '../auth';
import { taskStatus } from '../model';

const loaded = false;

const usageData = {
  totalCount: 0,
  streak: 0,
};

const getDoneCount = function getDoneCount(tasksArray) {
  let count = 0;
  if (tasksArray) {
    count = tasksArray.reduce((previousValue, item) => previousValue
      + (item.status === taskStatus.done ? 1 : 0), 0);
  }
  return count;
};

const getStreak = function getStreak(tasks) {
  let streak = 0;
  let indexDay = moment();

  streak = getDoneCount(tasks[indexDay.format('YYYYMMDD')]) > 0 ? 1 : 0;
  indexDay = indexDay.subtract(1, 'd');

  while (tasks[indexDay.format('YYYYMMDD')] || indexDay.day() === 0 || indexDay.day() === 6) {
    if (getDoneCount(tasks[indexDay.format('YYYYMMDD')]) > 0) {
      streak++;
      indexDay = indexDay.subtract(1, 'd');
    } else if (indexDay.day() === 0 || indexDay.day() === 6) {
      indexDay = indexDay.subtract(1, 'd');
    } else {
      break;
    }
  }

  return streak;
};

// const checkStreak = function checkStreak() {
//   let result = -1;
//   if (this.usageData && this.usageData.lastStreakTime) {
//     const last = moment(this.usageData.lastStreakTime);
//     const now = moment();
//     if ((now.diff(last, 'days') === 1)
//       || (now.diff(last, 'days') === 2 && now.day() === 0) // Sunday
//       || (now.diff(last, 'days') === 3 && now.day() === 1) // Monday
//     ) {
//       result = 1; // Is streak
//     } else if (now.diff(last, 'days') === 0) {
//       result = 0; // Same day
//     } else {
//       result = -1; // Not streak
//     }
//   }
//
//   return result;
// };

// const getStreak = function getStreak() {
//   return database.get(`userData/${auth.getUser().uid}/usageData`, {
//     currentStreak: 0,
//     lastStreakTime: null,
//   })
//   .then((data) => {
//     this.usageData = data;
//
//     if (this.checkStreak() === -1) {
//       this.usageData.currentStreak = 0;
//       this.usageData.lastStreakTime = moment();
//     }
//   });
// };

// const saveStreakData = function saveStreakData() {
//   return database.save(`userData/${auth.getUser().uid}/usageData`, this.usageData);
// };

const getUsageData = function getUsageData(tasks) {
  const totalCount = Object.keys(tasks).reduce((previousValue, item) => previousValue +
      getDoneCount(tasks[item])
  , 0);

  usageData.totalCount = totalCount;
  usageData.streak = getStreak(tasks);
};

const fillTasks = function fillTasks(tasks) {
  const keys = Object.keys(tasks);
  const filledTasks = {};
  const firstDay = keys[0] && moment(keys[0]);
  const lastDay = keys[keys.length - 1] && moment(keys[keys.length - 1]);
  const fillLength = 7;
  let indexDay;

  // Fill days in middle
  for (indexDay = firstDay; indexDay && indexDay <= lastDay; indexDay.add(1, 'd')) {
    filledTasks[indexDay.format('YYYYMMDD')] = tasks[indexDay.format('YYYYMMDD')] || [];
  }

  // Fill today if empty
  if (Object.keys(filledTasks).length === 0) {
    filledTasks[moment().format('YYYYMMDD')] = [];
  }

  // Fill to enough length
  while (Object.keys(filledTasks).length < fillLength) {
    filledTasks[moment(Object.keys(filledTasks)[0]).subtract(1, 'd').format('YYYYMMDD')] = [];
  }

  return filledTasks;
};

const getTasksData = function getTasksData() {
  return database.get(`tasks/${auth.getUser().uid}`, {});
};

const lineOptions = {
  label: 'Tomato completed',
  fill: false,
  lineTension: 0.1,
  backgroundColor: 'rgba(95,210,219,0.4)',
  borderColor: 'rgba(95,210,219,0.8)',
  borderCapStyle: 'butt',
  borderDash: [],
  borderDashOffset: 0.0,
  borderJoinStyle: 'miter',
  pointBorderColor: 'rgba(95,210,219,0.8)',
  pointBackgroundColor: '#fff',
  pointBorderWidth: 1,
  pointHoverRadius: 5,
  pointHoverBackgroundColor: 'rgba(95,210,219,0.8)',
  pointHoverBorderColor: 'rgba(220,220,220,1)',
  pointHoverBorderWidth: 2,
  pointRadius: 1,
  pointHitRadius: 10,
  spanGaps: false,
};

const lineChartOptions = {
  legend: {
    display: false,
  },
  scales: {
    yAxes: [{
      ticks: {
        beginAtZero: true,
        suggestedMax: 5,
        stepSize: 1,
      },
    }],
  },
  animation: {
    duration: 0,
  },
};

const barOptions = {
  label: 'Tomato completed',
  backgroundColor: 'rgba(95,210,219,0.4)',
  borderColor: 'rgba(95,210,219,0.8)',
  borderWidth: 1,
};

const barChartOptions = {
  legend: {
    display: false,
  },
  scales: {
    yAxes: [{
      ticks: {
        beginAtZero: true,
        stepSize: 1,
        suggestedMax: 5,
      },
    }],
  },
  animation: {
    duration: 0,
  },
};

const getLineChartData = function getLineChartData(tasks) {
  const xData = [];
  const yData = [];
  // const keys = Object.keys(tasks);
  let day;
  let count;
  const trimLength = -21;

  Object.keys(tasks).forEach((key) => {
    day = moment(key);
    xData.push(day.date());

    count = getDoneCount(tasks[key]);
    yData.push(count);
  });

  return { xData: xData.slice(trimLength), yData: yData.slice(trimLength) };
};

const getBarChartData = function getBarChartData(tasks) {
  const distribution = {
  };

  for (let i = 0; i < 24; i++) {
    distribution[i] = 0;
  }

  Object.keys(tasks).forEach((key) => {
    tasks[key].forEach((task) => {
      if (task.status === taskStatus.done && task.startTime) {
        const starthour = moment(task.startTime).hour();
        distribution[starthour]++;
      }
    });
  });

  const yData = [];
  Object.keys(distribution).forEach((key) => {
    yData.push(distribution[key]);
  });

  return { xData: Object.keys(distribution), yData };
};

const initChart = function initChart(id, xData, yData, type, dataOptions, options) {
  const ctx = document.getElementById(id);

  const data = {
    labels: xData,
    datasets: [Object.assign(dataOptions, {
      data: yData,
    })],
  };

  const myChart = new Chart(ctx, { /* eslint no-unused-vars: 0 */
    type,
    data,
    options,
  });

  return myChart;
};

let lineChart;
let barChart;

const initUsage = function initUsage() {
  return getTasksData()
  .then((data) => fillTasks(data))
  .then((data) => {
    getUsageData(data);
    return data;
  })
  .then((data) => {
    const lineChartData = getLineChartData(data);
    window.setTimeout(() => {
      if (lineChart) {
        lineChart.destroy();
      }
      lineChart = initChart('line-chart',
        lineChartData.xData,
        lineChartData.yData,
        'line',
        lineOptions,
        lineChartOptions
      );
    }, 1);

    const barChartData = getBarChartData(data);
    window.setTimeout(() => {
      if (barChart) {
        barChart.destroy();
      }
      barChart = initChart('bar-chart',
        barChartData.xData,
        barChartData.yData,
        'bar',
        barOptions,
        barChartOptions
      );
    }, 1);

    return data;
  });
};

const onRecount = function onRecount() {
  initUsage();
};

const init = function init() {
  initUsage().then(() => {
    this.loaded = true;
  });
};

export default {
  data() {
    return { usageData, loaded };
  },
  created: init,
  events: {
    recount: onRecount,
  },
  methods: {
  },
};
</script>

<style lang="scss" scoped>
@import "../base";

.usage {
  margin-top: 50px;

  h2 {
    margin-bottom: 50px;
  }

  .chartTitle {
    text-align: center;
    margin: 40px 0 15px 0;
  }

  .streak {
    margin-top: 50px;
  }
}
</style>


================================================
FILE: src/configs.js
================================================
/* eslint import/no-mutable-exports: 0 */

let taskDurations = {
  standard: 1500,
  resting: 300,
};

let offLineThreshold = 60; // minutes
let checkMemberInterval = 1000; // ms

let firebaseConfig = {
  apiKey: 'AIzaSyCbB0yB-qabmzQ_STyAngEd5-D0MypBbBE', // This is not a private key
  authDomain: 'tomato5-pro.firebaseapp.com',
  databaseURL: 'https://tomato5-pro.firebaseio.com',
  storageBucket: 'tomato5-pro.appspot.com',
};

if (window.location.hostname === 'localhost'
    || window.location.hostname === 'tomato5-dev.firebaseapp.com') {
  taskDurations = {
    standard: 5,
    resting: 3,
  };

  offLineThreshold = 1;
  checkMemberInterval = 1000;

  firebaseConfig = {
    apiKey: 'AIzaSyBanxSDWrWUKStXGi8lSQtA1AeMmGDNmks',
    authDomain: 'tomato5-dev.firebaseapp.com',
    databaseURL: 'https://tomato5-dev.firebaseio.com',
    storageBucket: 'tomato5-dev.appspot.com',
  };
}

export { taskDurations, offLineThreshold, checkMemberInterval, firebaseConfig };


================================================
FILE: src/dashboard.js
================================================
import Vue from 'vue';
import VueValidator from 'vue-validator';
import App from './App';

Vue.use(VueValidator);

/* eslint-disable no-new */
new Vue({
  el: 'body',
  components: { App },
});


================================================
FILE: src/dataBase.js
================================================
import firebase from 'firebase';

let database = null;

const init = function init() {
  database = firebase.database();
};

const watch = function watch(path, callback) {
  const ref = database.ref(path);
  ref.on('value', callback);
  return ref;
};

const get = function get(path, defaultData, validator) {
  const promise = new Promise((resolve, reject) => {
    // console.log("Checking remote");
    database.ref(path).once('value', (snapshot) => {
      const data = snapshot.val();

      // console.log('Reveived Data');
      if ((validator && validator(data)) || (!validator && data)) {
        // console.log('Valid Data');
        resolve(data);
      } else {
        // console.log('No valid Data');
        // ref.set(defaultData).then(function(){
        //   console.log("Default setted");
        //   resolve(defaultData);
        // });
        resolve(defaultData);
      }
    }, () => {
      reject();
    });
  });

  return promise;
};

const save = function save(path, data) {
  const parsedData = JSON.parse(JSON.stringify(data));
  return database.ref(path).set(parsedData).then(() => {
    // console.log('Saved');
  });
};

export default { init, get, save, watch };


================================================
FILE: src/database.js
================================================
import firebase from 'firebase';

let database = null;

const init = function init() {
  database = firebase.database();
};

const watch = function watch(path, callback) {
  const ref = database.ref(path);
  ref.on('value', callback);
  return ref;
};

const get = function get(path, defaultData, validator) {
  const promise = new Promise((resolve, reject) => {
    // console.log("Checking remote");
    database.ref(path).once('value', (snapshot) => {
      const data = snapshot.val();

      // console.log('Reveived Data');
      if ((validator && validator(data)) || (!validator && data)) {
        // console.log('Valid Data');
        resolve(data);
      } else {
        // console.log('No valid Data');
        // ref.set(defaultData).then(function(){
        //   console.log("Default setted");
        //   resolve(defaultData);
        // });
        resolve(defaultData);
      }
    }, () => {
      reject();
    });
  });

  return promise;
};

const save = function save(path, data) {
  const parsedData = JSON.parse(JSON.stringify(data));
  return database.ref(path).set(parsedData).then(() => {
    // console.log('Saved');
  });
};

export default { init, get, save, watch };


================================================
FILE: src/index.js
================================================
import normalizeCss from 'normalize.css'; /* eslint no-unused-vars: 0 */
import animateCss from 'animate.css'; /* eslint no-unused-vars: 0 */
import FastClick from 'fastclick';

import commonCss from './common.scss'; /* eslint no-unused-vars: 0 */
import indexCss from './index.scss'; /* eslint no-unused-vars: 0 */
import utils from './utils';

const init = function initApp() {
  if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', () => {
      FastClick.attach(document.body);
    }, false);
  }

  const initAnimation = function initAnimation() {
    setTimeout(() => {
      const btn = document.getElementById('startNow');
      btn.className += ' animated tada ready';
    }, 2000);

    setTimeout(() => {
      const welcome = document.getElementById('welcome');
      const instruction = document.getElementById('instruction');

      if (window.scrollY === 0) {
        instruction.className += ' noScroll';
      }

      welcome.className += ' ready';
      instruction.className += ' pop';
    }, 1500);
  };

  initAnimation();

  utils.report('workflow', 'initIndex');
};

init();


================================================
FILE: src/index.scss
================================================
@import 'base';

$longScreenHeight: 600px;
$wideScreenWidth: 600px;

.welcome {
  height: 900px;
  min-height: 450px;
  box-sizing: border-box;
  transition: padding 0.5s;

  padding-top: 50px;
  @media (min-height: $longScreenHeight) {
      padding-top: 100px;
  }

  &.ready {
    height: 100%;
  }

  .brand {
    text-align: center;

    .title {
      margin: auto;
    }

    .slogan {
      margin-top: 50px;
      text-align: center;
    }
  }

  .indexStart {
    text-align: center;
    margin: 100px 0 100px 0;

    a {
      display: block;
      background-color: $red;
      width: 150px;
      margin: auto;

      color: white;
      line-height: 55px;
      font-size: $font-size-l;
      border-radius: 15px;
    }

    a.ready {
      transition: all 0.5s;
    }

    a:hover {
      opacity: 0.8;
    }
    a:active {
      position: relative;
      top: 2px;
    }
  }
}

.introduction {
  // font-family: proxima-nova,Helvetica Neue,Helvetica,sans-serif;
  section {
    padding: 50px 10%;
    clear: both;
  }

  h2 {
    font-size: $font-size-xl;
  }

  &.noScroll.pop .what {
    transition: all 0.5s ease-out;
  }

  &.pop {
    .what {
      top: -20px;
    }

    .features h2 {
      margin-top: 10px;
    }
  }

  @media (min-height: $longScreenHeight) {
    &.pop {
      .what {
        top: -120px;
      }

      .features h2 {
        margin-top: -90px;
      }
    }
  }

  .what {
    position: relative;
    top: 0px;
    color: white;
    text-align: center;
    background-color: $red;
    padding-left: 0;
    padding-right: 0;
    font-size: $font-size-m;

    h2 {
      margin: 20px 0 25px 10px;
    }
  }

  .features {
    background-color: white;
    max-width: 800px;
    margin: auto;

    h2 {
      color: $green;
      margin: 30px 0 20px 0;
    }

    @media (min-width: $wideScreenWidth) {
      h2 {
        margin-bottom: 60px;
      }
    }

    .feature {
      margin-bottom: 50px;
      .detail {
        font-size: $font-size-l;
        color: $green;
        margin: 40px 0;
      }

      img {
        display: block;
        width: 100%;
      }
    }

    @media (min-width: $wideScreenWidth) {
      .feature {
        .detail {
          float: left;
          width: 50%;
          line-height: 250px;
          text-align: center;
        }

        &.flowers {
          margin: 100px 0;
          .detail {
            line-height: 150px;
          }
        }

        &.bubble {
          margin: 70px 0;
          .detail {
            line-height: 150px;
          }
        }

        img {
          float: right;
          width: 50%;
        }

        &.right {
          img {
            float: left;
          }
          .detail {
            float: right;
            // text-align: left;
          }
        }
      }
    }


  }

  .why {
    background-color: $green;
    color: white;
    font-size: $font-size-s;
    line-height: 1.5;

    strong {
      font-size: $font-size-l;
    }

    h2 {
      margin-top: 10px;
    }
  }

  .tools {
    img {
      display: block;
      width: 100%;
      max-width: 400px;
      margin: 70px auto;
    }
    h2 {
      font-size: $font-size-l;
    }
  }
}

.indexFooter {
  margin: 50px 0 0 0;;
  font-size: $font-size-l;
  text-align: center;
  line-height: 120px;

  // background-color: $blue;
  a {
    // color: white;
  }
}


================================================
FILE: src/model.js
================================================
import moment from 'moment';

const taskStatus = {
  dropped: -1,
  idle: 0,
  ongoing: 1,
  paused: 2,
  active: 3,
  done: 4,
};

const availabilities = {
  unknown: -1,
  idle: 0,
  active: 1,
  resting: 2,
  busy: 3,
};

const taskTips = [
  '✎ Write about what you are doing',
  'Click tomato to start',
  'After finishing, take a rest',
  'Restart if you are disturbed',
  'Share status with your team',
];

const getDefaultTasks = function getDefaultTasks() {
  const tasks = [];
  for (let i = 0; i < 5; i++) {
    // Avoid same object ref
    const defaultTask = {
      note: '',
      status: taskStatus.idle,
      startTime: null,
      createTime: moment(),
      emotion: 2,
    };

    tasks.push(defaultTask);
  }
  return tasks;
};

const userStatus = {
  availability: availabilities.idle,
  emotion: '2',
};

const team = {
  info: {
    inviteCode: 'HFE-Train',
    name: 'HFE-Train',
  },
  common: {
    messages: {
      DATE: {
        headline: '',
        speeches: {
          UID: {
            content: '',
            isShowSpeech: false,
          },
        },
      },
    },
    flowers: {
      DATE: {
        UID: {
          count: 0,
        },
      },
    },
  },
  members: {
    UID: {
      rule: 'member',
      userInfo: {},
      userStatus: {},
      tasks: [],
      updateTime: '', // moment
    },
  },
};

const userData = {
  teamData: {
    currentTeam: '',
  },
  systemData: {
    rule: 'normal',
  },
};

export { taskStatus, taskTips, availabilities, getDefaultTasks, team, userData, userStatus };


================================================
FILE: src/timer.js
================================================
import moment from 'moment';
import { taskDurations } from './configs';

const getRemaning = function getRemaning(startTime, type) {
  const duration = moment.duration(taskDurations[type], 'seconds');
  const elapsed = moment().diff(startTime, 'seconds');
  const remaning = duration.subtract(elapsed, 'seconds');

  return remaning;
};

const getContdown = function getContdown(startTime, type, level) {
  const remaning = getRemaning(startTime, type);

  let minutes = remaning.minutes() > 0 ? remaning.minutes() : 0;
  let seconds = remaning.seconds() > 0 ? remaning.seconds() : 0;
  let result = '';

  if (level === 'minute') {
    result = `${minutes + (seconds > 0 ? 1 : 0)}m`;
  } else {
    minutes = minutes > 9 ? `${minutes}` : `0${minutes}`;
    seconds = seconds > 9 ? `${seconds}` : `0${seconds}`;
    result = `${minutes}:${seconds}`;
  }

  return result;
};

export default { getRemaning, getContdown };


================================================
FILE: src/utils.js
================================================
const report = function report(eventCategory, eventAction, eventLabel, eventValue, fieldsObject) {
  if (window.ga) {
    window.ga('send', 'event', eventCategory, eventAction, eventLabel, eventValue, fieldsObject);
  }
};

const utils = {
  report,
};

export default utils;


================================================
FILE: static/.gitkeep
================================================


================================================
FILE: test/e2e/custom-assertions/elementCount.js
================================================
// A custom Nightwatch assertion.
// the name of the method is the filename.
// can be used in tests like this:
//
//   browser.assert.elementCount(selector, count)
//
// for how to write custom assertions see
// http://nightwatchjs.org/guide#writing-custom-assertions
exports.assertion = function (selector, count) {
  this.message = 'Testing if element <' + selector + '> has count: ' + count
  this.expected = count
  this.pass = function (val) {
    return val === this.expected
  }
  this.value = function (res) {
    return res.value
  }
  this.command = function (cb) {
    var self = this
    return this.api.execute(function (selector) {
      return document.querySelectorAll(selector).length
    }, [selector], function (res) {
      cb.call(self, res)
    })
  }
}


================================================
FILE: test/e2e/nightwatch.conf.js
================================================
// http://nightwatchjs.org/guide#settings-file
module.exports = {
  "src_folders": ["test/e2e/specs"],
  "output_folder": "test/e2e/reports",
  "custom_assertions_path": ["test/e2e/custom-assertions"],

  "selenium": {
    "start_process": true,
    "server_path": "node_modules/selenium-server/lib/runner/selenium-server-standalone-2.53.0.jar",
    "host": "127.0.0.1",
    "port": 4444,
    "cli_args": {
      "webdriver.chrome.driver": require('chromedriver').path
    }
  },

  "test_settings": {
    "default": {
      "selenium_port": 4444,
      "selenium_host": "localhost",
      "silent": true
    },

    "chrome": {
      "desiredCapabilities": {
        "browserName": "chrome",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    },

    "firefox": {
      "desiredCapabilities": {
        "browserName": "firefox",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    }
  }
}


================================================
FILE: test/e2e/runner.js
================================================
// 1. start the dev server using production config
process.env.NODE_ENV = 'testing'
var server = require('../../build/dev-server.js')

// 2. run the nightwatch test suite against it
// to run in additional browsers:
//    1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
//    2. add it to the --env flag below
// or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
// For more information on Nightwatch's config file, see
// http://nightwatchjs.org/guide#settings-file
var opts = process.argv.slice(2)
if (opts.indexOf('--config') === -1) {
  opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
}
if (opts.indexOf('--env') === -1) {
  opts = opts.concat(['--env', 'chrome'])
}

var spawn = require('cross-spawn')
var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })

runner.on('exit', function (code) {
  server.close()
  process.exit(code)
})

runner.on('error', function (err) {
  server.close()
  throw err
})


================================================
FILE: test/e2e/specs/test.js
================================================
// For authoring Nightwatch tests, see
// http://nightwatchjs.org/guide#usage

module.exports = {
  'default e2e tests': function (browser) {
    browser
    .url('http://localhost:8080')
      .waitForElementVisible('#app', 5000)
      .assert.elementPresent('.logo')
      .assert.containsText('h1', 'Hello World!')
      .assert.elementCount('p', 3)
      .end()
  }
}


================================================
FILE: test/unit/.eslintrc
================================================
{
  "env": {
    "mocha": true
  },
  "globals": {
    "expect": true,
    "sinon": true
  }
}


================================================
FILE: test/unit/index.js
================================================
// Polyfill fn.bind() for PhantomJS
/* eslint-disable no-extend-native */
Function.prototype.bind = require('function-bind')

// require all test files (files that ends with .spec.js)
var testsContext = require.context('./specs', true, /\.spec$/)
testsContext.keys().forEach(testsContext)

// require all src files except main.js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
var srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
srcContext.keys().forEach(srcContext)


================================================
FILE: test/unit/karma.conf.js
================================================
// This is a karma config file. For more details see
//   http://karma-runner.github.io/0.13/config/configuration-file.html
// we are also using it with karma-webpack
//   https://github.com/webpack/karma-webpack

var path = require('path')
var merge = require('webpack-merge')
var baseConfig = require('../../build/webpack.base.conf')
var utils = require('../../build/utils')
var webpack = require('webpack')
var projectRoot = path.resolve(__dirname, '../../')

var webpackConfig = merge(baseConfig, {
  // use inline sourcemap for karma-sourcemap-loader
  module: {
    loaders: utils.styleLoaders()
  },
  devtool: '#inline-source-map',
  vue: {
    loaders: {
      js: 'isparta'
    }
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../../config/test.env')
    })
  ]
})

// no need for app entry during tests
delete webpackConfig.entry

// make sure isparta loader is applied before eslint
webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || []
webpackConfig.module.preLoaders.unshift({
  test: /\.js$/,
  loader: 'isparta',
  include: path.resolve(projectRoot, 'src')
})

// only apply babel for test files when using isparta
webpackConfig.module.loaders.some(function (loader, i) {
  if (loader.loader === 'babel') {
    loader.include = path.resolve(projectRoot, 'test/unit')
    return true
  }
})

module.exports = function (config) {
  config.set({
    // to run in additional browsers:
    // 1. install corresponding karma launcher
    //    http://karma-runner.github.io/0.13/config/browsers.html
    // 2. add it to the `browsers` array below.
    browsers: ['PhantomJS'],
    frameworks: ['mocha', 'sinon-chai'],
    reporters: ['spec', 'coverage'],
    files: ['./index.js'],
    preprocessors: {
      './index.js': ['webpack', 'sourcemap']
    },
    webpack: webpackConfig,
    webpackMiddleware: {
      noInfo: true
    },
    coverageReporter: {
      dir: './coverage',
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' }
      ]
    }
  })
}


================================================
FILE: test/unit/specs/Hello.spec.js
================================================
import Vue from 'vue'
import Hello from 'src/components/Hello'

describe('Hello.vue', () => {
  it('should render correct contents', () => {
    const vm = new Vue({
      template: '<div><hello></hello></div>',
      components: { Hello }
    }).$mount()
    expect(vm.$el.querySelector('.hello h1').textContent).to.contain('Hello World!')
  })
})


================================================
FILE: vendors/firebase-ui-auth.css
================================================
/*! Terms: https://developers.google.com/terms */
@import url(https://fonts.googleapis.com/css?family=Roboto:400,500,700);.mdl-button{background:transparent;border:none;border-radius:2px;color:rgb(0,0,0);position:relative;height:36px;margin:0;min-width:64px;padding:0 16px;display:inline-block;font-family:"Roboto","Helvetica","Arial",sans-serif;font-size:14px;font-weight:500;text-transform:uppercase;line-height:1;letter-spacing:0;overflow:hidden;will-change:box-shadow;transition:box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1),background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1),color 0.2s cubic-bezier(0.4, 0, 0.2, 1);outline:none;cursor:pointer;text-decoration:none;text-align:center;line-height:36px;vertical-align:middle}.mdl-button::-moz-focus-inner{border:0}.mdl-button:hover{background-color:rgba(158,158,158, 0.20)}.mdl-button:focus:not(:active){background-color:rgba(0,0,0, 0.12)}.mdl-button:active{background-color:rgba(158,158,158, 0.40)}.mdl-button.mdl-button--colored{color:rgb(63,81,181)}.mdl-button.mdl-button--colored:focus:not(:active){background-color:rgba(0,0,0, 0.12)}input.mdl-button[type="submit"]{-webkit-appearance:none}.mdl-button--raised{background:rgba(158,158,158, 0.20);box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.2),0 1px 5px 0 rgba(0,0,0,0.12)}.mdl-button--raised:active{box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.2);background-color:rgba(158,158,158, 0.40)}.mdl-button--raised:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,0.18),0 8px 16px rgba(0,0,0,0.36);background-color:rgba(158,158,158, 0.40)}.mdl-button--raised.mdl-button--colored{background:rgb(63,81,181);color:rgb(255,255,255)}.mdl-button--raised.mdl-button--colored:hover{background-color:rgb(63,81,181)}.mdl-button--raised.mdl-button--colored:active{background-color:rgb(63,81,181)}.mdl-button--raised.mdl-button--colored:focus:not(:active){background-color:rgb(63,81,181)}.mdl-button--raised.mdl-button--colored .mdl-ripple{background:rgb(255,255,255)}.mdl-button--fab{border-radius:50%;font-size:24px;height:56px;margin:auto;min-width:56px;width:56px;padding:0;overflow:hidden;background:rgba(158,158,158, 0.20);box-shadow:0 1px 1.5px 0 rgba(0,0,0,0.12),0 1px 1px 0 rgba(0,0,0,0.24);position:relative;line-height:normal}.mdl-button--fab .material-icons{position:absolute;top:50%;left:50%;transform:translate(-12px, -12px);line-height:24px;width:24px}.mdl-button--fab.mdl-button--mini-fab{height:40px;min-width:40px;width:40px}.mdl-button--fab .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle, #fff, #000)}.mdl-button--fab:active{box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.2);background-color:rgba(158,158,158, 0.40)}.mdl-button--fab:focus:not(:active){box-shadow:0 0 8px rgba(0,0,0,0.18),0 8px 16px rgba(0,0,0,0.36);background-color:rgba(158,158,158, 0.40)}.mdl-button--fab.mdl-button--colored{background:rgb(255,64,129);color:rgb(255,255,255)}.mdl-button--fab.mdl-button--colored:hover{background-color:rgb(255,64,129)}.mdl-button--fab.mdl-button--colored:focus:not(:active){background-color:rgb(255,64,129)}.mdl-button--fab.mdl-button--colored:active{background-color:rgb(255,64,129)}.mdl-button--fab.mdl-button--colored .mdl-ripple{background:rgb(255,255,255)}.mdl-button--icon{border-radius:50%;font-size:24px;height:32px;margin-left:0;margin-right:0;min-width:32px;width:32px;padding:0;overflow:hidden;color:inherit;line-height:normal}.mdl-button--icon .material-icons{position:absolute;top:50%;left:50%;transform:translate(-12px, -12px);line-height:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon{height:24px;min-width:24px;width:24px}.mdl-button--icon.mdl-button--mini-icon .material-icons{top:0px;left:0px}.mdl-button--icon .mdl-button__ripple-container{border-radius:50%;-webkit-mask-image:-webkit-radial-gradient(circle, #fff, #000)}.mdl-button__ripple-container{display:block;height:100%;left:0px;position:absolute;top:0px;width:100%;z-index:0;overflow:hidden}.mdl-button[disabled] .mdl-button__ripple-container .mdl-ripple,.mdl-button.mdl-button--disabled .mdl-button__ripple-container .mdl-ripple{background-color:transparent}.mdl-button--primary.mdl-button--primary{color:rgb(63,81,181)}.mdl-button--primary.mdl-button--primary .mdl-ripple{background:rgb(255,255,255)}.mdl-button--primary.mdl-button--primary.mdl-button--raised,.mdl-button--primary.mdl-button--primary.mdl-button--fab{color:rgb(255,255,255);background-color:rgb(63,81,181)}.mdl-button--accent.mdl-button--accent{color:rgb(255,64,129)}.mdl-button--accent.mdl-button--accent .mdl-ripple{background:rgb(255,255,255)}.mdl-button--accent.mdl-button--accent.mdl-button--raised,.mdl-button--accent.mdl-button--accent.mdl-button--fab{color:rgb(255,255,255);background-color:rgb(255,64,129)}.mdl-button[disabled][disabled],.mdl-button.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0, 0.26);cursor:default;background-color:transparent}.mdl-button--fab[disabled][disabled],.mdl-button--fab.mdl-button--disabled.mdl-button--disabled{background-color:rgba(0,0,0, 0.12);color:rgba(0,0,0, 0.26)}.mdl-button--raised[disabled][disabled],.mdl-button--raised.mdl-button--disabled.mdl-button--disabled{background-color:rgba(0,0,0, 0.12);color:rgba(0,0,0, 0.26);box-shadow:none}.mdl-button--colored[disabled][disabled],.mdl-button--colored.mdl-button--disabled.mdl-button--disabled{color:rgba(0,0,0, 0.26)}.mdl-button .material-icons{vertical-align:middle}
.mdl-card{display:flex;flex-direction:column;font-size:16px;font-weight:400;min-height:200px;overflow:hidden;width:330px;z-index:1;position:relative;background:rgb(255,255,255);border-radius:2px;box-sizing:border-box}.mdl-card__media{background-color:rgb(255,64,129);background-repeat:repeat;background-position:50% 50%;background-size:cover;background-origin:padding-box;background-attachment:scroll;box-sizing:border-box}.mdl-card__title{align-items:center;color:rgb(0,0,0);display:block;display:flex;justify-content:stretch;line-height:normal;padding:16px 16px;perspective-origin:165px 56px;transform-origin:165px 56px;box-sizing:border-box}.mdl-card__title.mdl-card--border{border-bottom:1px solid rgba(0,0,0,0.1)}.mdl-card__title-text{align-self:flex-end;color:inherit;display:block;display:flex;font-size:24px;font-weight:300;line-height:normal;overflow:hidden;transform-origin:149px 48px;margin:0}.mdl-card__subtitle-text{font-size:14px;color:rgba(0,0,0, 0.54);margin:0}.mdl-card__supporting-text{color:rgba(0,0,0, 0.54);font-size:1rem;line-height:18px;overflow:hidden;padding:16px 16px;width:90%}.mdl-card__actions{font-size:16px;line-height:normal;width:100%;background-color:transparent;padding:8px;box-sizing:border-box}.mdl-card__actions.mdl-card--border{border-top:1px solid rgba(0,0,0,0.1)}.mdl-card--expand{flex-grow:1}.mdl-card__menu{position:absolute;right:16px;top:16px}
.mdl-progress{display:block;position:relative;height:4px;width:500px;max-width:100%}.mdl-progress>.bar{display:block;position:absolute;top:0;bottom:0;width:0%;transition:width 0.2s cubic-bezier(0.4, 0, 0.2, 1)}.mdl-progress>.progressbar{background-color:rgb(63,81,181);z-index:1;left:0}.mdl-progress>.bufferbar{background-image:linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)),linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));z-index:0;left:0}.mdl-progress>.auxbar{right:0}@supports (-webkit-appearance: none){.mdl-progress:not(.mdl-progress--indeterminate):not(.mdl-progress--indeterminate)>.auxbar,.mdl-progress:not(.mdl-progress__indeterminate):not(.mdl-progress__indeterminate)>.auxbar{background-image:linear-gradient(to right, rgba(255,255,255, 0.7), rgba(255,255,255, 0.7)),linear-gradient(to right, rgb(63,81,181), rgb(63,81,181));mask:url("/images/buffer.svg?embed")}}.mdl-progress:not(.mdl-progress--indeterminate)>.auxbar,.mdl-progress:not(.mdl-progress__indeterminate)>.auxbar{background-image:linear-gradient(to right, rgba(255,255,255, 0.9), rgba(255,255,255, 0.9)),linear-gradient(to right, rgb(63,81,181), rgb(63,81,181))}.mdl-progress.mdl-progress--indeterminate>.bar1,.mdl-progress.mdl-progress__indeterminate>.bar1{background-color:rgb(63,81,181);animation-name:indeterminate1;animation-duration:2s;animation-iteration-count:infinite;animation-timing-function:linear}.mdl-progress.mdl-progress--indeterminate>.bar3,.mdl-progress.mdl-progress__indeterminate>.bar3{background-image:none;background-color:rgb(63,81,181);animation-name:indeterminate2;animation-duration:2s;animation-iteration-count:infinite;animation-timing-function:linear}@keyframes indeterminate1{0%{left:0%;width:0%}50%{left:25%;width:75%}75%{left:100%;width:0%}}@keyframes indeterminate2{0%{left:0%;width:0%}50%{left:0%;width:0%}75%{left:0%;width:25%}100%{left:100%;width:0%}}
.mdl-shadow--2dp{box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.2),0 1px 5px 0 rgba(0,0,0,0.12)}.mdl-shadow--3dp{box-shadow:0 3px 4px 0 rgba(0,0,0,0.14),0 3px 3px -2px rgba(0,0,0,0.2),0 1px 8px 0 rgba(0,0,0,0.12)}.mdl-shadow--4dp{box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.2)}.mdl-shadow--6dp{box-shadow:0 6px 10px 0 rgba(0,0,0,0.14),0 1px 18px 0 rgba(0,0,0,0.12),0 3px 5px -1px rgba(0,0,0,0.2)}.mdl-shadow--8dp{box-shadow:0 8px 10px 1px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.2)}.mdl-shadow--16dp{box-shadow:0 16px 24px 2px rgba(0,0,0,0.14),0 6px 30px 5px rgba(0,0,0,0.12),0 8px 10px -5px rgba(0,0,0,0.2)}.mdl-shadow--24dp{box-shadow:0 9px 46px 8px rgba(0,0,0,0.14),0 11px 15px -7px rgba(0,0,0,0.12),0 24px 38px 3px rgba(0,0,0,0.2)}
.mdl-textfield{position:relative;font-size:16px;display:inline-block;box-sizing:border-box;width:300px;max-width:100%;margin:0;padding:20px 0}.mdl-textfield .mdl-button{position:absolute;bottom:20px}.mdl-textfield--align-right{text-align:right}.mdl-textfield--full-width{width:100%}.mdl-textfield--expandable{min-width:32px;width:auto;min-height:32px}.mdl-textfield__input{border:none;border-bottom:1px solid rgba(0,0,0, 0.12);display:block;font-size:16px;font-family:"Helvetica","Arial",sans-serif;margin:0;padding:4px 0;width:100%;background:none;text-align:left;color:inherit}.mdl-textfield__input[type="number"]{-moz-appearance:textfield}.mdl-textfield__input[type="number"]::-webkit-inner-spin-button,.mdl-textfield__input[type="number"]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.mdl-textfield.is-focused .mdl-textfield__input{outline:none}.mdl-textfield.is-invalid .mdl-textfield__input{border-color:rgb(213,0,0);box-shadow:none}fieldset[disabled] .mdl-textfield .mdl-textfield__input,.mdl-textfield.is-disabled .mdl-textfield__input{background-color:transparent;border-bottom:1px dotted rgba(0,0,0, 0.12);color:rgba(0,0,0, 0.26)}.mdl-textfield textarea.mdl-textfield__input{display:block}.mdl-textfield__label{bottom:0;color:rgba(0,0,0, 0.26);font-size:16px;left:0;right:0;pointer-events:none;position:absolute;display:block;top:24px;width:100%;overflow:hidden;white-space:nowrap;text-align:left}.mdl-textfield.is-dirty .mdl-textfield__label,.mdl-textfield.has-placeholder .mdl-textfield__label{visibility:hidden}.mdl-textfield--floating-label .mdl-textfield__label{transition-duration:0.2s;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1)}.mdl-textfield--floating-label.has-placeholder .mdl-textfield__label{transition:none}fieldset[disabled] .mdl-textfield .mdl-textfield__label,.mdl-textfield.is-disabled.is-disabled .mdl-textfield__label{color:rgba(0,0,0, 0.26)}.mdl-textfield--floating-label.is-focused .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__label,.mdl-textfield--floating-label.has-placeholder .mdl-textfield__label{color:rgb(63,81,181);font-size:12px;top:4px;visibility:visible}.mdl-textfield--floating-label.is-focused .mdl-textfield__expandable-holder .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__expandable-holder .mdl-textfield__label,.mdl-textfield--floating-label.has-placeholder .mdl-textfield__expandable-holder .mdl-textfield__label{top:-16px}.mdl-textfield--floating-label.is-invalid .mdl-textfield__label{color:rgb(213,0,0);font-size:12px}.mdl-textfield__label:after{background-color:rgb(63,81,181);bottom:20px;content:'';height:2px;left:45%;position:absolute;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);visibility:hidden;width:10px}.mdl-textfield.is-focused .mdl-textfield__label:after{left:0;visibility:visible;width:100%}.mdl-textfield.is-invalid .mdl-textfield__label:after{background-color:rgb(213,0,0)}.mdl-textfield__error{color:rgb(213,0,0);position:absolute;font-size:12px;margin-top:3px;visibility:hidden;display:block}.mdl-textfield.is-invalid .mdl-textfield__error{visibility:visible}.mdl-textfield__expandable-holder{display:inline-block;position:relative;margin-left:32px;transition-duration:0.2s;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);display:inline-block;max-width:0.1px}.mdl-textfield.is-focused .mdl-textfield__expandable-holder,.mdl-textfield.is-dirty .mdl-textfield__expandable-holder{max-width:600px}.mdl-textfield__expandable-holder .mdl-textfield__label:after{bottom:0}
.firebaseui-container{background-color:#fff;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;color:rgba(0,0,0,0.87);direction:ltr;font:16px Roboto,arial,sans-serif;margin:0 auto;max-width:360px;overflow:hidden;position:relative;width:100%}.firebaseui-card-header{padding:24px 24px 0 24px}.firebaseui-card-content{padding:0 24px}.firebaseui-card-footer{font-size:14px;line-height:36px;padding:8px 24px 24px 24px}.firebaseui-card-footer::after{clear:both;content:"";display:table}.firebaseui-title,.firebaseui-subtitle{color:rgba(0,0,0,0.87);direction:ltr;font-size:20px;font-weight:500;line-height:24px;margin:0;padding:0;text-align:left}.firebaseui-title{padding-bottom:20px}.firebaseui-subtitle{margin:16px 0}.firebaseui-text{color:rgba(0,0,0,0.87);direction:ltr;font-size:16px;line-height:24px;text-align:left}.firebaseui-text-emphasis{font-weight:700}.firebaseui-error{color:#dd2c00;direction:ltr;font-size:12px;line-height:16px;margin:-16px 0 16px;text-align:left}.firebaseui-error-wrapper{min-height:16px}.firebaseui-list-item{direction:ltr;margin:0;padding:0;text-align:left}.firebaseui-hidden{display:none}.firebaseui-relative-wrapper{position:relative}.firebaseui-label{color:rgba(0,0,0,0.54);direction:ltr;font-size:16px}.mdl-textfield--floating-label.is-focused .mdl-textfield__label,.mdl-textfield--floating-label.is-dirty .mdl-textfield__label{color:#757575}.firebaseui-input,.firebaseui-input-invalid{border-radius:0;color:rgba(0,0,0,0.87);direction:ltr;font-size:16px;width:100%}.firebaseui-input-invalid{border-color:#dd2c00}.firebaseui-textfield{width:100%}.firebaseui-textfield.mdl-textfield .firebaseui-input{border-color:rgba(0,0,0,0.12)}.firebaseui-textfield.mdl-textfield .firebaseui-label::after{background-color:#3f51b5}.firebaseui-textfield-invalid.mdl-textfield .firebaseui-input{border-color:#dd2c00}.firebaseui-textfield-invalid.mdl-textfield .firebaseui-label::after{background-color:#dd2c00}.firebaseui-button{display:inline-block;height:36px;margin-left:8px;min-width:88px}.firebaseui-link{color:#4285f4;font-variant:normal;font-weight:normal;text-decoration:none}.firebaseui-link:hover{text-decoration:underline}.firebaseui-form-actions{direction:ltr;float:right}.firebaseui-edit-action{background:url(https://www.gstatic.com/authtoolkit/image/edit.png) top left no-repeat;display:inline-block;line-height:25px;padding-left:30px}.firebaseui-remove-action{background:url(https://www.gstatic.com/authtoolkit/image/trash.png) top left no-repeat;display:inline-block;height:24px;margin-left:22px;width:24px}.firebaseui-indent{margin-left:1em}.firebaseui-tos{color:#757575;direction:ltr;font-size:12px;line-height:16px;margin-bottom:24px;text-align:left}.firebaseui-recommend-option{margin-bottom:24px;margin-top:-24px}.firebaseui-page-provider-sign-in{background:inherit}.firebaseui-idp-list{list-style:none;margin:1em 0;padding:0}.firebaseui-idp-button{direction:ltr;font-weight:500;height:40px;max-width:220px;padding-left:16px;text-align:left;width:100%}.firebaseui-idp-list>.firebaseui-list-item{margin-bottom:15px;text-align:center}.firebaseui-idp-icon{border:none;display:inline-block;height:18px;vertical-align:middle;width:18px}.firebaseui-idp-favicon{border:none;display:inline-block;height:14px;margin-right:5px;vertical-align:middle;width:14px}.firebaseui-idp-text{color:#fff;font-size:14px;padding-left:16px;text-transform:none;vertical-align:middle}.firebaseui-idp-text.firebaseui-idp-text-long{display:inline-block}.firebaseui-idp-text.firebaseui-idp-text-short{display:none}@media (max-width:268px){.firebaseui-idp-text.firebaseui-idp-text-long{display:none}.firebaseui-idp-text.firebaseui-idp-text-short{display:inline-block}}.firebaseui-idp-password,.firebaseui-idp-password:hover{background-color:#db4437}.firebaseui-idp-google,.firebaseui-idp-google:hover{background-color:#ffffff}.firebaseui-idp-google>.firebaseui-idp-text{color:#757575}.firebaseui-idp-github,.firebaseui-idp-github:hover{background-color:#333333}.firebaseui-idp-facebook,.firebaseui-idp-facebook:hover{background-color:#3b5998}.firebaseui-idp-twitter,.firebaseui-idp-twitter:hover{background-color:#55acee}.firebaseui-account-photo{display:inline-block;height:48px;margin-left:15px;vertical-align:middle;width:48px}.firebaseui-avatar-with-idp{direction:ltr;margin:10px 16px;text-align:left}.firebaseui-info-bar{background-color:#f9edbe;border:1px solid #f0c36d;box-shadow:0 2px 4px rgba(0,0,0,0.2);-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.2);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.2);left:10%;padding:8px 16px;position:absolute;right:10%;text-align:center;top:0}.firebaseui-info-bar-message{font-size:12px;margin:0}.firebaseui-busy-indicator{height:2px;left:0;position:absolute;top:55px;width:100%}.firebaseui-callback-indicator-container .firebaseui-busy-indicator{top:0px}.firebaseui-callback-indicator-container{height:120px}.firebaseui-account-chip{background:#fff;border:1px solid #dcdcdc;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;display:inline-block;height:62px;padding:8px;position:relative;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;width:254px}.firebaseui-account-chip-photo{border:1px solid rgba(0,0,0,0.1);height:42px;left:8px;position:absolute;top:8px;width:42px}.firebaseui-account-chip-name,.firebaseui-account-chip-email,.firebaseui-account-chip-email-only{direction:ltr;display:block;line-height:18px;margin-left:52px;overflow:hidden;text-align:left;text-overflow:ellipsis}.firebaseui-account-chip-name{font-weight:bold;margin-top:2px;white-space:nowrap}.firebaseui-account-chip-email{color:#666}.firebaseui-account-chip-email-only{color:#111;font-weight:bold;margin-top:12px}.firebaseui-account-chip-email-label{display:inline-block;overflow:hidden;text-overflow:ellipsis;vertical-align:text-bottom;width:165px}.firebaseui-manage-account-list{list-style:none;padding:0}.firebaseui-manage-account-list>.firebaseui-list-item{padding:1px}.firebaseui-manage-account-list>.firebaseui-list-item:last-of-type{border-bottom:none}.firebaseui-account-chip-list{list-style:none;margin:1em 0;padding:0}.firebaseui-account-chip-list>.firebaseui-list-item{margin-bottom:24px}.firebaseui-new-password-component{display:inline-block;position:relative;width:100%}.firebaseui-input-floating-button{background-position:center;background-repeat:no-repeat;display:block;height:24px;position:absolute;right:0;top:20px;width:24px}.firebaseui-input-toggle-on{background-image:url("//www.gstatic.com/images/icons/material/system/1x/visibility_black_24dp.png")}.firebaseui-input-toggle-off{background-image:url("//www.gstatic.com/images/icons/material/system/1x/visibility_off_black_24dp.png")}.firebaseui-input-toggle-focus{opacity:0.87}.firebaseui-input-toggle-blur{opacity:0.38}@media (max-width:480px){.firebaseui-container{box-shadow:none;max-width:none;width:100%}.firebaseui-card-header{border-bottom:1px solid #e0e0e0;margin-bottom:16px;padding:16px 24px 0 24px}.firebaseui-title{padding-bottom:16px}.firebaseui-card-footer{padding-right:24px}.firebaseui-busy-indicator{top:0px}}.mdl-textfield__label{font-weight:normal;margin-bottom:0}


================================================
FILE: vendors/firebase-ui-auth.js
================================================
/*! Terms: https://developers.google.com/terms */
(function(){var h,l=this,aa=function(a){return void 0!==a},ba=function(){},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=
typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},da=function(a){return null!=a},fa=function(a){return"array"==ca(a)},ga=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},m=function(a){return"string"==typeof a},n=function(a){return"boolean"==typeof a},p=function(a){return"function"==ca(a)},ha=function(a){var b=typeof a;return"object"==b&&null!=
a||"function"==b},ia=function(a,b,c){return a.call.apply(a.bind,arguments)},ja=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}},q=function(a,b,c){q=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ia:ja;return q.apply(null,arguments)},ka=function(a,
b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},r=function(a,b){for(var c in b)a[c]=b[c]},la=Date.now||function(){return+new Date},ma=function(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)!c.length&&aa(b)?d[e]=b:d=d[e]?d[e]:d[e]={}},t=function(a,b){function c(){}c.prototype=b.prototype;a.superClass_=b.prototype;a.prototype=new c;a.prototype.constructor=
a;a.base=function(a,c,f){for(var g=Array(arguments.length-2),k=2;k<arguments.length;k++)g[k-2]=arguments[k];return b.prototype[c].apply(a,g)}};var u=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,u);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};t(u,Error);u.prototype.name="CustomError";var na;var oa=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join("%s")},pa=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},xa=function(a){if(!qa.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ra,"&amp;"));-1!=a.indexOf("<")&&(a=a.replace(sa,"&lt;"));-1!=a.indexOf(">")&&(a=a.replace(ta,"&gt;"));-1!=a.indexOf('"')&&(a=a.replace(ua,"&quot;"));-1!=a.indexOf("'")&&
(a=a.replace(va,"&#39;"));-1!=a.indexOf("\x00")&&(a=a.replace(wa,"&#0;"));return a},ra=/&/g,sa=/</g,ta=/>/g,ua=/"/g,va=/'/g,wa=/\x00/g,qa=/[\x00&<>"']/,ya=function(a,b){return a<b?-1:a>b?1:0};var za=function(a,b){b.unshift(a);u.call(this,oa.apply(null,b));b.shift()};t(za,u);za.prototype.name="AssertionError";
var Aa=function(a,b,c,d){var e="Assertion failed";if(c)var e=e+(": "+c),f=d;else a&&(e+=": "+a,f=b);throw new za(""+e,f||[]);},v=function(a,b,c){a||Aa("",null,b,Array.prototype.slice.call(arguments,2));return a},Ba=function(a,b){throw new za("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1));},Ca=function(a,b,c){"number"==typeof a||Aa("Expected number but got %s: %s.",[ca(a),a],b,Array.prototype.slice.call(arguments,2));return a},Da=function(a,b,c){m(a)||Aa("Expected string but got %s: %s.",
[ca(a),a],b,Array.prototype.slice.call(arguments,2));return a},Ea=function(a,b,c){p(a)||Aa("Expected function but got %s: %s.",[ca(a),a],b,Array.prototype.slice.call(arguments,2))},Fa=function(a,b,c){ha(a)||Aa("Expected object but got %s: %s.",[ca(a),a],b,Array.prototype.slice.call(arguments,2));return a},Ga=function(a,b,c){fa(a)||Aa("Expected array but got %s: %s.",[ca(a),a],b,Array.prototype.slice.call(arguments,2));return a};var Ha=Array.prototype.indexOf?function(a,b,c){v(null!=a.length);return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(m(a))return m(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},Ia=Array.prototype.forEach?function(a,b,c){v(null!=a.length);Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},Ja=Array.prototype.filter?function(a,
b,c){v(null!=a.length);return Array.prototype.filter.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=[],f=0,g=m(a)?a.split(""):a,k=0;k<d;k++)if(k in g){var w=g[k];b.call(c,w,k,a)&&(e[f++]=w)}return e},Ka=Array.prototype.map?function(a,b,c){v(null!=a.length);return Array.prototype.map.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=Array(d),f=m(a)?a.split(""):a,g=0;g<d;g++)g in f&&(e[g]=b.call(c,f[g],g,a));return e},La=Array.prototype.some?function(a,b,c){v(null!=a.length);return Array.prototype.some.call(a,
b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1},Ma=function(a,b){for(var c=a.length,d=m(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a))return e;return-1},Na=function(a,b){return 0<=Ha(a,b)},Pa=function(a,b){var c=Ha(a,b),d;(d=0<=c)&&Oa(a,c);return d},Oa=function(a,b){v(null!=a.length);Array.prototype.splice.call(a,b,1)},Qa=function(a){return Array.prototype.concat.apply(Array.prototype,arguments)},Ra=function(a){var b=
a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},Ta=function(a,b,c,d){v(null!=a.length);Array.prototype.splice.apply(a,Sa(arguments,1))},Sa=function(a,b,c){v(null!=a.length);return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var Ua=function(a){if(a.classList)return a.classList;a=a.className;return m(a)&&a.match(/\S+/g)||[]},Va=function(a,b){return a.classList?a.classList.contains(b):Na(Ua(a),b)},Wa=function(a,b){a.classList?a.classList.add(b):Va(a,b)||(a.className+=0<a.className.length?" "+b:b)},Xa=function(a,b){a.classList?a.classList.remove(b):Va(a,b)&&(a.className=Ja(Ua(a),function(a){return a!=b}).join(" "))};var Ya=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)},Za=function(a,b,c){if(null!==a&&b in a)throw Error('The object already contains the key "'+b+'"');a[b]=c},$a="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),ab=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<$a.length;f++)c=$a[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var bb;a:{var cb=l.navigator;if(cb){var db=cb.userAgent;if(db){bb=db;break a}}bb=""}var x=function(a){return-1!=bb.indexOf(a)};var fb=function(){this.stringConstValueWithSecurityContract__googStringSecurityPrivate_="";this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_=eb};fb.prototype.implementsGoogStringTypedString=!0;fb.prototype.getTypedStringValue=function(){return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_};fb.prototype.toString=function(){return"Const{"+this.stringConstValueWithSecurityContract__googStringSecurityPrivate_+"}"};
var gb=function(a){if(a instanceof fb&&a.constructor===fb&&a.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_===eb)return a.stringConstValueWithSecurityContract__googStringSecurityPrivate_;Ba("expected object of type Const, got '"+a+"'");return"type_error:Const"},eb={};var y=function(){this.privateDoNotAccessOrElseSafeHtmlWrappedValue_="";this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=hb};y.prototype.implementsGoogStringTypedString=!0;y.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_};y.prototype.getDirection=function(){return 1};y.prototype.toString=function(){return"SafeUrl{"+this.privateDoNotAccessOrElseSafeHtmlWrappedValue_+"}"};
var ib=function(a){if(a instanceof y&&a.constructor===y&&a.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===hb)return a.privateDoNotAccessOrElseSafeHtmlWrappedValue_;Ba("expected object of type SafeUrl, got '"+a+"' of type "+ca(a));return"type_error:SafeUrl"},jb=/^(?:(?:https?|mailto|ftp):|[^&:/?#]*(?:[/?#]|$))/i,lb=function(a){if(a instanceof y)return a;a=a.implementsGoogStringTypedString?a.getTypedStringValue():String(a);jb.test(a)||(a="about:invalid#zClosurez");return kb(a)},hb={},kb=function(a){var b=
new y;b.privateDoNotAccessOrElseSafeHtmlWrappedValue_=a;return b};kb("about:blank");var nb=function(){this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_="";this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=mb};nb.prototype.implementsGoogStringTypedString=!0;nb.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_};nb.prototype.getDirection=function(){return 1};nb.prototype.toString=function(){return"TrustedResourceUrl{"+this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_+"}"};
var ob=function(a){if(a instanceof nb&&a.constructor===nb&&a.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===mb)return a.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;Ba("expected object of type TrustedResourceUrl, got '"+a+"' of type "+ca(a));return"type_error:TrustedResourceUrl"},mb={};var qb=function(){this.privateDoNotAccessOrElseSafeHtmlWrappedValue_="";this.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=pb;this.dir_=null};qb.prototype.getDirection=function(){return this.dir_};qb.prototype.implementsGoogStringTypedString=!0;qb.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_};qb.prototype.toString=function(){return"SafeHtml{"+this.privateDoNotAccessOrElseSafeHtmlWrappedValue_+"}"};
var rb=function(a){if(a instanceof qb&&a.constructor===qb&&a.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===pb)return a.privateDoNotAccessOrElseSafeHtmlWrappedValue_;Ba("expected object of type SafeHtml, got '"+a+"' of type "+ca(a));return"type_error:SafeHtml"},pb={};qb.prototype.initSecurityPrivateDoNotAccessOrElse_=function(a){this.privateDoNotAccessOrElseSafeHtmlWrappedValue_=a;this.dir_=null;return this};var sb=x("Opera"),z=x("Trident")||x("MSIE"),tb=x("Edge"),ub=tb||z,vb=x("Gecko")&&!(-1!=bb.toLowerCase().indexOf("webkit")&&!x("Edge"))&&!(x("Trident")||x("MSIE"))&&!x("Edge"),A=-1!=bb.toLowerCase().indexOf("webkit")&&!x("Edge"),wb=A&&x("Mobile"),xb=x("Macintosh"),yb=function(){var a=l.document;return a?a.documentMode:void 0},zb;
a:{var Ab="",Bb=function(){var a=bb;if(vb)return/rv\:([^\);]+)(\)|;)/.exec(a);if(tb)return/Edge\/([\d\.]+)/.exec(a);if(z)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(A)return/WebKit\/(\S+)/.exec(a);if(sb)return/(?:Version)[ \/]?(\S+)/.exec(a)}();Bb&&(Ab=Bb?Bb[1]:"");if(z){var Cb=yb();if(null!=Cb&&Cb>parseFloat(Ab)){zb=String(Cb);break a}}zb=Ab}
var Db=zb,Eb={},B=function(a){var b;if(!(b=Eb[a])){b=0;for(var c=pa(String(Db)).split("."),d=pa(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",k=d[f]||"",w=RegExp("(\\d*)(\\D*)","g"),V=RegExp("(\\d*)(\\D*)","g");do{var D=w.exec(g)||["","",""],Vb=V.exec(k)||["","",""];if(0==D[0].length&&0==Vb[0].length)break;b=ya(0==D[1].length?0:parseInt(D[1],10),0==Vb[1].length?0:parseInt(Vb[1],10))||ya(0==D[2].length,0==Vb[2].length)||ya(D[2],Vb[2])}while(0==b)}b=Eb[a]=0<=
b}return b},Fb=l.document,Gb=Fb&&z?yb()||("CSS1Compat"==Fb.compatMode?parseInt(Db,10):5):void 0;var Hb=!z||9<=Number(Gb),Ib=!vb&&!z||z&&9<=Number(Gb)||vb&&B("1.9.1");z&&B("9");var Lb=function(a){return a?new Jb(Kb(a)):na||(na=new Jb)},Nb=function(a,b){var c=b||document;return c.querySelectorAll&&c.querySelector?c.querySelectorAll("."+a):Mb(a,b)},Mb=function(a,b){var c,d,e,f;c=document;c=b||c;if(c.querySelectorAll&&c.querySelector&&a)return c.querySelectorAll(""+(a?"."+a:""));if(a&&c.getElementsByClassName){var g=c.getElementsByClassName(a);return g}g=c.getElementsByTagName("*");if(a){f={};for(d=e=0;c=g[d];d++){var k=c.className;"function"==typeof k.split&&Na(k.split(/\s+/),
a)&&(f[e++]=c)}f.length=e;return f}return g},Pb=function(a,b){Ya(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:Ob.hasOwnProperty(d)?a.setAttribute(Ob[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})},Ob={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",nonce:"nonce",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",
width:"width"},Rb=function(a,b,c){function d(c){c&&b.appendChild(m(c)?a.createTextNode(c):c)}for(var e=2;e<c.length;e++){var f=c[e];!ga(f)||ha(f)&&0<f.nodeType?d(f):Ia(Qb(f)?Ra(f):f,d)}},Sb=function(a){a&&a.parentNode&&a.parentNode.removeChild(a)},Kb=function(a){v(a,"Node cannot be null or undefined.");return 9==a.nodeType?a:a.ownerDocument||a.document},Qb=function(a){if(a&&"number"==typeof a.length){if(ha(a))return"function"==typeof a.item||"string"==typeof a.item;if(p(a))return"function"==typeof a.item}return!1},
Ub=function(a){return Tb(a,function(a){return m(a.className)&&Na(a.className.split(/\s+/),"firebaseui-textfield")})},Tb=function(a,b){for(var c=0;a;){v("parentNode"!=a.name);if(b(a))return a;a=a.parentNode;c++}return null},Jb=function(a){this.document_=a||l.document||document};h=Jb.prototype;h.getDomHelper=Lb;h.getElement=function(a){return m(a)?this.document_.getElementById(a):a};h.getElementsByClass=function(a,b){return Nb(a,b||this.document_)};
h.getElementByClass=function(a,b){var c=b||this.document_,d=c||document;return(d.getElementsByClassName?d.getElementsByClassName(a)[0]:d.querySelectorAll&&d.querySelector?d.querySelector("."+a):Mb(a,c)[0])||null};
h.createDom=function(a,b,c){var d=this.document_,e=arguments,f=String(e[0]),g=e[1];if(!Hb&&g&&(g.name||g.type)){f=["<",f];g.name&&f.push(' name="',xa(g.name),'"');if(g.type){f.push(' type="',xa(g.type),'"');var k={};ab(k,g);delete k.type;g=k}f.push(">");f=f.join("")}f=d.createElement(f);g&&(m(g)?f.className=g:fa(g)?f.className=g.join(" "):Pb(f,g));2<e.length&&Rb(d,f,e);return f};h.createElement=function(a){return this.document_.createElement(String(a))};h.createTextNode=function(a){return this.document_.createTextNode(String(a))};
h.appendChild=function(a,b){a.appendChild(b)};h.getChildren=function(a){return Ib&&void 0!=a.children?a.children:Ja(a.childNodes,function(a){return 1==a.nodeType})};h.contains=function(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a};var Wb="StopIteration"in l?l.StopIteration:{message:"StopIteration",stack:""},Xb=function(){};Xb.prototype.next=function(){throw Wb;};Xb.prototype.__iterator__=function(){return this};var Yb=function(a,b){this.map_={};this.keys_=[];this.version_=this.count_=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.addAll(a)};Yb.prototype.getValues=function(){Zb(this);for(var a=[],b=0;b<this.keys_.length;b++)a.push(this.map_[this.keys_[b]]);return a};Yb.prototype.getKeys=function(){Zb(this);return this.keys_.concat()};Yb.prototype.containsKey=function(a){return $b(this.map_,a)};
Yb.prototype.remove=function(a){return $b(this.map_,a)?(delete this.map_[a],this.count_--,this.version_++,this.keys_.length>2*this.count_&&Zb(this),!0):!1};var Zb=function(a){if(a.count_!=a.keys_.length){for(var b=0,c=0;b<a.keys_.length;){var d=a.keys_[b];$b(a.map_,d)&&(a.keys_[c++]=d);b++}a.keys_.length=c}if(a.count_!=a.keys_.length){for(var e={},c=b=0;b<a.keys_.length;)d=a.keys_[b],$b(e,d)||(a.keys_[c++]=d,e[d]=1),b++;a.keys_.length=c}};h=Yb.prototype;
h.get=function(a,b){return $b(this.map_,a)?this.map_[a]:b};h.set=function(a,b){$b(this.map_,a)||(this.count_++,this.keys_.push(a),this.version_++);this.map_[a]=b};h.addAll=function(a){var b;if(a instanceof Yb)b=a.getKeys(),a=a.getValues();else{b=[];var c=0,d;for(d in a)b[c++]=d;c=[];d=0;for(var e in a)c[d++]=a[e];a=c}for(e=0;e<b.length;e++)this.set(b[e],a[e])};h.forEach=function(a,b){for(var c=this.getKeys(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};h.clone=function(){return new Yb(this)};
h.__iterator__=function(a){Zb(this);var b=0,c=this.version_,d=this,e=new Xb;e.next=function(){if(c!=d.version_)throw Error("The map has changed since the iterator was created");if(b>=d.keys_.length)throw Wb;var e=d.keys_[b++];return a?e:d.map_[e]};return e};var $b=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var C=function(a){var b=a.type;if(!aa(b))return null;switch(b.toLowerCase()){case "checkbox":case "radio":return a.checked?a.value:null;case "select-one":return b=a.selectedIndex,0<=b?a.options[b].value:null;case "select-multiple":for(var b=[],c,d=0;c=a.options[d];d++)c.selected&&b.push(c.value);return b.length?b:null;default:return aa(a.value)?a.value:null}};var ac=function(a){ac[" "](a);return a};ac[" "]=ba;var bc=!z||9<=Number(Gb),cc=z&&!B("9");!A||B("528");vb&&B("1.9b")||z&&B("8")||sb&&B("9.5")||A&&B("528");vb&&!B("8")||z&&B("9");var dc=function(){this.disposed_=this.disposed_;this.onDisposeCallbacks_=this.onDisposeCallbacks_};dc.prototype.disposed_=!1;dc.prototype.isDisposed=function(){return this.disposed_};dc.prototype.dispose=function(){this.disposed_||(this.disposed_=!0,this.disposeInternal())};var ec=function(a,b){a.disposed_?b.call(void 0):(a.onDisposeCallbacks_||(a.onDisposeCallbacks_=[]),a.onDisposeCallbacks_.push(aa(void 0)?q(b,void 0):b))};dc.prototype.disposeInternal=function(){if(this.onDisposeCallbacks_)for(;this.onDisposeCallbacks_.length;)this.onDisposeCallbacks_.shift()()};
var fc=function(a){a&&"function"==typeof a.dispose&&a.dispose()};var gc=function(a,b){this.type=a;this.currentTarget=this.target=b;this.defaultPrevented=this.propagationStopped_=!1;this.returnValue_=!0};gc.prototype.stopPropagation=function(){this.propagationStopped_=!0};gc.prototype.preventDefault=function(){this.defaultPrevented=!0;this.returnValue_=!1};var E=function(a,b){gc.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.event_=this.state=null;a&&this.init(a,b)};t(E,gc);
E.prototype.init=function(a,b){var c=this.type=a.type,d=a.changedTouches?a.changedTouches[0]:null;this.target=a.target||a.srcElement;this.currentTarget=b;var e=a.relatedTarget;if(e){if(vb){var f;a:{try{ac(e.nodeName);f=!0;break a}catch(g){}f=!1}f||(e=null)}}else"mouseover"==c?e=a.fromElement:"mouseout"==c&&(e=a.toElement);this.relatedTarget=e;null===d?(this.offsetX=A||void 0!==a.offsetX?a.offsetX:a.layerX,this.offsetY=A||void 0!==a.offsetY?a.offsetY:a.layerY,this.clientX=void 0!==a.clientX?a.clientX:
a.pageX,this.clientY=void 0!==a.clientY?a.clientY:a.pageY,this.screenX=a.screenX||0,this.screenY=a.screenY||0):(this.clientX=void 0!==d.clientX?d.clientX:d.pageX,this.clientY=void 0!==d.clientY?d.clientY:d.pageY,this.screenX=d.screenX||0,this.screenY=d.screenY||0);this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.event_=a;a.defaultPrevented&&
this.preventDefault()};E.prototype.stopPropagation=function(){E.superClass_.stopPropagation.call(this);this.event_.stopPropagation?this.event_.stopPropagation():this.event_.cancelBubble=!0};E.prototype.preventDefault=function(){E.superClass_.preventDefault.call(this);var a=this.event_;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,cc)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var hc="closure_listenable_"+(1E6*Math.random()|0),ic=function(a){return!(!a||!a[hc])},jc=0;var kc=function(a,b,c,d,e){this.listener=a;this.proxy=null;this.src=b;this.type=c;this.capture=!!d;this.handler=e;this.key=++jc;this.removed=this.callOnce=!1},lc=function(a){a.removed=!0;a.listener=null;a.proxy=null;a.src=null;a.handler=null};var mc=function(a){this.src=a;this.listeners={};this.typeCount_=0};mc.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.listeners[f];a||(a=this.listeners[f]=[],this.typeCount_++);var g=nc(a,b,d,e);-1<g?(b=a[g],c||(b.callOnce=!1)):(b=new kc(b,this.src,f,!!d,e),b.callOnce=c,a.push(b));return b};
mc.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.listeners))return!1;var e=this.listeners[a];b=nc(e,b,c,d);return-1<b?(lc(e[b]),Oa(e,b),0==e.length&&(delete this.listeners[a],this.typeCount_--),!0):!1};var oc=function(a,b){var c=b.type;c in a.listeners&&Pa(a.listeners[c],b)&&(lc(b),0==a.listeners[c].length&&(delete a.listeners[c],a.typeCount_--))};
mc.prototype.removeAll=function(a){a=a&&a.toString();var b=0,c;for(c in this.listeners)if(!a||c==a){for(var d=this.listeners[c],e=0;e<d.length;e++)++b,lc(d[e]);delete this.listeners[c];this.typeCount_--}return b};mc.prototype.getListener=function(a,b,c,d){a=this.listeners[a.toString()];var e=-1;a&&(e=nc(a,b,c,d));return-1<e?a[e]:null};var nc=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.listener==b&&f.capture==!!c&&f.handler==d)return e}return-1};var pc="closure_lm_"+(1E6*Math.random()|0),qc={},rc=0,sc=function(a,b,c,d,e){if(fa(b)){for(var f=0;f<b.length;f++)sc(a,b[f],c,d,e);return null}c=tc(c);return ic(a)?a.listen(b,c,d,e):uc(a,b,c,!1,d,e)},uc=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var g=!!e,k=vc(a);k||(a[pc]=k=new mc(a));c=k.add(b,c,d,e,f);if(c.proxy)return c;d=wc();c.proxy=d;d.src=a;d.listener=c;if(a.addEventListener)a.addEventListener(b.toString(),d,g);else if(a.attachEvent)a.attachEvent(xc(b.toString()),d);else throw Error("addEventListener and attachEvent are unavailable.");
rc++;return c},wc=function(){var a=yc,b=bc?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b},zc=function(a,b,c,d,e){if(fa(b)){for(var f=0;f<b.length;f++)zc(a,b[f],c,d,e);return null}c=tc(c);return ic(a)?a.listenOnce(b,c,d,e):uc(a,b,c,!0,d,e)},Ac=function(a,b,c,d,e){if(fa(b))for(var f=0;f<b.length;f++)Ac(a,b[f],c,d,e);else c=tc(c),ic(a)?a.unlisten(b,c,d,e):a&&(a=vc(a))&&(b=a.getListener(b,c,!!d,e))&&Bc(b)},Bc=function(a){if("number"!=
typeof a&&a&&!a.removed){var b=a.src;if(ic(b))oc(b.eventTargetListeners_,a);else{var c=a.type,d=a.proxy;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(xc(c),d);rc--;(c=vc(b))?(oc(c,a),0==c.typeCount_&&(c.src=null,b[pc]=null)):lc(a)}}},xc=function(a){return a in qc?qc[a]:qc[a]="on"+a},Dc=function(a,b,c,d){var e=!0;if(a=vc(a))if(b=a.listeners[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.removed&&(f=Cc(f,d),e=e&&!1!==f)}return e},
Cc=function(a,b){var c=a.listener,d=a.handler||a.src;a.callOnce&&Bc(a);return c.call(d,b)},yc=function(a,b){if(a.removed)return!0;if(!bc){var c;if(!(c=b))a:{c=["window","event"];for(var d=l,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break a}c=d}e=c;c=new E(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(w){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,g=
e.length-1;!c.propagationStopped_&&0<=g;g--){c.currentTarget=e[g];var k=Dc(e[g],f,!0,c),d=d&&k}for(g=0;!c.propagationStopped_&&g<e.length;g++)c.currentTarget=e[g],k=Dc(e[g],f,!1,c),d=d&&k}return d}return Cc(a,new E(b,this))},vc=function(a){a=a[pc];return a instanceof mc?a:null},Ec="__closure_events_fn_"+(1E9*Math.random()>>>0),tc=function(a){v(a,"Listener can not be null.");if(p(a))return a;v(a.handleEvent,"An object listener must have handleEvent method.");a[Ec]||(a[Ec]=function(b){return a.handleEvent(b)});
return a[Ec]};var F=function(){dc.call(this);this.eventTargetListeners_=new mc(this);this.actualEventTarget_=this;this.parentEventTarget_=null};t(F,dc);F.prototype[hc]=!0;h=F.prototype;h.setParentEventTarget=function(a){this.parentEventTarget_=a};h.addEventListener=function(a,b,c,d){sc(this,a,b,c,d)};h.removeEventListener=function(a,b,c,d){Ac(this,a,b,c,d)};
h.dispatchEvent=function(a){Fc(this);var b,c=this.parentEventTarget_;if(c){b=[];for(var d=1;c;c=c.parentEventTarget_)b.push(c),v(1E3>++d,"infinite loop")}c=this.actualEventTarget_;d=a.type||a;if(m(a))a=new gc(a,c);else if(a instanceof gc)a.target=a.target||c;else{var e=a;a=new gc(d,c);ab(a,e)}var e=!0,f;if(b)for(var g=b.length-1;!a.propagationStopped_&&0<=g;g--)f=a.currentTarget=b[g],e=Gc(f,d,!0,a)&&e;a.propagationStopped_||(f=a.currentTarget=c,e=Gc(f,d,!0,a)&&e,a.propagationStopped_||(e=Gc(f,d,!1,
a)&&e));if(b)for(g=0;!a.propagationStopped_&&g<b.length;g++)f=a.currentTarget=b[g],e=Gc(f,d,!1,a)&&e;return e};h.disposeInternal=function(){F.superClass_.disposeInternal.call(this);this.eventTargetListeners_&&this.eventTargetListeners_.removeAll(void 0);this.parentEventTarget_=null};h.listen=function(a,b,c,d){Fc(this);return this.eventTargetListeners_.add(String(a),b,!1,c,d)};h.listenOnce=function(a,b,c,d){return this.eventTargetListeners_.add(String(a),b,!0,c,d)};
h.unlisten=function(a,b,c,d){return this.eventTargetListeners_.remove(String(a),b,c,d)};var Gc=function(a,b,c,d){b=a.eventTargetListeners_.listeners[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var g=b[f];if(g&&!g.removed&&g.capture==c){var k=g.listener,w=g.handler||g.src;g.callOnce&&oc(a.eventTargetListeners_,g);e=!1!==k.call(w,d)&&e}}return e&&0!=d.returnValue_};F.prototype.getListener=function(a,b,c,d){return this.eventTargetListeners_.getListener(String(a),b,c,d)};
var Fc=function(a){v(a.eventTargetListeners_,"Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?")};var Hc=function(a){if(a.altKey&&!a.ctrlKey||a.metaKey||112<=a.keyCode&&123>=a.keyCode)return!1;switch(a.keyCode){case 18:case 20:case 93:case 17:case 40:case 35:case 27:case 36:case 45:case 37:case 224:case 91:case 144:case 12:case 34:case 33:case 19:case 255:case 44:case 39:case 145:case 16:case 38:case 252:case 224:case 92:return!1;case 0:return!vb;default:return 166>a.keyCode||183<a.keyCode}},Kc=function(a,b,c,d,e){if(!(z||tb||A&&B("525")))return!0;if(xb&&e)return Ic(a);if(e&&!d)return!1;"number"==
typeof b&&(b=Jc(b));if(!c&&(17==b||18==b||xb&&91==b))return!1;if((A||tb)&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(z&&d&&b==a)return!1;switch(a){case 13:return!0;case 27:return!(A||tb)}return Ic(a)},Ic=function(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||(A||tb)&&0==a)return!0;switch(a){case 32:case 43:case 63:case 64:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;
default:return!1}},Jc=function(a){if(vb)a=Lc(a);else if(xb&&A)a:switch(a){case 93:a=91;break a}return a},Lc=function(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};var Nc=function(a){F.call(this);this.element_=a;sc(a,Mc,this.handleKeyDown_,!1,this);sc(a,"click",this.handleClick_,!1,this)};t(Nc,F);var Mc=vb?"keypress":"keydown";Nc.prototype.handleKeyDown_=function(a){(13==a.keyCode||A&&3==a.keyCode)&&Oc(this,a)};Nc.prototype.handleClick_=function(a){Oc(this,a)};var Oc=function(a,b){var c=new Pc(b);if(a.dispatchEvent(c)){c=new Qc(b);try{a.dispatchEvent(c)}finally{b.stopPropagation()}}};
Nc.prototype.disposeInternal=function(){Nc.superClass_.disposeInternal.call(this);Ac(this.element_,Mc,this.handleKeyDown_,!1,this);Ac(this.element_,"click",this.handleClick_,!1,this);delete this.element_};var Qc=function(a){E.call(this,a.event_);this.type="action"};t(Qc,E);var Pc=function(a){E.call(this,a.event_);this.type="beforeaction"};t(Pc,E);var Rc=function(a){F.call(this);this.element_=a;a=z?"focusout":"blur";this.listenKeyIn_=sc(this.element_,z?"focusin":"focus",this,!z);this.listenKeyOut_=sc(this.element_,a,this,!z)};t(Rc,F);Rc.prototype.handleEvent=function(a){var b=new E(a.event_);b.type="focusin"==a.type||"focus"==a.type?"focusin":"focusout";this.dispatchEvent(b)};Rc.prototype.disposeInternal=function(){Rc.superClass_.disposeInternal.call(this);Bc(this.listenKeyIn_);Bc(this.listenKeyOut_);delete this.element_};var Sc=function(a,b,c){this.limit_=c;this.create_=a;this.reset_=b;this.occupants_=0;this.head_=null};Sc.prototype.get=function(){var a;0<this.occupants_?(this.occupants_--,a=this.head_,this.head_=a.next,a.next=null):a=this.create_();return a};Sc.prototype.put=function(a){this.reset_(a);this.occupants_<this.limit_&&(this.occupants_++,a.next=this.head_,this.head_=a)};var Tc=function(a){l.setTimeout(function(){throw a;},0)},Uc,Vc=function(){var a=l.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&!x("Presto")&&(a=function(){var a=document.createElement("IFRAME");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+"//"+b.location.host,
a=q(function(a){if(("*"==d||a.origin==d)&&a.data==c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!x("Trident")&&!x("MSIE")){var b=new a,c={},d=c;b.port1.onmessage=function(){if(aa(c.next)){c=c.next;var a=c.cb;c.cb=null;a()}};return function(a){d.next={cb:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("SCRIPT")?
function(a){var b=document.createElement("SCRIPT");b.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){l.setTimeout(a,0)}};var Wc=function(){this.workTail_=this.workHead_=null},Yc=new Sc(function(){return new Xc},function(a){a.reset()},100);Wc.prototype.add=function(a,b){var c=Yc.get();c.set(a,b);this.workTail_?this.workTail_.next=c:(v(!this.workHead_),this.workHead_=c);this.workTail_=c};Wc.prototype.remove=function(){var a=null;this.workHead_&&(a=this.workHead_,this.workHead_=this.workHead_.next,this.workHead_||(this.workTail_=null),a.next=null);return a};var Xc=function(){this.next=this.scope=this.fn=null};
Xc.prototype.set=function(a,b){this.fn=a;this.scope=b;this.next=null};Xc.prototype.reset=function(){this.next=this.scope=this.fn=null};var cd=function(a,b){Zc||$c();ad||(Zc(),ad=!0);bd.add(a,b)},Zc,$c=function(){if(l.Promise&&l.Promise.resolve){var a=l.Promise.resolve(void 0);Zc=function(){a.then(dd)}}else Zc=function(){var a=dd;!p(l.setImmediate)||l.Window&&l.Window.prototype&&!x("Edge")&&l.Window.prototype.setImmediate==l.setImmediate?(Uc||(Uc=Vc()),Uc(a)):l.setImmediate(a)}},ad=!1,bd=new Wc,dd=function(){for(var a;a=bd.remove();){try{a.fn.call(a.scope)}catch(b){Tc(b)}Yc.put(a)}ad=!1};var ed=function(a){a.prototype.then=a.prototype.then;a.prototype.$goog_Thenable=!0},fd=function(a){if(!a)return!1;try{return!!a.$goog_Thenable}catch(b){return!1}};var id=function(a,b){this.state_=0;this.result_=void 0;this.callbackEntriesTail_=this.callbackEntries_=this.parent_=null;this.hadUnhandledRejection_=this.executing_=!1;if(a!=ba)try{var c=this;a.call(b,function(a){gd(c,2,a)},function(a){if(!(a instanceof hd))try{if(a instanceof Error)throw a;throw Error("Promise rejected.");}catch(b){}gd(c,3,a)})}catch(d){gd(this,3,d)}},jd=function(){this.next=this.context=this.onRejected=this.onFulfilled=this.child=null;this.always=!1};
jd.prototype.reset=function(){this.context=this.onRejected=this.onFulfilled=this.child=null;this.always=!1};var kd=new Sc(function(){return new jd},function(a){a.reset()},100),ld=function(a,b,c){var d=kd.get();d.onFulfilled=a;d.onRejected=b;d.context=c;return d};
id.prototype.then=function(a,b,c){null!=a&&Ea(a,"opt_onFulfilled should be a function.");null!=b&&Ea(b,"opt_onRejected should be a function. Did you pass opt_context as the second argument instead of the third?");return md(this,p(a)?a:null,p(b)?b:null,c)};ed(id);id.prototype.cancel=function(a){0==this.state_&&cd(function(){var b=new hd(a);nd(this,b)},this)};
var nd=function(a,b){if(0==a.state_)if(a.parent_){var c=a.parent_;if(c.callbackEntries_){for(var d=0,e=null,f=null,g=c.callbackEntries_;g&&(g.always||(d++,g.child==a&&(e=g),!(e&&1<d)));g=g.next)e||(f=g);e&&(0==c.state_&&1==d?nd(c,b):(f?(d=f,v(c.callbackEntries_),v(null!=d),d.next==c.callbackEntriesTail_&&(c.callbackEntriesTail_=d),d.next=d.next.next):od(c),pd(c,e,3,b)))}a.parent_=null}else gd(a,3,b)},rd=function(a,b){a.callbackEntries_||2!=a.state_&&3!=a.state_||qd(a);v(null!=b.onFulfilled);a.callbackEntriesTail_?
a.callbackEntriesTail_.next=b:a.callbackEntries_=b;a.callbackEntriesTail_=b},md=function(a,b,c,d){var e=ld(null,null,null);e.child=new id(function(a,g){e.onFulfilled=b?function(c){try{var e=b.call(d,c);a(e)}catch(V){g(V)}}:a;e.onRejected=c?function(b){try{var e=c.call(d,b);!aa(e)&&b instanceof hd?g(b):a(e)}catch(V){g(V)}}:g});e.child.parent_=a;rd(a,e);return e.child};id.prototype.unblockAndFulfill_=function(a){v(1==this.state_);this.state_=0;gd(this,2,a)};
id.prototype.unblockAndReject_=function(a){v(1==this.state_);this.state_=0;gd(this,3,a)};
var gd=function(a,b,c){if(0==a.state_){a===c&&(b=3,c=new TypeError("Promise cannot resolve to itself"));a.state_=1;var d;a:{var e=c,f=a.unblockAndFulfill_,g=a.unblockAndReject_;if(e instanceof id)null!=f&&Ea(f,"opt_onFulfilled should be a function."),null!=g&&Ea(g,"opt_onRejected should be a function. Did you pass opt_context as the second argument instead of the third?"),rd(e,ld(f||ba,g||null,a)),d=!0;else if(fd(e))e.then(f,g,a),d=!0;else{if(ha(e))try{var k=e.then;if(p(k)){sd(e,k,f,g,a);d=!0;break a}}catch(w){g.call(a,
w);d=!0;break a}d=!1}}d||(a.result_=c,a.state_=b,a.parent_=null,qd(a),3!=b||c instanceof hd||td(a,c))}},sd=function(a,b,c,d,e){var f=!1,g=function(a){f||(f=!0,c.call(e,a))},k=function(a){f||(f=!0,d.call(e,a))};try{b.call(a,g,k)}catch(w){k(w)}},qd=function(a){a.executing_||(a.executing_=!0,cd(a.executeCallbacks_,a))},od=function(a){var b=null;a.callbackEntries_&&(b=a.callbackEntries_,a.callbackEntries_=b.next,b.next=null);a.callbackEntries_||(a.callbackEntriesTail_=null);null!=b&&v(null!=b.onFulfilled);
return b};id.prototype.executeCallbacks_=function(){for(var a;a=od(this);)pd(this,a,this.state_,this.result_);this.executing_=!1};
var pd=function(a,b,c,d){if(3==c&&b.onRejected&&!b.always)for(;a&&a.hadUnhandledRejection_;a=a.parent_)a.hadUnhandledRejection_=!1;if(b.child)b.child.parent_=null,ud(b,c,d);else try{b.always?b.onFulfilled.call(b.context):ud(b,c,d)}catch(e){vd.call(null,e)}kd.put(b)},ud=function(a,b,c){2==b?a.onFulfilled.call(a.context,c):a.onRejected&&a.onRejected.call(a.context,c)},td=function(a,b){a.hadUnhandledRejection_=!0;cd(function(){a.hadUnhandledRejection_&&vd.call(null,b)})},vd=Tc,hd=function(a){u.call(this,
a)};t(hd,u);hd.prototype.name="cancel";var wd=function(a,b){if(p(a))b&&(a=q(a,b));else if(a&&"function"==typeof a.handleEvent)a=q(a.handleEvent,a);else throw Error("Invalid listener argument");return 2147483647<Number(0)?-1:l.setTimeout(a,0)};var xd=function(a){dc.call(this);this.handler_=a;this.keys_={}};t(xd,dc);var yd=[];xd.prototype.listen=function(a,b,c,d){fa(b)||(b&&(yd[0]=b.toString()),b=yd);for(var e=0;e<b.length;e++){var f=sc(a,b[e],c||this.handleEvent,d||!1,this.handler_||this);if(!f)break;this.keys_[f.key]=f}return this};xd.prototype.listenOnce=function(a,b,c,d){return zd(this,a,b,c,d)};
var zd=function(a,b,c,d,e,f){if(fa(c))for(var g=0;g<c.length;g++)zd(a,b,c[g],d,e,f);else{b=zc(b,c,d||a.handleEvent,e,f||a.handler_||a);if(!b)return a;a.keys_[b.key]=b}return a};xd.prototype.unlisten=function(a,b,c,d,e){if(fa(b))for(var f=0;f<b.length;f++)this.unlisten(a,b[f],c,d,e);else c=c||this.handleEvent,e=e||this.handler_||this,c=tc(c),d=!!d,b=ic(a)?a.getListener(b,c,d,e):a?(a=vc(a))?a.getListener(b,c,d,e):null:null,b&&(Bc(b),delete this.keys_[b.key]);return this};
xd.prototype.removeAll=function(){Ya(this.keys_,function(a,b){this.keys_.hasOwnProperty(b)&&Bc(a)},this);this.keys_={}};xd.prototype.disposeInternal=function(){xd.superClass_.disposeInternal.call(this);this.removeAll()};xd.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var Ad=function(a){F.call(this);this.timer_=null;this.element_=a;a=z||tb||A&&!B("531")&&"TEXTAREA"==a.tagName;this.eventHandler_=new xd(this);this.eventHandler_.listen(this.element_,a?["keydown","paste","cut","drop","input"]:"input",this)};t(Ad,F);
Ad.prototype.handleEvent=function(a){if("input"==a.type)z&&B(10)&&0==a.keyCode&&0==a.charCode||(Bd(this),this.dispatchEvent(Cd(a)));else if("keydown"!=a.type||Hc(a)){var b="keydown"==a.type?this.element_.value:null;z&&229==a.keyCode&&(b=null);var c=Cd(a);Bd(this);this.timer_=wd(function(){this.timer_=null;this.element_.value!=b&&this.dispatchEvent(c)},this)}};var Bd=function(a){null!=a.timer_&&(l.clearTimeout(a.timer_),a.timer_=null)},Cd=function(a){a=new E(a.event_);a.type="input";return a};
Ad.prototype.disposeInternal=function(){Ad.superClass_.disposeInternal.call(this);this.eventHandler_.dispose();Bd(this);delete this.element_};var Dd=function(a,b){F.call(this);a&&(this.keyUpKey_&&this.detach(),this.element_=a,this.keyPressKey_=sc(this.element_,"keypress",this,b),this.keyDownKey_=sc(this.element_,"keydown",this.handleKeyDown_,b,this),this.keyUpKey_=sc(this.element_,"keyup",this.handleKeyup_,b,this))};t(Dd,F);h=Dd.prototype;h.element_=null;h.keyPressKey_=null;h.keyDownKey_=null;h.keyUpKey_=null;h.lastKey_=-1;h.keyCode_=-1;h.altKey_=!1;
var Ed={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},Fd={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Gd=z||tb||A&&B("525"),Hd=xb&&vb;h=Dd.prototype;
h.handleKeyDown_=function(a){if(A||tb)if(17==this.lastKey_&&!a.ctrlKey||18==this.lastKey_&&!a.altKey||xb&&91==this.lastKey_&&!a.metaKey)this.keyCode_=this.lastKey_=-1;-1==this.lastKey_&&(a.ctrlKey&&17!=a.keyCode?this.lastKey_=17:a.altKey&&18!=a.keyCode?this.lastKey_=18:a.metaKey&&91!=a.keyCode&&(this.lastKey_=91));Gd&&!Kc(a.keyCode,this.lastKey_,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.keyCode_=Jc(a.keyCode),Hd&&(this.altKey_=a.altKey))};
h.handleKeyup_=function(a){this.keyCode_=this.lastKey_=-1;this.altKey_=a.altKey};
h.handleEvent=function(a){var b=a.event_,c,d,e=b.altKey;z&&"keypress"==a.type?(c=this.keyCode_,d=13!=c&&27!=c?b.keyCode:0):(A||tb)&&"keypress"==a.type?(c=this.keyCode_,d=0<=b.charCode&&63232>b.charCode&&Ic(c)?b.charCode:0):sb&&!A?(c=this.keyCode_,d=Ic(c)?b.keyCode:0):(c=b.keyCode||this.keyCode_,d=b.charCode||0,Hd&&(e=this.altKey_),xb&&63==d&&224==c&&(c=191));var f=c=Jc(c),g=b.keyIdentifier;c?63232<=c&&c in Ed?f=Ed[c]:25==c&&a.shiftKey&&(f=9):g&&g in Fd&&(f=Fd[g]);a=f==this.lastKey_;this.lastKey_=
f;b=new Id(f,d,a,b);b.altKey=e;this.dispatchEvent(b)};h.getElement=function(){return this.element_};h.detach=function(){this.keyPressKey_&&(Bc(this.keyPressKey_),Bc(this.keyDownKey_),Bc(this.keyUpKey_),this.keyUpKey_=this.keyDownKey_=this.keyPressKey_=null);this.element_=null;this.keyCode_=this.lastKey_=-1};h.disposeInternal=function(){Dd.superClass_.disposeInternal.call(this);this.detach()};var Id=function(a,b,c,d){E.call(this,d);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};
t(Id,E);var Jd=function(){};Jd.getInstance=function(){return Jd.instance_?Jd.instance_:Jd.instance_=new Jd};Jd.prototype.nextId_=0;var G=function(a){F.call(this);this.dom_=a||Lb();this.id_=null;this.inDocument_=!1;this.element_=null;this.googUiComponentHandler_=void 0;this.childIndex_=this.children_=this.parent_=null;this.wasDecorated_=!1};t(G,F);G.prototype.idGenerator_=Jd.getInstance();G.prototype.getId=function(){return this.id_||(this.id_=":"+(this.idGenerator_.nextId_++).toString(36))};
var Kd=function(a,b){if(a.parent_&&a.parent_.childIndex_){var c=a.parent_.childIndex_,d=a.id_;d in c&&delete c[d];Za(a.parent_.childIndex_,b,a)}a.id_=b};G.prototype.getElement=function(){return this.element_};G.prototype.getElementsByClass=function(a){return this.element_?this.dom_.getElementsByClass(a,this.element_):[]};G.prototype.getElementByClass=function(a){return this.element_?this.dom_.getElementByClass(a,this.element_):null};
var Ld=function(a){a.googUiComponentHandler_||(a.googUiComponentHandler_=new xd(a));return a.googUiComponentHandler_},Nd=function(a,b){if(a==b)throw Error("Unable to set parent component");if(b&&a.parent_&&a.id_&&Md(a.parent_,a.id_)&&a.parent_!=b)throw Error("Unable to set parent component");a.parent_=b;G.superClass_.setParentEventTarget.call(a,b)};h=G.prototype;h.getParent=function(){return this.parent_};
h.setParentEventTarget=function(a){if(this.parent_&&this.parent_!=a)throw Error("Method not supported");G.superClass_.setParentEventTarget.call(this,a)};h.getDomHelper=function(){return this.dom_};h.createDom=function(){this.element_=this.dom_.createElement("DIV")};
h.render=function(a){if(this.inDocument_)throw Error("Component already rendered");this.element_||this.createDom();a?a.insertBefore(this.element_,null):this.dom_.document_.body.appendChild(this.element_);this.parent_&&!this.parent_.inDocument_||this.enterDocument()};h.enterDocument=function(){this.inDocument_=!0;Od(this,function(a){!a.inDocument_&&a.getElement()&&a.enterDocument()})};
h.exitDocument=function(){Od(this,function(a){a.inDocument_&&a.exitDocument()});this.googUiComponentHandler_&&this.googUiComponentHandler_.removeAll();this.inDocument_=!1};h.disposeInternal=function(){this.inDocument_&&this.exitDocument();this.googUiComponentHandler_&&(this.googUiComponentHandler_.dispose(),delete this.googUiComponentHandler_);Od(this,function(a){a.dispose()});!this.wasDecorated_&&this.element_&&Sb(this.element_);this.parent_=this.element_=this.childIndex_=this.children_=null;G.superClass_.disposeInternal.call(this)};
var Md=function(a,b){var c;a.childIndex_&&b?(c=a.childIndex_,c=(null!==c&&b in c?c[b]:void 0)||null):c=null;return c},Od=function(a,b){a.children_&&Ia(a.children_,b,void 0)};G.prototype.removeChild=function(a,b){if(a){var c=m(a)?a:a.getId();a=Md(this,c);if(c&&a){var d=this.childIndex_;c in d&&delete d[c];Pa(this.children_,a);b&&(a.exitDocument(),a.element_&&Sb(a.element_));Nd(a,null)}}if(!a)throw Error("Child is not in parent component");return a};var H=function(a,b){var c;c=Ub(a);b?(Xa(a,"firebaseui-input-invalid"),Wa(a,"firebaseui-input"),c&&Xa(c,"firebaseui-textfield-invalid")):(Xa(a,"firebaseui-input"),Wa(a,"firebaseui-input-invalid"),c&&Wa(c,"firebaseui-textfield-invalid"))},Pd=function(a,b,c){b=new Ad(b);ec(a,ka(fc,b));Ld(a).listen(b,"input",c)},Qd=function(a,b,c){b=new Dd(b);ec(a,ka(fc,b));Ld(a).listen(b,"key",function(a){13==a.keyCode&&(a.stopPropagation(),a.preventDefault(),c(a))})},Rd=function(a,b,c){b=new Rc(b);ec(a,ka(fc,b));Ld(a).listen(b,
"focusin",c)},Sd=function(a,b,c){b=new Rc(b);ec(a,ka(fc,b));Ld(a).listen(b,"focusout",c)},Td=function(a,b,c){b=new Nc(b);ec(a,ka(fc,b));Ld(a).listen(b,"action",function(a){a.stopPropagation();a.preventDefault();c(a)})},Ud=function(a){Wa(a,"firebaseui-hidden")},Vd=function(a,b){if(b)if(v(null!=a,"goog.dom.setTextContent expects a non-null value for node"),"textContent"in a)a.textContent=b;else if(3==a.nodeType)a.data=b;else if(a.firstChild&&3==a.firstChild.nodeType){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);
a.firstChild.data=b}else{for(var c;c=a.firstChild;)a.removeChild(c);c=Kb(a);a.appendChild(c.createTextNode(String(b)))}Xa(a,"firebaseui-hidden")},Wd=function(a){return!Va(a,"firebaseui-hidden")&&"none"!=a.style.display};z&&B(8);var Xd={sanitizedContentKindHtml:!0},Yd={sanitizedContentUri:!0},Zd={sanitizedContentTrustedResourceUri:!0},$d={sanitizedContentKindText:!0},I=function(){throw Error("Do not instantiate directly");};I.prototype.contentDir=null;I.prototype.toString=function(){return this.content};var ae=function(){I.call(this)};t(ae,I);var be=function(){I.call(this)};t(be,I);var fe=function(a,b,c,d){v(a,"Soy template may not be null.");a:if(a=a(b||ce,void 0,c),d=(d||Lb()).createElement("DIV"),a=de(a),b=a.match(ee),v(!b,"This template starts with a %s, which cannot be a child of a <div>, as required by soy internals. Consider using goog.soy.renderElement instead.\nTemplate output: %s",b&&b[0],a),d.innerHTML=a,1==d.childNodes.length&&(a=d.firstChild,1==a.nodeType)){d=a;break a}return d},de=function(a){if(!ha(a))return String(a);if(a instanceof I){if(a.contentKind===Xd)return Da(a.content);
if(a.contentKind===$d)return xa(a.content)}Ba("Soy template output is unsafe for use as HTML: "+a);return"zSoyz"},ee=/^<(body|caption|col|colgroup|head|html|tr|td|th|tbody|thead|tfoot)>/i,ce={};var ge=function(a){if(null!=a)switch(a.contentDir){case 1:return 1;case -1:return-1;case 0:return 0}return null},he=function(){I.call(this)};t(he,I);he.prototype.contentKind=Xd;var K=function(a){return null!=a&&a.contentKind===Xd?(v(a.constructor===he),a):a instanceof qb?J(rb(a),a.getDirection()):J(xa(String(String(a))),ge(a))},ie=function(){I.call(this)};t(ie,I);ie.prototype.contentKind={sanitizedContentJsChars:!0};ie.prototype.contentDir=1;var je=function(){I.call(this)};t(je,I);
je.prototype.contentKind=Yd;je.prototype.contentDir=1;var ke=function(){I.call(this)};t(ke,I);ke.prototype.contentKind=Zd;ke.prototype.contentDir=1;var le=function(){I.call(this)};t(le,I);le.prototype.contentKind={sanitizedContentHtmlAttribute:!0};le.prototype.contentDir=1;var me=function(){I.call(this)};t(me,be);me.prototype.contentKind={sanitizedContentCss:!0};me.prototype.contentDir=1;var ne=function(a,b){this.content=String(a);this.contentDir=null!=b?b:null};t(ne,ae);
ne.prototype.contentKind=$d;var oe=function(a){function b(a){this.content=a}b.prototype=a.prototype;return function(a){return new b(String(a))}},pe=function(a){return new ne(a,void 0)},J=function(a){function b(a){this.content=a}b.prototype=a.prototype;return function(a,d){var e=new b(String(a));void 0!==d&&(e.contentDir=d);return e}}(he);oe(ie);var qe=oe(je);oe(ke);oe(le);oe(me);var re=function(a){return(a=String(a))?new ne(a,void 0):""};
(function(a){function b(a){this.content=a}b.prototype=a.prototype;return function(a,d){var e=String(a);if(!e)return"";e=new b(e);void 0!==d&&(e.contentDir=d);return e}})(he);
var L=function(a){return null!=a&&a.contentKind===Xd?(v(a.constructor===he),String(String(a.content).replace(se,"").replace(te,"&lt;")).replace(ue,ve)):xa(String(a))},Ae=function(a){if(null!=a&&a.contentKind===Yd)return v(a.constructor===je),we(a);if(null!=a&&a.contentKind===Zd)return v(a.constructor===ke),we(a);a instanceof y?a=we(ib(a)):a instanceof nb?a=we(ob(a)):(a=String(a),xe.test(a)?a=a.replace(ye,ze):(Ba("Bad value `%s` for |filterNormalizeUri",[a]),a="#zSoyz"));return a},Ce=function(a){if(null!=
a&&a.contentKind===Yd)return v(a.constructor===je),we(a);if(null!=a&&a.contentKind===Zd)return v(a.constructor===ke),we(a);a instanceof y?a=we(ib(a)):a instanceof nb?a=we(ob(a)):(a=String(a),Be.test(a)?a=a.replace(ye,ze):(Ba("Bad value `%s` for |filterNormalizeMediaUri",[a]),a="about:invalid#zSoyz"));return a},M=function(a,b,c,d){c=c instanceof Function?c.displayName||c.name||"unknown type name":c instanceof Object?c.constructor.displayName||c.constructor.name||Object.prototype.toString.call(c):null===
c?"null":typeof c;v(a,"expected param "+b+" of type "+d+(", but got "+c)+".",void 0)},De={"\x00":"&#0;","\t":"&#9;","\n":"&#10;","\x0B":"&#11;","\f":"&#12;","\r":"&#13;"," ":"&#32;",'"':"&quot;","&":"&amp;","'":"&#39;","-":"&#45;","/":"&#47;","<":"&lt;","=":"&#61;",">":"&gt;","`":"&#96;","\u0085":"&#133;","\u00a0":"&#160;","\u2028":"&#8232;","\u2029":"&#8233;"},ve=function(a){return De[a]},Ee={"\x00":"\\x00","\b":"\\x08","\t":"\\t","\n":"\\n","\x0B":"\\x0b","\f":"\\f","\r":"\\r",'"':"\\x22",$:"\\x24",
"&":"\\x26","'":"\\x27","(":"\\x28",")":"\\x29","*":"\\x2a","+":"\\x2b",",":"\\x2c","-":"\\x2d",".":"\\x2e","/":"\\/",":":"\\x3a","<":"\\x3c","=":"\\x3d",">":"\\x3e","?":"\\x3f","[":"\\x5b","\\":"\\\\","]":"\\x5d","^":"\\x5e","{":"\\x7b","|":"\\x7c","}":"\\x7d","\u0085":"\\x85","\u2028":"\\u2028","\u2029":"\\u2029"},Fe=function(a){return Ee[a]},Ge={"\x00":"%00","\u0001":"%01","\u0002":"%02","\u0003":"%03","\u0004":"%04","\u0005":"%05","\u0006":"%06","\u0007":"%07","\b":"%08","\t":"%09","\n":"%0A",
"\x0B":"%0B","\f":"%0C","\r":"%0D","\u000e":"%0E","\u000f":"%0F","\u0010":"%10","\u0011":"%11","\u0012":"%12","\u0013":"%13","\u0014":"%14","\u0015":"%15","\u0016":"%16","\u0017":"%17","\u0018":"%18","\u0019":"%19","\u001a":"%1A","\u001b":"%1B","\u001c":"%1C","\u001d":"%1D","\u001e":"%1E","\u001f":"%1F"," ":"%20",'"':"%22","'":"%27","(":"%28",")":"%29","<":"%3C",">":"%3E","\\":"%5C","{":"%7B","}":"%7D","\u007f":"%7F","\u0085":"%C2%85","\u00a0":"%C2%A0","\u2028":"%E2%80%A8","\u2029":"%E2%80%A9","\uff01":"%EF%BC%81",
"\uff03":"%EF%BC%83","\uff04":"%EF%BC%84","\uff06":"%EF%BC%86","\uff07":"%EF%BC%87","\uff08":"%EF%BC%88","\uff09":"%EF%BC%89","\uff0a":"%EF%BC%8A","\uff0b":"%EF%BC%8B","\uff0c":"%EF%BC%8C","\uff0f":"%EF%BC%8F","\uff1a":"%EF%BC%9A","\uff1b":"%EF%BC%9B","\uff1d":"%EF%BC%9D","\uff1f":"%EF%BC%9F","\uff20":"%EF%BC%A0","\uff3b":"%EF%BC%BB","\uff3d":"%EF%BC%BD"},ze=function(a){return Ge[a]},ue=/[\x00\x22\x27\x3c\x3e]/g,He=/[\x00\x08-\x0d\x22\x26\x27\/\x3c-\x3e\\\x85\u2028\u2029]/g,ye=/[\x00- \x22\x27-\x29\x3c\x3e\\\x7b\x7d\x7f\x85\xa0\u2028\u2029\uff01\uff03\uff04\uff06-\uff0c\uff0f\uff1a\uff1b\uff1d\uff1f\uff20\uff3b\uff3d]/g,
xe=/^(?![^#?]*\/(?:\.|%2E){2}(?:[\/?#]|$))(?:(?:https?|mailto):|[^&:\/?#]*(?:[\/?#]|$))/i,Be=/^[^&:\/?#]*(?:[\/?#]|$)|^https?:|^data:image\/[a-z0-9+]+;base64,[a-z0-9+\/]+=*$|^blob:/i,we=function(a){return String(a).replace(ye,ze)},se=/<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g,te=/</g;var Ie=function(){return pe("Please login again to perform this operation")};var Je=/^[+a-zA-Z0-9_.!#$%&'*\/=?^`{|}~-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z0-9]{2,63}$/;var Ke=function(){return this.getElementByClass("firebaseui-id-email")},Oe=function(){return this.getElementByClass("firebaseui-id-email-error")},Pe=function(a){var b=Ke.call(this),c=Oe.call(this);Pd(this,b,function(){Wd(c)&&(H(b,!0),Ud(c))});a&&Qd(this,b,function(){a()})},Qe=function(){return pa(C(Ke.call(this))||"")},Re=function(){var a=Ke.call(this),b;b=Oe.call(this);var c=C(a)||"";c?Je.test(c)?(H(a,!0),Ud(b),b=!0):(H(a,!1),Vd(b,pe("That email address isn't correct").toString()),b=!1):(H(a,!1),
Vd(b,pe("Enter your email address to continue").toString()),b=!1);return b?pa(v(C(a))):null};var N=function(){return this.getElementByClass("firebaseui-id-submit")},Se=function(){return this.getElementByClass("firebaseui-id-secondary-link")},O=function(a,b){var c=N.call(this);Td(this,c,function(){a()});(c=Se.call(this))&&b&&Td(this,c,function(){b()})};var Te=!z,Ue=function(a){return Te&&a.dataset?"providerId"in a.dataset?a.dataset.providerId:null:a.getAttribute("data-"+"providerId".replace(/([A-Z])/g,"-$1").toLowerCase())};var Ve=function(a){a=a||{};M(null==a.email||a.email instanceof I||m(a.email),"email",a.email,"null|string|undefined");var b=a.email;M(null==a.disabled||n(a.disabled)||1===a.disabled||0===a.disabled,"disabled",a.disabled,"boolean|null|undefined");var c=a.disabled;M(null==a.changeEmail||n(a.changeEmail)||1===a.changeEmail||0===a.changeEmail,"changeEmail",a.changeEmail,"boolean|null|undefined");var d='<div class="firebaseui-textfield mdl-textfield mdl-js-textfield mdl-textfield--floating-label"><label class="mdl-textfield__label firebaseui-label" for="email">',
d=a.changeEmail?d+"Enter new email address":d+"Email",d=d+('</label><input type="email" name="email" autocomplete="username" class="mdl-textfield__input firebaseui-input firebaseui-id-email" value="'+L(null==b?"":b)+'"'+(c?"disabled":"")+'></div><div class="firebaseui-error-wrapper"><p class="firebaseui-error firebaseui-hidden firebaseui-id-email-error"></p></div>');return J(d)},P=function(a){a=a||{};M(null==a.label||a.label instanceof I||m(a.label),"label",a.label,"null|string|undefined");a=a.label;
var b='<button type="submit" class="firebaseui-id-submit firebaseui-button mdl-button mdl-js-button mdl-button--raised mdl-button--colored">',b=a?b+K(a):b+"Next";return J(b+"</button>")},We=function(){var a;a=""+P({label:re("Sign In")});return J(a)},Xe=function(){var a;a=""+P({label:re("Save")});return J(a)},Ye=function(a){a=a||{};M(null==a.label||a.label instanceof I||m(a.label),"label",a.label,"null|string|undefined");a=a.label;var b='<div class="firebaseui-new-password-component"><div class="firebaseui-textfield mdl-textfield mdl-js-textfield mdl-textfield--floating-label"><label class="mdl-textfield__label firebaseui-label" for="newPassword">',
b=a?b+K(a):b+"Choose password";return J(b+'</label><input type="password" name="newPassword" autocomplete="new-password" class="mdl-textfield__input firebaseui-input firebaseui-id-new-password"></div><a href="javascript:void(0)" class="firebaseui-input-floating-button firebaseui-id-password-toggle firebaseui-input-toggle-on firebaseui-input-toggle-blur"></a><div class="firebaseui-error-wrapper"><p class="firebaseui-error firebaseui-hidden firebaseui-id-new-password-error"></p></div></div>')},Ze=function(){var a;
a={};M(null==a.current||n(a.current)||1===a.current||0===a.current,"current",a.current,"boolean|null|undefined");var b='<div class="firebaseui-textfield mdl-textfield mdl-js-textfield mdl-textfield--floating-label"><label class="mdl-textfield__label firebaseui-label" for="password">',b=a.current?b+"Current password":b+"Password";return J(b+'</label><input type="password" name="password" autocomplete="current-password" class="mdl-textfield__input firebaseui-input firebaseui-id-password"></div><div class="firebaseui-error-wrapper"><p class="firebaseui-error firebaseui-hidden firebaseui-id-password-error"></p></div>')},
$e=function(){return J('<a class="firebaseui-link firebaseui-id-secondary-link" href="javascript:void(0)">Trouble signing in?</a>')},af=function(){return J('<button class="firebaseui-id-secondary-link firebaseui-button mdl-button mdl-js-button mdl-button--raised mdl-button--colored">Cancel</button>')},bf=function(a){M(m(a.message)||a.message instanceof I,"message",a.message,"string|goog.soy.data.SanitizedContent");a=""+('<div class="firebaseui-info-bar firebaseui-id-info-bar"><p class="firebaseui-info-bar-message">'+
K(a.message)+'&nbsp;&nbsp;<a href="javascript:void(0)" class="firebaseui-link firebaseui-id-dismiss-info-bar">Dismiss</a></p></div>');return J(a)};bf.soyTemplateName="firebaseui.auth.soy2.element.infoBar";var cf=function(){return J('<div class="mdl-progress mdl-js-progress mdl-progress__indeterminate firebaseui-busy-indicator firebaseui-id-busy-indicator"></div>')};cf.soyTemplateName="firebaseui.auth.soy2.element.busyIndicator";
var df=function(a){var b;a=a||{};M(null==a.providerId||a.providerId instanceof I||m(a.providerId),"providerId",a.providerId,"null|string|undefined");var c="";switch(ha(b=a.providerId)?b.toString():b){case "google.com":c+="Google";break;case "github.com":c+="Github";break;case "facebook.com":c+="Facebook";break;case "twitter.com":c+="Twitter";break;default:c+="Password"}return pe(c)},ef=function(a){var b;a=a||{};M(null==a.providerId||a.providerId instanceof I||m(a.providerId),"providerId",a.providerId,
"null|string|undefined");var c="";switch(ha(b=a.providerId)?b.toString():b){case "google.com":c+="firebaseui-idp-google";break;case "github.com":c+="firebaseui-idp-github";break;case "facebook.com":c+="firebaseui-idp-facebook";break;case "twitter.com":c+="firebaseui-idp-twitter";break;default:c+="firebaseui-idp-password"}return pe(c)},ff=function(a,b){var c;a=a||{};M(null==a.providerId||a.providerId instanceof I||m(a.providerId),"providerId",a.providerId,"null|string|undefined");var d="";switch(ha(c=
a.providerId)?c.toString():c){case "google.com":d+=Ae(b.googleLogo);break;case "github.com":d+=Ae(b.githubLogo);break;case "facebook.com":d+=Ae(b.facebookLogo);break;case "twitter.com":d+=Ae(b.twitterLogo);break;default:d+=Ae(b.passwordLogo)}return qe(d)};var hf=function(){Sb(gf.call(this))},gf=function(){return this.getElementByClass("firebaseui-id-info-bar")},jf=function(){return this.getElementByClass("firebaseui-id-dismiss-info-bar")};var kf=function(){return this.getElementByClass("firebaseui-id-name")},lf=function(){return this.getElementByClass("firebaseui-id-name-error")};var mf=function(){return this.getElementByClass("firebaseui-id-new-password")},nf=function(){return this.getElementByClass("firebaseui-id-password-toggle")},of=function(){this.isPasswordVisible_=!this.isPasswordVisible_;var a=nf.call(this),b=mf.call(this);this.isPasswordVisible_?(b.type="text",Wa(a,"firebaseui-input-toggle-off"),Xa(a,"firebaseui-input-toggle-on")):(b.type="password",Wa(a,"firebaseui-input-toggle-on"),Xa(a,"firebaseui-input-toggle-off"));b.focus()},pf=function(){return this.getElementByClass("firebaseui-id-new-password-error")},
qf=function(){this.isPasswordVisible_=!1;var a=mf.call(this);a.type="password";var b=pf.call(this);Pd(this,a,function(){Wd(b)&&(H(a,!0),Ud(b))});var c=nf.call(this);Wa(c,"firebaseui-input-toggle-on");Xa(c,"firebaseui-input-toggle-off");Rd(this,a,function(){Wa(c,"firebaseui-input-toggle-focus");Xa(c,"firebaseui-input-toggle-blur")});Sd(this,a,function(){Wa(c,"firebaseui-input-toggle-blur");Xa(c,"firebaseui-input-toggle-focus")});Td(this,c,q(of,this))},rf=function(){var a=mf.call(this),b;b=pf.call(this);
C(a)?(H(a,!0),Ud(b),b=!0):(H(a,!1),Vd(b,pe("Enter your password").toString()),b=!1);return b?C(a):null};var sf=function(){return this.getElementByClass("firebaseui-id-password")},tf=function(){return this.getElementByClass("firebaseui-id-password-error")},uf=function(){var a=sf.call(this),b=tf.call(this);Pd(this,a,function(){Wd(b)&&(H(a,!0),Ud(b))})},vf=function(){var a=sf.call(this),b;b=tf.call(this);C(a)?(H(a,!0),Ud(b),b=!0):(H(a,!1),Vd(b,pe("Enter your password").toString()),b=!1);return b?C(a):null};var wf=function(a,b,c){a=a||{};c=c||{};M(null==a.email||a.email instanceof I||m(a.email),"email",a.email,"null|string|undefined");a=""+('<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-sign-in"><form onsubmit="'+(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Sign in with email</h1></div><div class="firebaseui-card-content"><div class="firebaseui-relative-wrapper">'+K(Ve(a))+'</div></div><div class="firebaseui-card-footer"><div class="firebaseui-form-actions">'+
K(P(null))+"</div></div></form></div>");return J(a)};wf.soyTemplateName="firebaseui.auth.soy2.page.signIn";
var xf=function(a,b,c){a=a||{};c=c||{};M(null==a.email||a.email instanceof I||m(a.email),"email",a.email,"null|string|undefined");a=""+('<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-sign-in"><form onsubmit="'+(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Sign in</h1></div><div class="firebaseui-card-content">'+K(Ve(a))+K(Ze())+'</div><div class="firebaseui-card-footer"><div class="firebaseui-form-actions">'+
K(We())+"</div>"+K($e())+"</div></form></div>");return J(a)};xf.soyTemplateName="firebaseui.auth.soy2.page.passwordSignIn";
var yf=function(a,b,c){a=a||{};c=c||{};M(null==a.email||a.email instanceof I||m(a.email),"email",a.email,"null|string|undefined");M(null==a.name||a.name instanceof I||m(a.name),"name",a.name,"null|string|undefined");M(null==a.tosUrl||a.tosUrl instanceof I||m(a.tosUrl),"tosUrl",a.tosUrl,"null|string|undefined");var d=a.tosUrl;M(null==a.allowCancel||n(a.allowCancel)||1===a.allowCancel||0===a.allowCancel,"allowCancel",a.allowCancel,"boolean|null|undefined");b=a.allowCancel;c='<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-sign-up"><form onsubmit="'+
(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Create account</h1></div><div class="firebaseui-card-content">'+K(Ve(a));var e,f;f=a||{};M(null==f.name||f.name instanceof I||m(f.name),"name",f.name,"null|string|undefined");f=""+('<div class="firebaseui-textfield mdl-textfield mdl-js-textfield mdl-textfield--floating-label"><label class="mdl-textfield__label firebaseui-label" for="name">First &amp; last name</label><input type="text" name="name" autocomplete="name" class="mdl-textfield__input firebaseui-input firebaseui-id-name" value="'+
L(null==(e=f.name)?"":e)+'"></div><div class="firebaseui-error-wrapper"><p class="firebaseui-error firebaseui-hidden firebaseui-id-name-error"></p></div>');e=J(f);e=c+K(e)+K(Ye({choose:!0}));d?(M(m(a.tosUrl)||a.tosUrl instanceof I,"tosUrl",a.tosUrl,"string|goog.soy.data.SanitizedContent"),a="By tapping SAVE, you are indicating that you agree to the "+('<a href="'+L(Ae(a.tosUrl))+'" class="firebaseui-link" target="_blank">Terms of Service</a>'),a=J(""+('<p class="firebaseui-tos">'+a+"</p>")),a=K(a)):
a="";b=""+(e+a+'</div><div class="firebaseui-card-footer"><div class="firebaseui-form-actions">'+(b?K(af()):"")+K(Xe())+"</div></div></form></div>");return J(b)};yf.soyTemplateName="firebaseui.auth.soy2.page.passwordSignUp";
var zf=function(a,b,c){a=a||{};c=c||{};M(null==a.email||a.email instanceof I||m(a.email),"email",a.email,"null|string|undefined");M(null==a.allowCancel||n(a.allowCancel)||1===a.allowCancel||0===a.allowCancel,"allowCancel",a.allowCancel,"boolean|null|undefined");b=a.allowCancel;a=""+('<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-recovery"><form onsubmit="'+(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Recover password</h1></div><div class="firebaseui-card-content"><p class="firebaseui-text">Get instructions sent to this email that explain how to reset your password</p>'+
K(Ve(a))+'</div><div class="firebaseui-card-footer"><div class="firebaseui-form-actions">'+(b?K(af()):"")+K(P({label:re("Send")}))+"</div></div></form></div>");return J(a)};zf.soyTemplateName="firebaseui.auth.soy2.page.passwordRecovery";
var Af=function(a){M(m(a.email)||a.email instanceof I,"email",a.email,"string|goog.soy.data.SanitizedContent");var b=a.email;M(null==a.allowContinue||n(a.allowContinue)||1===a.allowContinue||0===a.allowContinue,"allowContinue",a.allowContinue,"boolean|null|undefined");a=a.allowContinue;var c="",b="Follow the instructions sent to <strong>"+(K(b)+"</strong> to recover your password"),c=c+('<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-recovery-email-sent"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Check your email</h1></div><div class="firebaseui-card-content"><p class="firebaseui-text">'+
b+'</p></div><div class="firebaseui-card-footer">');a&&(c+='<div class="firebaseui-form-actions">'+K(P({label:re("Done")}))+"</div>");return J(c+"</div></div>")};Af.soyTemplateName="firebaseui.auth.soy2.page.passwordRecoveryEmailSent";var Bf=function(){return J('<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-callback"><div class="firebaseui-callback-indicator-container">'+K(cf())+"</div></div>")};Bf.soyTemplateName="firebaseui.auth.soy2.page.callback";
var Cf=function(a,b,c){c=c||{};M(m(a.email)||a.email instanceof I,"email",a.email,"string|goog.soy.data.SanitizedContent");b="";a="You\u2019ve already used <strong>"+(K(a.email)+"</strong> to sign in. Enter your password for that account.");b+='<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-linking"><form onsubmit="'+(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Sign in</h1></div><div class="firebaseui-card-content"><h2 class="firebaseui-subtitle">You already have an account</h2><p class="firebaseui-text">'+
a+"</p>"+K(Ze())+'</div><div class="firebaseui-card-footer">'+K($e())+'<div class="firebaseui-form-actions">'+K(We())+"</div></div></form></div>";return J(b)};Cf.soyTemplateName="firebaseui.auth.soy2.page.passwordLinking";
var Df=function(a,b,c){c=c||{};M(m(a.email)||a.email instanceof I,"email",a.email,"string|goog.soy.data.SanitizedContent");var d=a.email;M(m(a.providerId)||a.providerId instanceof I,"providerId",a.providerId,"string|goog.soy.data.SanitizedContent");b="";var e=""+df(a),e=re(e);a="Sign in with "+e;d="You\u2019ve already used <strong>"+(K(d)+("</strong>. Sign in with "+(K(e)+" to continue.")));b+='<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-federated-linking"><form onsubmit="'+
(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Sign in</h1></div><div class="firebaseui-card-content"><h2 class="firebaseui-subtitle">You already have an account</h2><p class="firebaseui-text">'+d+'</p></div><div class="firebaseui-card-footer"><div class="firebaseui-form-actions">'+K(P({label:re(""+a)}))+"</div></div></form></div>";return J(b)};Df.soyTemplateName="firebaseui.auth.soy2.page.federatedLinking";
var Ef=function(a,b,c){c=c||{};M(m(a.email)||a.email instanceof I,"email",a.email,"string|goog.soy.data.SanitizedContent");b="";var d='<p class="firebaseui-text">for <strong>'+(K(a.email)+"</strong></p>");c='<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-reset"><form onsubmit="'+(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Reset your password</h1></div><div class="firebaseui-card-content">'+
d;var d={label:re("New password")},e;for(e in a)e in d||(d[e]=a[e]);b+=c+K(Ye(d))+'</div><div class="firebaseui-card-footer"><div class="firebaseui-form-actions">'+K(Xe())+"</div></div></form></div>";return J(b)};Ef.soyTemplateName="firebaseui.auth.soy2.page.passwordReset";
var Ff=function(a){a=a||{};M(null==a.allowContinue||n(a.allowContinue)||1===a.allowContinue||0===a.allowContinue,"allowContinue",a.allowContinue,"boolean|null|undefined");a=""+('<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-reset-success"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Password changed</h1></div><div class="firebaseui-card-content"><p class="firebaseui-text">You can now sign in with your new password</p></div><div class="firebaseui-card-footer">'+(a.allowContinue?
'<div class="firebaseui-form-actions">'+K(P(null))+"</div>":"")+"</div></div>");return J(a)};Ff.soyTemplateName="firebaseui.auth.soy2.page.passwordResetSuccess";
var Gf=function(a){a=a||{};M(null==a.allowContinue||n(a.allowContinue)||1===a.allowContinue||0===a.allowContinue,"allowContinue",a.allowContinue,"boolean|null|undefined");a=""+('<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-password-reset-failure"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Try resetting your password again</h1></div><div class="firebaseui-card-content"><p class="firebaseui-text">Your request to reset your password has expired or the link has already been used</p></div><div class="firebaseui-card-footer">'+(a.allowContinue?
'<div class="firebaseui-form-actions">'+K(P(null))+"</div>":"")+"</div></div>");return J(a)};Gf.soyTemplateName="firebaseui.auth.soy2.page.passwordResetFailure";
var Hf=function(a,b,c){c=c||{};M(m(a.email)||a.email instanceof I,"email",a.email,"string|goog.soy.data.SanitizedContent");b=a.email;M(null==a.allowContinue||n(a.allowContinue)||1===a.allowContinue||0===a.allowContinue,"allowContinue",a.allowContinue,"boolean|null|undefined");a=a.allowContinue;var d="";b="Your sign-in email address has been changed back to <strong>"+(K(b)+"</strong>.");d+='<div class="mdl-card mdl-shadow--2dp firebaseui-container firebaseui-id-page-email-change-revoke-success"><form onsubmit="'+
(c.csp_nonce?"/*"+c.csp_nonce+"*/":"")+'return false;"><div class="firebaseui-card-header"><h1 class="firebaseui-title">Updated email address</h1></div><div class="firebaseui-card-content"><p class="firebaseui-text">'+b+'</p><p class="firebaseui-text">If you didn\u2019t ask to change your sign-in email, it\u2019s possible someone is trying to access your account and you should <a class="firebaseui-link firebaseui-id-reset-password-link" href="javascript:void(0)">change your password right away</a>.</p></div><div class="firebaseui-card-footer">'+
(a?'<div class="firebaseui-form-actions">'+K(P(null))+"</div>":"")+"</div></form></div>";return J(d)};Hf.soyTemplateName="firebaseui.auth.soy2.page.emailChangeRevokeSuccess";
var If=function(a){a=a||{};M(null==a.allowContinue||n(a.allowContinue)||1===a.allowContinue||0===a.allowContinue,"allowContinue",a.allowCo
Download .txt
gitextract_6_u3udeh/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .firebaserc
├── .gitignore
├── LICENSE
├── README.md
├── build/
│   ├── build.js
│   ├── dev-client.js
│   ├── dev-server.js
│   ├── utils.js
│   ├── webpack.base.conf.js
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── config/
│   ├── dev.env.js
│   ├── index.js
│   ├── prod.env.js
│   └── test.env.js
├── dashboard.html
├── deploy.sh
├── firebase.json
├── index.html
├── package.json
├── src/
│   ├── App.vue
│   ├── auth.js
│   ├── base.scss
│   ├── common.scss
│   ├── components/
│   │   ├── Account.vue
│   │   ├── ActiveTask.vue
│   │   ├── Emotion.vue
│   │   ├── Member.vue
│   │   ├── Panel.vue
│   │   ├── Task.vue
│   │   ├── TeamPanel.vue
│   │   └── Usage.vue
│   ├── configs.js
│   ├── dashboard.js
│   ├── dataBase.js
│   ├── database.js
│   ├── index.js
│   ├── index.scss
│   ├── model.js
│   ├── timer.js
│   └── utils.js
├── static/
│   └── .gitkeep
├── test/
│   ├── e2e/
│   │   ├── custom-assertions/
│   │   │   └── elementCount.js
│   │   ├── nightwatch.conf.js
│   │   ├── runner.js
│   │   └── specs/
│   │       └── test.js
│   └── unit/
│       ├── .eslintrc
│       ├── index.js
│       ├── karma.conf.js
│       └── specs/
│           └── Hello.spec.js
└── vendors/
    ├── firebase-ui-auth.css
    └── firebase-ui-auth.js
Download .txt
SYMBOL INDEX (13 symbols across 2 files)

FILE: build/utils.js
  function generateLoaders (line 12) | function generateLoaders (loaders) {

FILE: vendors/firebase-ui-auth.js
  function c (line 5) | function c(){}
  function d (line 22) | function d(c){c&&b.appendChild(m(c)?a.createTextNode(c):c)}
  function b (line 81) | function b(a){this.content=a}
  function b (line 81) | function b(a){this.content=a}
  function b (line 82) | function b(a){this.content=a}
  function a (line 268) | function a(a,b){for(var c=0;c<k.length;c++)if(k[c].className===a)return"...
  function b (line 268) | function b(a){a=a.getAttribute("data-upgraded");return null===a?[""]:a.s...
  function c (line 268) | function c(a,c){return-1!==b(a).indexOf(c)}
  function d (line 268) | function d(b,c){if("undefined"===
  function e (line 269) | function e(d,e){if(!("object"===typeof d&&d instanceof Element))throw Er...
  function f (line 271) | function f(a){Array.isArray(a)||(a=a instanceof Element?[a]:Array.protot...
  function g (line 271) | function g(a){if(a){w.splice(w.indexOf(a),1);var b=a.element_.getAttribu...
Condensed preview — 56 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (286K chars).
[
  {
    "path": ".babelrc",
    "chars": 96,
    "preview": "{\n  \"presets\": [\"es2015\", \"stage-2\"],\n  \"plugins\": [\"transform-runtime\"],\n  \"comments\": false\n}\n"
  },
  {
    "path": ".editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".eslintignore",
    "chars": 36,
    "preview": "build/*.js\nconfig/*.js\nvendors/*.js\n"
  },
  {
    "path": ".eslintrc.js",
    "chars": 359,
    "preview": "module.exports = {\n  root: true,\n  parserOptions: {\n    sourceType: 'module'\n  },\n  extends: 'airbnb-base',\n  // require"
  },
  {
    "path": ".firebaserc",
    "chars": 55,
    "preview": "{\n  \"projects\": {\n    \"default\": \"tomato5-685bf\"\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "chars": 118,
    "preview": ".DS_Store\nnode_modules/\ndist/\nnpm-debug.log\nselenium-debug.log\ntest/unit/coverage\ntest/e2e/reports\nfirebase-debug.log\n"
  },
  {
    "path": "LICENSE",
    "chars": 1076,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Zhang Xin\n\nPermission is hereby granted, free of charge, to any person obtaini"
  },
  {
    "path": "README.md",
    "chars": 2011,
    "preview": "![logo](http://tomato5.io/static/icons/logo.png)\n\nTomato5 is a real-time collaboration tool.\nIt combines Pomodoro Techni"
  },
  {
    "path": "build/build.js",
    "chars": 956,
    "preview": "// https://github.com/shelljs/shelljs\nrequire('shelljs/global')\nenv.NODE_ENV = 'production'\n\nvar path = require('path')\n"
  },
  {
    "path": "build/dev-client.js",
    "chars": 245,
    "preview": "/* eslint-disable */\nrequire('eventsource-polyfill')\nvar hotClient = require('webpack-hot-middleware/client?noInfo=true&"
  },
  {
    "path": "build/dev-server.js",
    "chars": 1929,
    "preview": "var path = require('path')\nvar express = require('express')\nvar webpack = require('webpack')\nvar config = require('../co"
  },
  {
    "path": "build/utils.js",
    "chars": 1722,
    "preview": "var path = require('path')\nvar config = require('../config')\nvar ExtractTextPlugin = require('extract-text-webpack-plugi"
  },
  {
    "path": "build/webpack.base.conf.js",
    "chars": 1910,
    "preview": "var path = require('path')\nvar config = require('../config')\nvar utils = require('./utils')\nvar projectRoot = path.resol"
  },
  {
    "path": "build/webpack.dev.conf.js",
    "chars": 1228,
    "preview": "var config = require('../config')\nvar webpack = require('webpack')\nvar merge = require('webpack-merge')\nvar utils = requ"
  },
  {
    "path": "build/webpack.prod.conf.js",
    "chars": 4137,
    "preview": "var path = require('path')\nvar config = require('../config')\nvar utils = require('./utils')\nvar webpack = require('webpa"
  },
  {
    "path": "config/dev.env.js",
    "chars": 139,
    "preview": "var merge = require('webpack-merge')\nvar prodEnv = require('./prod.env')\n\nmodule.exports = merge(prodEnv, {\n  NODE_ENV: "
  },
  {
    "path": "config/index.js",
    "chars": 835,
    "preview": "// see http://vuejs-templates.github.io/webpack for documentation.\nvar path = require('path')\n\nmodule.exports = {\n  buil"
  },
  {
    "path": "config/prod.env.js",
    "chars": 48,
    "preview": "module.exports = {\n  NODE_ENV: '\"production\"'\n}\n"
  },
  {
    "path": "config/test.env.js",
    "chars": 132,
    "preview": "var merge = require('webpack-merge')\nvar devEnv = require('./dev.env')\n\nmodule.exports = merge(devEnv, {\n  NODE_ENV: '\"t"
  },
  {
    "path": "dashboard.html",
    "chars": 1250,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name= \"viewport\" content=\"width=device-width, initi"
  },
  {
    "path": "deploy.sh",
    "chars": 50,
    "preview": "git checkout master\nnpm run build\nfirebase deploy\n"
  },
  {
    "path": "firebase.json",
    "chars": 44,
    "preview": "{\n  \"hosting\": {\n    \"public\": \"dist\"\n  }\n}\n"
  },
  {
    "path": "index.html",
    "chars": 4857,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name= \"viewport\" content=\"width=device-width, initi"
  },
  {
    "path": "package.json",
    "chars": 2565,
    "preview": "{\n  \"name\": \"tomato5\",\n  \"version\": \"1.3.1\",\n  \"description\": \"A real-time collaboration tool. Built with Vue and Google"
  },
  {
    "path": "src/App.vue",
    "chars": 4347,
    "preview": "<template>\n  <div id=\"app\">\n    <article class=\"main\" v-bind:class=\"{'start': !user.uid}\">\n      <div class=\"title\">\n   "
  },
  {
    "path": "src/auth.js",
    "chars": 1957,
    "preview": "// import firebaseUi from '../vendors/firebase-ui-auth.js';\nimport firebase from 'firebase';\nimport firebaseUiAuthCss fr"
  },
  {
    "path": "src/base.scss",
    "chars": 298,
    "preview": "$red: #ff5959;\n$green: #42b983;\n$ink: #2c3e50;\n$blue: #5fd2db;\n$yellow: #fff082;\n$gray: #8d97a1;\n$light-gray: #f0f0f0;\n\n"
  },
  {
    "path": "src/common.scss",
    "chars": 1748,
    "preview": "@import 'base';\n\nhtml {\n  height: 100%;\n}\n\nbody {\n  // font-family: 'Architects Daughter', cursive;\n  // font-family: 'P"
  },
  {
    "path": "src/components/Account.vue",
    "chars": 453,
    "preview": "<template>\n  <div class=\"account\">\n    User: {{ user.displayName }}\n    <a class=\"name\" v-on:click=\"logout\">(Change)</a>"
  },
  {
    "path": "src/components/ActiveTask.vue",
    "chars": 762,
    "preview": "<template>\n  <div class=\"activeTask\">\n    <p class=\"operations\" transition=\"fade\" v-show=\"task.status === taskStatus.act"
  },
  {
    "path": "src/components/Emotion.vue",
    "chars": 1657,
    "preview": "<template>\n  <div class=\"emotion\">\n    <span class=\"inner\", v-bind:class=\"[emotionLevelClass]\">\n      {{level}}\n    </sp"
  },
  {
    "path": "src/components/Member.vue",
    "chars": 8727,
    "preview": "<template>\n  <div class=\"member\">\n    <div class=\"speech\" v-bind:class=\"{\n      'show': speech.isShowSpeech && (authUser"
  },
  {
    "path": "src/components/Panel.vue",
    "chars": 13698,
    "preview": "<template>\n  <section class=\"panel\"\n            v-bind:class=\"{\n              'resting': userStatus.availability === ava"
  },
  {
    "path": "src/components/Task.vue",
    "chars": 4962,
    "preview": "<template>\n  <div class=\"task\"\n       v-bind:class=\"{\n             'idle': task.status === taskStatus.idle,\n            "
  },
  {
    "path": "src/components/TeamPanel.vue",
    "chars": 13440,
    "preview": "<template>\n  <section class=\"teamPanel\">\n    <div class=\"teamPanel-wrapper\">\n      <h2 class=\"\">\n        Team\n        <!"
  },
  {
    "path": "src/components/Usage.vue",
    "chars": 7964,
    "preview": "<template>\n  <article class=\"usage\" transition=\"fade\" v-if=\"loaded\">\n    <h2>Metrics</h2>\n    <p class=\"chartTitle\">\n   "
  },
  {
    "path": "src/configs.js",
    "chars": 972,
    "preview": "/* eslint import/no-mutable-exports: 0 */\n\nlet taskDurations = {\n  standard: 1500,\n  resting: 300,\n};\n\nlet offLineThresh"
  },
  {
    "path": "src/dashboard.js",
    "chars": 194,
    "preview": "import Vue from 'vue';\nimport VueValidator from 'vue-validator';\nimport App from './App';\n\nVue.use(VueValidator);\n\n/* es"
  },
  {
    "path": "src/dataBase.js",
    "chars": 1199,
    "preview": "import firebase from 'firebase';\n\nlet database = null;\n\nconst init = function init() {\n  database = firebase.database();"
  },
  {
    "path": "src/database.js",
    "chars": 1199,
    "preview": "import firebase from 'firebase';\n\nlet database = null;\n\nconst init = function init() {\n  database = firebase.database();"
  },
  {
    "path": "src/index.js",
    "chars": 1134,
    "preview": "import normalizeCss from 'normalize.css'; /* eslint no-unused-vars: 0 */\nimport animateCss from 'animate.css'; /* eslint"
  },
  {
    "path": "src/index.scss",
    "chars": 3366,
    "preview": "@import 'base';\n\n$longScreenHeight: 600px;\n$wideScreenWidth: 600px;\n\n.welcome {\n  height: 900px;\n  min-height: 450px;\n  "
  },
  {
    "path": "src/model.js",
    "chars": 1557,
    "preview": "import moment from 'moment';\n\nconst taskStatus = {\n  dropped: -1,\n  idle: 0,\n  ongoing: 1,\n  paused: 2,\n  active: 3,\n  d"
  },
  {
    "path": "src/timer.js",
    "chars": 921,
    "preview": "import moment from 'moment';\nimport { taskDurations } from './configs';\n\nconst getRemaning = function getRemaning(startT"
  },
  {
    "path": "src/utils.js",
    "chars": 276,
    "preview": "const report = function report(eventCategory, eventAction, eventLabel, eventValue, fieldsObject) {\n  if (window.ga) {\n  "
  },
  {
    "path": "static/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/e2e/custom-assertions/elementCount.js",
    "chars": 777,
    "preview": "// A custom Nightwatch assertion.\n// the name of the method is the filename.\n// can be used in tests like this:\n//\n//   "
  },
  {
    "path": "test/e2e/nightwatch.conf.js",
    "chars": 943,
    "preview": "// http://nightwatchjs.org/guide#settings-file\nmodule.exports = {\n  \"src_folders\": [\"test/e2e/specs\"],\n  \"output_folder\""
  },
  {
    "path": "test/e2e/runner.js",
    "chars": 1009,
    "preview": "// 1. start the dev server using production config\nprocess.env.NODE_ENV = 'testing'\nvar server = require('../../build/de"
  },
  {
    "path": "test/e2e/specs/test.js",
    "chars": 372,
    "preview": "// For authoring Nightwatch tests, see\n// http://nightwatchjs.org/guide#usage\n\nmodule.exports = {\n  'default e2e tests':"
  },
  {
    "path": "test/unit/.eslintrc",
    "chars": 95,
    "preview": "{\n  \"env\": {\n    \"mocha\": true\n  },\n  \"globals\": {\n    \"expect\": true,\n    \"sinon\": true\n  }\n}\n"
  },
  {
    "path": "test/unit/index.js",
    "chars": 552,
    "preview": "// Polyfill fn.bind() for PhantomJS\n/* eslint-disable no-extend-native */\nFunction.prototype.bind = require('function-bi"
  },
  {
    "path": "test/unit/karma.conf.js",
    "chars": 2055,
    "preview": "// This is a karma config file. For more details see\n//   http://karma-runner.github.io/0.13/config/configuration-file.h"
  },
  {
    "path": "test/unit/specs/Hello.spec.js",
    "chars": 349,
    "preview": "import Vue from 'vue'\nimport Hello from 'src/components/Hello'\n\ndescribe('Hello.vue', () => {\n  it('should render correc"
  },
  {
    "path": "vendors/firebase-ui-auth.css",
    "chars": 20367,
    "preview": "/*! Terms: https://developers.google.com/terms */\n@import url(https://fonts.googleapis.com/css?family=Roboto:400,500,700"
  },
  {
    "path": "vendors/firebase-ui-auth.js",
    "chars": 150162,
    "preview": "/*! Terms: https://developers.google.com/terms */\n(function(){var h,l=this,aa=function(a){return void 0!==a},ba=function"
  }
]

About this extraction

This page contains the full source code of the zhangxin840/tomato5 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 56 files (267.0 KB), approximately 81.0k tokens, and a symbol index with 13 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!