<commit_before>/*
 * Copyright (c) 2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint node:true, nomen: true */

/**
The `express-yui/lib/server` provides a set of features
to control a YUI instance on the server side.

@module express-yui/lib/server
**/

'use strict';

var libpath = require('path'),
    utils = require('./utils'),
    debug = require('debug')('express:yui:server');

/**
Provides a set of features to control a YUI instance on the server side.

@class server
@static
@uses *path, utils
@extensionfor express-yui/lib/yui
*/
module.exports = {

    /**
    Create a synthetic group off the bundle renference to register its modules
    into the server and/or client Y instance so they can be used thru `app.yui.use()`.

    @method registerBundle
    @public
    @param {Object} bundle A locator bundle reference.
    @chainable
    **/
    registerBundle: function (bundle) {
        var groups = {},
            modules,
            key;

        if (bundle.yui) {
            if (bundle.yui.client && bundle.yui.metaModuleName) {
                // storing client modules
                // - we don't own it, locator does it
                for (key in bundle.yui.client) {
                    if (bundle.yui.client.hasOwnProperty(key)) {
                        if (this._clientModules.hasOwnProperty(key) && this._clientModules[key].group !== bundle.yui.client[key].group) {
                            debug("Module name collision on the client: '%s' is used for both group '%s' and group '%s'",
                                key, this._clientModules[key].group, bundle.yui.client[key].group);
                        }
                        this._clientModules[key] = utils.clone(bundle.yui.client[key]);
                    }
                }
                // add the meta module into the core structure
                // to make sure it gets attached to Y upfront on the client side
                this.registerGroup(bundle.name, bundle.buildDirectory, bundle.yui.metaModuleName);
            }
            if (bundle.yui.server) {
                debug('Registering group "%s" for server side, and using it from "%s"',
                      bundle.name, bundle.buildDirectory);

                // provision modules for server side
                groups[bundle.name] = {
                    base: libpath.normalize(bundle.buildDirectory + '/'), // TODO: what if two modules have the same name but different affinity?
                    combine: false
                };
                // we don't own it, locator does it, and yui will potentially augment it
                modules = utils.clone(bundle.yui.server);
                // storing server modules
                // - we don't own it, locator does it

                for (key in modules) {
                    if (modules.hasOwnProperty(key)) {
                        if (this._serverModules.hasOwnProperty(key) && this._serverModules[key].group !== modules[key].group) {
                            debug("Module name collision on the server: '%s' is used for both group '%s' and group '%s'",
                                key, this._serverModules[key].group, modules[key].group);
                        }
                        this._serverModules[key] = modules[key];
                    }
                }

                // applying the new group config and modules globally and to the current Y instance when possible
                this.YUI.applyConfig({ groups: groups });
                this.YUI.applyConfig({ modules: modules });

                if (this._Y) {
                    this._Y.applyConfig({ groups: groups });
                    this._Y.applyConfig({ modules: modules });
                }
            }
        }

        return this;
    },

    /**
    Waits for the app to be ready, including the YUI instance to notify back that the
    `ready` state of the app was reached by calling the `callback`. The ready state is
    bound to the locator instance mounted into the express app thru `app.set('locator', locator);`
    and depending on `app.get('locator').ready.then()` promise result, the ready state
    will be reached or not.

        app.yui.ready(function (err) {
            if (err) {
                throw err;
            }
            // do something!
        });

    @method ready
    @param {Function} callback when the app is ready. If an error occurr, the error object
                        will be passed as the first argument of the callback function.
    @public
    **/
    ready: function (callback) {
        var self = this,
            app = this._app,
            locator = app.get('locator');

        if (!locator) {
            debug('Call `app.set("locator", locatorObj)` before extending the `express` app with `express-yui`');
            throw new Error('Locator instance should be mounted');
        }

        locator.ready.then(function () {
            var bundleNames = locator.listBundleNames(),
                bundle;

            bundleNames.forEach(function (bundleName) {
                bundle = locator.getBundle(bundleName);
                self.registerBundle(bundle);
            });

            // there is a possibility that callback fails, so we should
            // outsourced to avoid callback to be called twice from catch
            setTimeout(callback, 0);

        })["catch"](callback);
    },

    /**
    Creates a YUI Instance, and wraps the call to `Y.require()` to work appropiatly on
    the server side. The `require` method can be called once the `ready` state is achieved
    thru `app.yui.ready()`.

        app.yui.ready(function (err) {
            if (err) {
                throw err;
            }
            app.yui.require('json-stringify', function (Y, imports) {
                Y.JSON.stringify({ entry: 'value' });
            });
        });

    @method require
    @public
    **/
    require: function () {
        var config = this.config(),
            Y = this._Y,
            modules,
            callback,
            imports;

        if (!Y) {
            config = utils.clone(config);
            // cleaning up the config for to add on the server specific config
            delete config.modules;
            delete config.groups;

            this.YUI.applyConfig(config);
            this.YUI.applyConfig({
                base: libpath.normalize(this.path + '/'),
                combine: false
            });
            Y = this._Y = this.YUI({
                useSync: true,
                loadErrorFn: function (Y, i, o) {
                    debug('--> Something really bad happened when trying to load yui modules on the server side');
                    debug('--> ' + o.msg);
                    if (Y.config.modules) {
                        debug('--> Y.config.modules = ' + JSON.stringify(Y.config.modules));
                    }
                    if (Y.config.groups) {
                        debug('--> Y.config.groups = ' + JSON.stringify(Y.config.groups));
                    }
                }
            });

            // patching serverside Y
            if (this._patches && this._patches.length) {
                this._patches.forEach(function (patch) {
                    patch(Y, Y.Env._loader);
                }, this);
            }
        }

        // attaching modules
        // in case a callback is passed, we should consider that
        modules = Array.prototype.slice.call(arguments, 0);
        if (modules.length > 0 && (typeof modules[modules.length - 1] === 'function')) {
            callback = modules.pop();
        }
        if (modules.length) {
            try {
                Y.require(modules, function (Y, __imports__) {
                    // capture imports in outer scope
                    imports = __imports__;
                });
            } catch (e) {
                console.error('error attaching modules: ' + modules);
                console.error(e);
                console.error(e.stack);
            }
        }

        if (callback) {
            // support `use()` or `require()` callback signature
            if (imports) {
                callback(Y, imports);
            } else {
                callback(Y);
            }
        }

    },

    /**
    Creates a YUI Instance, and wraps the call to `Y.use()` to work appropiatly on
    the server side. The `use` method can be called once the `ready` state is achieved
    thru `app.yui.ready()`.

        app.yui.ready(function (err) {
            if (err) {
                throw err;
            }
            app.yui.use('json-stringify', function (Y) {
                Y.JSON.stringify({ entry: 'value' });
            });
        });

    @method use
    @public
    @return {Object} Y instance
    **/
    use: function () {
        this.require.apply(this, arguments);
        return this._Y;
    }

};
<commit_msg>preventing callback to be executed twice when ready is rejected<commit_after>/*
 * Copyright (c) 2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint node:true, nomen: true */

/**
The `express-yui/lib/server` provides a set of features
to control a YUI instance on the server side.

@module express-yui/lib/server
**/

'use strict';

var libpath = require('path'),
    utils = require('./utils'),
    debug = require('debug')('express:yui:server');

/**
Provides a set of features to control a YUI instance on the server side.

@class server
@static
@uses *path, utils
@extensionfor express-yui/lib/yui
*/
module.exports = {

    /**
    Create a synthetic group off the bundle renference to register its modules
    into the server and/or client Y instance so they can be used thru `app.yui.use()`.

    @method registerBundle
    @public
    @param {Object} bundle A locator bundle reference.
    @chainable
    **/
    registerBundle: function (bundle) {
        var groups = {},
            modules,
            key;

        if (bundle.yui) {
            if (bundle.yui.client && bundle.yui.metaModuleName) {
                // storing client modules
                // - we don't own it, locator does it
                for (key in bundle.yui.client) {
                    if (bundle.yui.client.hasOwnProperty(key)) {
                        if (this._clientModules.hasOwnProperty(key) && this._clientModules[key].group !== bundle.yui.client[key].group) {
                            debug("Module name collision on the client: '%s' is used for both group '%s' and group '%s'",
                                key, this._clientModules[key].group, bundle.yui.client[key].group);
                        }
                        this._clientModules[key] = utils.clone(bundle.yui.client[key]);
                    }
                }
                // add the meta module into the core structure
                // to make sure it gets attached to Y upfront on the client side
                this.registerGroup(bundle.name, bundle.buildDirectory, bundle.yui.metaModuleName);
            }
            if (bundle.yui.server) {
                debug('Registering group "%s" for server side, and using it from "%s"',
                      bundle.name, bundle.buildDirectory);

                // provision modules for server side
                groups[bundle.name] = {
                    base: libpath.normalize(bundle.buildDirectory + '/'), // TODO: what if two modules have the same name but different affinity?
                    combine: false
                };
                // we don't own it, locator does it, and yui will potentially augment it
                modules = utils.clone(bundle.yui.server);
                // storing server modules
                // - we don't own it, locator does it

                for (key in modules) {
                    if (modules.hasOwnProperty(key)) {
                        if (this._serverModules.hasOwnProperty(key) && this._serverModules[key].group !== modules[key].group) {
                            debug("Module name collision on the server: '%s' is used for both group '%s' and group '%s'",
                                key, this._serverModules[key].group, modules[key].group);
                        }
                        this._serverModules[key] = modules[key];
                    }
                }

                // applying the new group config and modules globally and to the current Y instance when possible
                this.YUI.applyConfig({ groups: groups });
                this.YUI.applyConfig({ modules: modules });

                if (this._Y) {
                    this._Y.applyConfig({ groups: groups });
                    this._Y.applyConfig({ modules: modules });
                }
            }
        }

        return this;
    },

    /**
    Waits for the app to be ready, including the YUI instance to notify back that the
    `ready` state of the app was reached by calling the `callback`. The ready state is
    bound to the locator instance mounted into the express app thru `app.set('locator', locator);`
    and depending on `app.get('locator').ready.then()` promise result, the ready state
    will be reached or not.

        app.yui.ready(function (err) {
            if (err) {
                throw err;
            }
            // do something!
        });

    @method ready
    @param {Function} callback when the app is ready. If an error occurr, the error object
                        will be passed as the first argument of the callback function.
    @public
    **/
    ready: function (callback) {
        var self = this,
            app = this._app,
            locator = app.get('locator');

        if (!locator) {
            debug('Call `app.set("locator", locatorObj)` before extending the `express` app with `express-yui`');
            throw new Error('Locator instance should be mounted');
        }

        // there is a possibility that callback fails, so we trick it to
        // outsource the execution of it to avoid calling it twice
        function end() {
            setTimeout.apply(null, [callback, 0].concat(Array.prototype.slice.apply(arguments)));
        }

        locator.ready.then(function () {
            var bundleNames = locator.listBundleNames(),
                bundle;

            bundleNames.forEach(function (bundleName) {
                bundle = locator.getBundle(bundleName);
                self.registerBundle(bundle);
            });

            end();

        })["catch"](end);
    },

    /**
    Creates a YUI Instance, and wraps the call to `Y.require()` to work appropiatly on
    the server side. The `require` method can be called once the `ready` state is achieved
    thru `app.yui.ready()`.

        app.yui.ready(function (err) {
            if (err) {
                throw err;
            }
            app.yui.require('json-stringify', function (Y, imports) {
                Y.JSON.stringify({ entry: 'value' });
            });
        });

    @method require
    @public
    **/
    require: function () {
        var config = this.config(),
            Y = this._Y,
            modules,
            callback,
            imports;

        if (!Y) {
            config = utils.clone(config);
            // cleaning up the config for to add on the server specific config
            delete config.modules;
            delete config.groups;

            this.YUI.applyConfig(config);
            this.YUI.applyConfig({
                base: libpath.normalize(this.path + '/'),
                combine: false
            });
            Y = this._Y = this.YUI({
                useSync: true,
                loadErrorFn: function (Y, i, o) {
                    debug('--> Something really bad happened when trying to load yui modules on the server side');
                    debug('--> ' + o.msg);
                    if (Y.config.modules) {
                        debug('--> Y.config.modules = ' + JSON.stringify(Y.config.modules));
                    }
                    if (Y.config.groups) {
                        debug('--> Y.config.groups = ' + JSON.stringify(Y.config.groups));
                    }
                }
            });

            // patching serverside Y
            if (this._patches && this._patches.length) {
                this._patches.forEach(function (patch) {
                    patch(Y, Y.Env._loader);
                }, this);
            }
        }

        // attaching modules
        // in case a callback is passed, we should consider that
        modules = Array.prototype.slice.call(arguments, 0);
        if (modules.length > 0 && (typeof modules[modules.length - 1] === 'function')) {
            callback = modules.pop();
        }
        if (modules.length) {
            try {
                Y.require(modules, function (Y, __imports__) {
                    // capture imports in outer scope
                    imports = __imports__;
                });
            } catch (e) {
                console.error('error attaching modules: ' + modules);
                console.error(e);
                console.error(e.stack);
            }
        }

        if (callback) {
            // support `use()` or `require()` callback signature
            if (imports) {
                callback(Y, imports);
            } else {
                callback(Y);
            }
        }

    },

    /**
    Creates a YUI Instance, and wraps the call to `Y.use()` to work appropiatly on
    the server side. The `use` method can be called once the `ready` state is achieved
    thru `app.yui.ready()`.

        app.yui.ready(function (err) {
            if (err) {
                throw err;
            }
            app.yui.use('json-stringify', function (Y) {
                Y.JSON.stringify({ entry: 'value' });
            });
        });

    @method use
    @public
    @return {Object} Y instance
    **/
    use: function () {
        this.require.apply(this, arguments);
        return this._Y;
    }

};
<|endoftext|>


<commit_before>'use strict';

var gulp = require('gulp');
var inject = require('gulp-inject');
var less = require('gulp-less');
var del = require('del');
var runSequence = require('run-sequence');
var browserify = require('browserify');
var babelify = require('babelify'); // for browserify
var tsify = require('tsify');
var ts = require('gulp-typescript');
var tslint = require('gulp-tslint');
var stylish = require('gulp-tslint-stylish');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var cssmin = require('gulp-clean-css');
var plumber = require('gulp-plumber');
var notify = require('gulp-notify');
var lr = require('tiny-lr')();
var nightwatch = require('gulp-nightwatch');
var server = require('gulp-live-server');
var sourcemaps = require('gulp-sourcemaps');

gulp.task('server', function() {
  var app = server.new('app.js');
  app.start();
  gulp.watch(['frontend/build/**/*'], function(file) {
    console.log('frontend updated, notifying livereload ', file);
    app.notify.apply(app, [file]);
  });

  //gulp.watch(['app.js', 'backend/build/**/*'], function() {
  //  app.start.bind(app); // does not work :(
  //});
});

gulp.task('clean', function(cb) {
  return del(['./frontend/build/', './backend/build/', './app.js'], cb);
});

gulp.task('img', function() {
  return gulp.src('./frontend/src/img/**/*.{jpg,gif,png}')
  .pipe(gulp.dest('./frontend/build/public/img/'));
});

gulp.task('fonts', function() {
  return gulp.src('./frontend/src/bootstrap/fonts/*')
  .pipe(gulp.dest('./frontend/build/public/fonts/'));
});

gulp.task('css:store', function() {
  return gulp.src(['./frontend/src/css/*.less', '!./frontend/src/css/admin*.less'])
      .pipe(less())
      .pipe(concat('style.css'))
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css:admin', function() {
  return gulp.src('./frontend/src/css/admin*.less')
      .pipe(less())
      .pipe(concat('admin.css'))
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css:store:min', function() {
  return gulp.src(['./frontend/src/css/*.less', '!./frontend/src/css/admin*.less'])
      .pipe(less())
      .pipe(concat('style.css'))
      .pipe(cssmin())
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css:admin:min', function() {
  return gulp.src('./frontend/src/css/admin*.less')
      .pipe(less())
      .pipe(concat('admin.css'))
      .pipe(cssmin())
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css', ['css:store', 'css:admin']);

gulp.task('css:min', ['css:store:min', 'css:admin:min']);

gulp.task('lint', function() {
  return gulp.src(['app.ts', 'frontend/src/**/*.{ts,tsx}', 'backend/src/**/*.{ts,tsx}'])
  .pipe(tslint())
  .pipe(tslint.report(stylish, {emitError: true}))
  .on('error', function(err) {
    notify.onError({
      message: '<%= error.message %>'
    }).apply(this, arguments);
    this.emit('end');
  });
});

function js(startPath, targetFile) {
  return function() {
    return browserify({entries: startPath, debug: true})
    .add(startPath)
    .add('typings/index.d.ts')
    .transform(babelify)
    .plugin(tsify, {sourceRoot: __dirname})
    .bundle()
    .on('error', function(err) {
      notify.onError({
        message: '<%= error.message %>'
      }).apply(this, arguments);

      this.emit('end');
    })
    .pipe(source(targetFile))
    .pipe(gulp.dest('./frontend/build/public/js/'));
  };
}

function jsMin(startPath, targetFile) {
  return function() {
    return browserify()
    .add(startPath)
    .add('typings/index.d.ts')
    .transform(babelify)
    .plugin(tsify)
    .bundle()
    .pipe(source(targetFile))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./frontend/build/public/js/'));
  };
}

gulp.task('js:store', ['lint'], js('./frontend/src/js/App.tsx', 'App.js'));
gulp.task('js:admin', js('./frontend/src/js-admin/AdminApp.tsx', 'adminApp.js'));

gulp.task('js', ['js:store', 'js:admin']);

gulp.task('js:store:min', jsMin('./frontend/src/js/App.tsx', 'App.js'));
gulp.task('js:admin:min', jsMin('./frontend/src/js-admin/AdminApp.tsx', 'adminApp.js'));

gulp.task('js:min', ['js:store:min', 'js:admin:min']);

gulp.task('backend', function() {
  return gulp.src(['backend/src/**/*.ts', 'typings/index.d.ts'])
    .pipe(sourcemaps.init())
    .pipe(ts({
      module: 'commonjs'
    }))
    .pipe(sourcemaps.write({sourceRoot: '../src'}))
    .pipe(gulp.dest('backend/build/'))
    .pipe(notify({message: 'backend re-compiled, restart gulp', onLast: true}));
});

gulp.task('app', function() {
  return gulp.src(['app.ts', 'typings/index.d.ts'])
    .pipe(sourcemaps.init())
    .pipe(ts({
      module: 'commonjs'
    }))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('./'))
    .pipe(notify({message: 'server re-compiled, restart gulp', onLast: true}));
});

gulp.task('index', function() {
  return gulp.src('./frontend/src/index.html')
      .pipe(inject(gulp.src(['./public/**/*.{css,js}', '!./public/**/admin*'], {read: false, cwd: __dirname + '/frontend/build/'}), {addRootSlash: false}))
      .pipe(gulp.dest('./frontend/build/'));
});

gulp.task('admin', function() {
  return gulp.src('./frontend/src/admin.html')
      .pipe(inject(gulp.src('./public/**/admin*.{css,js}', {read: false, cwd: __dirname + '/frontend/build/'}), {addRootSlash: false}))
      .pipe(gulp.dest('./frontend/build/'));
});

gulp.task('ete-test', function() {
  return gulp.src('')
    .pipe(nightwatch({
      configFile: 'nightwatch.json'
    }));
});

gulp.task('test', function() {
  runSequence(
    ['build-dev'],
    ['start-dev'],
    ['ete-test'],
    stopExpress
  );
});

gulp.task('build-dev', function(cb) {
  runSequence(
    ['clean'],
    ['app', 'backend', 'css', 'js', 'img', 'fonts'],
    ['index', 'admin'],
    cb);
});

gulp.task('build', function(cb) {
  runSequence(
    ['clean'],
    ['app', 'backend', 'css:min', 'js:min', 'img', 'fonts'],
    ['index', 'admin'],
    cb);
});

gulp.task('watch', function() {
  gulp.watch('frontend/src/css/**/*.{css,less}', ['css', 'index', 'admin']);
  gulp.watch('frontend/src/js/**/*.{js,jsx,ts,tsx}', ['js:store']);
  gulp.watch('frontend/src/js-admin/**/*.{js,jsx,ts,tsx}', ['js:admin']);
  gulp.watch('frontend/src/img/**/*.{jpg,gif,png}', ['img']);
  gulp.watch('frontend/src/index.html', ['index']);
  gulp.watch('frontend/src/admin.html', ['admin']);
  gulp.watch('backend/src/**/*.{js,jsx,ts,tsx}', ['backend']);
  gulp.watch('app.ts', ['app']);
});

gulp.task('rewatch', function(cb) {
  runSequence(
    ['build-dev'],
    ['watch'],
    cb);
});

gulp.task('watch-and-server', ['server', 'watch']);

gulp.task('default', function(cb) {
  runSequence(
    ['build-dev'],
    ['watch-and-server'],
    cb);
});
<commit_msg>add NODE_ENV = 'production' to jsMin to make react build in prod mode<commit_after>'use strict';

var gulp = require('gulp');
var inject = require('gulp-inject');
var less = require('gulp-less');
var del = require('del');
var runSequence = require('run-sequence');
var browserify = require('browserify');
var babelify = require('babelify'); // for browserify
var tsify = require('tsify');
var ts = require('gulp-typescript');
var tslint = require('gulp-tslint');
var stylish = require('gulp-tslint-stylish');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var cssmin = require('gulp-clean-css');
var plumber = require('gulp-plumber');
var notify = require('gulp-notify');
var lr = require('tiny-lr')();
var nightwatch = require('gulp-nightwatch');
var server = require('gulp-live-server');
var sourcemaps = require('gulp-sourcemaps');

gulp.task('server', function() {
  var app = server.new('app.js');
  app.start();
  gulp.watch(['frontend/build/**/*'], function(file) {
    console.log('frontend updated, notifying livereload ', file);
    app.notify.apply(app, [file]);
  });

  //gulp.watch(['app.js', 'backend/build/**/*'], function() {
  //  app.start.bind(app); // does not work :(
  //});
});

gulp.task('clean', function(cb) {
  return del(['./frontend/build/', './backend/build/', './app.js'], cb);
});

gulp.task('img', function() {
  return gulp.src('./frontend/src/img/**/*.{jpg,gif,png}')
  .pipe(gulp.dest('./frontend/build/public/img/'));
});

gulp.task('fonts', function() {
  return gulp.src('./frontend/src/bootstrap/fonts/*')
  .pipe(gulp.dest('./frontend/build/public/fonts/'));
});

gulp.task('css:store', function() {
  return gulp.src(['./frontend/src/css/*.less', '!./frontend/src/css/admin*.less'])
      .pipe(less())
      .pipe(concat('style.css'))
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css:admin', function() {
  return gulp.src('./frontend/src/css/admin*.less')
      .pipe(less())
      .pipe(concat('admin.css'))
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css:store:min', function() {
  return gulp.src(['./frontend/src/css/*.less', '!./frontend/src/css/admin*.less'])
      .pipe(less())
      .pipe(concat('style.css'))
      .pipe(cssmin())
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css:admin:min', function() {
  return gulp.src('./frontend/src/css/admin*.less')
      .pipe(less())
      .pipe(concat('admin.css'))
      .pipe(cssmin())
      .pipe(gulp.dest('./frontend/build/public/css/'));
});

gulp.task('css', ['css:store', 'css:admin']);

gulp.task('css:min', ['css:store:min', 'css:admin:min']);

gulp.task('lint', function() {
  return gulp.src(['app.ts', 'frontend/src/**/*.{ts,tsx}', 'backend/src/**/*.{ts,tsx}'])
  .pipe(tslint())
  .pipe(tslint.report(stylish, {emitError: true}))
  .on('error', function(err) {
    notify.onError({
      message: '<%= error.message %>'
    }).apply(this, arguments);
    this.emit('end');
  });
});

function js(startPath, targetFile) {
  return function() {
    return browserify({entries: startPath, debug: true})
    .add(startPath)
    .add('typings/index.d.ts')
    .transform(babelify)
    .plugin(tsify, {sourceRoot: __dirname})
    .bundle()
    .on('error', function(err) {
      notify.onError({
        message: '<%= error.message %>'
      }).apply(this, arguments);

      this.emit('end');
    })
    .pipe(source(targetFile))
    .pipe(gulp.dest('./frontend/build/public/js/'));
  };
}

function jsMin(startPath, targetFile) {
  return function() {
    process.env.NODE_ENV = 'production'; // to make react build in production mode
    return browserify()
    .add(startPath)
    .add('typings/index.d.ts')
    .transform(babelify)
    .plugin(tsify)
    .bundle()
    .pipe(source(targetFile))
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./frontend/build/public/js/'));
  };
}

gulp.task('js:store', ['lint'], js('./frontend/src/js/App.tsx', 'App.js'));
gulp.task('js:admin', js('./frontend/src/js-admin/AdminApp.tsx', 'adminApp.js'));

gulp.task('js', ['js:store', 'js:admin']);

gulp.task('js:store:min', jsMin('./frontend/src/js/App.tsx', 'App.js'));
gulp.task('js:admin:min', jsMin('./frontend/src/js-admin/AdminApp.tsx', 'adminApp.js'));

gulp.task('js:min', ['js:store:min', 'js:admin:min']);

gulp.task('backend', function() {
  return gulp.src(['backend/src/**/*.ts', 'typings/index.d.ts'])
    .pipe(sourcemaps.init())
    .pipe(ts({
      module: 'commonjs'
    }))
    .pipe(sourcemaps.write({sourceRoot: '../src'}))
    .pipe(gulp.dest('backend/build/'))
    .pipe(notify({message: 'backend re-compiled, restart gulp', onLast: true}));
});

gulp.task('app', function() {
  return gulp.src(['app.ts', 'typings/index.d.ts'])
    .pipe(sourcemaps.init())
    .pipe(ts({
      module: 'commonjs'
    }))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('./'))
    .pipe(notify({message: 'server re-compiled, restart gulp', onLast: true}));
});

gulp.task('index', function() {
  return gulp.src('./frontend/src/index.html')
      .pipe(inject(gulp.src(['./public/**/*.{css,js}', '!./public/**/admin*'], {read: false, cwd: __dirname + '/frontend/build/'}), {addRootSlash: false}))
      .pipe(gulp.dest('./frontend/build/'));
});

gulp.task('admin', function() {
  return gulp.src('./frontend/src/admin.html')
      .pipe(inject(gulp.src('./public/**/admin*.{css,js}', {read: false, cwd: __dirname + '/frontend/build/'}), {addRootSlash: false}))
      .pipe(gulp.dest('./frontend/build/'));
});

gulp.task('ete-test', function() {
  return gulp.src('')
    .pipe(nightwatch({
      configFile: 'nightwatch.json'
    }));
});

gulp.task('test', function() {
  runSequence(
    ['build-dev'],
    ['start-dev'],
    ['ete-test'],
    stopExpress
  );
});

gulp.task('build-dev', function(cb) {
  runSequence(
    ['clean'],
    ['app', 'backend', 'css', 'js', 'img', 'fonts'],
    ['index', 'admin'],
    cb);
});

gulp.task('build', function(cb) {
  runSequence(
    ['clean'],
    ['app', 'backend', 'css:min', 'js:min', 'img', 'fonts'],
    ['index', 'admin'],
    cb);
});

gulp.task('watch', function() {
  gulp.watch('frontend/src/css/**/*.{css,less}', ['css', 'index', 'admin']);
  gulp.watch('frontend/src/js/**/*.{js,jsx,ts,tsx}', ['js:store']);
  gulp.watch('frontend/src/js-admin/**/*.{js,jsx,ts,tsx}', ['js:admin']);
  gulp.watch('frontend/src/img/**/*.{jpg,gif,png}', ['img']);
  gulp.watch('frontend/src/index.html', ['index']);
  gulp.watch('frontend/src/admin.html', ['admin']);
  gulp.watch('backend/src/**/*.{js,jsx,ts,tsx}', ['backend']);
  gulp.watch('app.ts', ['app']);
});

gulp.task('rewatch', function(cb) {
  runSequence(
    ['build-dev'],
    ['watch'],
    cb);
});

gulp.task('watch-and-server', ['server', 'watch']);

gulp.task('default', function(cb) {
  runSequence(
    ['build-dev'],
    ['watch-and-server'],
    cb);
});
<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var UIPlugin = require('../../base/ui_plugin');
var Styler = require('../../base/styler');
var JST = require('../../base/jst');
var _ = require("underscore");

var FlashVideoPlaybackPlugin = UIPlugin.extend({
  name: 'flash_playback',
  tagName: 'object',
  template: JST.flash_playback,
  attributes: {
    'data-flash-playback': ''
  },

  initialize: function(options) {
    this.src = options.src;
    this.swfPath = options.swfPath || "assets/Player.swf";
    this.autoPlay = options.autoPlay;
    this.settings = {
      left: ["playpause"],
      default: ["position", "seekbar", "duration"],
      right: ["fullscreen", "volume"]
    };
    this.isReady = false;
    this.checkIfFlashIsReady();
  },
  bootstrap: function() {
    this.el.width = "100%";
    this.el.height = "100%";
    this.isReady = true;
    this.trigger('playback:ready', this.name);
    clearInterval(this.bootstrapId);
    this.currentState = "IDLE";
    this.timedCheckState();
  },
  checkIfFlashIsReady: function() {
    this.bootstrapId = setInterval(function() {
      if(this.el.getState && this.el.getState() === "IDLE") {
           this.bootstrap();
      }
    }.bind(this), 50);
  },
  setupFirefox: function() {
    var $el = this.$('embed');
    $el.attr('data-flash-playback', '');
    this.setElement($el[0]);
  },
  updateTime: function(interval) {
    return setInterval(function() {
      this.trigger('playback:timeupdate', this.el.getPosition(), this.el.getDuration(), this.name);
    }.bind(this), interval);
  },
  timedCheckState: function() {
    this.checkStateId = setInterval(this.checkState.bind(this), 250);
    this.progressId = setInterval(this.progress.bind(this), 1000);
  },
  checkState: function() {
    if (this.currentState !== "PLAYING_BUFFERING" && this.el.getState() === "PLAYING_BUFFERING") {
      this.trigger('playback:buffering', this.name);
      this.currentState = "PLAYING_BUFFERING";
    } else if (this.currentState === "PLAYING_BUFFERING" && this.el.getState() === "PLAYING") {
      this.trigger('playback:bufferfull', this.name);
      this.currentState = "PLAYING";
    } else if (this.el.getState() === "IDLE") {
      this.currentState = "IDLE";
    } else if (this.el.getState() === "ENDED") {
      this.trigger('playback:ended', this.name);
      this.trigger('playback:timeupdate', 0, this.el.getDuration(), this.name);
      this.currentState = "ENDED";
      clearInterval(this.id);
    }
  },
  progress: function() {
    if (this.currentState !== "IDLE" && this.currentState !== "ENDED") {
      this.trigger('playback:progress', 0, this.el.getBytesLoaded(), this.el.getBytesTotal(), this.name);
    }
  },
  firstPlay: function() {
    this.currentState = "PLAYING";
    this.el.playerPlay(this.src);
  },
  play: function() {
    if(this.el.getState() === 'IDLE') {
      this.id = this.updateTime(1000);
    }
    if(this.el.getState() === 'PAUSED') {
      this.el.playerResume();
    } else {
      this.firstPlay();
    }
  },
  volume: function(value) {
    this.el.playerVolume(value);
  },
  pause: function() {
    this.currentState = "PAUSED";
    this.el.playerPause();
  },
  stop: function() {
    this.el.playerStop();
    clearInterval(this.id);
    this.trigger('playback:timeupdate', 0, this.name);
  },
  isPlaying: function() {
    return !!(this.isReady && this.currentState == "PLAYING");
  },
  seek: function(time) {
    clearInterval(this.id);
    var seekTo = this.el.getDuration()* (time / 100);
    this.el.playerSeek(seekTo);
    this.timeUpdate(seekTo, this.el.getDuration());
    this.id = this.updateTime(1000);
  },
  timeUpdate: function(time, duration) {
    this.trigger('playback:timeupdate', time, duration, this.name);
  },
  destroy: function() {
    clearInterval(this.id);
    clearInterval(this.checkStateId);
    clearInterval(this.progressId);
  },
  render: function() {
    var style = Styler.getStyleFor(this.name);
    this.$el.html(this.template({swfPath: this.swfPath}));
    this.$el.append(style);
    this.el.id = this.cid;
    if(navigator.userAgent.match(/firefox/i)) { //FIXME remove it from here
      this.setupFirefox();
    }
    return this;
  }
});

FlashVideoPlaybackPlugin.canPlay = function(resource) {
  //http://help.adobe.com/en_US/flashmediaserver/techoverview/WS07865d390fac8e1f-4c43d6e71321ec235dd-7fff.html
  if (navigator.userAgent.match(/firefox/i)) {
    return !!resource.match(/(.*).(mp4|mov|f4v|3gpp|3gp)/);
  } else {
    return !!resource.match(/(.*).(mov|f4v|3gpp|3gp)/);
  }
}

module.exports = FlashVideoPlaybackPlugin;
<commit_msg>flash playback: fix seek when paused<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var UIPlugin = require('../../base/ui_plugin');
var Styler = require('../../base/styler');
var JST = require('../../base/jst');
var _ = require("underscore");

var FlashVideoPlaybackPlugin = UIPlugin.extend({
  name: 'flash_playback',
  tagName: 'object',
  template: JST.flash_playback,
  attributes: {
    'data-flash-playback': ''
  },

  initialize: function(options) {
    this.src = options.src;
    this.swfPath = options.swfPath || "assets/Player.swf";
    this.autoPlay = options.autoPlay;
    this.settings = {
      left: ["playpause"],
      default: ["position", "seekbar", "duration"],
      right: ["fullscreen", "volume"]
    };
    this.isReady = false;
    this.checkIfFlashIsReady();
  },
  bootstrap: function() {
    this.el.width = "100%";
    this.el.height = "100%";
    this.isReady = true;
    this.trigger('playback:ready', this.name);
    clearInterval(this.bootstrapId);
    this.currentState = "IDLE";
    this.timedCheckState();
  },
  checkIfFlashIsReady: function() {
    this.bootstrapId = setInterval(function() {
      if(this.el.getState && this.el.getState() === "IDLE") {
           this.bootstrap();
      }
    }.bind(this), 50);
  },
  setupFirefox: function() {
    var $el = this.$('embed');
    $el.attr('data-flash-playback', '');
    this.setElement($el[0]);
  },
  updateTime: function(interval) {
    return setInterval(function() {
      this.trigger('playback:timeupdate', this.el.getPosition(), this.el.getDuration(), this.name);
    }.bind(this), interval);
  },
  timedCheckState: function() {
    this.checkStateId = setInterval(this.checkState.bind(this), 250);
    this.progressId = setInterval(this.progress.bind(this), 1000);
  },
  checkState: function() {
    if (this.currentState !== "PLAYING_BUFFERING" && this.el.getState() === "PLAYING_BUFFERING") {
      this.trigger('playback:buffering', this.name);
      this.currentState = "PLAYING_BUFFERING";
    } else if (this.currentState === "PLAYING_BUFFERING" && this.el.getState() === "PLAYING") {
      this.trigger('playback:bufferfull', this.name);
      this.currentState = "PLAYING";
    } else if (this.el.getState() === "IDLE") {
      this.currentState = "IDLE";
    } else if (this.el.getState() === "ENDED") {
      this.trigger('playback:ended', this.name);
      this.trigger('playback:timeupdate', 0, this.el.getDuration(), this.name);
      this.currentState = "ENDED";
      clearInterval(this.id);
    }
  },
  progress: function() {
    if (this.currentState !== "IDLE" && this.currentState !== "ENDED") {
      this.trigger('playback:progress', 0, this.el.getBytesLoaded(), this.el.getBytesTotal(), this.name);
    }
  },
  firstPlay: function() {
    this.currentState = "PLAYING";
    this.el.playerPlay(this.src);
  },
  play: function() {
    if(this.el.getState() === 'IDLE') {
      this.id = this.updateTime(1000);
    }
    if(this.el.getState() === 'PAUSED') {
      this.el.playerResume();
      this.currentState = "PLAYING";
    } else {
      this.firstPlay();
    }
  },
  volume: function(value) {
    this.el.playerVolume(value);
  },
  pause: function() {
    this.currentState = "PAUSED";
    this.el.playerPause();
  },
  stop: function() {
    this.el.playerStop();
    clearInterval(this.id);
    this.trigger('playback:timeupdate', 0, this.name);
  },
  isPlaying: function() {
    return !!(this.isReady && this.currentState == "PLAYING");
  },
  seek: function(time) {
    clearInterval(this.id);
    var seekTo = this.el.getDuration()* (time / 100);
    this.el.playerSeek(seekTo);
    this.timeUpdate(seekTo, this.el.getDuration());
    this.id = this.updateTime(1000);
    if (this.currentState == "PAUSED") {
      this.pause();
    }
  },
  timeUpdate: function(time, duration) {
    this.trigger('playback:timeupdate', time, duration, this.name);
  },
  destroy: function() {
    clearInterval(this.id);
    clearInterval(this.checkStateId);
    clearInterval(this.progressId);
  },
  render: function() {
    var style = Styler.getStyleFor(this.name);
    this.$el.html(this.template({swfPath: this.swfPath}));
    this.$el.append(style);
    this.el.id = this.cid;
    if(navigator.userAgent.match(/firefox/i)) { //FIXME remove it from here
      this.setupFirefox();
    }
    return this;
  }
});

FlashVideoPlaybackPlugin.canPlay = function(resource) {
  //http://help.adobe.com/en_US/flashmediaserver/techoverview/WS07865d390fac8e1f-4c43d6e71321ec235dd-7fff.html
  if (navigator.userAgent.match(/firefox/i)) {
    return !!resource.match(/(.*).(mp4|mov|f4v|3gpp|3gp)/);
  } else {
    return !!resource.match(/(.*).(mov|f4v|3gpp|3gp)/);
  }
}

module.exports = FlashVideoPlaybackPlugin;
<|endoftext|>


<commit_before>'use strict'

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.static(__dirname + '/public'));
app.get('*', function(req, res){
  res.sendFile(__dirname + '/index.html')
});

app.listen(PORT, function(){
  console.log('Server is running on port: ' + PORT);
});
<commit_msg>localhost 300 running<commit_after>'use strict'

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.static(__dirname + '/public'));
app.get('*', function(req, res){
  res.sendFile(__dirname + '/index.html')
});

app.listen(PORT, function(){
  console.log('Server is running on port: ' + PORT);
});

// installed express and now the server now runs the page on localhost:3000
<|endoftext|>


<commit_before>"use strict";

/*
 * RESTBase web service entry point
 *
 * Sets up a restbase instance by loading and setting up handlers and the
 * storage layer, and then dispatches requests to it.
 */

global.Promise = require('bluebird');

var rbUtil = require('./rbUtil');
var Restbase = require('./restbase');
var http = require('http');
var fs = require('fs');
var Router = require('./router');
var URI = require('swagger-router').URI;

var app = {
    // The global proxy object
    proxy: null
};

function handleResponse (opts, newReq, resp, response) {
    //console.log('resp', response);
    if (response && response.status) {
        if (!response.headers) {
            response.headers = {};
        }

        // Set up CORS
        response.headers['Access-Control-Allow-Origin'] = '*';
        response.headers['Access-Control-Allow-Methods'] = 'GET';
        response.headers['Access-Control-Allow-Headers'] = 'accept, content-type';

        var logLevel = 'trace/request';
        if (response.status >= 500) {
            logLevel = 'error/request';
        } else if (response.status >= 400) {
            logLevel = 'info/request';
        }
        opts.log(logLevel, {
            message: response.message,
            req: newReq,
            res: response,
            stack: response.stack
        });

        var body;
        // Prepare error responses for the client
        if (response.status >= 400) {
            if (!response.body) {
                response.body = {};
            }
            // whitelist fields to avoid leaking sensitive info
            var rBody = response.body;
            body = {
                type: rBody.type,
                title: rBody.title,
                method: rBody.method,
                uri: rBody.uri
            };

            if (response.status === 404) {
                if (!body.type) { body.type = 'not_found'; }
                if (!body.title) { body.title = 'Not found.'; }
                if (!response.headers['content-type']) {
                    response.headers['content-type'] = 'application/problem+json';
                }
            }
            if (response.status >= 400) {
                if (!body.uri) { body.uri = newReq.url; }
                if (!body.method) { body.method = newReq.method; }
            }
            if (!body.type) {
                body.type = 'unknown_error';
            }
            // Prefix error base URL
            // XXX: make the prefix configurable
            body.type = 'https://restbase.org/errors/' + body.type;
            response.body = body;
        }

        if (response.body) {
            body = response.body;
            // Convert to a buffer
            if (!Buffer.isBuffer(body)) {
                if (typeof body === 'object') {
                    if (!response.headers['content-type']) {
                        response.headers['content-type'] = 'application/json';
                    }
                    body = new Buffer(JSON.stringify(body));
                } else  {
                    body = new Buffer(body);
                }
            }
            response.headers.connection = 'close';
            response.headers['content-length'] = body.length;
            resp.writeHead(response.status, '', response.headers);
            resp.end(body);
        } else {
            if (response.headers['content-length']) {
                opts.log('warn/response/content-length', {
                    req: newReq,
                    res: response,
                    reason: new Error('Invalid content-length')
                });
                delete response.headers['content-length'];
            }
            resp.writeHead(response.status, '', response.headers);
            resp.end();
        }
    } else {
        opts.log('error/request', {req: newReq}, "No content returned");
        response.headers['content-type'] = 'application/problem+json';
        resp.writeHead(response && response.status || 500, '', response && response.headers);
        resp.end(JSON.stringify({
            type: 'https://restbase.org/errors/no_content',
            title: 'RESTBase error: No content returned by backend.',
            uri: newReq.url,
            method: newReq.method
        }));
    }
}

// Handle a single request
function handleRequest (opts, req, resp) {
    var newReq;

    opts = {
        log: opts.log.child({
            req: {
                method: req.method,
                uri: req.uri
            }
        }),
        conf: opts.conf
    };

    // Start off by parsing any POST data with BusBoy
    return rbUtil.parsePOST(req)

    // Then process the request
    .then(function() {
        // Create a new, clean request object
        var urlData = rbUtil.parseURL(req.url);
        var body = req.body;

        if (/^application\/json/i.test(req.headers['content-type'])) {
            try {
                body = JSON.parse(req.body.toString());
            } catch (e) {
                opts.log('error/request/json-parsing', e);
            }
        }
        newReq = {
            uri: new URI(urlData.pathname),
            query: urlData.query,
            method: req.method.toLowerCase(),
            headers: req.headers,
            body: body
        };

        // Quick hack to set up general CORS
        if (newReq.method === 'options') {
            return Promise.resolve({
                status: 200
            });
        } else {
            return app.restbase.request(newReq);
        }
    })

    // And finally handle the response
    .then(function(result) {
        return handleResponse(opts, newReq, resp, result);
    })
    .catch (function(e) {
        if (e && !(e instanceof rbUtil.HTTPError)) {
            var stack = e.stack;
            e = new rbUtil.HTTPError({
                status: 500,
                body: {
                    type: 'internal_error',
                    description: e.toString(),
                    // Probably better to keep this private for now
                    //stack: e.stack
                }
            });
            // Log this internally
            e.stack = stack;
        }
        if (!e.status) {
            e.status = 500;
        }
        return handleResponse(opts, newReq, resp, e);
    });
}

function setupConfigDefaults(conf) {
    if (!conf) { conf = {}; }
    if (!conf.logging) { conf.logging = {}; }
    if (!conf.logging.name) { conf.logging.name = 'restbase'; }
    if (!conf.logging.level) { conf.logging.level = 'warn'; }
    return conf;
}

// Main app setup
function main(conf) {
    conf = setupConfigDefaults(conf);
    // Set up the global options object with a logger
    var opts = {
        log: rbUtil.makeLogger(conf.logging),
        conf: conf
    };

    opts.router = new Router(opts);
    app.restbase = new Restbase(opts);
    // Use a child restbase instance to sidestep the security protection for
    // direct requests to /sys
    var childRestBase = app.restbase.makeChild({uri:'#internal-startup'});
    // Main app startup happens during async spec loading:
    return opts.router.loadSpec(conf.spec, childRestBase)
    .then(function(router) {
        //console.log(JSON.stringify(router.tree, null, 2));
        opts.router = router;
        var server = http.createServer(handleRequest.bind(null, opts));
        // Use a large listen queue
        // Also, echo 1024 | sudo tee /proc/sys/net/core/somaxconn
        // (from 128 default)
        var port = conf.port || 7231;
        // Apply some back-pressure.
        server.maxConnections = 500;
        server.listen(port);
        opts.log('info', 'listening on port ' + port);
        return server;
    })
    .catch(function(e) {
        opts.log('fatal/startup', e.stack);
    });
}

if (module.parent === null) {
    main();
} else {
    module.exports = main;
}
<commit_msg>Simple gzip encoding support<commit_after>"use strict";

/*
 * RESTBase web service entry point
 *
 * Sets up a restbase instance by loading and setting up handlers and the
 * storage layer, and then dispatches requests to it.
 */

global.Promise = require('bluebird');

var rbUtil = require('./rbUtil');
var Restbase = require('./restbase');
var http = require('http');
var fs = require('fs');
var Router = require('./router');
var URI = require('swagger-router').URI;
var zlib = require('zlib');

var app = {
    // The global proxy object
    proxy: null
};

function handleResponse (opts, req, resp, response) {
    //console.log('resp', response);
    if (response && response.status) {
        if (!response.headers) {
            response.headers = {};
        }

        // Set up CORS
        response.headers['Access-Control-Allow-Origin'] = '*';
        response.headers['Access-Control-Allow-Methods'] = 'GET';
        response.headers['Access-Control-Allow-Headers'] = 'accept, content-type';

        var logLevel = 'trace/request';
        if (response.status >= 500) {
            logLevel = 'error/request';
        } else if (response.status >= 400) {
            logLevel = 'info/request';
        }
        opts.log(logLevel, {
            message: response.message,
            req: req,
            res: response,
            stack: response.stack
        });

        var body;
        // Prepare error responses for the client
        if (response.status >= 400) {
            if (!response.body) {
                response.body = {};
            }
            // whitelist fields to avoid leaking sensitive info
            var rBody = response.body;
            body = {
                type: rBody.type,
                title: rBody.title,
                method: rBody.method,
                uri: rBody.uri
            };

            if (response.status === 404) {
                if (!body.type) { body.type = 'not_found'; }
                if (!body.title) { body.title = 'Not found.'; }
                if (!response.headers['content-type']) {
                    response.headers['content-type'] = 'application/problem+json';
                }
            }
            if (response.status >= 400) {
                if (!body.uri) { body.uri = req.url; }
                if (!body.method) { body.method = req.method; }
            }
            if (!body.type) {
                body.type = 'unknown_error';
            }
            // Prefix error base URL
            // XXX: make the prefix configurable
            body.type = 'https://restbase.org/errors/' + body.type;
            response.body = body;
        }

        if (response.body) {
            body = response.body;
            // Convert to a buffer
            if (!Buffer.isBuffer(body)) {
                if (typeof body === 'object') {
                    if (!response.headers['content-type']) {
                        response.headers['content-type'] = 'application/json';
                    }
                    body = new Buffer(JSON.stringify(body));
                } else  {
                    body = new Buffer(body);
                }
            }
            response.headers.connection = 'close';
            var cType = response.headers['content-type'];
            if (/\bgzip\b/.test(req.headers['accept-encoding'])
                    && /^application\/json\b|^text\//.test(cType)) {
                response.headers['content-encoding'] = 'gzip';
                resp.writeHead(response.status, '', response.headers);
                var zStream = zlib.createGzip({ level: 3 });
                zStream.pipe(resp);
                zStream.end(body);
            } else {
                response.headers['content-length'] = body.length;
                resp.writeHead(response.status, '', response.headers);
                resp.end(body);
            }
        } else {
            if (response.headers['content-length']) {
                opts.log('warn/response/content-length', {
                    req: req,
                    res: response,
                    reason: new Error('Invalid content-length')
                });
                delete response.headers['content-length'];
            }
            resp.writeHead(response.status, '', response.headers);
            resp.end();
        }
    } else {
        opts.log('error/request', {req: req}, "No content returned");
        response.headers['content-type'] = 'application/problem+json';
        resp.writeHead(response && response.status || 500, '', response && response.headers);
        resp.end(JSON.stringify({
            type: 'https://restbase.org/errors/no_content',
            title: 'RESTBase error: No content returned by backend.',
            uri: req.url,
            method: req.method
        }));
    }
}

// Handle a single request
function handleRequest (opts, req, resp) {
    var newReq;

    opts = {
        log: opts.log.child({
            req: {
                method: req.method,
                uri: req.uri
            }
        }),
        conf: opts.conf
    };

    // Start off by parsing any POST data with BusBoy
    return rbUtil.parsePOST(req)

    // Then process the request
    .then(function() {
        // Create a new, clean request object
        var urlData = rbUtil.parseURL(req.url);
        var body = req.body;

        if (/^application\/json/i.test(req.headers['content-type'])) {
            try {
                body = JSON.parse(req.body.toString());
            } catch (e) {
                opts.log('error/request/json-parsing', e);
            }
        }
        newReq = {
            uri: new URI(urlData.pathname),
            query: urlData.query,
            method: req.method.toLowerCase(),
            headers: req.headers,
            body: body
        };

        // Quick hack to set up general CORS
        if (newReq.method === 'options') {
            return Promise.resolve({
                status: 200
            });
        } else {
            return app.restbase.request(newReq);
        }
    })

    // And finally handle the response
    .then(function(result) {
        return handleResponse(opts, newReq, resp, result);
    })
    .catch (function(e) {
        if (e && !(e instanceof rbUtil.HTTPError)) {
            var stack = e.stack;
            e = new rbUtil.HTTPError({
                status: 500,
                body: {
                    type: 'internal_error',
                    description: e.toString(),
                    // Probably better to keep this private for now
                    //stack: e.stack
                }
            });
            // Log this internally
            e.stack = stack;
        }
        if (!e.status) {
            e.status = 500;
        }
        return handleResponse(opts, newReq, resp, e);
    });
}

function setupConfigDefaults(conf) {
    if (!conf) { conf = {}; }
    if (!conf.logging) { conf.logging = {}; }
    if (!conf.logging.name) { conf.logging.name = 'restbase'; }
    if (!conf.logging.level) { conf.logging.level = 'warn'; }
    return conf;
}

// Main app setup
function main(conf) {
    conf = setupConfigDefaults(conf);
    // Set up the global options object with a logger
    var opts = {
        log: rbUtil.makeLogger(conf.logging),
        conf: conf
    };

    opts.router = new Router(opts);
    app.restbase = new Restbase(opts);
    // Use a child restbase instance to sidestep the security protection for
    // direct requests to /sys
    var childRestBase = app.restbase.makeChild({uri:'#internal-startup'});
    // Main app startup happens during async spec loading:
    return opts.router.loadSpec(conf.spec, childRestBase)
    .then(function(router) {
        //console.log(JSON.stringify(router.tree, null, 2));
        opts.router = router;
        var server = http.createServer(handleRequest.bind(null, opts));
        // Use a large listen queue
        // Also, echo 1024 | sudo tee /proc/sys/net/core/somaxconn
        // (from 128 default)
        var port = conf.port || 7231;
        // Apply some back-pressure.
        server.maxConnections = 500;
        server.listen(port);
        opts.log('info', 'listening on port ' + port);
        return server;
    })
    .catch(function(e) {
        opts.log('fatal/startup', e.stack);
    });
}

if (module.parent === null) {
    main();
} else {
    module.exports = main;
}
<|endoftext|>


<commit_before>/*jshint node: true, globalstrict: true, esnext: false */
"use strict";

// Imports
// -------

var gulp = require("gulp-help")(require("gulp"));
var to5 = require("gulp-6to5");
var uglify = require("gulp-uglify");
var rename = require("gulp-rename");

// `gulp build`
// ------------

gulp.task("build"
  , "Built the lot."
  , ["scripts"]
  );

// `gulp scripts`
// --------------

var scripts =
  { source: "source/n.js"
  };

gulp.task("scripts"
  , "Build ES5 and ES6 dist scripts."
  , ["scripts:es6", "scripts:es5"]
  );

gulp.task("scripts:es6", false, function () {
  return gulp
    .src(scripts.source)
    .pipe(gulp.dest("dist"))
    ;
  });

gulp.task("scripts:es5", false, function () {
  return gulp
    .src(scripts.source)
    .pipe(to5({modules: "6-to-library"}))
    .pipe(gulp.dest("dist.es5"))
    .pipe(uglify())
    .pipe(rename({extname: ".min.js"}))
    .pipe(gulp.dest("dist.es5"))
    ;
  });
<commit_msg>Fix typo<commit_after>/*jshint node: true, globalstrict: true, esnext: false */
"use strict";

// Imports
// -------

var gulp = require("gulp-help")(require("gulp"));
var to5 = require("gulp-6to5");
var uglify = require("gulp-uglify");
var rename = require("gulp-rename");

// `gulp build`
// ------------

gulp.task("build"
  , "Build the lot."
  , ["scripts"]
  );

// `gulp scripts`
// --------------

var scripts =
  { source: "source/n.js"
  };

gulp.task("scripts"
  , "Build ES5 and ES6 dist scripts."
  , ["scripts:es6", "scripts:es5"]
  );

gulp.task("scripts:es6", false, function () {
  return gulp
    .src(scripts.source)
    .pipe(gulp.dest("dist"))
    ;
  });

gulp.task("scripts:es5", false, function () {
  return gulp
    .src(scripts.source)
    .pipe(to5({modules: "6-to-library"}))
    .pipe(gulp.dest("dist.es5"))
    .pipe(uglify())
    .pipe(rename({extname: ".min.js"}))
    .pipe(gulp.dest("dist.es5"))
    ;
  });
<|endoftext|>


<commit_before>'use strict'
const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')
const BeepBoop = require('beepboop')
const bodyParser = require('body-parser')
const parameters = require('parameters-middleware');
var request = require('request')
const slackey = require('slackey')
// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000
var channels = {}

var slackAPIClient
var beepboop = BeepBoop.start()

beepboop.on('open', () => {
  console.log('connection to Beep Boop server opened')
})
beepboop.on('error', (error) => {
  console.log('Error from Beep Boop connection: ', err)
})
beepboop.on('close', (code, message) => {
  console.log('Connection to Beep Boop was closed')
})
beepboop.on('add_resource', (message) => {
  console.log('Team added: ', message);
  console.log(message.resource.SlackBotAccessToken);
  slackAPIClient = slackey.getAPIClient(message.resource.SlackBotAccessToken);
	fetchChannels();
})
var slapp = Slapp({
  // Beep Boop sets the SLACK_VERIFY_TOKEN env var
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})
var app = express()
app.use(bodyParser.json());
app.post('/recommendations',
  parameters({body : ["channel", "title", "url"]}),
  (req, res) => {
    var channelId = req.body.channel
    var channel = channels[channelId]
    if (channel == null) return res.status(400).send("Channel not found")
    var verifiedIcon = req.body.verified ? "http://smallbusiness.support/wp-content/uploads/2015/10/facebook-verified.png" : "https://cdn1.iconfinder.com/data/icons/rounded-flat-country-flag-collection-1/2000/_Unknown.png"
    var attachment = {
    	callback_id: "share",
	      author_icon: verifiedIcon,
	      title_link: req.body.url,
	      title: req.body.title,
	      actions: [
	          {
	            "name": "post",
	            "text": "Post",
	            "type": "button",
	            "style": "primary",
	            "value": channelId
	          },
	          {
	            "name": "discard",
	            "text": "Discard",
	            "type": "button",
	            "style": "danger",
	            "value": "discarded"
	          },
	          {
                  "name": "post",
                  "text": "Share to channel...",
                  "type": "select",
                  "options": [
            		   {
            		      "text":"Test",
            		      "value":1
            		   },
            		   {
        			     "text":"Digital Marketing",
        			     "value":7
        			   },
        			   {
        			      "text":"Legal",
        			      "value":40
        			   },
        			   {
        			      "text":"Innovation & Startups",
        			      "value":43
        			   },
        			   {
        			      "text":"Journalism",
        			      "value":66
        			   },
        			   {
        			      "text":"Ondernemen",
        			      "value":76
        			   },
        			   {
        			      "text":"Market Trends",
        			      "value":82
        			   },
        			   {
        			      "text":"9x Awesome Content",
        			      "value":83
        			   },
        			   {
        			      "text":"Business Practices",
        			      "value":84
        			   },
        			   {
        			      "text":"Education Market",
        			      "value":86
        			   },
        			   {
        			      "text":"Venture Capital & Startups",
        			      "value":87
        			   },
        			   {
        			      "text":"Fintech",
        			      "value":89
        			   },
        			   {
        			      "text":"Direct Marketing",
        			      "value":101
        			   },
        			   {
        			      "text":"IT Business",
        			      "value":109
        			   },
        			   {
        			      "text":"NN Algemeen",
        			      "value":116
        			   },
        			   {
        			      "text":"Customer Insights",
        			      "value":120
        			   },
        			   {
        			      "text":"CPS",
        			      "value":125
        			   },
        			   {
        			      "text":"RMS",
        			      "value":126
        			   },
        			   {
        			      "text":"IBM in the Media",
        			      "value":136
        			   },
        			   {
        			      "text":"Randstad in the News",
        			      "value":151
        			   },
        			   {
        			      "text":"Randstad Market Watch",
        			      "value":152
        			   },
        			   {
        			      "text":"IBM Cloud Market Watch",
        			      "value":170
        			   },
        			   {
        			      "text":"IBM Developer Advocates",
        			      "value":219
        			   },
        			   {
        			      "text":"Achmea Transport",
        			      "value":250
        			   },
        			   {
        			      "text":"Achmea Automotive",
        			      "value":251
        			   },
        			   {
        			      "text":"Achmea Innovation",
        			      "value":253
        			   }
        			]
              }
	        ]
    }
    if (req.body.host) {
      attachment.author_name = req.body.host
        attachment.author_link = req.body.host
    }
    if (req.body.summary) {
        attachment.text = req.body.summary
    }
    if (req.body.image_url) {
        attachment.image_url = req.body.image_url
    }
    if (req.body.pub_date) {
        attachment.ts = Date.parse(req.body.pub_date)
    }

    // Populating fields
    var fields = []
    if (req.body.keywords) {
      fields.push({
        title: "Keywords",
        short: false,
        value: req.body.keywords
      })
    }
    if (req.body.companies) {
      fields.push({
        title: "Companies",
        short: false,
        value: req.body.companies
      })
    }
    if (req.body.word_count) {
      fields.push({
        title: "Length",
        short: true,
        value: req.body.word_count + " words"
      })
    }
    if (req.body.shares) {
      fields.push({
        title: "Shares",
        short: true,
        value: formatCount(req.body.shares)
      })
    }
    if (req.body.sentiment) {
    	var sentiment
    	switch (req.body.sentiment) {
    		case "negative":
    			sentiment = ":rage:Negative";
    			break;
    		case "positive":
    			sentiment = ":smile:Positive";
    			break;
    		case "neutral":
    			sentiment = ":neutral_face:Neutral"
    			break;
    	}
    	fields.push({
            title: "Sentiment",
            short: true,
            value: sentiment
          })
    }
    fields.push({
    	"title": "Sponsored",
        "value": ":warning: Undetected",
        "short": true
    })
    attachment.fields = fields
    slackAPIClient.send('chat.postMessage',
          {
          channel,
          attachments: [attachment]
          },
          (err, response) => {
	          if (err) {
	            console.log(err)
	            res.status(500).send(err)
	          } else {
	            res.status(201).send(response)
	          }
          }
        )
  }
)

slapp.action('share', 'post', (msg, value) => {
	if (!value) value = msg.body.actions[0].selected_options[0].value
    console.log(`Article ${msg.body.original_message.attachments[0].title_link} shared to channel ${value}`)
    var originalMsg = msg.body.original_message;
	var chosenAttachment = originalMsg.attachments[msg.body.attachment_id - 1];
	addUrlToChannel(value, msg.body.original_message.attachments[0].title_link)
		.then(() => {
			chosenAttachment.actions = []
			var lastAttachment = {
					pretext: `:postbox: Article posted to channel ${value}`
			}
			originalMsg.attachments = [chosenAttachment, lastAttachment]
			msg.respond(msg.body.response_url, originalMsg)
		})
		.catch((err) => {
			console.log(err)
			var lastAttachment = {
					pretext: `:exclamation: Error posting article to channel ${value}`
			}
			originalMsg.attachments = [chosenAttachment, lastAttachment]
			msg.respond(msg.body.response_url, originalMsg)
		})
})

function addUrlToChannel(channelId, url) {
	return new Promise((resolve, reject) => {
		request.post('http://itao-server-55663464.eu-central-1.elb.amazonaws.com/itao/item/add/url',
			{body: url}, (err, res, body) => {
				if (err) return reject(err);
				if (!(body instanceof Array) && "success" in body && !body.success) return reject(JSON.stringify(body));
				request.post('http://itao-server-55663464.eu-central-1.elb.amazonaws.com/itao/channel/item/add',
	    			{ json: {
	    				channel_id: channelId,
	    				url: url
	    			}}, (err2, res2, body2) => {
	    				if (err) return reject(err);
	    				if ("success" in body && !body.success) return reject(JSON.stringify(body));
	    				resolve();
			})
		})
	})
}

slapp.action('share', 'discard', (msg, value) => {
	var originalMsg = msg.body.original_message
	var chosenAttachment = originalMsg.attachments[msg.body.attachment_id - 1]
    chosenAttachment.actions = []
	var lastAttachment = {
		pretext: `:no_entry: Article discarded`,
	}
	originalMsg.attachments = [chosenAttachment, lastAttachment]
	msg.respond(msg.body.response_url, originalMsg)
})

function fetchChannels() {
  slackAPIClient.send('channels.list',
      function(err, response) {
        if (err) console.log(err)
          for (var i = 0; i < response.channels.length; i++) {
            var channel = response.channels[i]
            try {
              var channelNumber = channel.name.split("-")[0]
              if (!isNaN(channelNumber)) {
                channels[channelNumber] = channel.id
              }
            } catch (err) {
              console.log(err)
            }
          }
        console.log(`${Object.keys(channels).length} channels loaded`)
        }
  )
}

function formatCount(x) {
  var magnitude = Math.max(Math.floor(Math.log10(Math.abs(x))), 0);
  var postfix = ""
  if (magnitude >= 6) {
    magnitude -= 6
    postfix = "M"
  } else if (magnitude >= 3) {
    magnitude -= 3
    postfix = "k"
  }
  var first_digit = String(x).charAt(0);
  var rounded_significant = first_digit >= 5 ? 5 : 1;
  return String(rounded_significant * Math.pow(10, magnitude)) + postfix + "+"
}

// attach Slapp to express server
var server = slapp.attachToExpress(app)
// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }

  console.log(`Listening on port ${port}`)
})<commit_msg>added debugging info<commit_after>'use strict'
const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')
const BeepBoop = require('beepboop')
const bodyParser = require('body-parser')
const parameters = require('parameters-middleware');
var request = require('request')
const slackey = require('slackey')
// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000
var channels = {}

var slackAPIClient
var beepboop = BeepBoop.start()

beepboop.on('open', () => {
  console.log('connection to Beep Boop server opened')
})
beepboop.on('error', (error) => {
  console.log('Error from Beep Boop connection: ', err)
})
beepboop.on('close', (code, message) => {
  console.log('Connection to Beep Boop was closed')
})
beepboop.on('add_resource', (message) => {
  console.log('Team added: ', message);
  console.log(message.resource.SlackBotAccessToken);
  slackAPIClient = slackey.getAPIClient(message.resource.SlackBotAccessToken);
	fetchChannels();
})
var slapp = Slapp({
  // Beep Boop sets the SLACK_VERIFY_TOKEN env var
  verify_token: process.env.SLACK_VERIFY_TOKEN,
  convo_store: ConvoStore(),
  context: Context()
})
var app = express()
app.use(bodyParser.json());
app.post('/recommendations',
  parameters({body : ["channel", "title", "url"]}),
  (req, res) => {
    var channelId = req.body.channel
    var channel = channels[channelId]
    if (channel == null) return res.status(400).send("Channel not found")
    var verifiedIcon = req.body.verified ? "http://smallbusiness.support/wp-content/uploads/2015/10/facebook-verified.png" : "https://cdn1.iconfinder.com/data/icons/rounded-flat-country-flag-collection-1/2000/_Unknown.png"
    var attachment = {
    	callback_id: "share",
	      author_icon: verifiedIcon,
	      title_link: req.body.url,
	      title: req.body.title,
	      actions: [
	          {
	            "name": "post",
	            "text": "Post",
	            "type": "button",
	            "style": "primary",
	            "value": channelId
	          },
	          {
	            "name": "discard",
	            "text": "Discard",
	            "type": "button",
	            "style": "danger",
	            "value": "discarded"
	          },
	          {
                  "name": "post",
                  "text": "Share to channel...",
                  "type": "select",
                  "options": [
            		   {
            		      "text":"Test",
            		      "value":1
            		   },
            		   {
        			     "text":"Digital Marketing",
        			     "value":7
        			   },
        			   {
        			      "text":"Legal",
        			      "value":40
        			   },
        			   {
        			      "text":"Innovation & Startups",
        			      "value":43
        			   },
        			   {
        			      "text":"Journalism",
        			      "value":66
        			   },
        			   {
        			      "text":"Ondernemen",
        			      "value":76
        			   },
        			   {
        			      "text":"Market Trends",
        			      "value":82
        			   },
        			   {
        			      "text":"9x Awesome Content",
        			      "value":83
        			   },
        			   {
        			      "text":"Business Practices",
        			      "value":84
        			   },
        			   {
        			      "text":"Education Market",
        			      "value":86
        			   },
        			   {
        			      "text":"Venture Capital & Startups",
        			      "value":87
        			   },
        			   {
        			      "text":"Fintech",
        			      "value":89
        			   },
        			   {
        			      "text":"Direct Marketing",
        			      "value":101
        			   },
        			   {
        			      "text":"IT Business",
        			      "value":109
        			   },
        			   {
        			      "text":"NN Algemeen",
        			      "value":116
        			   },
        			   {
        			      "text":"Customer Insights",
        			      "value":120
        			   },
        			   {
        			      "text":"CPS",
        			      "value":125
        			   },
        			   {
        			      "text":"RMS",
        			      "value":126
        			   },
        			   {
        			      "text":"IBM in the Media",
        			      "value":136
        			   },
        			   {
        			      "text":"Randstad in the News",
        			      "value":151
        			   },
        			   {
        			      "text":"Randstad Market Watch",
        			      "value":152
        			   },
        			   {
        			      "text":"IBM Cloud Market Watch",
        			      "value":170
        			   },
        			   {
        			      "text":"IBM Developer Advocates",
        			      "value":219
        			   },
        			   {
        			      "text":"Achmea Transport",
        			      "value":250
        			   },
        			   {
        			      "text":"Achmea Automotive",
        			      "value":251
        			   },
        			   {
        			      "text":"Achmea Innovation",
        			      "value":253
        			   }
        			]
              }
	        ]
    }
    if (req.body.host) {
      attachment.author_name = req.body.host
        attachment.author_link = req.body.host
    }
    if (req.body.summary) {
        attachment.text = req.body.summary
    }
    if (req.body.image_url) {
        attachment.image_url = req.body.image_url
    }
    if (req.body.pub_date) {
    	console.log(Date.parse(req.body.pub_date))
        attachment.ts = Date.parse(req.body.pub_date)
    }

    // Populating fields
    var fields = []
    if (req.body.keywords) {
      fields.push({
        title: "Keywords",
        short: false,
        value: req.body.keywords
      })
    }
    if (req.body.companies) {
      fields.push({
        title: "Companies",
        short: false,
        value: req.body.companies
      })
    }
    if (req.body.word_count) {
      fields.push({
        title: "Length",
        short: true,
        value: req.body.word_count + " words"
      })
    }
    if (req.body.shares) {
      fields.push({
        title: "Shares",
        short: true,
        value: formatCount(req.body.shares)
      })
    }
    if (req.body.sentiment) {
    	var sentiment
    	switch (req.body.sentiment) {
    		case "negative":
    			sentiment = ":rage:Negative";
    			break;
    		case "positive":
    			sentiment = ":smile:Positive";
    			break;
    		case "neutral":
    			sentiment = ":neutral_face:Neutral"
    			break;
    	}
    	fields.push({
            title: "Sentiment",
            short: true,
            value: sentiment
          })
    }
    fields.push({
    	"title": "Sponsored",
        "value": ":warning: Undetected",
        "short": true
    })
    attachment.fields = fields
    slackAPIClient.send('chat.postMessage',
          {
          channel,
          attachments: [attachment]
          },
          (err, response) => {
	          if (err) {
	            console.log(err)
	            res.status(500).send(err)
	          } else {
	            res.status(201).send(response)
	          }
          }
        )
  }
)

slapp.action('share', 'post', (msg, value) => {
	if (!value) value = msg.body.actions[0].selected_options[0].value
    console.log(`Article ${msg.body.original_message.attachments[0].title_link} shared to channel ${value}`)
    var originalMsg = msg.body.original_message;
	var chosenAttachment = originalMsg.attachments[msg.body.attachment_id - 1];
	addUrlToChannel(value, msg.body.original_message.attachments[0].title_link)
		.then(() => {
			chosenAttachment.actions = []
			var lastAttachment = {
					pretext: `:postbox: Article posted to channel ${value}`
			}
			originalMsg.attachments = [chosenAttachment, lastAttachment]
			msg.respond(msg.body.response_url, originalMsg)
		})
		.catch((err) => {
			console.log(err)
			var lastAttachment = {
					pretext: `:exclamation: Error posting article to channel ${value}`
			}
			originalMsg.attachments = [chosenAttachment, lastAttachment]
			msg.respond(msg.body.response_url, originalMsg)
		})
})

function addUrlToChannel(channelId, url) {
	return new Promise((resolve, reject) => {
		request.post('http://itao-server-55663464.eu-central-1.elb.amazonaws.com/itao/item/add/url',
			{body: url}, (err, res, body) => {
				if (err) return reject(err);
				console.log(`Array: ${body instanceof Array}`);
				console.log(`Object: ${body instanceof Object}`);
				if (!(body instanceof Array) && "success" in body && !body.success) return reject(JSON.stringify(body));
				request.post('http://itao-server-55663464.eu-central-1.elb.amazonaws.com/itao/channel/item/add',
	    			{ json: {
	    				channel_id: channelId,
	    				url: url
	    			}}, (err2, res2, body2) => {
	    				if (err) return reject(err);
	    				if ("success" in body && !body.success) return reject(JSON.stringify(body));
	    				resolve();
			})
		})
	})
}

slapp.action('share', 'discard', (msg, value) => {
	var originalMsg = msg.body.original_message
	var chosenAttachment = originalMsg.attachments[msg.body.attachment_id - 1]
    chosenAttachment.actions = []
	var lastAttachment = {
		pretext: `:no_entry: Article discarded`,
	}
	originalMsg.attachments = [chosenAttachment, lastAttachment]
	msg.respond(msg.body.response_url, originalMsg)
})

function fetchChannels() {
  slackAPIClient.send('channels.list',
      function(err, response) {
        if (err) console.log(err)
          for (var i = 0; i < response.channels.length; i++) {
            var channel = response.channels[i]
            try {
              var channelNumber = channel.name.split("-")[0]
              if (!isNaN(channelNumber)) {
                channels[channelNumber] = channel.id
              }
            } catch (err) {
              console.log(err)
            }
          }
        console.log(`${Object.keys(channels).length} channels loaded`)
        }
  )
}

function formatCount(x) {
  var magnitude = Math.max(Math.floor(Math.log10(Math.abs(x))), 0);
  var postfix = ""
  if (magnitude >= 6) {
    magnitude -= 6
    postfix = "M"
  } else if (magnitude >= 3) {
    magnitude -= 3
    postfix = "k"
  }
  var first_digit = String(x).charAt(0);
  var rounded_significant = first_digit >= 5 ? 5 : 1;
  return String(rounded_significant * Math.pow(10, magnitude)) + postfix + "+"
}

// attach Slapp to express server
var server = slapp.attachToExpress(app)
// start http server
server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }

  console.log(`Listening on port ${port}`)
})<|endoftext|>