define(['Twitter', 'TwitterConfirmer', 'TapsModel', 'JQMListView', 'ListPresenter', 'JQMEditView', 'EditPresenter', 'FollowingPresenter', 'SettingsPage'],
function(Twitter, TwitterConfirmer, TapsModel, JQMListView, ListPresenter, JQMEditView, EditPresenter, FollowingPresenter, SettingsPage) {

    function BeerTap(twitterProxy)
    {
        this.twitter = new Twitter(localStorage, twitterProxy);

        var listModel = new TapsModel(this.twitter);
        var listView = new JQMListView("listPage");
        var listPresenter = new ListPresenter("listPage", listModel, listView);

        var editView = new JQMEditView("editPage");
        var editModel = new TapsModel(new TwitterConfirmer(this.twitter, editView.page));
        var editPresenter = new EditPresenter("editPage", editModel, editView);

        var settingsPresenter = new SettingsPage("settings", this.twitter);

        this.mainPage = new FollowingPresenter("main", this.twitter, listPresenter, editPresenter, settingsPresenter);

        $(document).ajaxStart(function() { $.mobile.loading( 'show' ); });
        $(document).ajaxStop(function() { $.mobile.loading( 'hide' ); });
        $(document).ajaxError(function() { alert("Error fetching data"); });

        $.mobile.changePage("#main", { changeHash:false });
    }

    return BeerTap;

});

define(['Twitter', 'TwitterConfirmer', 'TapsModel', 'JQMListView', 'ListPresenter', 'JQMEditView', 'EditPresenter', 'FollowingPresenter', 'SettingsPage'],
function(Twitter, TwitterConfirmer, TapsModel, JQMListView, ListPresenter, JQMEditView, EditPresenter, FollowingPresenter, SettingsPage) {

    function BeerTap(twitterProxy)
    {
        this.twitter = new Twitter(localStorage, twitterProxy);

        var listModel = new TapsModel(this.twitter);
        var listView = new JQMListView("listPage");
        var listPresenter = new ListPresenter("listPage", listModel, listView);

        var editView = new JQMEditView("editPage");
        var editModel = new TapsModel(new TwitterConfirmer(this.twitter, editView.page));
        var editPresenter = new EditPresenter("editPage", editModel, editView);

        var settingsPresenter = new SettingsPage("settings", this.twitter);

        this.mainPage = new FollowingPresenter("main", this.twitter, listPresenter, editPresenter, settingsPresenter);

        $(document).ajaxStart(function() { $.mobile.loading( 'show' ); });
        $(document).ajaxStop(function() { $.mobile.loading( 'hide' ); });
        $(document).ajaxError(function() { alert("Error fetching data"); });

        $.mobile.changePage("#main");
    }

    return BeerTap;

});

Revert previous change to changePage() call - it's causing problems.


var express = require('express');
var http = require('http');
var routes = require('./routes');
var app = express();

app.set('views', __dirname);
app.set('view engine', 'ejs');

if (app.get('env') === 'development') {
  var browserSync = require('browser-sync');
  var bs = browserSync({ logSnippet: false });
  app.use(require('connect-browser-sync')(bs));
}

app.get('/', routes.index);

var port = 3000;
http.createServer(app).listen(port, function() {
  console.log('Listening on port ' + port + '...');
});

var express = require('express');
var http = require('http');
var routes = require('./routes');
var app = express();

app.set('views', __dirname);
app.set('view engine', 'ejs');

if (app.get('env') === 'development') {
  var browserSync = require('browser-sync');
  var bs = browserSync.create().init({ logSnippet: false });
  app.use(require('connect-browser-sync')(bs));
}

app.get('/', routes.index);

var port = 3000;
http.createServer(app).listen(port, function() {
  console.log('Listening on port ' + port + '...');
});

Use BrowserSync 2.0 form of initialization.


const path = require('path');
const webpack = require('webpack');

const buildEntryPoint = entryPoint => {
  return [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    entryPoint
  ];
};

const plugins = [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'commons',
    filename: 'common.js'
  }),
  new webpack.HotModuleReplacementPlugin()
];

module.exports = {
  entry: {
    app: buildEntryPoint('./src')
  },
  output: {
    path: path.join(__dirname, 'dev'),
    publicPath: '/',
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel',
        include: path.join(__dirname, 'src')
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      }
    ]
  },
  resolve: {
    root: [ path.resolve(__dirname, 'src') ],
    extensions: ['', '.js', '.jsx'],
  },
  plugins: plugins,
  devServer: {
    historyApiFallback: true,
    contentBase: path.resolve(__dirname, 'dev'),
    hot: true,
    inline: true,
    stats: {
      colors: true
    },
    port: 3000
  },
  devtool: 'source-map'
};

const path = require('path');
const webpack = require('webpack');

const buildEntryPoint = entryPoint => {
  return [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    entryPoint
  ];
};

const plugins = [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'commons',
    filename: 'common.js'
  }),
  new webpack.HotModuleReplacementPlugin()
];

module.exports = {
  entry: {
    app: buildEntryPoint('./src')
  },
  output: {
    path: path.join(__dirname, 'dev'),
    publicPath: '/',
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel',
        include: path.join(__dirname, 'src')
      },
      {
        test: /\.css$/,
        loader: 'style!css'
      }
    ]
  },
  resolve: {
    root: [ path.resolve(__dirname, 'src') ],
    extensions: ['', '.js', '.jsx'],
  },
  plugins: plugins,
  devServer: {
    historyApiFallback: true,
    contentBase: path.resolve(__dirname, 'dev'),
    hot: true,
    inline: true,
    stats: {
      assets: true,
      colors: true,
      version: false,
      hash: false,
      timings: true,
      chunks: false,
      chunkModules: false
    },
    port: 3000
  },
  devtool: 'source-map'
};

Clean up Webpack Dev Server Logs


const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const WebpackAutoInject = require('webpack-auto-inject-version');

module.exports = {
  entry: {
    'typedjson': './src/typedjson.ts',
    'typedjson.min': './src/typedjson.ts',
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.[jt]s$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [ '.ts', '.js' ],
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'js'),
    library: 'typedjson',
    libraryTarget: 'umd',
    umdNamedDefine: true,
  },
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        include: /\.min\.js$/,
        sourceMap: true,
      })
    ],
  },
  plugins: [
    new WebpackAutoInject({
      SHORT: 'typedjson',
      components: {
        AutoIncreaseVersion: false,
      },
      componentsOptions: {
        InjectAsComment: {
          tag: 'Version: {version} - {date}',
          dateFormat: 'isoDate',
        },
      },
    }),
  ],
  mode: "production",
};

const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const WebpackAutoInject = require('webpack-auto-inject-version');

module.exports = {
  entry: {
    'typedjson': './src/typedjson.ts',
    'typedjson.min': './src/typedjson.ts',
  },
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.[jt]s$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [ '.ts', '.js' ],
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'js'),
    library: 'typedjson',
    libraryTarget: 'umd',
    umdNamedDefine: true,
    globalObject: `(typeof self !== 'undefined' ? self : this)`,
  },
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        include: /\.min\.js$/,
        sourceMap: true,
      })
    ],
  },
  plugins: [
    new WebpackAutoInject({
      SHORT: 'typedjson',
      components: {
        AutoIncreaseVersion: false,
      },
      componentsOptions: {
        InjectAsComment: {
          tag: 'Version: {version} - {date}',
          dateFormat: 'isoDate',
        },
      },
    }),
  ],
  mode: "production",
};

Change global to node compatible


module.exports = {
    devtool: 'source-map',
    entry: './src/Home.jsx',
    output: {
        path: './public',
        filename: './js/app.js',
        publicPath: '/'
    },
    devServer: {
        inline: true,
        contentBase: './public',
    },
    module: {
        loaders: [
            {
                test: /[\.js$|\.jsx$]/,
                loader: 'babel-loader',
                exclude: /(node_modules|bower_components)/,
                query: {
                    cacheDirectory: false,
                    presets: ['es2015', 'react'],
                    plugins: ['transform-object-rest-spread']
                }
            }
        ]
    },
    cache: false,
    resolve: {
        extensions: ['', '.js', '.jsx']
    }
};

module.exports = {
    devtool: 'source-map',
    entry: './src/browser.jsx',
    output: {
        path: './public',
        filename: './js/app.js',
        publicPath: '/'
    },
    devServer: {
        inline: true,
        contentBase: './public',
    },
    module: {
        loaders: [
            {
                test: /[\.js$|\.jsx$]/,
                loader: 'babel-loader',
                exclude: /(node_modules|bower_components)/,
                query: {
                    cacheDirectory: false,
                    presets: ['es2015', 'react'],
                    plugins: ['transform-object-rest-spread']
                }
            }
        ]
    },
    cache: false,
    resolve: {
        extensions: ['', '.js', '.jsx']
    }
};

Include browser file as entry point


import React from 'react';
import { connect } from 'react-redux';

class ProfilePage extends React.Component {
  constructor(props){
    super(props);
    this.logoff = this.logoff.bind(this);
  }

  logoff(){
    localStorage.setItem('token', '');
    this.props.history.push('/login');
  }

  render(){
    return (

Profile Page

        <button onClick={this.logoff}>Log Off</button>

    );
  }
}
ProfilePage.propTypes = {
  logout: React.PropTypes.function
};
const mapStateToProps = () => {
  return {
    token: localStorage.token
  };
};

export default connect(mapStateToProps)(ProfilePage);

import React from 'react';
import { connect } from 'react-redux';
import { setChartAction } from '../actions/chartActions';

class ProfilePage extends React.Component {
  constructor(props){
    super(props);
    this.logoff = this.logoff.bind(this);
  }

  logoff(){
    localStorage.setItem('token', '');
    this.props.logout();
    this.props.history.push('/login');
  }

  render(){
    return (

Profile Page

        <button onClick={this.logoff}>Log Off</button>

    );
  }
}
ProfilePage.propTypes = {
  logout: React.PropTypes.function
};
const mapStateToProps = () => {
  return {
    token: localStorage.token
  };
};
const mapDispatchToProps = dispatch => {
return {
    logout: () => dispatch(setChartAction([]))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ProfilePage);

Delete charts in store on logout


/**
 * @module ol/format/XLink
 */

/**
 * @const
 * @type {string}
 */
const NAMESPACE_URI = 'http://www.w3.org/1999/xlink';

/**
 * @param {Node} node Node.
 * @return {boolean|undefined} Boolean.
 */
export function readHref(node) {
  return node.getAttributeNS(NAMESPACE_URI, 'href');
}

/**
 * @module ol/format/XLink
 */

/**
 * @const
 * @type {string}
 */
const NAMESPACE_URI = 'http://www.w3.org/1999/xlink';

/**
 * @param {Node} node Node.
 * @return {string|undefined} href.
 */
export function readHref(node) {
  return node.getAttributeNS(NAMESPACE_URI, 'href');
}

Fix wrong return type for readHref function


/* eslint-disable react/no-danger */

import Document, { Head, Main, NextScript } from 'next/document'
import React from 'react'

// The document (which is SSR-only) needs to be customized to expose the locale
// data for the user's locale for React Intl to work in the browser.
export default class IntlDocument extends Document {
  static async getInitialProps(context) {
    const props = await super.getInitialProps(context)
    const {
      req: { locale, localeDataScript },
    } = context

    return {
      ...props,
      locale,
      localeDataScript,
    }
  }

  render() {
    // Polyfill Intl API for older browsers
    const polyfill = `https://cdn.polyfill.io/v2/polyfill.min.js
      ?features=Intl.~locale.${this.props.locale}`

    return (
      <html lang={this.props.locale}>
        <Head>
          <meta content="text/html; charset=utf-8" httpEquiv="Content-type" />
          <meta content="width=device-width, initial-scale=1" name="viewport" />
          <meta content="IE=Edge" httpEquiv="X-UA-Compatible" />
          <link href="/_next/static/style.css" rel="stylesheet" />
        </Head>
        <body>
          <Main />
          <script src={polyfill} />
          <script
            dangerouslySetInnerHTML={{
              __html: this.props.localeDataScript,
            }}
          />
          <NextScript />
        </body>
      </html>
    )
  }
}

/* eslint-disable react/no-danger */

import Document, { Head, Main, NextScript } from 'next/document'
import React from 'react'

// The document (which is SSR-only) needs to be customized to expose the locale
// data for the user's locale for React Intl to work in the browser.
export default class IntlDocument extends Document {
  static async getInitialProps(context) {
    const props = await super.getInitialProps(context)
    const {
      req: { locale, localeDataScript },
    } = context

    return {
      ...props,
      locale,
      localeDataScript,
    }
  }

  render() {
    // Polyfill Intl API for older browsers
    const polyfill = `https://cdn.polyfill.io/v2/polyfill.min.js
      ?features=default,fetch,Intl,Intl.~locale.de,Intl.~locale.en`

    return (
      <html lang={this.props.locale}>
        <Head>
          <meta content="text/html; charset=utf-8" httpEquiv="Content-type" />
          <meta content="width=device-width, initial-scale=1" name="viewport" />
          <meta content="IE=Edge" httpEquiv="X-UA-Compatible" />
          <link href="/_next/static/style.css" rel="stylesheet" />
        </Head>
        <body>
          <Main />
          <script src={polyfill} />
          <script
            dangerouslySetInnerHTML={{
              __html: this.props.localeDataScript,
            }}
          />
          <NextScript />
        </body>
      </html>
    )
  }
}

Update polyfill url to include default


(function() {
    'use strict';

    angular
        .module('<%=angularAppName%>')
        .factory('SocialService', SocialService);

    SocialService.$inject = ['$document'];

    function SocialService ($document) {
        var socialService = {
            getProviderSetting: getProviderSetting,
            getProviderURL: getProviderURL,
            getCSRF: getCSRF
        };

        return socialService;

        function getProviderSetting (provider) {
            switch(provider) {
            case 'google': return 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email';
            case 'facebook': return 'public_profile,email';
            case 'twitter': return '';
                // jhipster-needle-add-social-button
            default: return 'Provider setting not defined';
            }
        }

        function getProviderURL (provider) {
            return 'signin/' + provider;
        }

        function getCSRF () {
            /* globals document */
            var name = 'XSRF-TOKEN=';
            var ca = $document[0].cookie.split(';');
            for (var i = 0; i < ca.length; i++) {
                var c = ca[i];
                while (c.charAt(0) === ' ') {
                    c = c.substring(1);
                }
                if (c.indexOf(name) !== -1) {
                    return c.substring(name.length, c.length);
                }
            }
            return '';
        }
    }
})();

(function() {
    'use strict';

    angular
        .module('<%=angularAppName%>')
        .factory('SocialService', SocialService);

    SocialService.$inject = ['$document', '$http', '$cookies'];

    function SocialService ($document, $http, $cookies) {
        var socialService = {
            getProviderSetting: getProviderSetting,
            getProviderURL: getProviderURL,
            getCSRF: getCSRF
        };

        return socialService;

        function getProviderSetting (provider) {
            switch(provider) {
            case 'google': return 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email';
            case 'facebook': return 'public_profile,email';
            case 'twitter': return '';
                // jhipster-needle-add-social-button
            default: return 'Provider setting not defined';
            }
        }

        function getProviderURL (provider) {
            return 'signin/' + provider;
        }

        function getCSRF () {
            return $cookies.get($http.defaults.xsrfCookieName);
        }
    }
})();

Use $cookies to retrive CSRF cookie token value


const Hapi = require('@hapi/hapi');
const routes = require('./routes');
const auth = require('./auth');

module.exports = async (elastic, config, cb) => {
  const server = new Hapi.Server({ port: config.port, routes: { cors: true, log: { collect: true } } });

  server.route(routes(elastic, config));

  if (config.auth) {
    server.route(auth());
    await server.register(require('hapi-auth-jwt2'));
    await server.register(require('./auth/authentication'));
  }

  try {
    await server.register([
      {
        plugin: require('good'),
        options: {
          reporters: {
            console: [{ module: 'good-console' }, 'stdout']
          }
        }
      },
      require('inert'),
      require('vision'),
      require('h2o2'),
      {
        plugin: require('./routes/plugins/error'),
        options: {
          config: config
        }
      }
    ]);
  } catch (err) {
    return cb(err);
  }

  server.views({
    engines: { html: { module: require('handlebars'), compileMode: 'sync' } },
    relativeTo: __dirname,
    path: './templates/pages',
    layout: 'default',
    layoutPath: './templates/layouts',
    partialsPath: './templates/partials',
    helpersPath: './templates/helpers'
  });

  cb(null, { server, elastic });
};

const Hapi = require('@hapi/hapi');
const routes = require('./routes');
const auth = require('./auth');

module.exports = async (elastic, config, cb) => {
  const server = new Hapi.Server({ port: config.port, routes: { cors: { origin: "ignore" }, log: { collect: true } } });

  server.route(routes(elastic, config));

  if (config.auth) {
    server.route(auth());
    await server.register(require('hapi-auth-jwt2'));
    await server.register(require('./auth/authentication'));
  }

  try {
    await server.register([
      {
        plugin: require('good'),
        options: {
          reporters: {
            console: [{ module: 'good-console' }, 'stdout']
          }
        }
      },
      require('inert'),
      require('vision'),
      require('h2o2'),
      {
        plugin: require('./routes/plugins/error'),
        options: {
          config: config
        }
      }
    ]);
  } catch (err) {
    return cb(err);
  }

  server.views({
    engines: { html: { module: require('handlebars'), compileMode: 'sync' } },
    relativeTo: __dirname,
    path: './templates/pages',
    layout: 'default',
    layoutPath: './templates/layouts',
    partialsPath: './templates/partials',
    helpersPath: './templates/helpers'
  });

  cb(null, { server, elastic });
};

Enable CORS ignore orgin header


// Copyright (c) Microsoft Corporation. All rights reserved.

var http = require('http'),
    url = require('url');

module.exports.attach = function (app) {
    app.all('/xhr_proxy', function proxyXHR(request, response) {
        var requestURL = url.parse(unescape(request.query.rurl));

        request.headers.host = requestURL.host;
        // fixes encoding issue
        delete request.headers['accept-encoding'];

        var options = {
            host: requestURL.host,
            path: requestURL.path,
            port: requestURL.port,
            method: request.method,
            headers: request.headers
        };

        var proxyCallback = function (proxyReponse) {
            proxyReponse.pipe(response);
        };

        http.request(options, proxyCallback).end();
    });
};

// Copyright (c) Microsoft Corporation. All rights reserved.

var http = require('http'),
    https = require('https'),
    url = require('url');

module.exports.attach = function (app) {
    app.all('/xhr_proxy', function proxyXHR(request, response) {
        var requestURL = url.parse(unescape(request.query.rurl));

        request.headers.host = requestURL.host;
        // fixes encoding issue
        delete request.headers['accept-encoding'];

        var options = {
            host: requestURL.host,
            path: requestURL.path,
            port: requestURL.port,
            method: request.method,
            headers: request.headers
        };

        var proxyCallback = function (proxyReponse) {
            proxyReponse.pipe(response);
        };

        if (requestURL.protocol === "https:") {
            https.request(options, proxyCallback).end();
        } else {
            http.request(options, proxyCallback).end();
        }
    });
};

Support for HTTPS in XHR proxy.


var gulp = require("gulp");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var buffer = require("vinyl-buffer");
var reactify = require("reactify");
var watch = require("gulp-watch");
var plumber = require("gulp-plumber");
var uglify = require('gulp-uglify');

gulp.task("browserify", function() {
  var bs = browserify({
    entries: ["./src/main.js"],
    transform: [reactify]
  });
  bs.bundle()
    .on("error", function() {})
    .pipe(plumber())
    .pipe(source("app.js"))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest("./public/js"));
});

gulp.task("watch", function() {
  gulp.watch("src/*.js",  ["browserify"]);
  gulp.watch("src/*.jsx", ["browserify"]);
});

gulp.task("default", ["browserify"]);

var gulp = require("gulp");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var buffer = require("vinyl-buffer");
var reactify = require("reactify");
var watch = require("gulp-watch");
var plumber = require("gulp-plumber");
var uglify = require('gulp-uglify');

gulp.task("browserify", function() {
  var bs = browserify({
    entries: ["./src/main.js"],
    transform: [reactify]
  });
  bs.bundle()
    .on("error", function(e) { console.log(e.message) })
    .pipe(plumber())
    .pipe(source("app.js"))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest("./public/js"));
});

gulp.task("watch", function() {
  gulp.watch("src/*.js",  ["browserify"]);
  gulp.watch("src/*.jsx", ["browserify"]);
});

gulp.task("default", ["browserify"]);

Change to show error message when browserify failed


const WIDTH = 800;
const HEIGHT = 600;

var setup = function() {
  createCanvas(WIDTH, HEIGHT);
  background(0);
  fill(255);

  angleMode(DEGREES);

  stroke(255);
  strokeWeight(1);
  strokeCap(SQUARE);
}

var draw = function() {
  translate(200, 200);
  rotate(45);
  rect(-50, -25, 100, 50);
}

const WIDTH = 800;
const HEIGHT = 600;

var setup = function() {
  createCanvas(WIDTH, HEIGHT);
  background(0);
  fill(255);

  angleMode(DEGREES);

  stroke(255);
  strokeWeight(1);
  strokeCap(SQUARE);
}

var draw = function() {
  push();
  translate(200, 200);
  rotate(45);
  rect(-50, -25, 100, 50);
  pop();

  push();
  translate(100, 300);
  rotate(45);
  rect(-50, -25, 100, 50);
  pop();

  push();
  translate(300, 100);
  rotate(45);
  rect(-50, -25, 100, 50);
  pop();
}

Use push and pop for different rectangles


const songs = [...document.querySelectorAll('.songList li')];
const currentSong = document.querySelector('#currentSong');
songs.map((s) => {
  s.addEventListener('click', () => {
    currentSong.setAttribute('src', "https://archive.org/download/plays_some_standards/" + s.textContent.toLowerCase().replace(/\s/g, '_') + ".mp3");
  });
});

const songs = [...document.querySelectorAll('.songList li')];
const currentSong = document.querySelector('#currentSong');
songs.map((s) => {
  s.addEventListener('click', () => {
    currentSong.setAttribute('src', "https://archive.org/download/plays_some_standards/" + s.textContent.toLowerCase().replace(/\s/g, '_') + ".mp3");
    songs.map((i) => {
      i.style.textDecoration = 'none';
    });
    s.style.textDecoration = 'underline';
  });
});

Add underline to playing song.


import moment from 'moment';
import {has} from 'ramda';

export default (newProps, momentProps) => {
    const dest = {};

    momentProps.forEach(key => {
        const value = newProps[key];

        if (value === null || value === undefined) {
            dest[key] = null;

            if (key === 'initial_visible_month') {
                dest[key] = moment(
                    newProps.start_date ||
                        newProps.min_date_allowed ||
                        newProps.end_date ||
                        newProps.max_date_allowed ||
                        undefined
                );
            }
        } else if (Array.isArray(value)) {
            dest[key] = value.map(moment);
        } else {
            dest[key] = moment(value);

            if (key === 'max_date_allowed' && has(key, dest)) {
                dest[key].add(1, 'days');
            }
        }
    });

    return dest;
};

import moment from 'moment';
import {has} from 'ramda';

export default (newProps, momentProps) => {
    const dest = {};

    momentProps.forEach(key => {
        const value = newProps[key];

        if (value === null || value === undefined) {
            dest[key] = null;

            if (key === 'initial_visible_month') {
                dest[key] = moment(
                    newProps.start_date ||
                        newProps.min_date_allowed ||
                        newProps.end_date ||
                        newProps.max_date_allowed ||
                        undefined
                );
            }
        } else if (Array.isArray(value)) {
            dest[key] = value.map(d => moment(d));
        } else {
            dest[key] = moment(value);

            if (key === 'max_date_allowed' && has(key, dest)) {
                dest[key].add(1, 'days');
            }
        }
    });

    return dest;
};

Fix problem with moment conversion


var gulp = require('gulp');
var gutil = require('gulp-util');
var bower = require('bower');
var concat = require('gulp-concat');
var sh = require('shelljs');
var del = require('del');
var copyHTML = require('ionic-gulp-html-copy');
var requireDir = require('require-dir');
var gulpTask = requireDir('./gulp');

gulp.task('default', ['clean'], function() {
    gulp.start('build');
});

gulp.task('install', ['git-check'], function() {
    return bower.commands.install()
        .on('log', function(data) {
            gutil.log('bower', gutil.colors.cyan(data.id), data.message);
        });
});

gulp.task('git-check', function(done) {
    if (!sh.which('git')) {
        console.log(
            '  ' + gutil.colors.red('Git is not installed.'),
            '\n  Git, the version control system, is required to download Ionic.',
            '\n  Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.',
            '\n  Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.'
        );
        process.exit(1);
    }
    done();
});

gulp.task('html', function() {
    return copyHTML({ dest: 'www' });
});

gulp.task('clean', function() {
    return del(['www/css', 'www/pages', 'www/js']);
});

var gulp = require('gulp');
var gutil = require('gulp-util');
var bower = require('bower');
var concat = require('gulp-concat');
var sh = require('shelljs');
var del = require('del');
var copyHTML = require('ionic-gulp-html-copy');
var requireDir = require('require-dir');
var gulpTask = requireDir('./gulp');

gulp.task('default', ['clean'], function() {
    gulp.start('build', 'watch');
});

gulp.task('install', ['git-check'], function() {
    return bower.commands.install()
        .on('log', function(data) {
            gutil.log('bower', gutil.colors.cyan(data.id), data.message);
        });
});

gulp.task('git-check', function(done) {
    if (!sh.which('git')) {
        console.log(
            '  ' + gutil.colors.red('Git is not installed.'),
            '\n  Git, the version control system, is required to download Ionic.',
            '\n  Download git here:', gutil.colors.cyan('http://git-scm.com/downloads') + '.',
            '\n  Once git is installed, run \'' + gutil.colors.cyan('gulp install') + '\' again.'
        );
        process.exit(1);
    }
    done();
});

gulp.task('html', function() {
    return copyHTML({ dest: 'www' });
});

gulp.task('clean', function() {
    return del(['www/css', 'www/pages', 'www/js']);
});

Add watch task to default


var gulp = require('gulp');
var elm = require('gulp-elm');
var plumber = require('gulp-plumber');
var del = require('del');
var browserSync = require('browser-sync');

// builds elm files and static resources (i.e. html and css) from src to dist folder
var paths = {
    dest: 'dist',
    elm: 'src/*.elm',
    staticAssets: 'src/*.{html,css}'
};

gulp.task('clean', function(cb) {
    del([paths.dest], cb);
});

gulp.task('elm-init', elm.init);

gulp.task('elm', ['elm-init'], function() {
    return gulp.src(paths.elm)
        .pipe(plumber())
        .pipe(elm())
        .pipe(gulp.dest(paths.dest))
        .pipe(browserSync.stream());
});

gulp.task('staticAssets', function() {
    return gulp.src(paths.staticAssets)
        .pipe(plumber())
        .pipe(gulp.dest(paths.dest))
        .pipe(browserSync.stream());
});

gulp.task('browser-sync', function() {
    browserSync.init({
        server: {
            baseDir: "dist/"
        }
    });
});

gulp.task('watch', function() {
    gulp.watch(paths.elm, ['elm']);
    gulp.watch(paths.staticAssets, ['staticAssets']);
});

gulp.task('build', ['elm', 'staticAssets']);
gulp.task('dev', ['build', 'browser-sync', 'watch']);
gulp.task('default', ['build']);

var gulp = require('gulp');
var elm = require('gulp-elm');
var plumber = require('gulp-plumber');
var del = require('del');
var browserSync = require('browser-sync');

// builds elm files and static resources (i.e. html and css) from src to dist folder
var paths = {
    dest: 'dist',
    elm: 'src/*.elm',
    staticAssets: 'src/*.{html,css}'
};

gulp.task('clean', function(cb) {
    del([paths.dest], cb);
});

gulp.task('elm-init', elm.init);

gulp.task('elm', ['elm-init'], function() {
    return gulp.src(paths.elm)
        .pipe(plumber())
        .pipe(elm())
        .pipe(gulp.dest(paths.dest))
        .pipe(browserSync.stream());
});

gulp.task('staticAssets', function() {
    return gulp.src(paths.staticAssets)
        .pipe(plumber())
        .pipe(gulp.dest(paths.dest))
        .pipe(browserSync.stream());
});

gulp.task('browser-sync', function() {
    browserSync.init({
        server: {
            baseDir: "dist/"
        },
        notify: false
    });
});

gulp.task('watch', function() {
    gulp.watch(paths.elm, ['elm']);
    gulp.watch(paths.staticAssets, ['staticAssets']);
});

gulp.task('build', ['elm', 'staticAssets']);
gulp.task('dev', ['build', 'browser-sync', 'watch']);
gulp.task('default', ['build']);

Disable notification in browser-sync as it caused issues with layout


const checkStatus = () => {
  fetch("/load_status", {
    credentials: "same-origin"
  })
    .then(response => {
      return response.text();
    })
    .then(data => {
      document.getElementById("repo-alert").style.display = "block";
      if (data === "true") {
        document.getElementById("repo-load-info").innerHTML =
          "Repositories refreshed! Click here to reload.";
      } else {
        setTimeout(checkStatus, 1000);
      }
    });
};

document.addEventListener("DOMContentLoaded", () => {
  if (document.getElementById("repo-alert")) {
    checkStatus();
  }
});

const checkStatus = () => {
  fetch("/repositories/load_status", {
    credentials: "same-origin"
  })
    .then(response => {
      return response.text();
    })
    .then(data => {
      document.getElementById("repo-alert").style.display = "block";
      if (data === "true") {
        document.getElementById("repo-load-info").innerHTML =
          "Repositories refreshed! Click here to reload.";
      } else {
        setTimeout(checkStatus, 1000);
      }
    });
};

document.addEventListener("DOMContentLoaded", () => {
  if (document.getElementById("repo-alert")) {
    checkStatus();
  }
});

Use correct load_status path in JS


var gulp = require('gulp')
var browserify = require('browserify')
var source = require('vinyl-source-stream')
var serve = require('gulp-serve')
var concat = require('gulp-concat')

// Static file server
gulp.task('server', serve({
  root: ['example', 'dist'],
  port: 7000
}))

gulp.task('build', function () {
  return browserify('./src/opbeat.js', {
    standalone: 'Opbeat'
  }).bundle()
    .pipe(source('opbeat.js'))
    .pipe(gulp.dest('./dist'))
})

// Development mode
gulp.task('watch', [], function (cb) {
  gulp.run(
    'build',
    'server'
  )

  // Watch JS files
  gulp.watch(['libs/**', 'src/**'], ['build'])

  console.log('\nExample site running on http://localhost:7000/\n')
})

//
// Default task
//
gulp.task('default', function () {
  var response = ['',
    'No task selected.',
    'Available tasks:', '',
    'gulp watch       - Watch files and preview example site on localhost.', ''
  ].join('\n')

  console.log(response)
})

var gulp = require('gulp')
var source = require('vinyl-source-stream')
var serve = require('gulp-serve')
var rename = require('gulp-rename')
var browserify = require('browserify')
var buffer = require('vinyl-buffer')
var uglify = require('gulp-uglify')
var taskListing = require('gulp-task-listing')

// Static file server
gulp.task('server', serve({
  root: ['example', 'dist'],
  port: 7000
}))

gulp.task('release', function () {
  return browserify('./src/opbeat.js', {
    standalone: 'Opbeat'
  }).bundle()
    .pipe(source('opbeat.js'))
    .pipe(gulp.dest('./dist'))
    .pipe(rename('opbeat.min.js'))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./dist'))
})

gulp.task('build', function () {
  return browserify('./src/opbeat.js', {
    standalone: 'Opbeat'
  }).bundle()
    .pipe(source('opbeat.js'))
    .pipe(gulp.dest('./dist'))
})

// Development mode
gulp.task('watch', [], function (cb) {
  gulp.run(
    'build',
    'server'
  )

  // Watch JS files
  gulp.watch(['libs/**', 'src/**'], ['build'])
  console.log('\nExample site running on http://localhost:7000/\n')
})

gulp.task('default', taskListing)

Add gulp release script to generate minified build.


var gulp = require('gulp');
var less = require('gulp-less');
var plumber = require("gulp-plumber");
var browserSync = require('browser-sync').create();

gulp.task('run', function() {
  browserSync.init({
    server: "./"
  });
  gulp.watch(['komforta.less', 'less/*.less'], ['compile']);
  gulp.watch('*.css').on('change', browserSync.reload);
});

gulp.task('compile', function() {
  return gulp.src('./komforta.less')
    .pipe(plumber())
    .pipe(less({
      paths: ['./']
    }))
    .pipe(gulp.dest('./'))
    .pipe(browserSync.stream());
});

var gulp = require('gulp');
var less = require('gulp-less');
var plumber = require("gulp-plumber");
var browserSync = require('browser-sync').create();

gulp.task('run', function() {
  browserSync.init({
    server: "./"
  });
  gulp.watch(['komforta.less', 'less/*.less'], ['compile']);
  gulp.watch('*.css').on('change', browserSync.reload);
});

gulp.task('compile', function() {
  return gulp.src('./komforta.less')
    .pipe(plumber({
      errorHandler: function(error) {
        console.log(error.message);
        this.emit('end');
      }
    }))
    .pipe(less())
    .pipe(gulp.dest('./'))
    .pipe(browserSync.stream());
});

Add settings when error occured


Target = function(x, y) {
  this.x = x;
  this.y = y;
  this.life = 11;
  this.size = 12;

  return this;
}
Target.prototype.render = function() {
  var x = this.x - gCamera.x;
  var y = this.y - gCamera.y;

  // Only render if within the camera's bounds
  if (gCamera.isInView(this)) {
    renderPath([['arc', x + 2, y + 2, 13, 0, Math.PI * 2]]);
    fill('#222');

    renderPath([['arc', x, y, this.size, 0, Math.PI * 2]]);
    fill('rgb(' + Math.round(255 * (this.life / this.size)) + ',' + Math.round(255 * ((this.size - this.life) / this.size)) + ',' + 0x22 + ')');
  }
}

Target = function(x, y, type) {
  this.x = x;
  this.y = y;
  this.life = 11;
  this.size = 12;
  this.type = typeof type === 'string' ? type : 'circle';

  return this;
}
Target.prototype.render = function() {
  var x = this.x - gCamera.x;
  var y = this.y - gCamera.y;

  // Only render if within the camera's bounds
  if (gCamera.isInView(this)) {
    switch(this.type) {
      case 'circle':
        renderPath([['arc', x + 2, y + 2, 13, 0, Math.PI * 2]]);
        fill('#222');

        renderPath([['arc', x, y, this.size, 0, Math.PI * 2]]);
        fill('rgb(' + Math.round(255 * (this.life / this.size)) + ',' + Math.round(255 * ((this.size - this.life) / this.size)) + ',' + 0x22 + ')');
        break;
    }
  }
}

Update Target to accept different types


'use strict';

function PromiseWrap() {}

module.exports = function patchPromise() {
  const hooks = this._hooks;
  const state = this._state;

  const Promise = global.Promise;
  const oldThen = Promise.prototype.then;
  Promise.prototype.then = wrappedThen;

  function makeWrappedHandler(fn, handle, uid) {
    if ('function' !== typeof fn) return fn;

    return function wrappedHandler() {
      hooks.pre.call(handle);
      try {
        return fn.apply(this, arguments);
      } finally {
        hooks.post.call(handle);
        hooks.destroy.call(null, uid);
      }
    };
  }

  function wrappedThen(onFulfilled, onRejected) {
    if (!state.enabled) return oldThen.call(this, onFulfilled, onRejected);

    const handle = new PromiseWrap();
    const uid = --state.counter;

    hooks.init.call(handle, 0, uid, null);

    return oldThen.call(
      this,
      makeWrappedHandler(onFulfilled, handle, uid),
      makeWrappedHandler(onRejected, handle, uid)
    );
  }
};

'use strict';

function PromiseWrap() {}

module.exports = function patchPromise() {
  const hooks = this._hooks;
  const state = this._state;

  const Promise = global.Promise;

  /* As per ECMAScript 2015, .catch must be implemented by calling .then, as
   * such we need needn't patch .catch as well. see:
   * http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.catch
   */
  const oldThen = Promise.prototype.then;
  Promise.prototype.then = wrappedThen;

  function makeWrappedHandler(fn, handle, uid) {
    if ('function' !== typeof fn) return fn;

    return function wrappedHandler() {
      hooks.pre.call(handle);
      try {
        return fn.apply(this, arguments);
      } finally {
        hooks.post.call(handle);
        hooks.destroy.call(null, uid);
      }
    };
  }

  function wrappedThen(onFulfilled, onRejected) {
    if (!state.enabled) return oldThen.call(this, onFulfilled, onRejected);

    const handle = new PromiseWrap();
    const uid = --state.counter;

    hooks.init.call(handle, 0, uid, null);

    return oldThen.call(
      this,
      makeWrappedHandler(onFulfilled, handle, uid),
      makeWrappedHandler(onRejected, handle, uid)
    );
  }
};

Add comment explaining why it is unnecessary to patch .catch


import Ember from 'ember';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';

let App;

Ember.MODEL_FACTORY_INJECTIONS = true;

App = Ember.Application.extend({
  modulePrefix: config.modulePrefix,
  podModulePrefix: config.podModulePrefix,
  Resolver
});

loadInitializers(App, config.modulePrefix);

export default App;

import Ember from 'ember';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';

let App;

App = Ember.Application.extend({
  modulePrefix: config.modulePrefix,
  podModulePrefix: config.podModulePrefix,
  Resolver
});

loadInitializers(App, config.modulePrefix);

export default App;

Fix Model Factory Injections Deprecation


'use strict';

let isCI = !!process.env.CI;
let smokeTests = !!process.env.SMOKE_TESTS;

let config = {
  framework: 'qunit',
  test_page: smokeTests ? 'tests/index.html?smoke_tests=true' : 'tests/index.html?hidepassed',
  disable_watching: true,
  browser_start_timeout: smokeTests ? 300000 : 30000,
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: [
        // --no-sandbox is needed when running Chrome inside a container
        process.env.CI ? '--no-sandbox' : null,
        '--headless',
        '--disable-gpu',
        '--disable-dev-shm-usage',
        '--disable-software-rasterizer',
        '--mute-audio',
        '--remote-debugging-port=0',
        '--window-size=1440,900',
      ],
    },
  },
  launch_in_dev: ['Chrome'],
  launch_in_ci: ['Chrome'],
};

if (isCI) {
  config.tap_quiet_logs = true;
}

module.exports = config;

'use strict';

let isCI = !!process.env.CI;
let smokeTests = !!process.env.SMOKE_TESTS;

let config = {
  framework: 'qunit',
  test_page: smokeTests ? 'tests/index.html?smoke_tests=true' : 'tests/index.html?hidepassed',
  disable_watching: true,
  browser_start_timeout: smokeTests ? 300000 : 30000,
  browser_disconnect_timeout: smokeTests ? 120 : 10,
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: [
        // --no-sandbox is needed when running Chrome inside a container
        process.env.CI ? '--no-sandbox' : null,
        '--headless',
        '--disable-gpu',
        '--disable-dev-shm-usage',
        '--disable-software-rasterizer',
        '--mute-audio',
        '--remote-debugging-port=0',
        '--window-size=1440,900',
      ],
    },
  },
  launch_in_dev: ['Chrome'],
  launch_in_ci: ['Chrome'],
};

if (isCI) {
  config.tap_quiet_logs = true;
}

module.exports = config;

Increase browser disconnect timeout in smoke tests


/*jslint browser: true*/

if (navigator.serviceWorker && location.protocol === 'https://') {
    try {
        navigator.serviceWorker.register('/serviceworker.js', {
            scope: '/'
        });
        window.addEventListener('load', function () {
            if (navigator.serviceWorker.controller) {
                navigator.serviceWorker.controller.postMessage({'command': 'trimCaches'});
            }
        });
    } catch {}
}

(function () {
    var cookieMessage = document.getElementById('cookie-message');
    if (cookieMessage && document.cookie.indexOf('seen_cookie_message=yes') === -1) {
        cookieMessage.style.display = 'block';
        document.cookie = 'seen_cookie_message=yes; max-age=31536000; path=/';
    }
})();

/*jslint browser: true*/

if (navigator.serviceWorker && location.host !== 'localhost:8000') {
    navigator.serviceWorker.register('/serviceworker.js', {
        scope: '/'
    });
    window.addEventListener('load', function () {
        if (navigator.serviceWorker.controller) {
            navigator.serviceWorker.controller.postMessage({'command': 'trimCaches'});
        }
    });
}

(function () {
    var cookieMessage = document.getElementById('cookie-message');
    if (cookieMessage && document.cookie.indexOf('seen_cookie_message=yes') === -1) {
        cookieMessage.style.display = 'block';
        document.cookie = 'seen_cookie_message=yes; max-age=31536000; path=/';
    }
})();

Revert "catch service worker error?"


module("About Numbers (topics/about_numbers.js)");

test("types", function() {
    var typeOfIntegers = typeof(6);
    var typeOfFloats = typeof(3.14159);
    equal(__, typeOfIntegers === typeOfFloats, 'are ints and floats the same type?');
    equal(__, typeOfIntegers, 'what is the javascript numeric type?');
    equal(__, 1.0, 'what is a integer number equivalent to 1.0?');
});

test("NaN", function() {
    var resultOfFailedOperations = 7/'apple';
    equal(__, isNaN(resultOfFailedOperations), 'what will satisfy the equals assertion?');
    equal(__, resultOfFailedOperations == NaN, 'is NaN == NaN?');
});

module("About Numbers (topics/about_numbers.js)");

test("types", function() {
    var typeOfIntegers = typeof(6);
    var typeOfFloats = typeof(3.14159);
    equal(true, typeOfIntegers === typeOfFloats, 'are ints and floats the same type?');
    equal('number', typeOfIntegers, 'what is the javascript numeric type?');
    equal(1, 1.0, 'what is a integer number equivalent to 1.0?');
});

test("NaN", function() {
    var resultOfFailedOperations = 7/'apple';
    equal(true, isNaN(resultOfFailedOperations), 'what will satisfy the equals assertion?');
    equal(false, resultOfFailedOperations == NaN, 'is NaN == NaN?');
});

Complete numbers tests to pass


/*!
 * stylus-initial
 * Copyright (c) 2014 Pierre-Antoine "Leny" Delnatte <info@flatland.be>
 * (Un)Licensed
 */

"use strict";

var utils = require( "stylus" ).utils,
    rInitial = /([a-z\-]+)\s*:\s*(initial);/gi,
    oInitialValues = require( "./values.json" );

var plugin = function() {
    return function( stylus ) {
        stylus.on( "end", function( err, css ) {
            return css.replace( rInitial, function( sRule, sProperty ) {
                var mInitialValue;
                if( ( mInitialValue = oInitialValues[ sProperty ] ) != null ) {
                    return sProperty + ": " + mInitialValue + ";";
                }
                return sRule;
            } );
        } );
    };
};

module.exports = plugin;

module.exports.version = require( "../package.json" ).version;

/*!
 * stylus-initial
 * Copyright (c) 2014 Pierre-Antoine "Leny" Delnatte <info@flatland.be>
 * (Un)Licensed
 */

"use strict";

var utils = require( "stylus" ).utils,
    rInitial = /([a-z\-]+)\s*:\s*(initial);/gi,
    oInitialValues = require( "./values.json" );

var plugin = function() {
    return function( stylus ) {
        var oEvaluator = this.evaluator,
            nodes = this.nodes,
            _fVisitProperty = oEvaluator.visitProperty;

        oEvaluator.visitProperty = function( oNode ) {
            var oCurrentValue, mInitialValue;
            _fVisitProperty.call( oEvaluator, oNode );
            if( oNode.nodeName !== "property" || oNode.expr.isList || ( oCurrentValue = oNode.expr.first ).nodeName !== "ident" || oCurrentValue.string !== "initial" ) {
                return oNode;
            }
            if( ( mInitialValue = oInitialValues[ oNode.name ] ) != null ) {
                oNode.expr = new nodes.Literal( mInitialValue );
            }
            return oNode;
        };
    };
};

module.exports = plugin;

module.exports.version = require( "../package.json" ).version;

Refactor the code to be a pre-processing task.


const getTaggedTemplateLiteralContent = require('./tagged-template-literal').getTaggedTemplateLiteralContent

const getCSS = (node) => `
.selector {
  ${getTaggedTemplateLiteralContent(node)}
}
`

const getKeyframes = (node) => `
@keyframes {
  ${getTaggedTemplateLiteralContent(node)}
}
`

exports.getKeyframes = getKeyframes
exports.getCSS = getCSS

const getTaggedTemplateLiteralContent = require('./tagged-template-literal').getTaggedTemplateLiteralContent

const getCSS = (node) => `.selector {${getTaggedTemplateLiteralContent(node)}}\n`

const getKeyframes = (node) => `@keyframes {${getTaggedTemplateLiteralContent(node)}}\n`

exports.getKeyframes = getKeyframes
exports.getCSS = getCSS

Fix whitespace in generated CSS


module.exports = {
  SESSION_TOKEN_KEY: 'SESSION_TOKEN_KEY',
  backend: {
    hapiRemote: true,
    hapiLocal: false,
    parseRemote: false,
    parseLocal: false
  },
  HAPI: {
    local: {
      url: 'http://localhost:5000'
    },
    remote: {
      url: 'https://snowflakeserver-bartonhammond.rhcloud.com/'
    }
  },
  PARSE: {
    appId: 'snowflake', // match APP_ID in parse-server's index.js
    local: {
      url: 'http://localhost:1337/parse' // match SERVER_URL in parse-server's index.js
    },
    remote: {
      url: 'http://snowflake-parse.herokuapp.com/parse' // match SERVER_URL in parse-server's index.js
    }
  }
}

module.exports = {
  SESSION_TOKEN_KEY: 'SESSION_TOKEN_KEY',
  backend: {
    hapiRemote: false,
    hapiLocal: false,
    parseRemote: true,
    parseLocal: false
  },
  HAPI: {
    local: {
      url: 'http://localhost:5000'
    },
    remote: {
      url: 'https://snowflakeserver-bartonhammond.rhcloud.com/'
    }
  },
  PARSE: {
    appId: 'snowflake', // match APP_ID in parse-server's index.js
    local: {
      url: 'http://localhost:1337/parse' // match SERVER_URL in parse-server's index.js
    },
    remote: {
      url: 'http://snowflake-parse.herokuapp.com/parse' // match SERVER_URL in parse-server's index.js
    }
  }
}

Change default remote server to a working one


'use strict';

/* App Module */

angular.module('nestorshop', ['nestorshopServices']).
  config(['$routeProvider', function($routeProvider) {
  $routeProvider.
      when('/products', {templateUrl: 'partials/product-list.html',   controller: ProductListCtrl}).
      when('/products/:productId', {templateUrl: 'partials/product-detail.html', controller: ProductDetailCtrl}).
      when('/shoplists', {templateUrl: 'partials/shoplist-list.html',   controller: ShopListsCtrl}).
      when('/shoplists/:shoplistId', {templateUrl: 'partials/shoplist-detail.html',   controller: ShopListDetailCtrl}).
      otherwise({redirectTo: '/products'});
}]);

'use strict';

/* App Module */

angular.module('nestorshop', ['nestorshopServices'], function($httpProvider) {
	  // Use x-www-form-urlencoded Content-Type
	$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';

	  // Override $http service's default transformRequest
	$httpProvider.defaults.transformRequest = [function(data) {
	    return angular.isObject(data) && String(data) !== '[object File]' ? jQuery.param(data) : data;
	  }];
  }).
  config(['$routeProvider', function($routeProvider) {
  $routeProvider.
      when('/products', {templateUrl: 'partials/product-list.html',   controller: ProductListCtrl}).
      when('/products/:productId', {templateUrl: 'partials/product-detail.html', controller: ProductDetailCtrl}).
      when('/shoplists', {templateUrl: 'partials/shoplist-list.html',   controller: ShopListsCtrl}).
      when('/shoplists/:shoplistId', {templateUrl: 'partials/shoplist-detail.html',   controller: ShopListDetailCtrl}).
      otherwise({redirectTo: '/products'});

}]);

Change POST of angular to send values in FormData (for jersey to read)


// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

function doSearch() {

    var topic = document.getElementById("topic-search-box").value;

    fetch("/search?topic="+topic).then(response => response.json()).then((results) => {
       console.log(results);
    });

}

// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

function doSearch() {

    var topic = document.getElementById("topic-search-box").value;

    fetch("/search?topic="+topic).then(response => response.text()).then((results) => {
       console.log(results);
    });

}

Change response to text instead of json


// Rollup plugins.
import babel from 'rollup-plugin-babel'
import cjs from 'rollup-plugin-commonjs'
import globals from 'rollup-plugin-node-globals'
import replace from 'rollup-plugin-replace'
import resolve from 'rollup-plugin-node-resolve'

export default {
  dest: 'build/app.js',
  entry: 'src/index.js',
  format: 'iife',
  plugins: [
    babel({
      babelrc: false,
      exclude: 'node_modules/**',
      presets: [ [ 'es2015', { modules: false } ], 'stage-0', 'react' ],
      plugins: [ 'external-helpers' ]
    }),
    cjs({
      exclude: 'node_modules/process-es6/**',
      include: [
        'node_modules/fbjs/**',
        'node_modules/object-assign/**',
        'node_modules/react/**',
        'node_modules/react-dom/**',
        'node_modules/prop-types/**'
      ]
    }),
    globals(),
    replace({ 'process.env.NODE_ENV': JSON.stringify('development') }),
    resolve({
      browser: true,
      main: true
    })
  ],
  sourceMap: true
}

// Rollup plugins.
import babel from 'rollup-plugin-babel'
import cjs from 'rollup-plugin-commonjs'
import globals from 'rollup-plugin-node-globals'
import replace from 'rollup-plugin-replace'
import resolve from 'rollup-plugin-node-resolve'

export default {
  dest: 'build/app.js',
  entry: 'src/index.js',
  format: 'iife',
  plugins: [
    babel({
      babelrc: false,
      exclude: 'node_modules/**',
      presets: [ [ 'es2015', { modules: false } ], 'stage-0', 'react' ],
      plugins: [ 'external-helpers' ]
    }),
    cjs({
      exclude: 'node_modules/process-es6/**',
      include: [
        'node_modules/create-react-class/**',
        'node_modules/fbjs/**',
        'node_modules/object-assign/**',
        'node_modules/react/**',
        'node_modules/react-dom/**',
        'node_modules/prop-types/**'
      ]
    }),
    globals(),
    replace({ 'process.env.NODE_ENV': JSON.stringify('development') }),
    resolve({
      browser: true,
      main: true
    })
  ],
  sourceMap: true
}

Include create-react-class in Rollup configuration :newspaper:.


import React from 'react'
import PropTypes from 'prop-types'
import hoist from 'hoist-non-react-statics'

import ErrorPage from '../../pages/_error'

export default Page => {
  const Extended = hoist(class extends React.Component {
    static propTypes = {
      error: PropTypes.object
    }

    static defaultProps = {
      error: null
    }

    state = {
      error: null
    }

    componentDidCatch(error) {
      this.setState({
        error
      })
    }

    render() {
      const {error: stateError} = this.state
      const {error: propsError} = this.props

      const error = stateError || propsError

      if (error) {
        return (
          <ErrorPage {...this.props} code={error.code} />
        )
      }

      return (
        <Page {...this.props} />
      )
    }
  }, Page)

  if (Page.getInitialProps) {
    Extended.getInitialProps = async context => {
      try {
        return await Page.getInitialProps(context)
      } catch (error) {
        return {
          error
        }
      }
    }
  }

  return Extended
}

import React from 'react'
import PropTypes from 'prop-types'
import hoist from 'hoist-non-react-statics'

import ErrorPage from '../../pages/_error'

export default Page => {
  const Extended = hoist(class extends React.Component {
    static propTypes = {
      error: PropTypes.object
    }

    static defaultProps = {
      error: null
    }

    state = {
      error: null
    }

    componentDidCatch(error) {
      this.setState({
        error
      })
    }

    render() {
      const {error: stateError} = this.state
      const {error: propsError} = this.props

      const error = stateError || propsError

      if (error) {
        return (
          <ErrorPage {...this.props} code={error.code} />
        )
      }

      return (
        <Page {...this.props} />
      )
    }
  }, Page)

  Extended.getInitialProps = async context => {
    if (Page.getInitialProps) {
      try {
        return await Page.getInitialProps(context)
      } catch (error) {
        if (context.res) {
          context.res.statusCode = error.code || 500
        }

        return {
          error
        }
      }
    }

    return {}
  }

  return Extended
}

Set server statusCode on error


'use strict';

const { addOauthHeader } = require('@clevercloud/client/cjs/oauth.node.js');
const { prefixUrl } = require('@clevercloud/client/cjs/prefix-url.js');
const { request } = require('@clevercloud/client/cjs/request.request.js');
const { conf, loadOAuthConf } = require('../models/configuration.js');

async function sendToApi (requestParams) {
  const tokens = await loadOAuthConf().toPromise();
  return Promise.resolve(requestParams)
    .then(prefixUrl(conf.API_HOST))
    .then(addOauthHeader({
      OAUTH_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
      OAUTH_CONSUMER_SECRET: conf.OAUTH_CONSUMER_SECRET,
      API_OAUTH_TOKEN: tokens.token,
      API_OAUTH_TOKEN_SECRET: tokens.secret,
    }))
    .then(request);
}

async function getHostAndTokens () {
  const userTokens = await loadOAuthConf().toPromise();
  return {
    apiHost: conf.API_HOST,
    tokens: {
      OAUTH_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
      OAUTH_CONSUMER_SECRET: conf.OAUTH_CONSUMER_SECRET,
      API_OAUTH_TOKEN: userTokens.token,
      API_OAUTH_TOKEN_SECRET: userTokens.secret,
    },
  };
}

module.exports = { sendToApi, getHostAndTokens };

'use strict';

const { addOauthHeader } = require('@clevercloud/client/cjs/oauth.node.js');
const { prefixUrl } = require('@clevercloud/client/cjs/prefix-url.js');
const { request } = require('@clevercloud/client/cjs/request.superagent.js');
const { conf, loadOAuthConf } = require('../models/configuration.js');

async function sendToApi (requestParams) {
  const tokens = await loadOAuthConf().toPromise();
  return Promise.resolve(requestParams)
    .then(prefixUrl(conf.API_HOST))
    .then(addOauthHeader({
      OAUTH_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
      OAUTH_CONSUMER_SECRET: conf.OAUTH_CONSUMER_SECRET,
      API_OAUTH_TOKEN: tokens.token,
      API_OAUTH_TOKEN_SECRET: tokens.secret,
    }))
    .then(request);
}

async function getHostAndTokens () {
  const userTokens = await loadOAuthConf().toPromise();
  return {
    apiHost: conf.API_HOST,
    tokens: {
      OAUTH_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
      OAUTH_CONSUMER_SECRET: conf.OAUTH_CONSUMER_SECRET,
      API_OAUTH_TOKEN: userTokens.token,
      API_OAUTH_TOKEN_SECRET: userTokens.secret,
    },
  };
}

module.exports = { sendToApi, getHostAndTokens };

Use @clevercloud/client superagent helper instead of request


function checkWindowSize(){
    var w = document.documentElement.clientWidth;
    var panel = document.getElementById("help-panel");
    if (w < 978) {
        panel.style.position = "static";
        panel.style.width = "auto";
    } else if (w >= 978 && w < 1184) {
        panel.style.position = "fixed";
        panel.style.width = "250px";
        unfix();
    } else {
        panel.style.position = "fixed";
        panel.style.width = "275px";
        unfix();
    }
}

function unfix() {
    if (document.readyState === "complete") {
        var panel = document.getElementById("help-panel");
        if (panel.style.position !== "static") {
            var panelRect = panel.getBoundingClientRect();
            var yOffset = document.body.clientHeight - 600 - panelRect.height - 95;
            if (window.pageYOffset > yOffset) {
                panel.style.top = "" + yOffset + "px";
                panel.style.position = "absolute";
            } else if (window.pageYOffset < yOffset) {
                panel.style.top = "95px";
                panel.style.position = "fixed";
            }
        }
    }
}

window.addEventListener("resize", checkWindowSize);
window.addEventListener("scroll", unfix);

$(document).ready(function() {
    checkWindowSize();
});

Add newline to end of file


/**
 * Created by rgwozdz on 8/15/15.
 */

var moduleUnderTest = require("../../app/common.js")
var assert = require('chai').assert;
var equal = require('deep-equal');

describe('common.js module', function() {
    describe('parseQueryOptions', function () {
        it('should return expected Object with properties and values', function () {

            var result = moduleUnderTest.parseQueryOptions({fields:'a,b', limit: 2, order_by: 'a,b', order:'DESC', returnGeom:'false'},'a,b,c', {
                geometryColumn: null });

            var expectedResult = {
                columns: 'a,b',
                geometryColumn: null,
                limit: 'LIMIT 2',
                order_by: 'ORDER BY a,b'
            };

            assert.equal(equal(result,expectedResult), true);

        });

        it('should return expected Object with properties and values', function () {

            var result = moduleUnderTest.parseQueryOptions({},'a,b,c', {
                columns: 'a,b,c',
                geometryColumn: 'geom'});

            var expectedResult = {
                columns: 'a,b,c',
                geometryColumn: null
            };

            assert.equal(equal(result,expectedResult), true);

        });

    });
});

/**
 * Created by rgwozdz on 8/15/15.
 */

var moduleUnderTest = require("../../app/common.js")
var assert = require('chai').assert;
var equal = require('deep-equal');

describe('common.js module', function() {
    describe('parseQueryOptions', function () {
        it('should return expected Object with properties and values', function () {

            var result = moduleUnderTest.parseQueryOptions({fields:'a,b', limit: 2, order_by: 'a,b', order:'DESC', returnGeom:'false'},'a,b,c', {
                geometryColumn: null });

            var expectedResult = {
                columns: 'a,b',
                geometryColumn: null,
                limit: 'LIMIT 2',
                order_by: 'ORDER BY a,b DESC'
            };

            assert.equal(equal(result,expectedResult), true);

        });

        it('should return expected Object with properties and values', function () {

            var result = moduleUnderTest.parseQueryOptions({},'a,b,c', {
                columns: 'a,b,c',
                geometryColumn: 'geom'});

            var expectedResult = {
                columns: 'a,b,c',
                geometryColumn: null
            };

            assert.equal(equal(result,expectedResult), true);

        });

    });
});

Adjust unit test expected result.


// # Sandstorm API
// RESTful API for the Sandstorm resource
var capnp          = require('capnp'),
    url            = require('url'),
    sandstorm;

var HackSession = capnp.importSystem("hack-session.capnp");

var publicIdPromise;

var initPromise = function () {
    if (!publicIdPromise) {
        var connection = capnp.connect("unix:/tmp/sandstorm-api");
        var session = connection.restore("HackSessionContext", HackSession.HackSessionContext);
        publicIdPromise = session.getPublicId();
        publicIdPromise.tap = function () {
            return publicIdPromise;
        };
    }
};

sandstorm = {

    /**
     * ## live
     * Return link to live site
     * @param {{context}} options (optional)
     * @returns hash containing url
     */
    live: function browse() {
        initPromise();
        var promise = publicIdPromise.then(function (data) {
            return {url: data.autoUrl};
        });
        promise.tap = function () {
            return promise;
        };
        return promise;
    },

    /**
     * ## faq
     * Return data for faq page
     * @param {{context}} options (optional)
     * @returns hash of relevant data
     */
    faq: function browse() {
        initPromise();
        var promise = publicIdPromise.then(function (data) {
            data.autoUrl = url.parse(data.autoUrl).host;
            return data;
        });
        promise.tap = function () {
            return promise;
        };
        return promise;
    },
};

module.exports = sandstorm;

// # Sandstorm API
// RESTful API for the Sandstorm resource
var capnp          = require('capnp'),
    url            = require('url'),
    _              = require('lodash'),
    sandstorm;

var HackSession = capnp.importSystem("hack-session.capnp");

var publicIdPromise;

var initPromise = function () {
    if (!publicIdPromise) {
        var connection = capnp.connect("unix:/tmp/sandstorm-api");
        var session = connection.restore("HackSessionContext", HackSession.HackSessionContext);
        publicIdPromise = session.getPublicId();
        publicIdPromise.tap = function () {
            return publicIdPromise;
        };
    }
};

sandstorm = {

    /**
     * ## live
     * Return link to live site
     * @param {{context}} options (optional)
     * @returns hash containing url
     */
    live: function browse() {
        initPromise();
        var promise = publicIdPromise.then(function (data) {
            return {url: data.autoUrl};
        });
        promise.tap = function () {
            return promise;
        };
        return promise;
    },

    /**
     * ## faq
     * Return data for faq page
     * @param {{context}} options (optional)
     * @returns hash of relevant data
     */
    faq: function browse() {
        initPromise();
        var promise = publicIdPromise.then(function (data) {
            var ret = _.clone(data);
            ret.autoUrl = url.parse(ret.autoUrl).host;
            return ret;
        });
        promise.tap = function () {
            return promise;
        };
        return promise;
    },
};

module.exports = sandstorm;

Fix bug in returning autoUrl for Sandstorm


const idea = (state = [], action) => {
  switch (action.type) {
    case "SET_IDEAS":
      return action.ideas
    case "ADD_IDEA":
      return [...state, action.idea]
    case "UPDATE_IDEA":
      return state.map(idea => {
        return (idea.id === action.ideaId) ? Object.assign({}, idea, action.newAttributes) : idea
      })
    case "DELETE_IDEA":
      return state.filter(idea => idea.id !== action.ideaId)
    default:
      return state
  }
}

export default idea

const idea = (state = [], action) => {
  switch (action.type) {
    case "SET_IDEAS":
      return action.ideas
    case "ADD_IDEA":
      return [...state, action.idea]
    case "UPDATE_IDEA":
      return state.map(idea => {
        return (idea.id === action.ideaId) ? { ...idea, ...action.newAttributes } : idea
      })
    case "DELETE_IDEA":
      return state.filter(idea => idea.id !== action.ideaId)
    default:
      return state
  }
}

export default idea

Use spread operator rather than Object.assign


import request from 'request';
import { parseString } from 'xml2js';

export default function loadFromBGG(req) {
  return new Promise((resolve, reject) => {
    request({
      url: `http://www.boardgamegeek.com/xmlapi2/search?query=${req.query.q}&type=boardgame`,
      timeout: 10000
    }, (error, response, body) => {
      if (error) reject(error);
      parseString(body, (err, res) => {
        if (err) reject(err);
        if (Array.isArray(res.items.item)) {
          resolve(res.items.item
            .map((el) => {
              return {
                bggid: el.$.id,
                type: el.$.type,
                name: el.name[0].$.value,
                year: el.yearpublished ? Number(el.yearpublished[0].$.value) : 0
              };
            })
            .filter((el) => {
              return el.type === 'boardgame';
            })
            .sort((prev, current) => {
              return current.year - prev.year;
            }));
        } else {
          resolve([]);
        }
      });
    });
  });
}

import request from 'request';
import { parseString } from 'xml2js';

export default function loadFromBGG(req) {
  return new Promise((resolve, reject) => {
    request({
      url: `http://www.boardgamegeek.com/xmlapi2/search?query=${req.query.q}&type=boardgame`,
      timeout: 10000
    }, (error, response, body) => {
      if (error) reject(error);
      parseString(body, (err, res) => {
        if (err) reject(err);
        if (Array.isArray(res.items.item)) {
          resolve(res.items.item
            .map((el) => {
              return {
                bggid: el.$.id,
                type: el.$.type,
                name: el.name[0].$.value,
                year: el.yearpublished ? Number(el.yearpublished[0].$.value) : 0
              };
            })
            .filter((el) => {
              return el.type === 'boardgame';
            })
            .sort((prev, current) => {
              // TODO: Should the items be sorted by year?
              // return current.year - prev.year;
            }));
        } else {
          resolve([]);
        }
      });
    });
  });
}

Comment out sorting function for now


// Config object to be passed to Msal on creation
const msalConfig = {
    auth: {
        clientId: "e760cab2-b9a1-4c0d-86fb-ff7084abd902",
        authority: "https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_susi",
        knownAuthorities: ["fabrikamb2c.b2clogin.com"]
    },
    cache: {
        cacheLocation: "localStorage", // This configures where your cache will be stored
        storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
    },
    system: {
        telemetry: {
            applicationName: 'msalVanillaTestApp',
            applicationVersion: 'test1.0',
            telemetryEmitter: (events) => {
                console.log("Telemetry Events", events);
            }
        }
    }
};

// Add here scopes for id token to be used at MS Identity Platform endpoints.
const loginRequest = {
    scopes: ["https://fabrikamb2c.onmicrosoft.com/helloapi/demo.read"],
    forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
};

const editProfileRequest = {
    scopes: ["openid"],
    authority: "https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_edit_profile",
    forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
}

// Config object to be passed to Msal on creation
const msalConfig = {
    auth: {
        clientId: "5dac5d6d-225c-4a98-a5e4-e29c82c0c4c9",
        authority: "https://public.msidlabb2c.com/tfp/cpimtestpartners.onmicrosoft.com/b2c_1_signupsignin_userflow",
        knownAuthorities: ["public.msidlabb2c.com"]
    },
    cache: {
        cacheLocation: "localStorage", // This configures where your cache will be stored
        storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
    },
    system: {
        telemetry: {
            applicationName: 'msalVanillaTestApp',
            applicationVersion: 'test1.0',
            telemetryEmitter: (events) => {
                console.log("Telemetry Events", events);
            }
        }
    }
};

// Add here scopes for id token to be used at MS Identity Platform endpoints.
const loginRequest = {
    scopes: ["openid", "profile"],
    forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
};

const editProfileRequest = {
    scopes: ["openid"],
    authority: "https://public.msidlabb2c.com/tfp/cpimtestpartners.onmicrosoft.com/b2c_1_editprofile_userflow",
    forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
}

Update with lab app registration


/* global $ */
/* global GOVUK */

// Warn about using the kit in production
if (
  window.sessionStorage && window.sessionStorage.getItem('prototypeWarning') !== 'false' &&
  window.console && window.console.info
) {
  window.console.info('GOV.UK Prototype Kit - do not use for production')
  window.sessionStorage.setItem('prototypeWarning', true)
}

$(document).ready(function () {
  // Use GOV.UK shim-links-with-button-role.js to trigger a link styled to look like a button,
  // with role="button" when the space key is pressed.
  GOVUK.shimLinksWithButtonRole.init()

  // Show and hide toggled content
  // Where .multiple-choice uses the data-target attribute
  // to toggle hidden content
  var showHideContent = new GOVUK.ShowHideContent()
  showHideContent.init()
})

/* global $ */
/* global GOVUK */

// Warn about using the kit in production
if (window.console && window.console.info) {
  window.console.info('GOV.UK Prototype Kit - do not use for production')
}

$(document).ready(function () {
  // Use GOV.UK shim-links-with-button-role.js to trigger a link styled to look like a button,
  // with role="button" when the space key is pressed.
  GOVUK.shimLinksWithButtonRole.init()

  // Show and hide toggled content
  // Where .multiple-choice uses the data-target attribute
  // to toggle hidden content
  var showHideContent = new GOVUK.ShowHideContent()
  showHideContent.init()
})

Fix JS error in Safari’s Private Browsing mode


var _ = require('lodash');

module.exports = function(grunt) {
  var module = {};

  module.domain = function() {
    return process.env.GRUNT_DRUPAL_DOMAIN || grunt.config('config.domain') || require('os').hostname();
  };

  // Ensure a default URL for the project.
  module.siteUrls = function() {
    return grunt.config('config.siteUrls.default') || "http://<%= config.domain %>";
  }

  module.buildPaths = function() {
    // Set implicit global configuration.
    var buildPaths = grunt.config('config.buildPaths');
    buildPaths = _.extend({
      build: 'build',
      html: 'build/html',
      package: 'build/packages',
      reports: 'build/reports',
      temp: 'build/temp'
    }, buildPaths);

    return buildPaths;
  }

  module.init = function() {
    if (grunt.config('config.siteUrls') === undefined)
      grunt.config('config.siteUrls', {});

    grunt.config('config.domain', module.domain());
    grunt.config('config.siteUrls.default', module.siteUrls(defaultUrl));
    grunt.config('config.buildPaths', module.buildPaths());
  };

  return module;
};

var _ = require('lodash');

module.exports = function(grunt) {
  var module = {};

  module.domain = function() {
    return process.env.GRUNT_DRUPAL_DOMAIN || grunt.config('config.domain') || require('os').hostname();
  };

  // Ensure a default URL for the project.
  module.siteUrls = function() {
    return grunt.config('config.siteUrls.default') || "http://<%= config.domain %>";
  }

  module.buildPaths = function() {
    // Set implicit global configuration.
    var buildPaths = grunt.config('config.buildPaths');
    buildPaths = _.extend({
      build: 'build',
      html: 'build/html',
      package: 'build/packages',
      reports: 'build/reports',
      temp: 'build/temp'
    }, buildPaths);

    return buildPaths;
  }

  module.init = function() {
    if (grunt.config('config.siteUrls') === undefined)
      grunt.config('config.siteUrls', {});

    grunt.config('config.domain', module.domain());
    grunt.config('config.siteUrls.default', module.siteUrls());
    grunt.config('config.buildPaths', module.buildPaths());
  };

  return module;
};

Remove stray variable breaking defaultUrl calculation.


/*!
 * YUI Mocha
 * Copyright 2011 Yahoo! Inc.
 * Licensed under the BSD license.
 */

/**
 * @module mime
 */

/**
 * File extension to MIME type map.
 *
 * Used by `serve.js` when streaming files from disk.
 *
 * @property contentTypes
 * @type object
 */
this.contentTypes = {
  "css": "text/css",
  "html": "text/html",
  "ico": "image/vnd.microsoft.icon",
  "jpeg": "image/jpeg",
  "jpg": "image/jpeg",
  "js": "application/javascript",
  "json": "application/json",
  "less": "text/css",
  "png": "image/png",
  "svg": "image/svg+xml",
  "swf": "application/x-shockwave-flash",
  "tiff": "image/tiff",
  "txt": "text/plain",
  "xml": "text/xml"
};

/*!
 * YUI Mocha
 * Copyright 2011 Yahoo! Inc.
 * Licensed under the BSD license.
 */

/**
 * @module mime
 */

/**
 * File extension to MIME type map.
 *
 * Used by `serve.js` when streaming files from disk.
 *
 * @property contentTypes
 * @type object
 */
this.contentTypes = {
  "css": "text/css",
  "html": "text/html",
  "ico": "image/vnd.microsoft.icon",
  "jpeg": "image/jpeg",
  "jpg": "image/jpeg",
  "js": "application/javascript",
  "json": "application/json",
  "less": "text/css",
  "png": "image/png",
  "svg": "image/svg+xml",
  "swf": "application/x-shockwave-flash",
  "tiff": "image/tiff",
  "txt": "text/plain",
  "xml": "application/xml"
};

Use same .xml MIME type as rgrove/combohandler.


'use strict';
/**
 *
 * @param {integer } digit
 * @returns {string} Returns Nepali converted string of given digit or numbers.
 * @example
 *
 * numbers(2090873)
 * // => २०९०८७३
 *
 * numbers()
 * // => false
 *
 */

function numbers (digit) {

    if(!digit) {
        console.log("@numbers: Yuck, got nothing");
        return false;
    }

    if(isNaN(parseInt(digit, 10))) {
        console.log("@numbers: Commooon, give it a numbers");
        return false;
    }

    const nepaliNumberss = ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९'];
    const digitToConvert = digit.toString().split('');

    const output = digitToConvert.map(num => {
      let i = 0;
      const l = parseInt(num, 10);
      for (i; i < 10; i += 1) {
        if (i === l) {
            return nepaliNumberss[i];
        }
      }
    });

    return output.join('');
}

export default numbers;

'use strict';
/**
 *
 * @param {integer } digit
 * @returns {string} Returns Nepali converted string of given digit or numbers.
 * @example
 *
 * numbers(2090873)
 * // => २०९०८७३
 *
 * numbers()
 * // => false
 *
 */

function numbers (digit) {

    const isdigit = parseInt(digit, 10);
    if(typeof isdigit !== "number") {
        console.log("@numbers: Yuck, got nothing");
        return false;
    }

    if(isNaN(isdigit)) {
        console.log("@numbers: Commooon, give it a numbers");
        return false;
    }

    const nepaliNumberss = ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९'];
    const digitToConvert = digit.toString().split('');

    const output = digitToConvert.map(num => {
      let i = 0;
      const l = parseInt(num, 10);
      for (i; i < 10; i += 1) {
        if (i === l) {
            return nepaliNumberss[i];
        }
      }
    });

    return output.join('');
}

export default numbers;

Check typeof of given value


'use strict';

/**
 * Retrieve a localized configuration value or the default if the localization
 * is unavailable.
 *
 * @param {String} configuration value
 * @param {String} language
 * @param {Object} Hexo configuration object
 * @param {String} Hexo locals object
 * @returns {*} localized or default configuration value
 */
exports._c = function _c(value, lang, config, locals) {
	if (locals.data['config_' + lang] != null) {
		return retrieveItem(locals.data['config_' + lang], value) || retrieveItem(config, value);
	}
	return retrieveItem(config, value);
};

/**
 * Retrieve nested item from object/array (http://stackoverflow.com/a/16190716)
 * @param {Object|Array} obj
 * @param {String} path dot separated
 * @param {*} def default value ( if result undefined )
 * @returns {*}
 */
function retrieveItem(obj, path, def) {
	var i, len;
	for (i = 0, path = path.split('.'), len = path.length; i < len; i++) {
		if (!obj || typeof obj !== 'object') return def;
		obj = obj[path[i]];
	}
	if (obj === undefined) return def;
	return obj;
}

'use strict';

/**
 * Retrieve a localized configuration value or the default if the localization
 * is unavailable.
 *
 * @param {String} configuration value
 * @param {String} language
 * @param {Object} Hexo configuration object
 * @param {String} Hexo locals object
 * @returns {*} localized or default configuration value
 */
exports._c = function _c(value, lang, config, locals) {
	if (locals.data['config_' + lang] != null) {
		var localized = retrieveItem(locals.data['config_' + lang], value)
		return localized != undefined ? localized : retrieveItem(config, value);
	}
	return retrieveItem(config, value);
};

/**
 * Retrieve nested item from object/array (http://stackoverflow.com/a/16190716)
 * @param {Object|Array} obj
 * @param {String} path dot separated
 * @param {*} def default value ( if result undefined )
 * @returns {*}
 */
function retrieveItem(obj, path, def) {
	var i, len;
	for (i = 0, path = path.split('.'), len = path.length; i < len; i++) {
		if (!obj || typeof obj !== 'object') return def;
		obj = obj[path[i]];
	}
	if (obj === undefined) return def;
	return obj;
}

Check for undefined localized values


'use strict';

header.$inject = ['$rootScope', '$state', 'AuthService'];
function header($rootScope, $state, AuthService) {
    return {
        name: 'header',
        template: require('./templates/header.html'),
        scope: true,
        link: function link(scope) {
            scope.toggled = false;
            scope.toggleNav = toggleNav;
			scope.logout = logout;

            $rootScope.$on('$stateChangeSuccess', function() {
                scope.toggled = false;
				scope.isAuthenticated = AuthService.isAuthenticated;
            });

            function toggleNav() {
                scope.toggled = !scope.toggled;
            }

			function logout() {
				AuthService.logout();
				$state.go('home');
			}
        }
    };
}

module.exports = header;

'use strict';

header.$inject = ['$rootScope', '$state', 'AuthService'];
function header($rootScope, $state, AuthService) {
    return {
        name: 'header',
        template: require('./templates/header.html'),
        scope: true,
        link: function link(scope) {
            scope.toggled = false;
            scope.toggleNav = toggleNav;
			scope.logout = logout;

            $rootScope.$on('$stateChangeSuccess', function() {
                scope.toggled = false;
				scope.isAuthenticated = AuthService.isAuthenticated;
            });

            function toggleNav() {
                scope.toggled = !scope.toggled;
            }

			function logout() {
				AuthService.logout();
				$state.go('home', {}, {reload: true});
			}
        }
    };
}

module.exports = header;

Set logout button to reload state if current state is 'home'


/* task for generating html from sisdocs markdown */
module.exports = function(grunt) {
    'use strict';
    var marked = require('marked');
    var hljs = require('highlight.js');
    marked.setOptions({
        breaks : true,
        highlight: function(code, lang) {
            var result =  hljs.highlight(lang, code).value;
            return result;
        }
    });

    grunt.registerMultiTask('sismd', "Converts SIS-web markdown to html", function() {
        var options = this.options({ });
        this.files.forEach(function(file) {
            var data = file.src.filter(function(path) {
                if (!grunt.file.isFile(path)) {
                    grunt.log.warn("File does not exist. %s", source);
                    return false;
                }
                return true;
            }).map(function(path) {
                return grunt.file.read(path);
            }).join("\n");
            var dest = file.dest;
            if (options.preprocess) {
                data = options.preprocess(data);
            }
            var output = marked(data);
            grunt.file.write(dest, output);
        });
    });
};

/* task for generating html from sisdocs markdown */
module.exports = function(grunt) {
    'use strict';
    var marked = require('marked');
    var hljs = require('highlight.js');
    var renderer = new marked.Renderer();

    var oldLink = renderer.link;
    renderer.link = function(href, title, text) {
        if (href[0] === '#' && text.indexOf('.') !== -1) {
            href = '#' + text.replace(/[^\w:]/g, '-').toLowerCase();
        }
        return oldLink.call(this, href, title, text);
    };

    marked.setOptions({
        breaks : true,
        highlight: function(code, lang) {
            var result =  hljs.highlight(lang, code).value;
            return result;
        },
        renderer : renderer
    });

    grunt.registerMultiTask('sismd', "Converts SIS-web markdown to html", function() {
        var options = this.options({ });
        this.files.forEach(function(file) {
            var data = file.src.filter(function(path) {
                if (!grunt.file.isFile(path)) {
                    grunt.log.warn("File does not exist. %s", source);
                    return false;
                }
                return true;
            }).map(function(path) {
                return grunt.file.read(path);
            }).join("\n");
            var dest = file.dest;
            if (options.preprocess) {
                data = options.preprocess(data);
            }
            var output = marked(data);
            grunt.file.write(dest, output);
        });
    });
};

Fix a bug in documentation generation


export default {
  "id": 101,
  "email": "hugo@demo.studentinsights.org",
  "admin": false,
  "full_name": "Teacher, Hugo",
  "staff_type": null,
  "schoolwide_access": false,
  "grade_level_access": [],
  "restricted_to_sped_students": false,
  "restricted_to_english_language_learners": false,
  "can_view_restricted_notes": false,
  "districtwide_access": false,
  "labels": [],
  "school": {
    "id": 9,
    "name": "Somerville High"
  },
  "sections": [
    {
      "id": 3,
      "section_number": "ART-302A",
      "course_description": "Ceramic Art 3"
    },
    {
      "id": 4,
      "section_number": "ART-302B",
      "course_description": "Ceramic Art 3"
    }
  ],
  "labels": []
};

export default {
  "id": 101,
  "email": "hugo@demo.studentinsights.org",
  "admin": false,
  "full_name": "Teacher, Hugo",
  "staff_type": null,
  "schoolwide_access": false,
  "grade_level_access": [],
  "restricted_to_sped_students": false,
  "restricted_to_english_language_learners": false,
  "can_view_restricted_notes": false,
  "districtwide_access": false,
  "school": {
    "id": 9,
    "name": "Somerville High"
  },
  "sections": [
    {
      "id": 3,
      "section_number": "ART-302A",
      "course_description": "Ceramic Art 3"
    },
    {
      "id": 4,
      "section_number": "ART-302B",
      "course_description": "Ceramic Art 3"
    }
  ],
  "labels": []
};

Fix lint error in JS fixture


import { select } from 'd3';
import Item from './item';

export default class ButtonItem extends Item {
  constructor() {
    super();

    this._root
      .classed('button', true)
      .styles({
        'background': '#FFF',
        'cursor': 'pointer',
        'height': '3em',
        'justify-content': 'center'
      });

    this._text = this._root
      .append('button')
      .attrs({
        'tabindex': -1,
        'type': 'button'
      })
      .styles({
        'background': 'none',
        'border': '1px solid transparent',
        'color': 'inherit',
        'cursor': 'inherit',
        'line-height': '2em',
        'margin': 0,
        'padding': '0 0.25em'
      });

    this._padding.styles({
      'display': 'none'
    });
  }

  tabindex(value = null) {
    if (value === null) {
      return this._text.attr('tabindex');
    }

    this._text.attr('tabindex', value);
    return this;
  }

  text(value = null) {
    if (value === null) {
      return this._text;
    }

    this._text.text(value);
    return this;
  }

  _click() {
    if (this._disabled === false && this._model) {
      this._model.set(this._name, this._value);
    }
  }
}

import Item from './item';

export default class ButtonItem extends Item {
  constructor() {
    super();

    this._root
      .classed('button', true)
      .styles({
        'background': '#FFF',
        'cursor': 'pointer',
        'height': '3em',
        'justify-content': 'center',
        'padding': '0.5em 0'
      });

    this._text = this._root
      .append('button')
      .attrs({
        'tabindex': -1,
        'type': 'button'
      })
      .styles({
        'background': 'none',
        'border': '1px solid transparent',
        'color': 'inherit',
        'cursor': 'inherit',
        'line-height': '2em',
        'margin': 0,
        'padding': '0 0.25em'
      });

    this._padding.styles({
      'display': 'none'
    });
  }

  tabindex(value = null) {
    if (value === null) {
      return this._text.attr('tabindex');
    }

    this._text.attr('tabindex', value);
    return this;
  }

  text(value = null) {
    if (value === null) {
      return this._text;
    }

    this._text.text(value);
    return this;
  }

  _click() {
    if (this._disabled === false && this._model) {
      this._model.set(this._name, this._value);
    }
  }
}

Add padding to button item


window.admin = {};

$(document).ready(function() {
	$("#admin-feedback").on("tabOpened", function() {
		window.api.get("admin/feedback/getList", function(resp) {
			$("#admin-feedback-list").text("");
			for (var feedbackIndex in resp.feedback) {
				var feedbackItem = resp.feedback[feedbackIndex];
				var $feedbackLi = $('

');
					var $feedbackDesc = $('

');
						$feedbackDesc.text(" #" + feedbackItem.feedbackId + " " + feedbackItem.msg);
						var $icon = $('');
							if (feedbackItem.type == "smile") {
								$icon.addClass("fa-smile-o");
							} else if (feedbackItem.type == "frown") {
								$icon.addClass("fa-frown-o");
							} else if (feedbackItem.type == "idea") {
								$icon.addClass("fa-lightbulb-o");
							}
						$feedbackDesc.prepend($icon);
					$feedbackLi.append($feedbackDesc);
					var $feedbackName = $('

');
						$feedbackName.text(feedbackItem.name);
					$feedbackLi.append($feedbackName);
				$("#admin-feedback-list").append($feedbackLi);
			};
		});
	});
});

window.admin = {};

$(document).ready(function() {
	$("#admin-feedback").on("tabOpened", function() {
		window.api.get("admin/feedback/getList", function(resp) {
			$("#admin-feedback-list").text("");
			for (var feedbackIndex in resp.feedback) {
				var feedbackItem = resp.feedback[feedbackIndex];
				var $feedbackLi = $('

');
					var $feedbackDesc = $('

');
						$feedbackDesc.text(" #" + feedbackItem.feedbackId + " " + feedbackItem.msg);
						var $icon = $('');
							if (feedbackItem.type == "smile") {
								$icon.addClass("fa-smile-o");
							} else if (feedbackItem.type == "frown") {
								$icon.addClass("fa-frown-o");
							} else if (feedbackItem.type == "idea") {
								$icon.addClass("fa-lightbulb-o");
							}
						$feedbackDesc.prepend($icon);
					$feedbackLi.append($feedbackDesc);
					var $feedbackName = $('

');
						$feedbackName.text(feedbackItem.name + " (" + feedbackItem.username + ")");
					$feedbackLi.append($feedbackName);
				$("#admin-feedback-list").append($feedbackLi);
			};
		});
	});
});

Add username to feedback entries


import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import 'jest-enzyme'; // better matchers

Enzyme.configure({
  adapter: new Adapter(),
});

if (process.env.CI) {
  // Hide all console output
  console.log = jest.fn();
  console.warn = jest.fn();
  console.error = jest.fn();
}

Disable console output in Travis


/**
 * Fetch module.
 * @module base/utils/fetch
 */

import fetchJsonP from 'fetch-jsonp'

export var defaultOptions = {
  credentials: 'same-origin'
}

export var defaultJsonpOptions = {
  timeout: 5000,
  jsonpCallback: 'callback',
  jsonpCallbackFunction: null
}

export function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

export function url(u, opts = {}) {
  opts = Object.assign({}, defaultOptions, opts)
  return fetch(u, opts).then(checkStatus)
}

export function json(u, opts = {}) {
  return url(u, opts).then(r => r.json())
}

export function jsonP(u, opts = {}) {
  opts = Object.assign({}, defaultJsonpOptions, opts)
  return fetchJsonP(u, opts).then(r => r.json())
}

export function text(u, opts = {}) {
  return url(u, opts).then(r => r.text())
}

export default {
  defaultOptions,
  defaultJsonpOptions,
  url, json, jsonP, text
}

/**
 * Fetch module.
 * @module base/utils/fetch
 */

import fetchJsonP from 'fetch-jsonp'

export var defaultOptions = {
  credentials: 'same-origin',
  headers: {
    'http_x_requested_with': 'fetch'
  }
}

export var defaultJsonpOptions = {
  timeout: 5000,
  jsonpCallback: 'callback',
  jsonpCallbackFunction: null
}

export function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

export function url(u, opts = {}) {
  opts = Object.assign({}, defaultOptions, opts)

  // TODO: implement queryParams
  // if (opts.queryParams) {

  // }

  return fetch(u, opts).then(checkStatus)
}

export function json(u, opts = {}) {
  return url(u, opts).then(r => r.json())
}

export function jsonP(u, opts = {}) {
  opts = Object.assign({}, defaultJsonpOptions, opts)

  // TODO: implement queryParams
  // if (opts.queryParams) {

  // }

  return fetchJsonP(u, opts).then(r => r.json())
}

export function text(u, opts = {}) {
  return url(u, opts).then(r => r.text())
}

export default {
  defaultOptions,
  defaultJsonpOptions,
  url, json, jsonP, text
}

Send http_x_requested_with header to be able to check for ajax request


var events = require('events');
var util = require('util');

module.exports = Job;

function Job(collection, data) {
    this.collection = collection;
    if(data){
        data.__proto__ = JobData.prototype; //Convert plain object to JobData type
        this.data = data;
    } else {
        this.data = new JobData();
    }
}

util.inherits(Job, events.EventEmitter);

Job.prototype.save = function(callback) {
    var self = this;

    this.collection.save(this.data, function(err, doc) {
        if (err) return callback(err);

        if (doc && self.data._id === undefined) self.data._id = doc._id;

        callback(null, self);
    });
};

Job.prototype.complete = function(result, callback) {
    this.data.status = 'complete';
    this.data.ended = new Date();
    this.data.result = result;
    this.save(callback);
};

Job.prototype.fail = function(error, callback) {
    this.data.status = 'failed';
    this.data.ended = new Date();
    this.data.error = error.message;
    this.save(callback);
};

function JobData() {}

Object.defineProperty(JobData.prototype, 'id', {
    get: function(){
        return this._id && this._id.toHexString && this._id.toHexString();
    }
});

var events = require('events');
var util = require('util');

module.exports = Job;

function Job(collection, data) {
    this.collection = collection;
    if(data){
        data.__proto__ = JobData.prototype; //Convert plain object to JobData type
        this.data = data;
    } else {
        this.data = new JobData();
    }
}

util.inherits(Job, events.EventEmitter);

Job.prototype.save = function(callback) {
    var self = this;

    this.collection.save(this.data, function(err, doc) {
        if (err) return callback(err);

        if (doc && self.data._id === undefined) self.data._id = doc._id;

        callback(null, self);
    });
};

Job.prototype.complete = function(result, callback) {
    this.data.status = 'complete';
    this.data.ended = new Date();
    this.data.result = result;
    this.save(callback);
};

Job.prototype.fail = function(error, callback) {
    this.data.status = 'failed';
    this.data.ended = new Date();
    this.data.error = error.message;
    this.save(callback);
};

function JobData() {}

Object.defineProperty(JobData.prototype, 'id', {
    get: function(){
        return this._id && this._id.toString && this._id.toString();
    }
});

Use toString instead of toHexString


import React, { Component, PropTypes } from 'react';
import moment from 'moment';

class Activity extends Component {

  render() {
    const { timestamp, activity, deleteActivity } = this.props;

    return (

{moment(+timestamp).format('MMMM DD, YYYY')}

          {activity.description}
           deleteActivity(timestamp)}>Delete

    );
  }
}

Activity.propTypes = {
  timestamp: PropTypes.string.isRequired,
  activity: PropTypes.object.isRequired,
  deleteActivity: PropTypes.func.isRequired,
}

export default Activity;

import React, { Component, PropTypes } from 'react';
import moment from 'moment';

class Activity extends Component {

  render() {
    const { timestamp, activity, deleteActivity } = this.props;

    return (

            {activity.description}
             deleteActivity(timestamp)}>Delete

    );
  }
}

Activity.propTypes = {
  timestamp: PropTypes.string.isRequired,
  activity: PropTypes.object.isRequired,
  deleteActivity: PropTypes.func.isRequired,
}

export default Activity;

Remove date string from activity


import React from 'react';
import '../styles/games.css';

export default class GameList extends React.Component {
    constructor(props) {
        super(props);

        this.state = {games: []};
    }

    componentWillReceiveProps(nextProps) {
        let games = [];
        nextProps.games.forEach(game => {
            games.push(
{game.title}
);
        });
        this.setState({games: games});
    }

    render() {
        if (this.props.games.length > 0) {
            return (

{this.state.games}

            );
        } else {
            return (

No games found.

            );
        }
    }
}

import React from 'react';
import '../styles/games.css';

export default class GameList extends React.Component {
    constructor(props) {
        super(props);

        this.state = {games: []};
    }

    componentWillReceiveProps(nextProps) {
        let games = [];
        nextProps.games.forEach(game => {
            games.push(
{game.title}
);
        });
        this.setState({games: games});
    }

    handleClick(event) {
        this.props.onSelect(this.props.games.find(game => game.id === Number(event.target.dataset.id)));
    }

    render() {
        if (this.props.games.length > 0) {
            return (

{this.state.games}

            );
        } else {
            return (

No games found.

            );
        }
    }
}

Call onSelect event when a game is clicked.