<commit_before>'use strict';

var React     = require('react');
var invariant = require('react/lib/invariant');

var isArray = function(object) {
  return Object.prototype.toString.call(object) === '[object Array]';
};

var isString = function(object) {
  return Object.prototype.toString.call(object) === '[object String]';
};

var REGEXP = /\%\((.+?)\)s/;

var Interpolate = React.createClass({
  displayName: 'Interpolate',

  propTypes: {
    children: React.PropTypes.string
  },

  getDefaultProps: function() {
    return { component: React.DOM.span };
  },

  render: function() {
    var props  = this.props;
    var format = props.children || '';
    var parent = props.component;
    var unsafe = props.unsafe === true;

    invariant(
      isString(format) || isArray(format) && format.length === 1 && isString(format = format[0]),
      'Interpolate expects a format string as only child'
    );

    var children = format.split(REGEXP).reduce(function(memo, match, index) {
      var child = (index % 2 === 0) ? match : props[match];

      if (!React.isValidComponent(child)) {
        if (unsafe) {
          child = React.DOM.span({ dangerouslySetInnerHTML: { __html: child } });
        } else {
          child = React.DOM.span(null, child);
        }
      }

      memo['_' + index] = child;

      return memo;
    }, {});

    return this.transferPropsTo(parent(null, children));
  }
});

module.exports = Interpolate;
<commit_msg>remove empty matches<commit_after>'use strict';

var React     = require('react');
var invariant = require('react/lib/invariant');

var isArray = function(object) {
  return Object.prototype.toString.call(object) === '[object Array]';
};

var isString = function(object) {
  return Object.prototype.toString.call(object) === '[object String]';
};

var REGEXP = /\%\((.+?)\)s/;

var Interpolate = React.createClass({
  displayName: 'Interpolate',

  propTypes: {
    children: React.PropTypes.string
  },

  getDefaultProps: function() {
    return { component: React.DOM.span };
  },

  render: function() {
    var props  = this.props;
    var format = props.children || '';
    var parent = props.component;
    var unsafe = props.unsafe === true;

    invariant(
      isString(format) || isArray(format) && format.length === 1 && isString(format = format[0]),
      'Interpolate expects a format string as only child'
    );

    var children = format.split(REGEXP).reduce(function(memo, match, index) {
      var child = (index % 2 === 0) ? match : props[match];

      if (!React.isValidComponent(child)) {
        if (match.length === 0) {
          return memo;
        }

        if (unsafe) {
          child = React.DOM.span({ dangerouslySetInnerHTML: { __html: child } });
        } else {
          child = React.DOM.span(null, child);
        }
      }

      memo['_' + index] = child;

      return memo;
    }, {});

    return this.transferPropsTo(parent(null, children));
  }
});

module.exports = Interpolate;
<|endoftext|>


<commit_before>//
// # prunk
// ## A mocking utility for node.js require
//
//     Author: Daniel Koch <daniel@suitsoft.eu>
//     Version: 1.0.0
//

// Require node's `module` module that provides
// access to all the things `require()` uses
var Module = require('module');

// ## _load
// The original `module._load` function
var _load = void 0;

// ## cache
// The cache that contains mocks and suppressed imports
var cache = [];

// ## init
// Overwrites `Module._load` with a function that either
// returns a mocked value from the cache or whatever the original
// function returns if nothing has been mocked.
var init = function() {
    _load = Module._load;

    // Overwrite the original `_load` function
    // The important argument for us is `req` because its the
    // name of whatever was required.
    Module._load = function(req, parent, isMain) {

        // Take the cache...
        var mock = cache
                        // Find the tests that match ...
                        .filter(function(mockDef) {
                            var test = mockDef.test;

                            // If we have a regex we match it against the string
                            if( test instanceof RegExp )
                                return null !== req.match( test );

                            // If we have a function we let the
                            // function decide
                            if( 'function' === typeof test )
                                return !! test(req, parent, isMain);

                            // Last case: we simply have no idea
                            // Thus, we use a strict equal
                            return test === req;
                        })
                        // ... and grab the mocked value from it
                        .map( function(mockDef) {
                            return mockDef.value;
                        });

        // If we found a mocked import we return it.
        // We only use the first mock we find. Since the `createMock()` function
        // uses `Array.prototype.unshift` we always use the mock added at last.
        if( mock.length )
            return mock[0];

        // No mock; let the load system do its work
        return _load.apply(Module, arguments);
    };
};

// ## createMock
// Creates a mock with the given test, mocking with the given
// value and use the given type.
//
// - `test`  - Something that is used to compare. Might be a string, a function or something else
// - `value` - The mock value that replaces the original import
// - `type`  - Type, either `'mock'` or `'sup'`. This is used to remove it later on.
//
var createMock = function(test, value, type) {
    // Initialize the cache if not done already
    if( ! _load )
        init.call(this);

    // Push the new item to the front
    cache.unshift({
        test: test,
        value: value,
        _type: type
    })
};

// ## remove
// Removes a mock definition from the collection.
//
// - `test`  - Something that is used to compare. Might be a string, a function or something else
// - `type`  - Type, either `'mock'` or `'sup'`.
//
var remove = function(test, type) {
    // We want to use strict equals below thus
    // we use the string representation of RegExp's
    if( test instanceof RegExp )
            test = ('' + test);

    cache = cache.filter( function(def) {
            // If the type does not match we ignore it
            if( type !== def._type )
                return true;

            // Again, convert RegExp's if found
            var cmp = (def.test instanceof RegExp) ? '' + def.test : def.test;

            return test !== cmp;
        });
};

// ## removeAll
// Removes all mocks of the given type
//
// - `type`  - Type, either `'mock'` or `'sup'`.
//
var removeAll = function(type) {
    cache = cache.filter( function(item) {
        return item._type !== type;
    });
};

// ## Exports
module.exports = {

    // Provide access to the cache.
    // This is done to make the testing more reliable.
    get _cache() {
        return cache;
    },

    set _cache(what) {
        cache = what;
    },

    // ### mock
    // Mocks the given import with the given value.
    // `test` can be a **function** that is used to compare
    // whatever is required and returns `true` or `false`.
    // If the return value is truthy the import will be
    // mocked with the given value.
    //
    // The function's arguments are the same as with `Module._load`.
    // Most of the time you will look at the first argument, a `string`.
    //
    //     var mockStyles = (req) => 'style.css' === req;
    //     prunk.mock( mockStyles, 'no css, dude.');
    //
    // `test` can also be a `RegExp` that is matched agains the name
    // of the import or a string. It can be anything else, too, if your
    // imports are gone totally crazy.
    //
    //     prunk.mock( 'style.css', 'no css, dude.' );
    //     prunk.mock( /\.(css|scss|sass|less)/, 'no styles, dude.');
    //
    mock: function(test, value) {
        createMock(test, value, 'mock');
    },

    // ### suppress
    // Suppresses the given import. A suppressed import always
    // returns `undefined`.
    // The `test` argument is the same as used by [mock()](#section-25).
    //
    suppress: function(test) {
        createMock(test, void 0, 'sup');
    },

    // ### unmock
    // Removes the mock registered for the given `test`.
    // `unmock()` uses strict equal to compare the registered
    // mocks.
    unmock: function(test) {
        remove(test, 'mock');
    },

    // ### unsuppress
    // Removes the mock registered for the given `test`.
    // `unsuppress()` uses strict equal to compare the suppressed
    // imports.
    unsuppress: function(test) {
        remove(test, 'sup');
    },

    // ### unmockAll
    // Removes all mocks for imports
    unmockAll: function() {
        removeAll('mock');
    },

    // ### unsuppressAll
    // Removes all suppressed imports
    unsuppressAll: function() {
        removeAll('sup');
    }

};<commit_msg>Enable chaining<commit_after>//
// # prunk
// ## A mocking utility for node.js require
//
//     Author: Daniel Koch <daniel@suitsoft.eu>
//     Version: 1.0.0
//

// Require node's `module` module that provides
// access to all the things `require()` uses
var Module = require('module');

// ## _load
// The original `module._load` function
var _load = void 0;

// ## cache
// The cache that contains mocks and suppressed imports
var cache = [];

// ## init
// Overwrites `Module._load` with a function that either
// returns a mocked value from the cache or whatever the original
// function returns if nothing has been mocked.
var init = function() {
    _load = Module._load;

    // Overwrite the original `_load` function
    // The important argument for us is `req` because its the
    // name of whatever was required.
    Module._load = function(req, parent, isMain) {

        // Take the cache...
        var mock = cache
                        // Find the tests that match ...
                        .filter(function(mockDef) {
                            var test = mockDef.test;

                            // If we have a regex we match it against the string
                            if( test instanceof RegExp )
                                return null !== req.match( test );

                            // If we have a function we let the
                            // function decide
                            if( 'function' === typeof test )
                                return !! test(req, parent, isMain);

                            // Last case: we simply have no idea
                            // Thus, we use a strict equal
                            return test === req;
                        })
                        // ... and grab the mocked value from it
                        .map( function(mockDef) {
                            return mockDef.value;
                        });

        // If we found a mocked import we return it.
        // We only use the first mock we find. Since the `createMock()` function
        // uses `Array.prototype.unshift` we always use the mock added at last.
        if( mock.length )
            return mock[0];

        // No mock; let the load system do its work
        return _load.apply(Module, arguments);
    };
};

// ## createMock
// Creates a mock with the given test, mocking with the given
// value and use the given type.
//
// - `test`  - Something that is used to compare. Might be a string, a function or something else
// - `value` - The mock value that replaces the original import
// - `type`  - Type, either `'mock'` or `'sup'`. This is used to remove it later on.
//
var createMock = function(test, value, type) {
    // Initialize the cache if not done already
    if( ! _load )
        init.call(this);

    // Push the new item to the front
    cache.unshift({
        test: test,
        value: value,
        _type: type
    })
};

// ## remove
// Removes a mock definition from the collection.
//
// - `test`  - Something that is used to compare. Might be a string, a function or something else
// - `type`  - Type, either `'mock'` or `'sup'`.
//
var remove = function(test, type) {
    // We want to use strict equals below thus
    // we use the string representation of RegExp's
    if( test instanceof RegExp )
            test = ('' + test);

    cache = cache.filter( function(def) {
            // If the type does not match we ignore it
            if( type !== def._type )
                return true;

            // Again, convert RegExp's if found
            var cmp = (def.test instanceof RegExp) ? '' + def.test : def.test;

            return test !== cmp;
        });
};

// ## removeAll
// Removes all mocks of the given type
//
// - `type`  - Type, either `'mock'` or `'sup'`.
//
var removeAll = function(type) {
    cache = cache.filter( function(item) {
        return item._type !== type;
    });
};

// ## The prunk object
var prunk = {

    // Provide access to the cache.
    // This is done to make the testing more reliable.
    get _cache() {
        return cache;
    },

    set _cache(what) {
        cache = what;
    },

    // ### mock
    // Mocks the given import with the given value.
    // `test` can be a **function** that is used to compare
    // whatever is required and returns `true` or `false`.
    // If the return value is truthy the import will be
    // mocked with the given value.
    //
    // The function's arguments are the same as with `Module._load`.
    // Most of the time you will look at the first argument, a `string`.
    //
    //     var mockStyles = (req) => 'style.css' === req;
    //     prunk.mock( mockStyles, 'no css, dude.');
    //
    // `test` can also be a `RegExp` that is matched agains the name
    // of the import or a string. It can be anything else, too, if your
    // imports are gone totally crazy.
    //
    //     prunk.mock( 'style.css', 'no css, dude.' );
    //     prunk.mock( /\.(css|scss|sass|less)/, 'no styles, dude.');
    //
    // It returns the prunk object so that you can chain calls.
    mock: function(test, value) {
        createMock(test, value, 'mock');
        return prunk;
    },

    // ### suppress
    // Suppresses the given import. A suppressed import always
    // returns `undefined`.
    // The `test` argument is the same as used by [mock()](#section-25).
    //
    // It returns the prunk object so that you can chain calls.
    suppress: function(test) {
        createMock(test, void 0, 'sup');
        return prunk;
    },

    // ### unmock
    // Removes the mock registered for the given `test`.
    // `unmock()` uses strict equal to compare the registered
    // mocks.
    // It returns the prunk object so that you can chain calls.
    unmock: function(test) {
        remove(test, 'mock');
        return prunk;
    },

    // ### unsuppress
    // Removes the mock registered for the given `test`.
    // `unsuppress()` uses strict equal to compare the suppressed
    // imports.
    // It returns the prunk object so that you can chain calls.
    unsuppress: function(test) {
        remove(test, 'sup');
        return prunk;
    },

    // ### unmockAll
    // Removes all mocks for imports
    // It returns the prunk object so that you can chain calls.
    unmockAll: function() {
        removeAll('mock');
        return prunk;
    },

    // ### unsuppressAll
    // Removes all suppressed imports
    // It returns the prunk object so that you can chain calls.
    unsuppressAll: function() {
        removeAll('sup');
        return prunk;
    }

};

// ## Exports
module.exports = prunk;<|endoftext|>


<commit_before>console.log("process.env.TRAVIS_JOB_NUMBER")
console.log("value is: " + process.env.TRAVIS_JOB_NUMBER);
console.log("type is: " + typeof process.env.TRAVIS_JOB_NUMBER);

console.log("process.env.TRAVIS_TAG")
console.log("value is: " + process.env.TRAVIS_TAG);
console.log("type is: " + typeof process.env.TRAVIS_TAG);<commit_msg>add TRAVIS_TAG<commit_after>console.log("process.env.TRAVIS_JOB_NUMBER")
console.log("value is: " + process.env.TRAVIS_JOB_NUMBER);
console.log("type is: " + typeof process.env.TRAVIS_JOB_NUMBER);

console.log("process.env.TRAVIS_TAG")
console.log("value is: " + process.env.TRAVIS_TAG);
console.log("type is: " + typeof process.env.TRAVIS_TAG);

setTimeout(function(){ alert("Done!"); }, 5000);<|endoftext|>


<commit_before>'use strict';

/**
 * This microservice retrieves all the selfies from the database.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  4 Aug. 2015
 */

// module dependencies
var db = require('dynongo'),
    Q = require('q');

// connect with the database
db.connect();

var Selfie = db.table('Selfie');

/**
 * Main entrypoint of the service.
 *
 * @param {object}  event       The data regarding the event.
 * @param {object}  context     The AWS Lambda execution context.
 */
exports.handler = function(event, context) {
    Q.fcall(function() {
        // Retrieve all the selfies from the database
        return Selfie.find({active: 1}).sort(1).exec();
    }).then(function(result) {
        // Send the result back to the client
        context.succeed(result);
    }).catch(function(err) {
        // Print the error if something went wrong
        console.error(err, err.stack);

        // Something went wrong
        context.fail(err);
    });
};<commit_msg>Providing the name of the index<commit_after>'use strict';

/**
 * This microservice retrieves all the selfies from the database.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  4 Aug. 2015
 */

// module dependencies
var db = require('dynongo'),
    Q = require('q');

// connect with the database
db.connect();

var Selfie = db.table('Selfie');

/**
 * Main entrypoint of the service.
 *
 * @param {object}  event       The data regarding the event.
 * @param {object}  context     The AWS Lambda execution context.
 */
exports.handler = function(event, context) {
    Q.fcall(function() {
        // Retrieve all the selfies from the database
        return Selfie.find({active: 1}, 'ActiveDateIndex').sort(1).exec();
    }).then(function(result) {
        // Send the result back to the client
        context.succeed(result);
    }).catch(function(err) {
        // Print the error if something went wrong
        console.error(err, err.stack);

        // Something went wrong
        context.fail(err);
    });
};<|endoftext|>


<commit_before>/*! cookie function. get, set, or forget a cookie. [c]2014 @scottjehl, Filament Group, Inc. Licensed MIT */
(function(w){
	w.cookie = function( name, value, days ){
		// if value is undefined, get the cookie value
		if( value === undefined ){
			var cookiestring = "; " + w.document.cookie;
			var cookies = cookiestring.split( "; " + name + "=" );
			if ( cookies.length === 2 ){
				return cookies.pop().split( ";" ).shift();
			}
			return null;
		}
		else {
			// if value is a false boolean, we'll treat that as a delete
			if( value === false ){
				days = -1;
			}
			var expires = "";
			if ( days ) {
				var date = new Date();
				date.setTime( date.getTime() + ( days * 24 * 60 * 60 * 1000 ) );
				expires = "; expires="+date.toGMTString();
			}
			w.document.cookie = name + "=" + value + expires + "; path=/";
		}
	};
	// commonjs
	if( typeof module !== "undefined" ){
		module.exports = w.cookie;
	}
}( this ));
<commit_msg>Expose cookie on window, even when require'd via CommonJS and wrapped with Browserify<commit_after>/*! cookie function. get, set, or forget a cookie. [c]2014 @scottjehl, Filament Group, Inc. Licensed MIT */
(function(w){
	w.cookie = function( name, value, days ){
		// if value is undefined, get the cookie value
		if( value === undefined ){
			var cookiestring = "; " + w.document.cookie;
			var cookies = cookiestring.split( "; " + name + "=" );
			if ( cookies.length === 2 ){
				return cookies.pop().split( ";" ).shift();
			}
			return null;
		}
		else {
			// if value is a false boolean, we'll treat that as a delete
			if( value === false ){
				days = -1;
			}
			var expires = "";
			if ( days ) {
				var date = new Date();
				date.setTime( date.getTime() + ( days * 24 * 60 * 60 * 1000 ) );
				expires = "; expires="+date.toGMTString();
			}
			w.document.cookie = name + "=" + value + expires + "; path=/";
		}
	};
	// commonjs
	if( typeof module !== "undefined" ){
		module.exports = w.cookie;
	}
}( typeof global !== "undefined" ? global : this ));
<|endoftext|>


<commit_before>'use strict';

var _       = require('lodash'),
    Promise = require('es6-promise').Promise,
    mkdirp  = require('mkdirp'),
    fs      = require('fs'),
    path    = require('path'),
    rmdir   = require('rimraf'),
    crypto  = require('crypto');

module.exports = function (options) {

    // check args
    if (typeof options !== 'object') { throw new Error('options of type object expected'); }
    if (typeof options.cachePath !== 'string') { throw new Error('option cachePath of type string expected'); }

    // check for existing cache folder, if not found, create folder, then resolve
    function initCache(cachePath) {
        return new Promise(function (resolve, reject) {
            mkdirp(cachePath, function (err) {
                if (err) {
                    reject(err);
                } else {
                    resolve();
                }
            });
        });
    }

    function checkOptions(optExt) {
        if (optExt.salt && typeof optExt.salt !== 'string') { throw new Error('salt option of type string expected, got \'' + typeof optExt.salt + '\''); }
        if (optExt.cacheId && typeof optExt.cacheId !== 'string') { throw new Error('cacheId option of type string expected, got \'' + typeof optExt.cacheId + '\''); }
    }

    function getCacheFilePath(fn, args, opt) {
        /* jshint unused: vars */
        var circRefColl = [],
            fnJson = JSON.stringify(args, function (name, value) {
                if (typeof value === 'function') {
                    return value;
                }
                if (typeof value === 'object' && value !== null) {
                    if (circRefColl.indexOf(value) !== -1) {
                        // circular reference found, discard key
                        return;
                    }
                    // store value in collection
                    circRefColl.push(value);
                }
                return value;
            }),
            salt = opt.salt || '',
            hash = crypto.createHash('md5').update(String(fn), fnJson + salt).digest('hex');
        return path.join(options.cachePath, opt.cacheId, hash);
    }

    function memoizeFn(fn, opt) {
        if (opt && typeof opt !== 'object') { throw new Error('opt of type object expected, got \'' + typeof opt + '\''); }

        var optExt = _.extend({}, opt);

        if (typeof fn !== 'function') { throw new Error('fn of type function expected'); }
        checkOptions(optExt);

        optExt.cacheId = optExt.cacheId || './';

        function resolveWithMemFn() {
            return new Promise(function (resolve) {
                var memFn = function () {
                    var args = arguments;
                    return new Promise(function (resolve, reject) {
                        /* jshint unused: vars */
                        var filePath = getCacheFilePath(fn, args, optExt);

                        fs.readFile(filePath, { encoding: 'utf8' }, function (err, data) {
                            var result,
                                resultArr,
                                resultType,
                                resultStr;

                            function stringifyResult(r) {
                                if (r && typeof r === 'object') {
                                    return JSON.stringify(r);
                                } else {
                                    return String(r);
                                }
                            }

                            function parseResult(r, t) {
                                /* jshint maxcomplexity:6 */
                                if (r === 'null') {
                                    return null;
                                }
                                if (r === 'undefined') {
                                    return undefined;
                                }
                                if (t === 'object') {
                                    return JSON.parse(r);
                                }
                                if (t === 'number') {
                                    return Number(r);
                                }
                                return r;
                            }

                            if (err || optExt.force) {
                                delete optExt.force;
                                // result has not been cached yet or needs to be recached - cache and return it!
                                result = fn.apply(null, args);
                                if (result && result.then) {
                                    // result is a promise instance
                                    return result.then(function (retObj) {
                                            fs.writeFile(filePath, typeof retObj + '\n' + stringifyResult(retObj)); // async without callback!
                                            resolve(retObj);
                                        },
                                        function (err) {
                                            // if we have an exception we don't cache anything
                                            reject(err);
                                        });
                                } else {
                                    resultStr = stringifyResult(result);
                                    fs.writeFile(filePath, typeof result + '\n' + resultStr, function (err) {
                                        if (err) {
                                            reject(err);
                                        } else {
                                            resolve(result);
                                        }
                                    });
                                }
                            } else {
                                // result has already been cached - return it!
                                resultArr = data.split('\n');
                                resultType = _.first(resultArr);
                                resolve(parseResult(_.rest(resultArr).join('\n'), resultType));
                            }
                        });
                    });
                };
                resolve(memFn);
            });
        }

        return initCache(path.join(options.cachePath, optExt.cacheId)).then(resolveWithMemFn);
    }

    function invalidateCache(cacheId) {
        return new Promise(function (resolve, reject) {
            if (cacheId && typeof cacheId !== 'string') {
                reject(Error('cacheId option of type string expected, got \'' + typeof cacheId + '\''));
            } else {
                var cachPath = cacheId ? path.join(options.cachePath, cacheId) : options.cachePath;
                rmdir(cachPath, function (err) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve();
                    }
                });
            }
        });
    }

    var cache = initCache(options.cachePath);

    return {
        'fn': function (fn, opt) {
            return cache.then(function () {
                    return memoizeFn(fn, opt);
                }, function (err) {
                    throw err;
                });
            },
        'invalidate': invalidateCache
    };
};
<commit_msg>fixed hash calculation<commit_after>'use strict';

var _       = require('lodash'),
    Promise = require('es6-promise').Promise,
    mkdirp  = require('mkdirp'),
    fs      = require('fs'),
    path    = require('path'),
    rmdir   = require('rimraf'),
    crypto  = require('crypto');

module.exports = function (options) {

    // check args
    if (typeof options !== 'object') { throw new Error('options of type object expected'); }
    if (typeof options.cachePath !== 'string') { throw new Error('option cachePath of type string expected'); }

    // check for existing cache folder, if not found, create folder, then resolve
    function initCache(cachePath) {
        return new Promise(function (resolve, reject) {
            mkdirp(cachePath, function (err) {
                if (err) {
                    reject(err);
                } else {
                    resolve();
                }
            });
        });
    }

    function checkOptions(optExt) {
        if (optExt.salt && typeof optExt.salt !== 'string') { throw new Error('salt option of type string expected, got \'' + typeof optExt.salt + '\''); }
        if (optExt.cacheId && typeof optExt.cacheId !== 'string') { throw new Error('cacheId option of type string expected, got \'' + typeof optExt.cacheId + '\''); }
    }

    function getCacheFilePath(fn, args, opt) {
        /* jshint unused: vars */
        var circRefColl = [],
            fnJson = JSON.stringify(args, function (name, value) {
                if (typeof value === 'function') {
                    return value;
                }
                if (typeof value === 'object' && value !== null) {
                    if (circRefColl.indexOf(value) !== -1) {
                        // circular reference found, discard key
                        return;
                    }
                    // store value in collection
                    circRefColl.push(value);
                }
                return value;
            }),
            salt = opt.salt || '',
            hash = crypto.createHash('md5').update(String(fn) + fnJson + salt).digest('hex');
        return path.join(options.cachePath, opt.cacheId, hash);
    }

    function memoizeFn(fn, opt) {
        if (opt && typeof opt !== 'object') { throw new Error('opt of type object expected, got \'' + typeof opt + '\''); }

        var optExt = _.extend({}, opt);

        if (typeof fn !== 'function') { throw new Error('fn of type function expected'); }
        checkOptions(optExt);

        optExt.cacheId = optExt.cacheId || './';

        function resolveWithMemFn() {
            return new Promise(function (resolve) {
                var memFn = function () {
                    var args = arguments;
                    return new Promise(function (resolve, reject) {
                        /* jshint unused: vars */
                        var filePath = getCacheFilePath(fn, args, optExt);

                        fs.readFile(filePath, { encoding: 'utf8' }, function (err, data) {
                            var result,
                                resultArr,
                                resultType,
                                resultStr;

                            function stringifyResult(r) {
                                if (r && typeof r === 'object') {
                                    return JSON.stringify(r);
                                } else {
                                    return String(r);
                                }
                            }

                            function parseResult(r, t) {
                                /* jshint maxcomplexity:6 */
                                if (r === 'null') {
                                    return null;
                                }
                                if (r === 'undefined') {
                                    return undefined;
                                }
                                if (t === 'object') {
                                    return JSON.parse(r);
                                }
                                if (t === 'number') {
                                    return Number(r);
                                }
                                return r;
                            }

                            if (err || optExt.force) {
                                delete optExt.force;
                                // result has not been cached yet or needs to be recached - cache and return it!
                                result = fn.apply(null, args);
                                if (result && result.then) {
                                    // result is a promise instance
                                    return result.then(function (retObj) {
                                            fs.writeFile(filePath, typeof retObj + '\n' + stringifyResult(retObj)); // async without callback!
                                            resolve(retObj);
                                        },
                                        function (err) {
                                            // if we have an exception we don't cache anything
                                            reject(err);
                                        });
                                } else {
                                    resultStr = stringifyResult(result);
                                    fs.writeFile(filePath, typeof result + '\n' + resultStr, function (err) {
                                        if (err) {
                                            reject(err);
                                        } else {
                                            resolve(result);
                                        }
                                    });
                                }
                            } else {
                                // result has already been cached - return it!
                                resultArr = data.split('\n');
                                resultType = _.first(resultArr);
                                resolve(parseResult(_.rest(resultArr).join('\n'), resultType));
                            }
                        });
                    });
                };
                resolve(memFn);
            });
        }

        return initCache(path.join(options.cachePath, optExt.cacheId)).then(resolveWithMemFn);
    }

    function invalidateCache(cacheId) {
        return new Promise(function (resolve, reject) {
            if (cacheId && typeof cacheId !== 'string') {
                reject(Error('cacheId option of type string expected, got \'' + typeof cacheId + '\''));
            } else {
                var cachPath = cacheId ? path.join(options.cachePath, cacheId) : options.cachePath;
                rmdir(cachPath, function (err) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve();
                    }
                });
            }
        });
    }

    var cache = initCache(options.cachePath);

    return {
        'fn': function (fn, opt) {
            return cache.then(function () {
                    return memoizeFn(fn, opt);
                }, function (err) {
                    throw err;
                });
            },
        'invalidate': invalidateCache
    };
};
<|endoftext|>


<commit_before>module.exports = runWaterfall

function runWaterfall (tasks, cb) {
  var current = 0
  var isSync = true

  function done (err, args) {
    function end () {
      args = args ? [].concat(err, args) : [ err ]
      if (cb) cb.apply(undefined, args)
    }
    if (isSync) process.nextTick(end)
    else end()
  }

  function each (err) {
    var args = Array.prototype.slice.call(arguments, 1)
    if (++current >= tasks.length || err) {
      done(err, args)
    } else {
      tasks[current].apply(undefined, [].concat(args, each))
    }
  }

  if (tasks.length) {
    tasks[0](each)
  } else {
    done(null)
  }

  isSync = false
}
<commit_msg>standard<commit_after>module.exports = runWaterfall

function runWaterfall (tasks, cb) {
  var current = 0
  var isSync = true

  function done (err, args) {
    function end () {
      args = args ? [].concat(err, args) : [err]
      if (cb) cb.apply(undefined, args)
    }
    if (isSync) process.nextTick(end)
    else end()
  }

  function each (err) {
    var args = Array.prototype.slice.call(arguments, 1)
    if (++current >= tasks.length || err) {
      done(err, args)
    } else {
      tasks[current].apply(undefined, [].concat(args, each))
    }
  }

  if (tasks.length) {
    tasks[0](each)
  } else {
    done(null)
  }

  isSync = false
}
<|endoftext|>


<commit_before>
var bind = require('event-component').bind
var clean = require('function-source')
var Progress = require('progress-svg')
var escape = require('escape-html')
var classes = require('classes')
var domify = require('domify')
var text = require('text')
var trim = require('trim')

/**
 * Save timer references because some testing tools
 * mess with them
 */

var global = function(){return this}()
var Date = global.Date

/**
 * Expose `HTML`.
 */

exports = module.exports = HTML

/**
 * Stats template.
 */

var statsTemplate = '
'
  + '

'
  + '
passes: 0
'
  + '
failures: 0
'
  + '
duration: 0s
'
  + '
'

/**
 * Initialize a new `HTML` reporter.
 *
 * @param {Hydro} hydro
 * @param {Element} root
 * @api public
 */

function HTML(hydro, root){}

HTML.prototype.use = function(hydro, root){
  var self = this
  var stats = track(hydro)
  var stat = domify(statsTemplate)
  var items = stat.getElementsByTagName('li')
  var passes = items[1].getElementsByTagName('em')[0]
  var passesLink = items[1].getElementsByTagName('a')[0]
  var failures = items[2].getElementsByTagName('em')[0]
  var failuresLink = items[2].getElementsByTagName('a')[0]
  var duration = items[3].getElementsByTagName('em')[0]
  var progress = new Progress().size(40).text('%d%')
  var report = domify('

')
  var stack = [report]

  root = root
    || document.getElementById('hydro')
    || document.body.appendChild(domify('

'))

  items[0].appendChild(progress.el)
  root.appendChild(stat)
  root.appendChild(report)

  // pass toggle
  bind(passesLink, 'click', function(){
    unhide()
    classes.remove('fail', report)
    classes.toggle('pass', report)
    classes.has('pass', report) && hideSuitesWithout('test pass')
  })

  // failure toggle
  bind(failuresLink, 'click', function(){
    unhide()
    classes.remove('pass', report)
    classes.toggle('fail', report)
    classes.has('fail', report) && hideSuitesWithout('test fail')
  })

  hydro.on('pre:suite', function(suite){
    var el = fragment(
      '

%e

',
      grep(suite),
      suite.title)

    stack[0].appendChild(el)
    stack.unshift(document.createElement('ul'))
    el.appendChild(stack[0])
  })

  hydro.on('post:suite', function(suite){
    stack.shift()
  })

  hydro.on('post:test', function(test){
    progress.update(stats.percent)

    // update stats
    text(passes, stats.passes)
    text(failures, stats.failures)
    text(duration, (stats.elapsed / 1000).toFixed(2))

    // test
    switch (test.status) {
      case 'passed':
        var el = fragment(
          '

%e%sms

',
          test.speed || 'fast',
          test.title,
          test.time)
        break
      case 'pending':
      case 'skipped':
        var el = fragment(
          '

%e

',
          test.title)
        break
      case 'failed':
        var str = test.error.stack || test.error.toString()

        // FF / Opera do not add the message
        if (!~str.indexOf(test.error.message)) {
          str = test.error.message + '\n' + str
        }

        // Safari doesn't give you a stack. Let's at least provide a source line.
        if (!test.error.stack && test.error.sourceURL && test.error.line !== undefined) {
          str += "\n(" + test.error.sourceURL + ":" + test.error.line + ")"
        }

        var el = fragment(
          '

%e

%e

',
          test.title,
          str)
    }

    // hide code
    if (test.status != 'pending') {
      var h2 = el.getElementsByTagName('h2')[0]
      var pre = fragment('
%s
', highlight(trim(clean(test.fn))))
      el.appendChild(pre)
      pre.style.display = 'none'

      bind(h2, 'dblclick', function(){
        location.search = grep(test)
      })

      bind(h2, 'click', function(){
        pre.style.display = 'none' == pre.style.display
          ? 'block'
          : 'none'
      })
    }

    stack[0].appendChild(el)
  })
}

function track(hydro){
  var stats = {
    pending: 0,
    tests: 0,
    passes: 0,
    failures: 0
  }

  hydro.on('post:test', function(test){
    stats.percent = (++stats.tests / stats.total) * 100 | 0
    stats.elapsed = new Date - stats.start
    switch (test.status) {
      case 'pending':
      case 'skipped':
        stats.pending++
        break
      case 'passed':
        stats.passes++
        break
      case 'failed':
        stats.failures++
    }
  })

  hydro.on('pre:all', function() {
    stats.start = new Date
    stats.total = hydro.tests().length
  })

  hydro.on('post:all', function(){
    stats.end = new Date
    stats.duration = Number(stats.end) - Number(stats.start)
  })

  return stats
}

/**
 * create a querystring to grep for `obj`
 *
 * @param {Suite|Test} obj
 * @api private
 */

function grep(obj){
  return '?focus=' + encodeURIComponent(obj.title) // TODO: fullTitle
}

/**
 * Return a DOM fragment from `html`.
 */

function fragment(html) {
  var args = arguments
  var i = 1
  return domify(html.replace(/%([se])/g, function(_, type){
    switch (type) {
      case 's': return String(args[i++])
      case 'e': return escape(args[i++])
    }
  }))
}

/**
 * Check for suites that do not have elements
 * with `classname`, and hide them.
 */

function hideSuitesWithout(classname) {
  var suites = document.getElementsByClassName('suite')
  for (var i = 0; i < suites.length; i++) {
    var suite = suites[i]
    var els = suite.getElementsByClassName(classname)
    if (!els.length) classes.add('hidden', suite)
  }
}

/**
 * Unhide .hidden suites.
 */

function unhide() {
  var els = document.getElementsByClassName('suite hidden')
  for (var i = 0; i < els.length; ++i) classes.remove('hidden', els[i])
}

/**
 * Highlight the given string of `js`.
 *
 * @param {String} js
 * @return {String}
 * @api private
 */

function highlight(js) {
  return js
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/\/\/(.*)/gm, '//$1')
    .replace(/('.*?')/gm, '$1')
    .replace(/(\d+\.\d+)/gm, '$1')
    .replace(/(\d+)/gm, '$1')
    .replace(/\bnew *(\w+)/gm, 'new $1')
    .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1')
}
<commit_msg>fix: unhide deep<commit_after>
var bind = require('event-component').bind
var clean = require('function-source')
var Progress = require('progress-svg')
var escape = require('escape-html')
var classes = require('classes')
var domify = require('domify')
var text = require('text')
var trim = require('trim')

/**
 * Save timer references because some testing tools
 * mess with them
 */

var global = function(){return this}()
var Date = global.Date

/**
 * Expose `HTML`.
 */

exports = module.exports = HTML

/**
 * Stats template.
 */

var statsTemplate = '
'
  + '

'
  + '
passes: 0
'
  + '
failures: 0
'
  + '
duration: 0s
'
  + '
'

/**
 * Initialize a new `HTML` reporter.
 *
 * @param {Hydro} hydro
 * @param {Element} root
 * @api public
 */

function HTML(){}

HTML.prototype.use = function(hydro, root){
  var stats = track(hydro)
  var stat = domify(statsTemplate)
  var items = stat.getElementsByTagName('li')
  var passes = items[1].getElementsByTagName('em')[0]
  var passesLink = items[1].getElementsByTagName('a')[0]
  var failures = items[2].getElementsByTagName('em')[0]
  var failuresLink = items[2].getElementsByTagName('a')[0]
  var duration = items[3].getElementsByTagName('em')[0]
  var progress = new Progress().size(40).text('%d%')
  var report = domify('

')
  var stack = [report]

  root = root
    || document.getElementById('hydro')
    || document.body.appendChild(domify('

'))

  items[0].appendChild(progress.el)
  root.appendChild(stat)
  root.appendChild(report)

  // pass toggle
  bind(passesLink, 'click', function(){
    unhide()
    classes.remove('fail', report)
    classes.toggle('pass', report)
    classes.has('pass', report) && hideSuitesWithout('test pass')
  })

  // failure toggle
  bind(failuresLink, 'click', function(){
    unhide()
    classes.remove('pass', report)
    classes.toggle('fail', report)
    classes.has('fail', report) && hideSuitesWithout('test fail')
  })

  hydro.on('pre:suite', function(suite){
    var el = fragment(
      '

%e

',
      grep(suite),
      suite.title)

    stack[0].appendChild(el)
    stack.unshift(document.createElement('ul'))
    el.appendChild(stack[0])
  })

  hydro.on('post:suite', function(){
    stack.shift()
  })

  hydro.on('post:test', function(test){
    progress.update(stats.percent)

    // update stats
    text(passes, stats.passes)
    text(failures, stats.failures)
    text(duration, (stats.elapsed / 1000).toFixed(2))

    // test
    switch (test.status) {
      case 'passed':
        var el = fragment(
          '

%e%sms

',
          test.speed || 'fast',
          test.title,
          test.time)
        break
      case 'pending':
      case 'skipped':
        var el = fragment(
          '

%e

',
          test.title)
        break
      case 'failed':
        var str = test.error.stack || test.error.toString()

        // FF / Opera do not add the message
        if (!~str.indexOf(test.error.message)) {
          str = test.error.message + '\n' + str
        }

        // Safari doesn't give you a stack. Let's at least provide a source line.
        if (!test.error.stack && test.error.sourceURL && test.error.line !== undefined) {
          str += "\n(" + test.error.sourceURL + ":" + test.error.line + ")"
        }

        var el = fragment(
          '

%e

%e

',
          test.title,
          str)
    }

    // hide code
    if (test.status != 'pending') {
      var h2 = el.getElementsByTagName('h2')[0]
      var pre = fragment('
%s
', highlight(trim(clean(test.fn))))
      el.appendChild(pre)
      pre.style.display = 'none'

      bind(h2, 'dblclick', function(){
        location.search = grep(test)
      })

      bind(h2, 'click', function(){
        pre.style.display = 'none' == pre.style.display
          ? 'block'
          : 'none'
      })
    }

    stack[0].appendChild(el)
  })
}

function track(hydro){
  var stats = {
    pending: 0,
    tests: 0,
    passes: 0,
    failures: 0
  }

  hydro.on('post:test', function(test){
    stats.percent = (++stats.tests / stats.total) * 100 | 0
    stats.elapsed = new Date - stats.start
    switch (test.status) {
      case 'pending':
      case 'skipped':
        stats.pending++
        break
      case 'passed':
        stats.passes++
        break
      case 'failed':
        stats.failures++
    }
  })

  hydro.on('pre:all', function() {
    stats.start = new Date
    stats.total = hydro.tests().length
  })

  hydro.on('post:all', function(){
    stats.end = new Date
    stats.duration = Number(stats.end) - Number(stats.start)
  })

  return stats
}

/**
 * create a querystring to grep for `obj`
 *
 * @param {Suite|Test} obj
 * @api private
 */

function grep(obj){
  return '?focus=' + encodeURIComponent(obj.title) // TODO: fullTitle
}

/**
 * Return a DOM fragment from `html`.
 */

function fragment(html) {
  var args = arguments
  var i = 1
  return domify(html.replace(/%([se])/g, function(_, type){
    switch (type) {
      case 's': return String(args[i++])
      case 'e': return escape(args[i++])
    }
  }))
}

/**
 * Check for suites that do not have elements
 * with `classname`, and hide them.
 */

function hideSuitesWithout(classname) {
  var suites = document.getElementsByClassName('suite')
  for (var i = 0; i < suites.length; i++) {
    var suite = suites[i]
    var els = suite.getElementsByClassName(classname)
    if (!els.length) classes.add('hidden', suite)
  }
}

/**
 * Unhide .hidden suites.
 */

function unhide() {
  var els = document.getElementsByClassName('suite')
  for (var i = 0; i < els.length; ++i) classes.remove('hidden', els[i])
}

/**
 * Highlight the given string of `js`.
 *
 * @param {String} js
 * @return {String}
 * @api private
 */

function highlight(js) {
  return js
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/\/\/(.*)/gm, '//$1')
    .replace(/('.*?')/gm, '$1')
    .replace(/(\d+\.\d+)/gm, '$1')
    .replace(/(\d+)/gm, '$1')
    .replace(/\bnew *(\w+)/gm, 'new $1')
    .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1')
}
<|endoftext|>


<commit_before>'use strict'
import React, { Component } from 'react'
import {
  NetInfo,
  StyleSheet,
  Text,
  View
} from 'react-native'

const RequiresConnection = (WhenOnline, WhenOffline) => class RequiresConnection extends Component {
  constructor (props) {
    super(props)
    this.state = { isConnected: true }
  }

  componentDidMount () {
    let connect = (reach) => this.setState({ isConnected: reach !== 'none' })

    NetInfo.fetch().done((reach) => {
      connect(reach)
      NetInfo.addEventListener('change', connect)
    })
  }

  render () {
    if (this.state.isConnected) {
      return <WhenOnline {...this.props} />
    } else if (typeof WhenOffline === 'function') {
      return <WhenOffline {...this.props} />
    } else {
      const message = WhenOffline || "We're sorry, there seems to be a problem with your internet connection. The application will resume as soon as it is able to reconnect to the internet."

      return <View style={styles.container}>
        <Text style={styles.connectionProblemMessage}>
          {message}
        </Text>
      </View>
    }
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },

  connectionProblemMessage: {
    padding: 24,
    textAlign: 'center'
  }
})

module.exports = RequiresConnection
<commit_msg>Replaced Deprecated Methods<commit_after>'use strict'
import React, { Component } from 'react'
import {
  NetInfo,
  StyleSheet,
  Text,
  View
} from 'react-native'

const RequiresConnection = (WhenOnline, WhenOffline) => class RequiresConnection extends Component {
  constructor (props) {
    super(props)
    this.state = { isConnected: true }
  }

  componentDidMount () {
    let connect = ({ type }) => this.setState({ isConnected: type !== 'none' })

    NetInfo.getConnectionInfo().done((reach) => {
      connect(reach)
      NetInfo.addEventListener('connectionChange', connect)
    })
  }

  render () {
    if (this.state.isConnected) {
      return <WhenOnline {...this.props} />
    } else if (typeof WhenOffline === 'function') {
      return <WhenOffline {...this.props} />
    } else {
      const message = WhenOffline || "We're sorry, there seems to be a problem with your internet connection. The application will resume as soon as it is able to reconnect to the internet."

      return <View style={styles.container}>
        <Text style={styles.connectionProblemMessage}>
          {message}
        </Text>
      </View>
    }
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },

  connectionProblemMessage: {
    padding: 24,
    textAlign: 'center'
  }
})

module.exports = RequiresConnection
<|endoftext|>


<commit_before>/**
 *
 */
var gapFillURL = "https://gadfly-api.herokuapp.com/gadfly/api/v1.0/gap_fill_questions"
var mcqURL = "https://gadfly-api.herokuapp.com/gadfly/api/v1.0/multiple_choice_questions"
var http = require('http');
var https = require('https');
var request = require('request');
var d = require('domain').create()

replies = {
    idk: new RegExp(/^(idk|not sure|i don\'t know|don\'t know')/i),
    stop: new RegExp(/^(stop|Stop|STOP)/i),
};

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */
function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function(err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am a bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

/**
 * Configure the persistence options
 */
var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_slack_bot_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

/**
 * Are being run as an app or a custom integration? The initialization will differ, depending
 */
if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment.')
    console.log('If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment')
    process.exit(1);
}

/*
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function(bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function(bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/*
 * Core bot logic
 */
controller.on('bot_channel_join', function(bot, message) {
    bot.reply(message, "I'm here!")
});

controller.hears(['what\'s your purpose', 'why are you here', 'what do you do'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    bot.reply(message, 'Hi there! I\'m a bot. If you paste a news article URL in this channel, I can start asking you questions about it.');
});

controller.hears(['hey', 'hello', 'hi', 'greetings', 'sup', 'yo'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
     bot.reply(message, 'Hi there! I\'m a bot. If you paste a news article URL in this channel, I can start asking you questions about it.');
 });

controller.hears(['http(.*)'], ['ambient', 'direct_mention', 'mention', 'direct_message'], function(bot, message) {
    url = message.text.replace("<", "").replace(">", "")
    bot.startConversation(message, function(err, convo) {
        convo.ask('I see you posted a link. Would you like to be quizzed on it?', [
        {
            pattern: bot.utterances.no,
            callback: function(response, convo) {
                convo.say('Perhaps later.')
                convo.next();
            }
        },
        {
            pattern: bot.utterances.yes,
            callback: function(response, convo) {
                convo.say('Cool, you said: ' + response.text);
                callGadflyGapFill(url, convo, bot)
            }
        },
        {
            default: true,
            callback: function(response, convo) {
                convo.repeat();
                convo.next();
            }
        }
        ]);
    })
});

controller.hears(['more', 'next', 'bring it on'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    bot.startConversation(message, function(err, convo) {
        convo.ask('Ready?', [
        {
            pattern: bot.utterances.yes,
            callback: function(response, convo) {
                convo.say('Alright!')
                callGadflyGapFill(url, convo, bot)
            }
        },
        {
            pattern: bot.utterances.no,
            callback: function(response,convo) {
                convo.say('Okay, ready when you are.')
                convo.next();
            }
        }
        ]);
    });
});

// call the gadfly web api to get gap fill questions from the user input article.
// randomize the question to be asked using getRandomInt & push it to the conversation
function callGadflyGapFill (url, convo, bot) {
    var apiURL = gapFillURL + "?url=" + url;
    d.on('error', function(err) {
        convo.say('Uh oh! Hang on, something went wrong behind the scenes.')
        convo.say('I\'m just a bot so I don\'t know what went wrong. But I\'m pretty sure people will fix it.')
        convo.next()
        console.log(err.stack)
    });
    d.run(function() {
        request(apiURL, function(e, r, b) {
        if (e) { console.log(e); callback(true); return; }
        obj = JSON.parse(b)
        questions = obj['questions']
        index = getRandomInt(obj['num_questions'])
        q = questions[index]
        convo.next();
        convo.ask(q.question_text, [
        {
            pattern: q.correct_answer,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('That is correct! :100: Say more and mention me to get more questions.');
                bot.say({
                    text: 'Click on the :thumbsup: if you liked this question or the :thumbsdown: if you think this question needs improvement.',
                    channel: currentChannel
                });
                if (currentChannel[0] == 'C') {
                    bot.api.channels.history({
                    channel: currentChannel,
                    count: 1,
                    inclusive: 1
                }, function (err, body) {
                    if (err) {
                        console.log(err);
                    }
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsup'
                    });
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsdown'
                    });
                });
                }
                if (currentChannel[0] == 'D') {
                    bot.api.im.history({
                    channel: currentChannel,
                    count: 1,
                    inclusive: 1
                }, function (err, body) {
                    if (err) {
                        console.log(err);
                    }
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsup'
                    });
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsdown'
                    });
                });
                }
                if (currentChannel[0] == 'G') {
                    bot.api.groups.history({
                    channel: currentChannel,
                    count: 1,
                    inclusive: 1
                }, function (err, body) {
                    if (err) {
                        console.log(err);
                    }
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsup'
                    });
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsdown'
                    });
                });
                }
                convo.next();
            }
        },
        {
            pattern: replies.idk,
            callback: function(response, convo) {
                convo.say('That\'s okay! If you want, you can read the article here' + ' ' + url);
                convo.next();
            }
        },
        {
            pattern: replies.stop,
            callback: function(response, convo) {
                convo.say('I heard you loud and clear boss.');
                convo.next();
            }
        },
        {
            default: true,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('Whoops! That is incorrect. :frowning:');
                bot.api.groups.history({
                    channel: currentChannel,
                    count: 1,
                    inclusive: 1
                }, function (err, body) {
                    if (err) {
                        console.log(err);
                    }
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsup'
                    });
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsdown'
                    });
                });
                convo.repeat();
                convo.next();
            }
        }
        ]);
    });
    });
};

// call the gadfly web api to get multiple choice questions from the user input article.
// randomize the question to be asked using getRandomInt & push it to the conversation
function callGadflyMCQ(url, convo, bot) {
    var apiURL = mcqURL + "?url=" + url;
    d.on('error', function(err) {
        convo.say('Uh oh! Hang on, something went wrong behind the scenes.')
        convo.say('I\'m just a bot so I don\'t know what went wrong. But I\'m pretty sure people will fix it.')
        convo.next()
        console.log(err.stack)
    });
    d.run(function() {
        request(apiURL, function(e, r, b) {
        if (e) { console.log(e); callback(true); return; }
        obj = JSON.parse(b)
        questions = obj['questions']
        index = getRandomInt(obj['num_questions'])
        q = questions[index]
        choices = q.answer_choices
        convo.next();
        convo.ask(q.question + '\n\n' + ':one:\t' + choices[0] + '\n' + ':two:\t' + choices[1] + '\n' + ':three:\t' + choices[2] + '\n' + ':four:\t' + choices[3], [
        {
            pattern: q.answer,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('That is correct! :100: Say more and mention me to get more questions.');
                bot.say({
                    text: 'Click on the :thumbsup: if you liked this question or the :thumbsdown: if you think this question needs improvement.',
                    channel: currentChannel
                });
                bot.api.groups.history({
                    channel: currentChannel,
                    count: 1,
                    inclusive: 1
                }, function (err, body) {
                    if (err) {
                        console.log(err);
                    }
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsup'
                    });
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsdown'
                    });
                });
                convo.next();
            }
        },
        {
            pattern: replies.idk,
            callback: function(response, convo) {
                convo.say('That\'s okay! If you want, you can read the article here' + ' ' + url);
                convo.next();
            }
        },
        {
            pattern: replies.stop,
            callback: function(response, convo) {
                convo.say('I heard you loud and clear boss.');
                convo.next();
            }
        },
        {
            default: true,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('Whoops! That is incorrect. :frowning:');
                bot.api.groups.history({
                    channel: currentChannel,
                    count: 1,
                    inclusive: 1
                }, function (err, body) {
                    if (err) {
                        console.log(err);
                    }
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsup'
                    });
                    bot.api.reactions.add({
                        timestamp: body.messages[0].ts,
                        channel: currentChannel,
                        name: 'thumbsdown'
                    });
                });
                convo.repeat();
                convo.next();
            }
        }
        ]);
    });
    });
};

// get random integers between 0-12
function getRandomInt(range) {
    return Math.floor(Math.random() * range);
}

// stop
controller.hears(['stop', 'Stop', 'STOP', 'stahp', 'STAHP'],['direct_message','mention'], function(bot, message) {
    return bot.reply(message, 'I heard you loud and clear boss.');
});

// for personality
controller.hears(['who are you', 'are you a bot', 'what are you'], ['direct_message','mention','direct_mention'], function(bot, message) {
    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'robot_face',
    }, function(err) {
        if (err) {
            console.log(err)
        }
        bot.reply(message, 'I\'m just a poor bot, I need no sympathy, Because I\'m easy come, easy go.');
    });
});
controller.hears('open the (.*) doors',['direct_message','mention'], function(bot, message) {
  var doorType = message.match[1]; //match[1] is the (.*) group. match[0] is the entire group (open the (.*) doors).
  if (doorType === 'pod bay') {
    return bot.reply(message, 'I\'m sorry, Dave. I\'m afraid I can\'t do that.');
  }
  return bot.reply(message, 'Okay');
});

//monitor reactions
controller.on('reaction_added', function(bot, message) {
    console.log(message.reaction);
    console.log(message.user);
})

// all un-handled direct mentions get a reaction and a pat response!
controller.on('direct_message, mention, direct_mention', function(bot, message) {
    bot.reply(message, 'Hi there! I\'m a bot. If you paste a news article URL here, I can ask you questions about it.');
});<commit_msg>conditional slack api calls based on where the bot is<commit_after>/**
 *
 */
var gapFillURL = "https://gadfly-api.herokuapp.com/gadfly/api/v1.0/gap_fill_questions"
var mcqURL = "https://gadfly-api.herokuapp.com/gadfly/api/v1.0/multiple_choice_questions"
var http = require('http');
var https = require('https');
var request = require('request');
var d = require('domain').create()

replies = {
    idk: new RegExp(/^(idk|not sure|i don\'t know|don\'t know')/i),
    stop: new RegExp(/^(stop|Stop|STOP)/i),
};

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */
function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function(err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am a bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

/**
 * Configure the persistence options
 */
var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_slack_bot_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

/**
 * Are being run as an app or a custom integration? The initialization will differ, depending
 */
if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment.')
    console.log('If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment')
    process.exit(1);
}

/*
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function(bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function(bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/*
 * Core bot logic
 */
controller.on('bot_channel_join', function(bot, message) {
    bot.reply(message, "I'm here!")
});

controller.hears(['what\'s your purpose', 'why are you here', 'what do you do'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    bot.reply(message, 'Hi there! I\'m a bot. If you paste a news article URL in this channel, I can start asking you questions about it.');
});

controller.hears(['hey', 'hello', 'hi', 'greetings', 'sup', 'yo'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
     bot.reply(message, 'Hi there! I\'m a bot. If you paste a news article URL in this channel, I can start asking you questions about it.');
 });

controller.hears(['http(.*)'], ['ambient', 'direct_mention', 'mention', 'direct_message'], function(bot, message) {
    url = message.text.replace("<", "").replace(">", "")
    bot.startConversation(message, function(err, convo) {
        convo.ask('I see you posted a link. Would you like to be quizzed on it?', [
        {
            pattern: bot.utterances.no,
            callback: function(response, convo) {
                convo.say('Perhaps later.')
                convo.next();
            }
        },
        {
            pattern: bot.utterances.yes,
            callback: function(response, convo) {
                convo.say('Cool, you said: ' + response.text);
                callGadflyGapFill(url, convo, bot)
            }
        },
        {
            default: true,
            callback: function(response, convo) {
                convo.repeat();
                convo.next();
            }
        }
        ]);
    })
});

controller.hears(['more', 'next', 'bring it on'], ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    bot.startConversation(message, function(err, convo) {
        convo.ask('Ready?', [
        {
            pattern: bot.utterances.yes,
            callback: function(response, convo) {
                convo.say('Alright!')
                callGadflyGapFill(url, convo, bot)
            }
        },
        {
            pattern: bot.utterances.no,
            callback: function(response,convo) {
                convo.say('Okay, ready when you are.')
                convo.next();
            }
        }
        ]);
    });
});

// call the gadfly web api to get gap fill questions from the user input article.
// randomize the question to be asked using getRandomInt & push it to the conversation
function callGadflyGapFill (url, convo, bot) {
    var apiURL = gapFillURL + "?url=" + url;
    d.on('error', function(err) {
        convo.say('Uh oh! Hang on, something went wrong behind the scenes.')
        convo.say('I\'m just a bot so I don\'t know what went wrong. But I\'m pretty sure people will fix it.')
        convo.next()
        console.log(err.stack)
    });
    d.run(function() {
        request(apiURL, function(e, r, b) {
        if (e) { console.log(e); callback(true); return; }
        obj = JSON.parse(b)
        questions = obj['questions']
        index = getRandomInt(obj['num_questions'])
        q = questions[index]
        convo.next();
        convo.ask(q.question_text, [
        {
            pattern: q.correct_answer,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('That is correct! :100: Say more and mention me to get more questions.');
                bot.say({
                    text: 'Click on the :thumbsup: if you liked this question or the :thumbsdown: if you think this question needs improvement.',
                    channel: currentChannel
                });
                if (currentChannel[0] == 'G') {
                    botIsInAGroup(bot, currentChannel);
                }
                if (currentChannel[0] == 'D') {
                    botIsInADM(bot, currentChannel);
                }
                if (currentChannel[0] == 'C') {
                    botIsInAChannel(bot, currentChannel);
                }
                convo.next();
            }
        },
        {
            pattern: replies.idk,
            callback: function(response, convo) {
                convo.say('That\'s okay! If you want, you can read the article here' + ' ' + url);
                convo.next();
            }
        },
        {
            pattern: replies.stop,
            callback: function(response, convo) {
                convo.say('I heard you loud and clear boss.');
                convo.next();
            }
        },
        {
            default: true,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('Whoops! That is incorrect. :frowning:');
                if (currentChannel[0] == 'G') {
                    botIsInAGroup(bot, currentChannel);
                }
                if (currentChannel[0] == 'D') {
                    botIsInADM(bot, currentChannel);
                }
                if (currentChannel[0] == 'C') {
                    botIsInAChannel(bot, currentChannel);
                }
                convo.repeat();
                convo.next();
            }
        }
        ]);
    });
    });
};

// call the gadfly web api to get multiple choice questions from the user input article.
// randomize the question to be asked using getRandomInt & push it to the conversation
function callGadflyMCQ(url, convo, bot) {
    var apiURL = mcqURL + "?url=" + url;
    d.on('error', function(err) {
        convo.say('Uh oh! Hang on, something went wrong behind the scenes.')
        convo.say('I\'m just a bot so I don\'t know what went wrong. But I\'m pretty sure people will fix it.')
        convo.next()
        console.log(err.stack)
    });
    d.run(function() {
        request(apiURL, function(e, r, b) {
        if (e) { console.log(e); callback(true); return; }
        obj = JSON.parse(b)
        questions = obj['questions']
        index = getRandomInt(obj['num_questions'])
        q = questions[index]
        choices = q.answer_choices
        convo.next();
        convo.ask(q.question + '\n\n' + ':one:\t' + choices[0] + '\n' + ':two:\t' + choices[1] + '\n' + ':three:\t' + choices[2] + '\n' + ':four:\t' + choices[3], [
        {
            pattern: q.answer,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('That is correct! :100: Say more and mention me to get more questions.');
                bot.say({
                    text: 'Click on the :thumbsup: if you liked this question or the :thumbsdown: if you think this question needs improvement.',
                    channel: currentChannel
                });
                if (currentChannel[0] == 'G') {
                    botIsInAGroup(bot, currentChannel);
                }
                if (currentChannel[0] == 'D') {
                    botIsInADM(bot, currentChannel);
                }
                if (currentChannel[0] == 'C') {
                    botIsInAChannel(bot, currentChannel);
                }
                convo.next();
            }
        },
        {
            pattern: replies.idk,
            callback: function(response, convo) {
                convo.say('That\'s okay! If you want, you can read the article here' + ' ' + url);
                convo.next();
            }
        },
        {
            pattern: replies.stop,
            callback: function(response, convo) {
                convo.say('I heard you loud and clear boss.');
                convo.next();
            }
        },
        {
            default: true,
            callback: function(response, convo) {
                msg = {}
                currentChannel = convo.source_message.channel;
                convo.say('Whoops! That is incorrect. :frowning:');
                if (currentChannel[0] == 'G') {
                    botIsInAGroup(bot, currentChannel);
                }
                if (currentChannel[0] == 'D') {
                    botIsInADM(bot, currentChannel);
                }
                if (currentChannel[0] == 'C') {
                    botIsInAChannel(bot, currentChannel);
                }
                convo.repeat();
                convo.next();
            }
        }
        ]);
    });
    });
};

// if the bot is in a group
function botIsInAGroup(bot, currentChannel) {
    bot.api.groups.history({
        channel: currentChannel,
        count: 1,
        inclusive: 1
    }, function (err, body) {
        if (err) {
            console.log(err);
        }
        bot.api.reactions.add({
            timestamp: body.messages[0].ts,
            channel: currentChannel,
            name: 'thumbsup'
        });
        bot.api.reactions.add({
            timestamp: body.messages[0].ts,
            channel: currentChannel,
            name: 'thumbsdown'
        });
    });
}

// if the bot is in a dm
function botIsInADM(bot, currentChannel) {
    bot.api.im.history({
        channel: currentChannel,
        count: 1,
        inclusive: 1
    }, function (err, body) {
        if (err) {
            console.log(err);
        }
        bot.api.reactions.add({
            timestamp: body.messages[0].ts,
            channel: currentChannel,
            name: 'thumbsup'
        });
        bot.api.reactions.add({
            timestamp: body.messages[0].ts,
            channel: currentChannel,
            name: 'thumbsdown'
        });
    });
}

// if the bot is in a channel
function botIsInAChannel(bot, currentChannel) {
    bot.api.channels.history({
        channel: currentChannel,
        count: 1,
        inclusive: 1
    }, function (err, body) {
        if (err) {
            console.log(err);
        }
        bot.api.reactions.add({
            timestamp: body.messages[0].ts,
            channel: currentChannel,
            name: 'thumbsup'
        });
        bot.api.reactions.add({
            timestamp: body.messages[0].ts,
            channel: currentChannel,
            name: 'thumbsdown'
        });
    });
}

// get random integers between 0-12
function getRandomInt(range) {
    return Math.floor(Math.random() * range);
}

// stop
controller.hears(['stop', 'Stop', 'STOP', 'stahp', 'STAHP'],['direct_message','mention'], function(bot, message) {
    return bot.reply(message, 'I heard you loud and clear boss.');
});

// for personality
controller.hears(['who are you', 'are you a bot', 'what are you'], ['direct_message','mention','direct_mention'], function(bot, message) {
    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'robot_face',
    }, function(err) {
        if (err) {
            console.log(err)
        }
        bot.reply(message, 'I\'m just a poor bot, I need no sympathy, Because I\'m easy come, easy go.');
    });
});
controller.hears('open the (.*) doors',['direct_message','mention'], function(bot, message) {
  var doorType = message.match[1]; //match[1] is the (.*) group. match[0] is the entire group (open the (.*) doors).
  if (doorType === 'pod bay') {
    return bot.reply(message, 'I\'m sorry, Dave. I\'m afraid I can\'t do that.');
  }
  return bot.reply(message, 'Okay');
});

//monitor reactions
controller.on('reaction_added', function(bot, message) {
    console.log(message.reaction);
    console.log(message.user);
})

// all un-handled direct mentions get a reaction and a pat response!
controller.on('direct_message, mention, direct_mention', function(bot, message) {
    bot.reply(message, 'Hi there! I\'m a bot. If you paste a news article URL here, I can ask you questions about it.');
});<|endoftext|>


<commit_before>"use strict";
 /*
  * Google drive storage for ghost blog
  * @author : Robin C Samuel <hi@robinz.in> http://robinz.in
  * @updated : 10th April 2022
  *
*/

const StorageBase = require("ghost-storage-base");
const fs = require("fs");
const { google } = require("googleapis");
const drive = google.drive("v3");

var auth = config => {
  var jwtClient = new google.auth.JWT(
    config.key.client_email,
    null,
    config.key.private_key,
    ['https://www.googleapis.com/auth/drive'],
    null
  );

  return new Promise((resolve, reject) => {
    jwtClient.authorize(err => {
      if (err) {
        reject(err);
      }

      resolve(jwtClient);
    });
  });
}

var upload = (client, file) => {
  return new Promise((resolve, reject) => {
    drive.files.create({
      auth: client,
      resource: {
        name: file.name,
        mimeType: file.type
      },
      media: {
        mimeType: file.type,
        body: fs.createReadStream(file.path)
      },
      fields: 'id, fileExtension'
    }, (err, uploadedFile) => {
      if (err) {
        reject(err);
      }

      // Promise is resolved with the result of create call
      resolve(uploadedFile);
    });
  });
}

var setPermissions = (client, data, callback) => {
  drive.permissions.create({
    auth: client,
    fileId: data.id,
    supportsAllDrives: true,
    supportsTeamDrives: true,
    resource: {
      'type': 'anyone',
      'role': 'reader',
    },
    fields: 'id',
  }, function (error, response) {
      callback(error, response);
  });
}

var get = (client, fileId, callback) => {
  drive.files.get({
    auth: client,
    fileId: fileId,
    alt: 'media'
  }, { responseType: "stream" },
    function (error, response) {
      callback(error, response);
  });
}

var remove = (client, file) => {
  return new Promise(function (resolve, reject) {
    drive.files.delete({
      auth: client,
      fileId: file
    },
    function (err, data) {
      if (err) {
        console.error(err);
        return reject(err);
      }
      resolve();
    });
  });
}

class ghostGoogleDrive extends StorageBase {
  constructor(config) {
    super();
    this.config = config;
  }

  /**
   * Saves the image to storage (the file system)
   * - image is the express image object
   * - returns a promise which ultimately returns the full url to the uploaded image
   *
   * @param image
   * @param targetDir
   * @returns {*}
   */
  save(file, targetDir) {
    return new Promise((resolve, reject) => {
      auth(this.config)
        .then(client => {
          upload(client, file)
            .then(resp => {
              setPermissions(client, resp.data, (err, res) => {
                if (err) {
                  console.error(err);
                }
                resolve('/content/images/' + resp.data.id + '.' + resp.data.fileExtension);
              });
            });
        });
    });
  }

  /**
   * checks for existance of file (handle 404's proper)
   * @param {*} fileName
   * @param {*} targetDir
   * @returns Promise.<*>
   */
  exists(fileName, targetDir) {
    return new Promise((resolve, reject) => {
      auth(this.config)
        .then(client => {
          get(client, fileName, (error, response) => {
            if (error) {
              console.error(fileName, " not found");
              resolve(false);
            }

            resolve(true);
          })
        });
    });
  }

  /**
   * TODO: implement 404 functionality
   * @returns {serveStaticContent}
   */
  serve() {
    const _this = this;
    return function serveContent(req, res, next) {
      // get the file id from url
      var fileId = req.path.replace('/', '').split('.')[0];
      auth(_this.config).then(client => {
        get(client, fileId, (err, resp) => {
            if (err) {
              console.error("fileId: " + fileId, "err: " + err);
              return next();
            }
            resp.data.pipe(res);
        });
      });
    }
  }

  /**
   * @returns {Promise.<*>}
   */
  delete() {
    return function deleteContent(req, res, next) {
      var fileId = req.path.replace('/', '').split('.')[0];
      auth(this.config)
        .then(client => {
          remove(client, fileId)
        });
    };
  }

  /**
   * Reads bytes from disk for a target image
   * - path of target image (without content path!)
   *
   * @param options
   */
  read(options) {
    var fileId = options.path.replace('/', '').split('.')[0];
    return new Promise((resolve, reject) => {
      auth(this.config)
        .then(client => {
          get(client, fileId, (error, response) => {
            if (error) {
              reject(error);
            }

            resolve(response);
          });
        });
    });
  }
}

module.exports = ghostGoogleDrive;
<commit_msg>setPermissions() resolves promise<commit_after>"use strict";
 /*
  * Google drive storage for ghost blog
  * @author : Robin C Samuel <hi@robinz.in> http://robinz.in
  * @updated : 10th April 2022
  *
*/

const StorageBase = require("ghost-storage-base");
const fs = require("fs");
const { google } = require("googleapis");
const drive = google.drive("v3");

var auth = config => {
  var jwtClient = new google.auth.JWT(
    config.key.client_email,
    null,
    config.key.private_key,
    ['https://www.googleapis.com/auth/drive'],
    null
  );

  return new Promise((resolve, reject) => {
    jwtClient.authorize(err => {
      if (err) {
        reject(err);
      }

      resolve(jwtClient);
    });
  });
}

var upload = (client, file) => {
  return new Promise((resolve, reject) => {
    drive.files.create({
      auth: client,
      resource: {
        name: file.name,
        mimeType: file.type
      },
      media: {
        mimeType: file.type,
        body: fs.createReadStream(file.path)
      },
      fields: 'id, fileExtension'
    }, (err, uploadedFile) => {
      if (err) {
        reject(err);
      }

      // Promise is resolved with the result of create call
      resolve(uploadedFile);
    });
  });
}

var setPermissions = (client, data) => {
  return new Promise((resolve, reject) => {
    drive.permissions.create({
      auth: client,
      fileId: data.id,
      supportsAllDrives: true,
      supportsTeamDrives: true,
      resource: {
        'type': 'anyone',
        'role': 'reader',
      },
      fields: 'id',
    }, function (err, res) {
      if (err) {
        console.error(err);
        reject(err);
      }
      resolve(res);
    });
  });
}

var get = (client, fileId, callback) => {
  drive.files.get({
    auth: client,
    fileId: fileId,
    alt: 'media'
  }, { responseType: "stream" },
    function (error, response) {
      callback(error, response);
  });
}

var remove = (client, file) => {
  return new Promise(function (resolve, reject) {
    drive.files.delete({
      auth: client,
      fileId: file
    },
    function (err, data) {
      if (err) {
        console.error(err);
        reject(err);
      }
      resolve();
    });
  });
}

class ghostGoogleDrive extends StorageBase {
  constructor(config) {
    super();
    this.config = config;
  }

  /**
   * Saves the image to storage (the file system)
   * - image is the express image object
   * - returns a promise which ultimately returns the full url to the uploaded image
   *
   * @param image
   * @param targetDir
   * @returns {*}
   */
  save(file, targetDir) {
    return new Promise((resolve, reject) => {
      auth(this.config)
        .then(client => {
          upload(client, file)
            .then(resp => {
              setPermissions(client, resp.data)
                .then(res => {
                  resolve('/content/images/' + resp.data.id + '.' + resp.data.fileExtension);
              });
            });
        });
    });
  }

  /**
   * checks for existance of file (handle 404's proper)
   * @param {*} fileName
   * @param {*} targetDir
   * @returns Promise.<*>
   */
  exists(fileName, targetDir) {
    return new Promise((resolve, reject) => {
      auth(this.config)
        .then(client => {
          get(client, fileName, (error, response) => {
            if (error) {
              console.error(fileName, " not found");
              resolve(false);
            }

            resolve(true);
          })
        });
    });
  }

  /**
   * TODO: implement 404 functionality
   * @returns {serveStaticContent}
   */
  serve() {
    const _this = this;
    return function serveContent(req, res, next) {
      // get the file id from url
      var fileId = req.path.replace('/', '').split('.')[0];
      auth(_this.config).then(client => {
        get(client, fileId, (err, resp) => {
            if (err) {
              console.error("fileId: " + fileId, "err: " + err);
              return next();
            }
            resp.data.pipe(res);
        });
      });
    }
  }

  /**
   * @returns {Promise.<*>}
   */
  delete() {
    return function deleteContent(req, res, next) {
      var fileId = req.path.replace('/', '').split('.')[0];
      auth(this.config)
        .then(client => {
          remove(client, fileId)
        });
    };
  }

  /**
   * Reads bytes from disk for a target image
   * - path of target image (without content path!)
   *
   * @param options
   */
  read(options) {
    var fileId = options.path.replace('/', '').split('.')[0];
    return new Promise((resolve, reject) => {
      auth(this.config)
        .then(client => {
          get(client, fileId, (error, response) => {
            if (error) {
              reject(error);
            }

            resolve(response);
          });
        });
    });
  }
}

module.exports = ghostGoogleDrive;
<|endoftext|>


<commit_before>/*global require: true, exports: true*/
var UniqueGroup = function (items) {
    this.items = [];
    if (items) {
        this.add(items);
    }
};
UniqueGroup.prototype = {
    add: function (items) {
        if (Object.prototype.toString.apply(items) !== '[object Array]') {
            items = [items];
        }
        items.forEach(function (item) {
            if (item instanceof this.constructor) {
                item = item.items;
            }
            this.items = this.items.concat(item);
        }.bind(this));
        this._makeUnique();
    },
    _makeUnique: function () {
        var items = [], hash = {};
        this.items.forEach(function (item) {
            if (!hash[item]) {
                items.push(item);
                hash[item] = true;
            }
        });
        this.items = items;
    }
};
var FileGroup = function (items) {
    this.items = [];
    if (items) {
        this.add(items);
    }
};
FileGroup.prototype = new UniqueGroup();
FileGroup.prototype.generate = function (cb) {
    var output = '',
        me = this,
        index = 0, read,
        fs = require('fs');
    read = function read() {
        fs.readFile(me.items[index], function (err, data) {
            if (err) {
                throw err;
            }
            output += data;
            index += 1;
            if (index < me.items.length) {
                read();
            } else {
                cb(output);
            }
        });
    };
    read();
};
exports.UniqueGroup = UniqueGroup;
exports.FileGroup = FileGroup;
<commit_msg>Create named methods<commit_after>/*global require: true, exports: true*/

var UniqueGroup = function UniqueGroup (items) {
    this.items = [];
    if (items) {
        this.add(items);
    }
};
UniqueGroup.prototype = {
    add: function uniqueGroupAdd (items) {
        if (Object.prototype.toString.apply(items) !== '[object Array]') {
            items = [items];
        }
        items.forEach(function (item) {
            if (typeof item === 'object') {
                item = item.items;
            }
            this.items = this.items.concat(item);
        }.bind(this));
        this._makeUnique();
    },
    _makeUnique: function uniqueGroupMakeUniqe () {
        var items = [], hash = {};
        this.items.forEach(function (item) {
            if (!hash[item]) {
                items.push(item);
                hash[item] = true;
            }
        });
        this.items = items;
    }
};
var FileGroup = function FileGroup (items) {
    this.items = [];
    if (items) {
        this.add(items);
    }
};
FileGroup.prototype = new UniqueGroup();
FileGroup.prototype.generate = function fileGroupGenerate (cb) {
    var output = '',
        me = this,
        index = 0, read,
        fs = require('fs');
    read = function read() {
        fs.readFile(me.items[index], function (err, data) {
            if (err) {
                throw err;
            }
            output += data;
            index += 1;
            if (index < me.items.length) {
                read();
            } else {
                cb(output);
            }
        });
    };
    read();
};
exports.UniqueGroup = UniqueGroup;
exports.FileGroup = FileGroup;
<|endoftext|>