<commit_before>module.exports = function(sails) {

  /**
   * Module dependencies
   */

  var path = require('path');
  var async = require('async');
  var _ = require('lodash');
  var buildDictionary = require('sails-build-dictionary');

  // TODO:
  // Look at improving `includeAll` to work asynchronously
  // CommonJS `require` is a blocking operation, and makes apps
  // start slower.

  /**
   * Module loader
   *
   * Load a module into memory
   */
  return {

    // Default configuration
    defaults: function (config) {

      // TODO:
      // lazy-load (i.e. only do this if a `.coffee` file is encountered), e.g.
      // if (filepath.match(/\.coffee$/)) {}

      // TODO:
      // negotiate the error-- if it's NOT a require error, log it directly.
      // if it IS, display the error we have currently
      // (the only reason it's verbose right now is that if you're NOT using coffeescript
      //  it's pretty annoying to see the error pop up every time-- see previous todo)

      // Enable server-side CoffeeScript support
      try {
        require('coffee-script/register');
      } catch(e0){
        try {
          var appPath = config.appPath || process.cwd();
          require(path.join(appPath, 'node_modules/coffee-script/register'));
        }
        catch (e1) {
          sails.log.verbose('Please run `npm install coffee-script` to use coffescript (skipping for now)');
          sails.log.silly('Here\'s the require error(s): ',e0,e1);
        }
      }

      return {

        // The path to the application
        appPath: config.appPath ? path.resolve(config.appPath) : process.cwd(),

        // Paths for application modules and key files
        // If `paths.app` not specified, use process.cwd()
        // (the directory where this Sails process is being initiated from)
        paths: {

          // Configuration
          //
          // For `userconfig` hook
          config: path.resolve(config.appPath, 'config'),

          // Server-Side Code
          //
          // For `controllers` hook
          controllers: path.resolve(config.appPath, 'api/controllers'),
          // For `policies` hook
          policies: path.resolve(config.appPath, 'api/policies'),
          // For `services` hook
          services: path.resolve(config.appPath, 'api/services'),
          // For `orm` hook
          adapters: path.resolve(config.appPath, 'api/adapters'),
          models: path.resolve(config.appPath, 'api/models'),
          // For `userhooks` hook
          hooks: path.resolve(config.appPath, 'api/hooks'),
          // For `blueprints` hook
          blueprints: path.resolve(config.appPath, 'api/blueprints'),
          // For `responses` hook
          responses: path.resolve(config.appPath, 'api/responses'),

          // Server-Side HTML
          //
          // For `views` hook
          views: path.resolve(config.appPath, 'views'),
          layout: path.resolve(config.appPath, 'views/layout.ejs'),
        }
      };
    },

    initialize: function(cb) {

      // Expose self as `sails.modules` (for backwards compatibility)
      sails.modules = sails.hooks.moduleloader;

      return cb();
    },

    configure: function() {
      if (sails.config.moduleLoaderOverride) {
        var override = sails.config.moduleLoaderOverride(sails, this);
        sails.util.extend(this, override);
        if (override.configure) {
          this.configure();
        }
      }
      // console.log('Trying to use appPath:',sails.config.appPath);
      // console.log('Trying to use config dir at:',path.resolve(sails.config.appPath, 'config'));
      sails.config.appPath = sails.config.appPath ? path.resolve(sails.config.appPath) : process.cwd();

      _.extend(sails.config.paths, {

        // Configuration
        //
        // For `userconfig` hook
        config: path.resolve(sails.config.appPath, sails.config.paths.config),

        // Server-Side Code
        //
        // For `controllers` hook
        controllers: path.resolve(sails.config.appPath, sails.config.paths.controllers),
        // For `policies` hook
        policies: path.resolve(sails.config.appPath, sails.config.paths.policies),
        // For `services` hook
        services: path.resolve(sails.config.appPath, sails.config.paths.services),
        // For `orm` hook
        adapters: path.resolve(sails.config.appPath, sails.config.paths.adapters),
        models: path.resolve(sails.config.appPath, sails.config.paths.models),
        // For `userhooks` hook
        hooks: path.resolve(sails.config.appPath, sails.config.paths.hooks),
        // For `blueprints` hook
        blueprints: path.resolve(sails.config.appPath, sails.config.paths.blueprints),
        // For `responses` hook
        responses: path.resolve(sails.config.appPath, sails.config.paths.responses),

        // Server-Side HTML
        //
        // For `views` hook
        views: path.resolve(sails.config.appPath, sails.config.paths.views),
        layout: path.resolve(sails.config.appPath, sails.config.paths.layout)
      });
    },

    /**
     * Load user config from app
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadUserConfig: function (cb) {

      async.auto({

        'config/*': function loadOtherConfigFiles (cb) {
          // console.log('TRYING T LOAD CONFIG AT:',sails.config.paths.config);
          buildDictionary.aggregate({
            dirname   : sails.config.paths.config || sails.config.appPath + '/config',
            exclude   : ['locales', 'local.js', 'local.json', 'local.coffee', 'local.litcoffee'],
            excludeDirs: /(locales|env)$/,
            filter    : /(.+)\.(js|json|coffee|litcoffee)$/,
            flattenDirectories: !(sails.config.dontFlattenConfig),
            identity  : false
          }, cb);
        },

        'config/local' : function loadLocalOverrideFile (cb) {
          buildDictionary.aggregate({
            dirname   : sails.config.paths.config || sails.config.appPath + '/config',
            filter    : /local\.(js|json|coffee|litcoffee)$/,
            identity  : false
          }, cb);
        },

        // Load environment-specific config folder, e.g. config/env/development/*
        'config/env/**': ['config/local', function loadEnvConfigFolder (cb, async_data) {
          // If there's an environment already set in sails.config, then it came from the environment
          // or the command line, so that takes precedence.  Otherwise, check the config/local.js file
          // for an environment setting.  Lastly, default to development.
          var env = sails.config.environment || async_data['config/local'].environment || 'development';
          buildDictionary.aggregate({
            dirname   : (sails.config.paths.config || sails.config.appPath + '/config') + '/env/' + env,
            filter    : /(.+)\.(js|json|coffee|litcoffee)$/,
            optional  : true,
            flattenDirectories: !(sails.config.dontFlattenConfig),
            identity  : false
          }, cb);
        }],

        // Load environment-specific config file, e.g. config/env/development.js
        'config/env/*' : ['config/local', function loadEnvConfigFile (cb, async_data) {
          // If there's an environment already set in sails.config, then it came from the environment
          // or the command line, so that takes precedence.  Otherwise, check the config/local.js file
          // for an environment setting.  Lastly, default to development.
          var env = sails.config.environment || async_data['config/local'].environment || 'development';
          buildDictionary.aggregate({
            dirname   : (sails.config.paths.config || sails.config.appPath + '/config') + '/env',
            filter    : new RegExp(env + '.(js|json|coffee|litcoffee)$'),
            optional  : true,
            flattenDirectories: !(sails.config.dontFlattenConfig),
            identity  : false
          }, cb);
        }]

      }, function (err, async_data) {
        if (err) return cb(err);
        // Save the environment override, if any.
        var env = sails.config.environment;
        // Merge the configs, with env/*.js files taking precedence over others, and local.js
        // taking precedence over everything
        var config = sails.util.merge(
          async_data['config/*'],
          async_data['config/env/**'],
          async_data['config/env/*'],
          async_data['config/local']
        );
        // Set the environment, but don't allow env/* files to change it; that'd be weird.
        config.environment = env || async_data['config/local'].environment || 'development';
        // Return the user config
        cb(null, config);
      });
    },

    /**
     * Load app controllers
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadControllers: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.controllers,
        filter: /(.+)Controller\.(js|coffee|litcoffee)$/,
        flattenDirectories: true,
        keepDirectoryPath: true,
        replaceExpr: /Controller/
      }, bindToSails(cb));
    },

    /**
     * Load adapters
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadAdapters: function (cb) {
      buildDictionary.optional({
        dirname   : sails.config.paths.adapters,
        filter    : /(.+Adapter)\.(js|coffee|litcoffee)$/,
        replaceExpr : /Adapter/,
        flattenDirectories: true
      }, bindToSails(cb));
    },

    /**
     * Load app's model definitions
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadModels: function (cb) {
      // Get the main model files
      buildDictionary.optional({
        dirname   : sails.config.paths.models,
        filter    : /^([^.]+)\.(js|coffee|litcoffee)$/,
        replaceExpr : /^.*\//,
        flattenDirectories: true
      }, function(err, models) {
        if (err) {return cb(err);}
        // Get any supplemental files
        buildDictionary.optional({
          dirname   : sails.config.paths.models,
          filter    : /(.+)\.attributes.json$/,
          replaceExpr : /^.*\//,
          flattenDirectories: true
        }, bindToSails(function(err, supplements) {
          if (err) {return cb(err);}
          return cb(null, sails.util.merge(models, supplements));
        }));
      });
    },

    /**
     * Load app services
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadServices: function (cb) {
      buildDictionary.optional({
        dirname     : sails.config.paths.services,
        filter      : /(.+)\.(js|coffee|litcoffee)$/,
        depth     : 1,
        caseSensitive : true
      }, bindToSails(cb));
    },

    /**
     * Check for the existence of views in the app
     *
     * @param {Object} options
     * @param {Function} cb
     */
    statViews: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.views,
        filter: /(.+)\..+$/,
        replaceExpr: null,
        dontLoad: true
      }, cb);
    },

    /**
     * Load app policies
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadPolicies: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.policies,
        filter: /(.+)\.(js|coffee|litcoffee)$/,
        replaceExpr: null,
        flattenDirectories: true,
        keepDirectoryPath: true
      }, bindToSails(cb));
    },

    /**
     * Load app hooks
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadUserHooks: function (cb) {

      async.auto({
        // Load apps from the "api/hooks" folder
        hooksFolder: function(cb) {
          buildDictionary.optional({
            dirname: sails.config.paths.hooks,
            filter: /^(.+)\.(js|coffee|litcoffee)$/,

            // Hooks should be defined as either single files as a function
            // OR (better yet) a subfolder with an index.js file
            // (like a standard node module)
            depth: 2
          }, cb);
        },

        // Load package.json files from node_modules to check for hooks
        nodeModulesFolder: function(cb) {
          buildDictionary.optional({
            dirname: path.resolve(sails.config.appPath, "node_modules"),
            filter: /^(package\.json)$/,
            depth: 2
          }, cb);
        }
      }, function(err, results) {
        if (err) {return cb(err);}

        // Marshall the hooks by checking that they are valid.  The ones from the
        // api/hooks folder are assumed to be okay.
        var hooks = results.hooksFolder;

        try {

          _.extend(hooks, _.reduce(results.nodeModulesFolder, function(memo, module, identity) {

            // Hooks loaded from "node_modules" need to have "sails.isHook: true" in order for us
            // to know that they are a sails hook
            if (module['package.json'] && module['package.json'].sails && module['package.json'].sails.isHook) {

              // Determine the name the hook should be added as
              var hookName;

              // If an identity was specified in sails.config.installedHooks, use that
              if (sails.config.installedHooks && sails.config.installedHooks[identity] && sails.config.installedHooks[identity].name) {
                hookName = sails.config.installedHooks[identity].name;
              }
              // Otherwise use the module name, with initial "sails-hook" stripped off if it exists
              else {
                hookName = identity.match(/^sails-hook-/) ? identity.replace(/^sails-hook-/,'') : identity;
              }

              // Allow overriding core hooks
              if (sails.hooks[hookName]) {
                sails.log.verbose('Found hook: `'+hookName+'` in `node_modules/`.  Overriding core hook w/ the same identity...');
              }

              // If we have a hook in api/hooks with this name, throw an error
              if (hooks[hookName]) {
                var err = (function (){
                  var msg =
                  'Found hook: `' + hookName + '`, in `node_modules/`, but a hook with that identity already exists in `api/hooks/`. '+
                  'The hook defined in your `api/hooks/` folder will take precedence.';
                  var err = new Error(msg);
                  err.code = 'E_INVALID_HOOK_NAME';
                  return err;
                });
                sails.log.warn(err);
                return memo;
              }

              // Load the hook code
              var hook = require(path.resolve(sails.config.appPath, "node_modules", identity));

              // Set its config key (defaults to the hook name)
              hook.configKey = (sails.config.installedHooks && sails.config.installedHooks[identity] && sails.config.installedHooks[identity].configKey) || hookName;

              // Add this to the list of hooks to load
              memo[hookName] = hook;
            }
            return memo;
          }, {}));

          return bindToSails(cb)(null, hooks);

        } catch (e) {
          return cb(e);
        }
      });
    },

    /**
     * Load app blueprint middleware.
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadBlueprints: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.blueprints,
        filter: /(.+)\.(js|coffee|litcoffee)$/,
        useGlobalIdForKeyName: true
      }, cb);
    },

    /**
     * Load custom API responses.
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadResponses: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.responses,
        filter: /(.+)\.(js|coffee|litcoffee)$/,
        useGlobalIdForKeyName: true
      }, bindToSails(cb));
    },

    optional: buildDictionary.optional,
    required: buildDictionary.required,
    aggregate: buildDictionary.aggregate,
    exits: buildDictionary.exists

  };

  function bindToSails(cb) {
    return function(err, modules) {
      if (err) {return cb(err);}
      _.each(modules, function(module) {
        // Add a reference to the Sails app that loaded the module
        module.sails = sails;
        // Bind all methods to the module context
        _.bindAll(module);
      });
      return cb(null, modules);
    };
  }

};
<commit_msg>Add LiveScript support<commit_after>module.exports = function(sails) {

  /**
   * Module dependencies
   */

  var path = require('path');
  var async = require('async');
  var _ = require('lodash');
  var buildDictionary = require('sails-build-dictionary');

  // TODO:
  // Look at improving `includeAll` to work asynchronously
  // CommonJS `require` is a blocking operation, and makes apps
  // start slower.

  /**
   * Module loader
   *
   * Load a module into memory
   */
  return {

    // Default configuration
    defaults: function (config) {

      // TODO:
      // lazy-load (i.e. only do this if a `.coffee` file is encountered), e.g.
      // if (filepath.match(/\.coffee$/)) {}

      // TODO:
      // negotiate the error-- if it's NOT a require error, log it directly.
      // if it IS, display the error we have currently
      // (the only reason it's verbose right now is that if you're NOT using coffeescript
      //  it's pretty annoying to see the error pop up every time-- see previous todo)

      // Enable server-side CoffeeScript support
      try {
        require('coffee-script/register');
      } catch(e0){
        try {
          var appPath = config.appPath || process.cwd();
          require(path.join(appPath, 'node_modules/coffee-script/register'));
        }
        catch (e1) {
          sails.log.verbose('Please run `npm install coffee-script` to use coffescript (skipping for now)');
          sails.log.silly('Here\'s the require error(s): ',e0,e1);
        }
      }

      // Enable server-side LiveScript support
      try {
        require('LiveScript');
      } catch(e0){
        try {
          var appPath = config.appPath || process.cwd();
          require(path.join(appPath, 'node_modules/LiveScript/index'));
        }
        catch (e1) {
          sails.log.verbose('Please run `npm install LiveScript` to use LiveScript (skipping for now)');
          sails.log.silly('Here\'s the require error(s): ',e0,e1);
        }
      }

      return {

        // The path to the application
        appPath: config.appPath ? path.resolve(config.appPath) : process.cwd(),

        // Paths for application modules and key files
        // If `paths.app` not specified, use process.cwd()
        // (the directory where this Sails process is being initiated from)
        paths: {

          // Configuration
          //
          // For `userconfig` hook
          config: path.resolve(config.appPath, 'config'),

          // Server-Side Code
          //
          // For `controllers` hook
          controllers: path.resolve(config.appPath, 'api/controllers'),
          // For `policies` hook
          policies: path.resolve(config.appPath, 'api/policies'),
          // For `services` hook
          services: path.resolve(config.appPath, 'api/services'),
          // For `orm` hook
          adapters: path.resolve(config.appPath, 'api/adapters'),
          models: path.resolve(config.appPath, 'api/models'),
          // For `userhooks` hook
          hooks: path.resolve(config.appPath, 'api/hooks'),
          // For `blueprints` hook
          blueprints: path.resolve(config.appPath, 'api/blueprints'),
          // For `responses` hook
          responses: path.resolve(config.appPath, 'api/responses'),

          // Server-Side HTML
          //
          // For `views` hook
          views: path.resolve(config.appPath, 'views'),
          layout: path.resolve(config.appPath, 'views/layout.ejs'),
        }
      };
    },

    initialize: function(cb) {

      // Expose self as `sails.modules` (for backwards compatibility)
      sails.modules = sails.hooks.moduleloader;

      return cb();
    },

    configure: function() {
      if (sails.config.moduleLoaderOverride) {
        var override = sails.config.moduleLoaderOverride(sails, this);
        sails.util.extend(this, override);
        if (override.configure) {
          this.configure();
        }
      }
      // console.log('Trying to use appPath:',sails.config.appPath);
      // console.log('Trying to use config dir at:',path.resolve(sails.config.appPath, 'config'));
      sails.config.appPath = sails.config.appPath ? path.resolve(sails.config.appPath) : process.cwd();

      _.extend(sails.config.paths, {

        // Configuration
        //
        // For `userconfig` hook
        config: path.resolve(sails.config.appPath, sails.config.paths.config),

        // Server-Side Code
        //
        // For `controllers` hook
        controllers: path.resolve(sails.config.appPath, sails.config.paths.controllers),
        // For `policies` hook
        policies: path.resolve(sails.config.appPath, sails.config.paths.policies),
        // For `services` hook
        services: path.resolve(sails.config.appPath, sails.config.paths.services),
        // For `orm` hook
        adapters: path.resolve(sails.config.appPath, sails.config.paths.adapters),
        models: path.resolve(sails.config.appPath, sails.config.paths.models),
        // For `userhooks` hook
        hooks: path.resolve(sails.config.appPath, sails.config.paths.hooks),
        // For `blueprints` hook
        blueprints: path.resolve(sails.config.appPath, sails.config.paths.blueprints),
        // For `responses` hook
        responses: path.resolve(sails.config.appPath, sails.config.paths.responses),

        // Server-Side HTML
        //
        // For `views` hook
        views: path.resolve(sails.config.appPath, sails.config.paths.views),
        layout: path.resolve(sails.config.appPath, sails.config.paths.layout)
      });
    },

    /**
     * Load user config from app
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadUserConfig: function (cb) {

      async.auto({

        'config/*': function loadOtherConfigFiles (cb) {
          // console.log('TRYING T LOAD CONFIG AT:',sails.config.paths.config);
          buildDictionary.aggregate({
            dirname   : sails.config.paths.config || sails.config.appPath + '/config',
            exclude   : ['locales', 'local.js', 'local.json', 'local.coffee', 'local.litcoffee', 'local.ls'],
            excludeDirs: /(locales|env)$/,
            filter    : /(.+)\.(js|json|coffee|litcoffee|ls)$/,
            flattenDirectories: !(sails.config.dontFlattenConfig),
            identity  : false
          }, cb);
        },

        'config/local' : function loadLocalOverrideFile (cb) {
          buildDictionary.aggregate({
            dirname   : sails.config.paths.config || sails.config.appPath + '/config',
            filter    : /local\.(js|json|coffee|litcoffee|ls)$/,
            identity  : false
          }, cb);
        },

        // Load environment-specific config folder, e.g. config/env/development/*
        'config/env/**': ['config/local', function loadEnvConfigFolder (cb, async_data) {
          // If there's an environment already set in sails.config, then it came from the environment
          // or the command line, so that takes precedence.  Otherwise, check the config/local.js file
          // for an environment setting.  Lastly, default to development.
          var env = sails.config.environment || async_data['config/local'].environment || 'development';
          buildDictionary.aggregate({
            dirname   : (sails.config.paths.config || sails.config.appPath + '/config') + '/env/' + env,
            filter    : /(.+)\.(js|json|coffee|litcoffee|ls)$/,
            optional  : true,
            flattenDirectories: !(sails.config.dontFlattenConfig),
            identity  : false
          }, cb);
        }],

        // Load environment-specific config file, e.g. config/env/development.js
        'config/env/*' : ['config/local', function loadEnvConfigFile (cb, async_data) {
          // If there's an environment already set in sails.config, then it came from the environment
          // or the command line, so that takes precedence.  Otherwise, check the config/local.js file
          // for an environment setting.  Lastly, default to development.
          var env = sails.config.environment || async_data['config/local'].environment || 'development';
          buildDictionary.aggregate({
            dirname   : (sails.config.paths.config || sails.config.appPath + '/config') + '/env',
            filter    : new RegExp(env + '.(js|json|coffee|litcoffee|ls)$'),
            optional  : true,
            flattenDirectories: !(sails.config.dontFlattenConfig),
            identity  : false
          }, cb);
        }]

      }, function (err, async_data) {
        if (err) return cb(err);
        // Save the environment override, if any.
        var env = sails.config.environment;
        // Merge the configs, with env/*.js files taking precedence over others, and local.js
        // taking precedence over everything
        var config = sails.util.merge(
          async_data['config/*'],
          async_data['config/env/**'],
          async_data['config/env/*'],
          async_data['config/local']
        );
        // Set the environment, but don't allow env/* files to change it; that'd be weird.
        config.environment = env || async_data['config/local'].environment || 'development';
        // Return the user config
        cb(null, config);
      });
    },

    /**
     * Load app controllers
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadControllers: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.controllers,
        filter: /(.+)Controller\.(js|coffee|litcoffee|ls)$/,
        flattenDirectories: true,
        keepDirectoryPath: true,
        replaceExpr: /Controller/
      }, bindToSails(cb));
    },

    /**
     * Load adapters
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadAdapters: function (cb) {
      buildDictionary.optional({
        dirname   : sails.config.paths.adapters,
        filter    : /(.+Adapter)\.(js|coffee|litcoffee|ls)$/,
        replaceExpr : /Adapter/,
        flattenDirectories: true
      }, bindToSails(cb));
    },

    /**
     * Load app's model definitions
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadModels: function (cb) {
      // Get the main model files
      buildDictionary.optional({
        dirname   : sails.config.paths.models,
        filter    : /^([^.]+)\.(js|coffee|litcoffee|ls)$/,
        replaceExpr : /^.*\//,
        flattenDirectories: true
      }, function(err, models) {
        if (err) {return cb(err);}
        // Get any supplemental files
        buildDictionary.optional({
          dirname   : sails.config.paths.models,
          filter    : /(.+)\.attributes.json$/,
          replaceExpr : /^.*\//,
          flattenDirectories: true
        }, bindToSails(function(err, supplements) {
          if (err) {return cb(err);}
          return cb(null, sails.util.merge(models, supplements));
        }));
      });
    },

    /**
     * Load app services
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadServices: function (cb) {
      buildDictionary.optional({
        dirname     : sails.config.paths.services,
        filter      : /(.+)\.(js|coffee|litcoffee|ls)$/,
        depth     : 1,
        caseSensitive : true
      }, bindToSails(cb));
    },

    /**
     * Check for the existence of views in the app
     *
     * @param {Object} options
     * @param {Function} cb
     */
    statViews: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.views,
        filter: /(.+)\..+$/,
        replaceExpr: null,
        dontLoad: true
      }, cb);
    },

    /**
     * Load app policies
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadPolicies: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.policies,
        filter: /(.+)\.(js|coffee|litcoffee|ls)$/,
        replaceExpr: null,
        flattenDirectories: true,
        keepDirectoryPath: true
      }, bindToSails(cb));
    },

    /**
     * Load app hooks
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadUserHooks: function (cb) {

      async.auto({
        // Load apps from the "api/hooks" folder
        hooksFolder: function(cb) {
          buildDictionary.optional({
            dirname: sails.config.paths.hooks,
            filter: /^(.+)\.(js|coffee|litcoffee|ls)$/,

            // Hooks should be defined as either single files as a function
            // OR (better yet) a subfolder with an index.js file
            // (like a standard node module)
            depth: 2
          }, cb);
        },

        // Load package.json files from node_modules to check for hooks
        nodeModulesFolder: function(cb) {
          buildDictionary.optional({
            dirname: path.resolve(sails.config.appPath, "node_modules"),
            filter: /^(package\.json)$/,
            depth: 2
          }, cb);
        }
      }, function(err, results) {
        if (err) {return cb(err);}

        // Marshall the hooks by checking that they are valid.  The ones from the
        // api/hooks folder are assumed to be okay.
        var hooks = results.hooksFolder;

        try {

          _.extend(hooks, _.reduce(results.nodeModulesFolder, function(memo, module, identity) {

            // Hooks loaded from "node_modules" need to have "sails.isHook: true" in order for us
            // to know that they are a sails hook
            if (module['package.json'] && module['package.json'].sails && module['package.json'].sails.isHook) {

              // Determine the name the hook should be added as
              var hookName;

              // If an identity was specified in sails.config.installedHooks, use that
              if (sails.config.installedHooks && sails.config.installedHooks[identity] && sails.config.installedHooks[identity].name) {
                hookName = sails.config.installedHooks[identity].name;
              }
              // Otherwise use the module name, with initial "sails-hook" stripped off if it exists
              else {
                hookName = identity.match(/^sails-hook-/) ? identity.replace(/^sails-hook-/,'') : identity;
              }

              // Allow overriding core hooks
              if (sails.hooks[hookName]) {
                sails.log.verbose('Found hook: `'+hookName+'` in `node_modules/`.  Overriding core hook w/ the same identity...');
              }

              // If we have a hook in api/hooks with this name, throw an error
              if (hooks[hookName]) {
                var err = (function (){
                  var msg =
                  'Found hook: `' + hookName + '`, in `node_modules/`, but a hook with that identity already exists in `api/hooks/`. '+
                  'The hook defined in your `api/hooks/` folder will take precedence.';
                  var err = new Error(msg);
                  err.code = 'E_INVALID_HOOK_NAME';
                  return err;
                });
                sails.log.warn(err);
                return memo;
              }

              // Load the hook code
              var hook = require(path.resolve(sails.config.appPath, "node_modules", identity));

              // Set its config key (defaults to the hook name)
              hook.configKey = (sails.config.installedHooks && sails.config.installedHooks[identity] && sails.config.installedHooks[identity].configKey) || hookName;

              // Add this to the list of hooks to load
              memo[hookName] = hook;
            }
            return memo;
          }, {}));

          return bindToSails(cb)(null, hooks);

        } catch (e) {
          return cb(e);
        }
      });
    },

    /**
     * Load app blueprint middleware.
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadBlueprints: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.blueprints,
        filter: /(.+)\.(js|coffee|litcoffee|ls)$/,
        useGlobalIdForKeyName: true
      }, cb);
    },

    /**
     * Load custom API responses.
     *
     * @param {Object} options
     * @param {Function} cb
     */
    loadResponses: function (cb) {
      buildDictionary.optional({
        dirname: sails.config.paths.responses,
        filter: /(.+)\.(js|coffee|litcoffee|ls)$/,
        useGlobalIdForKeyName: true
      }, bindToSails(cb));
    },

    optional: buildDictionary.optional,
    required: buildDictionary.required,
    aggregate: buildDictionary.aggregate,
    exits: buildDictionary.exists

  };

  function bindToSails(cb) {
    return function(err, modules) {
      if (err) {return cb(err);}
      _.each(modules, function(module) {
        // Add a reference to the Sails app that loaded the module
        module.sails = sails;
        // Bind all methods to the module context
        _.bindAll(module);
      });
      return cb(null, modules);
    };
  }

};
<|endoftext|>


<commit_before>/*
 * Notes: the accounts array on an IsometricaUser is a list of objects with
 * an id (id of an account) and type (access type, can be one of 'owner' or 'member')
 */

module.exports = function(IsometricaUser) {

	IsometricaUser.observe('before save', function updateTimestamp(ctx, next) {

		//set the created/ updated timestamps

		if (ctx.instance) {
		  	//create
		  	ctx.instance.created = new Date();
		  	ctx.instance.updated = new Date();
		} else {
		  	//update
		  	ctx.data.updated = new Date();
		}

	  	next();

	});

	/**
	 * Model validations
	 *
	 * @note Password validation: min 6 chars long containing both letters and numbers
	 * @note Defining here rather than in the json file because the [docs say
	 * 		 so](http://docs.strongloop.com/display/public/LB/Model+definition+JSON+file;jsessionid=D25089B559F634FAEA33260B9C331A20#ModeldefinitionJSONfile-Validations)
	 * @author Steve Fortune
	 */
	IsometricaUser.validatesPresenceOf('firstName', 'lastName', 'email', 'password');
	IsometricaUser.validatesUniquenessOf('email', {
		message: 'A user is already registered with this email address.'
	});
	IsometricaUser.validatesFormatOf('email', {
		// @note This regex is copied straight from the Loopback documentation for validating
		// an email address here: http://docs.strongloop.com/display/public/LB/Validating+model+data.
		// In the future it might be worth registering a custom validator to do a more sophisticated
		// check.
		with: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
		message: 'Email address should be in the format local-part@host'
	})
	IsometricaUser.validatesLengthOf('password', {
		min: 6,
		message: 'Password must be > 6 characters long'
	});
	IsometricaUser.validatesFormatOf('password', {
		with: /^(?=.*[a-zA-Z])(?=.*[0-9])/,
		message: 'Password must be > 6 characters long'
	});

};
<commit_msg>1.1 Added computation in before save user observer to set the user's full name<commit_after>/*
 * Notes: the accounts array on an IsometricaUser is a list of objects with
 * an id (id of an account) and type (access type, can be one of 'owner' or 'member')
 */

module.exports = function(IsometricaUser) {

	IsometricaUser.observe('before save', function(context, next) {
		if (context.instance) {
			var user = context.instance;
			user.name = computeFullName(user);
			user.created = new Date();
			user.updated = new Date();
		} else {
			context.data.name = computeFullName(context.data);
		}
	  	next();
	});

	/**
	 * @param 	model	Object
	 * @return 	String
	 * @author 	Steve Fortune
	 * @todo Find a better way of doing this. Can't we register some sort of dynamic
	 * 		 model property with loopback, so that we don't have to store redundant
	 *		 data ?
	 */
	var computeFullName = function(model) {
		return model.firstName + ' ' + model.lastName;
	};

	/**
	 * Model validations
	 *
	 * @note Password validation: min 6 chars long containing both letters and numbers
	 * @note Defining here rather than in the json file because the [docs say
	 * 		 so](http://docs.strongloop.com/display/public/LB/Model+definition+JSON+file;jsessionid=D25089B559F634FAEA33260B9C331A20#ModeldefinitionJSONfile-Validations)
	 * @author Steve Fortune
	 */
	IsometricaUser.validatesPresenceOf('firstName', 'lastName', 'email', 'password');
	IsometricaUser.validatesUniquenessOf('email', {
		message: 'A user is already registered with this email address.'
	});
	IsometricaUser.validatesFormatOf('email', {
		// @note This regex is copied straight from the Loopback documentation for validating
		// an email address here: http://docs.strongloop.com/display/public/LB/Validating+model+data.
		// In the future it might be worth registering a custom validator to do a more sophisticated
		// check.
		with: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
		message: 'Email address should be in the format local-part@host'
	})
	IsometricaUser.validatesLengthOf('password', {
		min: 6,
		message: 'Password must be > 6 characters long'
	});
	IsometricaUser.validatesFormatOf('password', {
		with: /^(?=.*[a-zA-Z])(?=.*[0-9])/,
		message: 'Password must be > 6 characters long'
	});

};
<|endoftext|>


<commit_before>"use strict";

import { Constants as AssessmentConstants }  from "../actions/assessment";

const initialState = null;

export default (state = initialState, action) => {

  switch(action.type){

    case AssessmentConstants.LOAD_ASSESSMENT_DONE:
      return action.payload.assessment;
      break;

    default:
      return state;
  }

};<commit_msg>Revert to only assessment as payload<commit_after>"use strict";

import { Constants as AssessmentConstants }  from "../actions/assessment";

const initialState = null;

export default (state = initialState, action) => {

  switch(action.type){

    case AssessmentConstants.LOAD_ASSESSMENT_DONE:
      return action.payload;
      break;

    default:
      return state;
  }

};<|endoftext|>


<commit_before>/* @flow strict-local */
import type { RealmState, Action, RealmEmojiById } from '../types';
import {
  REALM_INIT,
  EVENT_REALM_EMOJI_UPDATE,
  LOGOUT,
  LOGIN_SUCCESS,
  ACCOUNT_SWITCH,
  EVENT_UPDATE_DISPLAY_SETTINGS,
  EVENT_REALM_FILTERS,
} from '../actionConstants';
import { objectFromEntries } from '../jsBackport';

const initialState = {
  canCreateStreams: true,
  crossRealmBots: [],
  email: undefined,
  user_id: undefined,
  twentyFourHourTime: false,
  emoji: {},
  filters: [],
  isAdmin: false,
  nonActiveUsers: [],
};

const convertRealmEmoji = (data): RealmEmojiById =>
  objectFromEntries(Object.keys(data).map(id => [id, { ...data[id], code: id.toString() }]));

export default (state: RealmState = initialState, action: Action): RealmState => {
  switch (action.type) {
    case LOGOUT:
    case LOGIN_SUCCESS:
    case ACCOUNT_SWITCH:
      return initialState;

    case REALM_INIT: {
      return {
        ...state,
        canCreateStreams: action.data.can_create_streams,
        crossRealmBots: action.data.cross_realm_bots,
        email: action.data.email,
        user_id: action.data.user_id,
        emoji: convertRealmEmoji(action.data.realm_emoji),
        filters: action.data.realm_filters,
        isAdmin: action.data.is_admin,
        nonActiveUsers: action.data.realm_non_active_users,
        twentyFourHourTime: action.data.twenty_four_hour_time,
      };
    }

    // TODO on EVENT_USER_UPDATE for self: update email, isAdmin, etc.

    case EVENT_REALM_FILTERS:
      return {
        ...state,
        filters: action.realm_filters,
      };

    case EVENT_REALM_EMOJI_UPDATE:
      return {
        ...state,
        emoji: convertRealmEmoji(action.realm_emoji),
      };

    case EVENT_UPDATE_DISPLAY_SETTINGS:
      switch (action.setting_name) {
        case 'twenty_four_hour_time':
          return { ...state, twentyFourHourTime: action.setting };
        default:
          return state;
      }

    default:
      return state;
  }
};
<commit_msg>realm reducer [nfc]: Completely replace state on REALM_INIT.<commit_after>/* @flow strict-local */
import type { RealmState, Action, RealmEmojiById } from '../types';
import {
  REALM_INIT,
  EVENT_REALM_EMOJI_UPDATE,
  LOGOUT,
  LOGIN_SUCCESS,
  ACCOUNT_SWITCH,
  EVENT_UPDATE_DISPLAY_SETTINGS,
  EVENT_REALM_FILTERS,
} from '../actionConstants';
import { objectFromEntries } from '../jsBackport';

const initialState = {
  canCreateStreams: true,
  crossRealmBots: [],
  email: undefined,
  user_id: undefined,
  twentyFourHourTime: false,
  emoji: {},
  filters: [],
  isAdmin: false,
  nonActiveUsers: [],
};

const convertRealmEmoji = (data): RealmEmojiById =>
  objectFromEntries(Object.keys(data).map(id => [id, { ...data[id], code: id.toString() }]));

export default (state: RealmState = initialState, action: Action): RealmState => {
  switch (action.type) {
    case LOGOUT:
    case LOGIN_SUCCESS:
    case ACCOUNT_SWITCH:
      return initialState;

    case REALM_INIT: {
      return {
        canCreateStreams: action.data.can_create_streams,
        crossRealmBots: action.data.cross_realm_bots,
        email: action.data.email,
        user_id: action.data.user_id,
        emoji: convertRealmEmoji(action.data.realm_emoji),
        filters: action.data.realm_filters,
        isAdmin: action.data.is_admin,
        nonActiveUsers: action.data.realm_non_active_users,
        twentyFourHourTime: action.data.twenty_four_hour_time,
      };
    }

    // TODO on EVENT_USER_UPDATE for self: update email, isAdmin, etc.

    case EVENT_REALM_FILTERS:
      return {
        ...state,
        filters: action.realm_filters,
      };

    case EVENT_REALM_EMOJI_UPDATE:
      return {
        ...state,
        emoji: convertRealmEmoji(action.realm_emoji),
      };

    case EVENT_UPDATE_DISPLAY_SETTINGS:
      switch (action.setting_name) {
        case 'twenty_four_hour_time':
          return { ...state, twentyFourHourTime: action.setting };
        default:
          return state;
      }

    default:
      return state;
  }
};
<|endoftext|>


<commit_before>(function( window, $ ) {
  window.componentNamespace = window.componentNamespace || window;

  var Path = window.componentNamespace.Tau.Path = function() {
    this.reset();
  };

  Path.prototype.isSufficient = function() {
    return !!this.prevPoint && this.prevPrevPoint;
  };

  Path.prototype.distance = function() {
    return this.prevPoint.x - this.prevPrevPoint.x;
  };

  Path.prototype.duration = function() {
    return this.prevTime - this.prevPrevTime;
  };

  // TODO sort out variable names
  Path.prototype.record = function( point ) {
    this.prevPrevTime = this.prevTime;
    this.prevPrevPoint = this.prevPoint;

    // record the most recent drag point for decel on release
    this.prevTime = Date.now();
    this.prevPoint = point;
  };

  Path.prototype.velocity = function( timeStep ) {
    var distance, time;

    distance = this.distance();
    time = this.duration();

    return distance / ( time / timeStep );
  };

  Path.prototype.reset = function() {
    this.prevPoint = undefined;
    this.prevTime = undefined;

    this.prevPrevTime = undefined;
    this.prevPrevPoint = undefined;
  };

  Path.prototype.last = function() {
    return this.prevPoint;
  };
})(this, jQuery);
<commit_msg>ie 8 bugs<commit_after>(function( window, $ ) {
  window.componentNamespace = window.componentNamespace || window;

  Date.now = Date.now || function now() {
    return new Date().getTime();
  };

  var Path = window.componentNamespace.Tau.Path = function() {
    this.reset();
  };

  Path.prototype.isSufficient = function() {
    return !!this.prevPoint && this.prevPrevPoint;
  };

  Path.prototype.distance = function() {
    return this.prevPoint.x - this.prevPrevPoint.x;
  };

  Path.prototype.duration = function() {
    return this.prevTime - this.prevPrevTime;
  };

  // TODO sort out variable names
  Path.prototype.record = function( point ) {
    this.prevPrevTime = this.prevTime;
    this.prevPrevPoint = this.prevPoint;

    // record the most recent drag point for decel on release
    this.prevTime = Date.now();
    this.prevPoint = point;
  };

  Path.prototype.velocity = function( timeStep ) {
    var distance, time;

    distance = this.distance();
    time = this.duration();

    return distance / ( time / timeStep );
  };

  Path.prototype.reset = function() {
    this.prevPoint = undefined;
    this.prevTime = undefined;

    this.prevPrevTime = undefined;
    this.prevPrevPoint = undefined;
  };

  Path.prototype.last = function() {
    return this.prevPoint;
  };
})(this, jQuery);
<|endoftext|>


<commit_before>// import vl from 'vega-lite';

import { UPDATE_VEGA_SPEC, UPDATE_VEGA_LITE_SPEC, TOGGLE_DEBUG, CYCLE_RENDERER, SET_VEGA_EXAMPLE, SET_VEGA_LITE_EXAMPLE } from '../../actions/editor';
import { MODES, RENDERERS } from '../../constants';

export default (state = {
  editorString: JSON.stringify({}, null, 2),
  vegaSpec: {},
  vegaLiteSpec: null,
  selectedExample: null,
  mode: MODES.Vega,
  debug: false,
  vegaRenderer: RENDERERS.Canvas
}, action) => {
  let spec, vegaSpec;
  switch (action.type) {
    case UPDATE_VEGA_SPEC:
      try {
        spec = JSON.parse(action.spec);
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaSpec: spec,
        mode: MODES.Vega,
        editorString: action.spec
      });
    case SET_VEGA_EXAMPLE:
      try {
        spec = JSON.parse(action.spec);
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaSpec: spec,
        mode: MODES.Vega,
        editorString: action.spec,
        selectedExample: action.example
      });
    case SET_VEGA_LITE_EXAMPLE:
      try {
        console.log('Temporarily disable VL');
        // spec = JSON.parse(action.spec);
        // vegaSpec = vl.compile(spec).spec;
        // console.log('Generated Vega spec', JSON.stringify(vegaSpec))
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaLiteSpec: spec,
        vegaSpec: vegaSpec,
        mode: MODES.VegaLite,
        editorString: action.spec,
        selectedExample: action.example
      });

    case UPDATE_VEGA_LITE_SPEC:
      try {
        console.log('Temporarily disable VL');
        // spec = JSON.parse(action.spec);
        // vegaSpec = vl.compile(spec).spec;
        // console.log('Generated Vega spec', JSON.stringify(vegaSpec))
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaLiteSpec: spec,
        vegaSpec: vegaSpec,
        mode: MODES.VegaLite,
        editorString: action.spec
      });
    case TOGGLE_DEBUG:
      return Object.assign({}, state, {
        debug: !state.debug,
      });
    case CYCLE_RENDERER:
      const rendererVals = Object.values(RENDERERS);
      const currentRenderer = rendererVals.indexOf(state.renderer);
      const nextRenderer = rendererVals[(currentRenderer + 1) % rendererVals.length];
      return Object.assign({}, state, {
        renderer: nextRenderer
      });
    default:
      return state;
  }
}
<commit_msg>fix renderer undefined error<commit_after>// import vl from 'vega-lite';

import { UPDATE_VEGA_SPEC, UPDATE_VEGA_LITE_SPEC, TOGGLE_DEBUG, CYCLE_RENDERER, SET_VEGA_EXAMPLE, SET_VEGA_LITE_EXAMPLE } from '../../actions/editor';
import { MODES, RENDERERS } from '../../constants';

export default (state = {
  editorString: JSON.stringify({}, null, 2),
  vegaSpec: {},
  vegaLiteSpec: null,
  selectedExample: null,
  mode: MODES.Vega,
  debug: false,
  renderer: RENDERERS.Canvas
}, action) => {
  let spec, vegaSpec;
  switch (action.type) {
    case UPDATE_VEGA_SPEC:
      try {
        spec = JSON.parse(action.spec);
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaSpec: spec,
        mode: MODES.Vega,
        editorString: action.spec
      });
    case SET_VEGA_EXAMPLE:
      try {
        spec = JSON.parse(action.spec);
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaSpec: spec,
        mode: MODES.Vega,
        editorString: action.spec,
        selectedExample: action.example
      });
    case SET_VEGA_LITE_EXAMPLE:
      try {
        console.log('Temporarily disable VL');
        // spec = JSON.parse(action.spec);
        // vegaSpec = vl.compile(spec).spec;
        // console.log('Generated Vega spec', JSON.stringify(vegaSpec))
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaLiteSpec: spec,
        vegaSpec: vegaSpec,
        mode: MODES.VegaLite,
        editorString: action.spec,
        selectedExample: action.example
      });

    case UPDATE_VEGA_LITE_SPEC:
      try {
        console.log('Temporarily disable VL');
        // spec = JSON.parse(action.spec);
        // vegaSpec = vl.compile(spec).spec;
        // console.log('Generated Vega spec', JSON.stringify(vegaSpec))
      } catch (e) {
        console.warn('Error parsing json string');
        return state;
      }
      return Object.assign({}, state, {
        vegaLiteSpec: spec,
        vegaSpec: vegaSpec,
        mode: MODES.VegaLite,
        editorString: action.spec
      });
    case TOGGLE_DEBUG:
      return Object.assign({}, state, {
        debug: !state.debug,
      });
    case CYCLE_RENDERER:
      const rendererVals = Object.values(RENDERERS);
      const currentRenderer = rendererVals.indexOf(state.renderer);
      const nextRenderer = rendererVals[(currentRenderer + 1) % rendererVals.length];
      return Object.assign({}, state, {
        renderer: nextRenderer
      });
    default:
      return state;
  }
}
<|endoftext|>


<commit_before>import { Component, PropTypes } from "react";
import ReactMixin from "react-mixin";
import { connect } from "react-apollo";
import gql from "graphql-tag";

import Loading from "../../components/loading";
import {
  nav as navActions,
  audio as audioActions,
  header as headerActions,
} from "../../store";

import Headerable from "../../mixins/mixins.Header";
import Likeable from "../../mixins/mixins.Likeable";
import Shareable from "../../mixins/mixins.Shareable";

import time from "../../util/time";
import react from "../../util/react";
import content from "../../util/content";
import collections from "../../util/collections";

import SingleVideoPlayer from "../../components/players/video/Player";
import SeriesVideoList from "./series.VideoList";

const mapQueriesToProps = ({ ownProps, state }) => ({
  currentSermon: {
    query: gql`
      query getSermon($sermonId: ID!) {
        content: node(id: $sermonId) {
          ... on Content {
            entryId: id
            title
            status
            channelName
            meta {
              urlTitle
              siteId
              date
              actualDate
              channelId
            }
            content {
              audio {
                duration
                file: s3
              }
              description
              speaker
              ooyalaId
            }
          }
        }
      }
    `,
    variables: { sermonId: ownProps.params.sermonId },
    forceFetch: false,
    returnPartialData: false,
  },
  series: {
    query: gql`
      query getSeriesSingle($id: ID!) {
        content: node(id: $id) {
          id
          ... on Content {
            entryId: id
            title
            status
            channelName
            meta {
              urlTitle
              siteId
              date
              channelId
            }
            content {
              description
              images(sizes: ["large", "medium", "small"]) {
                fileName
                fileType
                fileLabel
                url
                size
              }
              ooyalaId
              colors {
                id
                value
                description
              }
              isLight
            }
          }
        }
      }
    `,
    variables: { id: ownProps.params.id },
    forceFetch: false,
    returnPartialData: false,
  },
});

const mapStateToProps = state => ({ live: state.live });

@connect({ mapQueriesToProps, mapStateToProps })
@ReactMixin.decorate(Likeable)
@ReactMixin.decorate(Shareable)
@ReactMixin.decorate(Headerable)
export default class SeriesSingleVideo extends Component {

  componentWillMount() {
    if (process.env.WEB) return;

    // needed for client cache
    this.handleHeader(this.props);

    this.props.dispatch(navActions.setLevel("CONTENT"));
    this.props.dispatch(navActions.setAction("CONTENT", {
      id: 2,
      action: this.likeableAction,
    }));
  }

  componentWillUpdate(nextProps) {
    this.handleHeader(nextProps);
  }

  handleHeader = (nextProps) => {
    const content = nextProps.series.content;
    if(!content) return;

    const { isLight } = nextProps.series.content.content;
    const color = collections.color(content);

    const { live } = this.props.live;

    const options = {
      title: "Series",
      color: color,
      light: !isLight,
    };

    if (!live) options.subTitle = content.title;

    this.props.dispatch(headerActions.set(options));
  }

  playAudio = (e) => {
    e.preventDefault();
    const currentSermon = this.props.currentSermon.content;
    const series = this.props.series.content;
    this.props.dispatch(audioActions.setPlaying({
      track: {
        ...currentSermon.content.audio[0],
        title: currentSermon.title,
        artist: content.speakers(currentSermon),
      },
      album: series,
    }));
  }

  render() {
    const sermonContent = this.props.currentSermon.content;
    const seriesContent = this.props.series.content;

    if (!(sermonContent && seriesContent)) {
      // loading
      return (

            <Loading />

      );
    }

    const currentSermon = sermonContent;
    const series = seriesContent;

    return (

        <SingleVideoPlayer ooyalaId={currentSermon.content.ooyalaId} />

          <h7 style={{ verticalAlign: "middle" }}>Listen To Audio</h7>

{currentSermon.title}

{content.speakers(currentSermon)}

{time.date(currentSermon)}

        <SeriesVideoList id={this.props.params.id} />

    );
  }
}
<commit_msg>lint series single video<commit_after>import { Component, PropTypes } from "react";
import ReactMixin from "react-mixin";
import { connect } from "react-apollo";
import gql from "graphql-tag";

import Loading from "../../components/loading";
import {
  nav as navActions,
  audio as audioActions,
  header as headerActions,
} from "../../store";

import Headerable from "../../mixins/mixins.Header";
import Likeable from "../../mixins/mixins.Likeable";
import Shareable from "../../mixins/mixins.Shareable";

import time from "../../util/time";
import react from "../../util/react";
import contentHelpers from "../../util/content";
import collections from "../../util/collections";

import SingleVideoPlayer from "../../components/players/video/Player";
import SeriesVideoList from "./series.VideoList";

const mapQueriesToProps = ({ ownProps }) => ({
  currentSermon: {
    query: gql`
      query getSermon($sermonId: ID!) {
        content: node(id: $sermonId) {
          ... on Content {
            entryId: id
            title
            status
            channelName
            meta {
              urlTitle
              siteId
              date
              actualDate
              channelId
            }
            content {
              audio {
                duration
                file: s3
              }
              description
              speaker
              ooyalaId
            }
          }
        }
      }
    `,
    variables: { sermonId: ownProps.params.sermonId },
    forceFetch: false,
    returnPartialData: false,
  },
  series: {
    query: gql`
      query getSeriesSingle($id: ID!) {
        content: node(id: $id) {
          id
          ... on Content {
            entryId: id
            title
            status
            channelName
            meta {
              urlTitle
              siteId
              date
              channelId
            }
            content {
              description
              images(sizes: ["large", "medium", "small"]) {
                fileName
                fileType
                fileLabel
                url
                size
              }
              ooyalaId
              colors {
                id
                value
                description
              }
              isLight
            }
          }
        }
      }
    `,
    variables: { id: ownProps.params.id },
    forceFetch: false,
    returnPartialData: false,
  },
});

const mapStateToProps = state => ({ live: state.live });

@connect({ mapQueriesToProps, mapStateToProps })
@ReactMixin.decorate(Likeable)
@ReactMixin.decorate(Shareable)
@ReactMixin.decorate(Headerable)
export default class SeriesSingleVideo extends Component {

  static propTypes = {
    dispatch: PropTypes.function.isRequired,
    live: {
      live: PropTypes.boolean.isRequired,
    },
    currentSermon: {
      content: PropTypes.object.isRequired,
    },
    series: {
      content: PropTypes.object.isRequired,
    },
    params: {
      id: PropTypes.string.isRequired,
    },
  }

  componentWillMount() {
    if (process.env.WEB) return;

    // needed for client cache
    this.handleHeader(this.props);

    this.props.dispatch(navActions.setLevel("CONTENT"));
    this.props.dispatch(navActions.setAction("CONTENT", {
      id: 2,
      action: this.likeableAction,
    }));
  }

  componentWillUpdate(nextProps) {
    this.handleHeader(nextProps);
  }

  handleHeader = (nextProps) => {
    const content = nextProps.series.content;
    if(!content) return;

    const { isLight } = nextProps.series.content.content;
    const color = collections.color(content);

    const { live } = this.props.live;

    const options = {
      title: "Series",
      color: color,
      light: !isLight,
    };

    if (!live) options.subTitle = content.title;

    this.props.dispatch(headerActions.set(options));
  }

  playAudio = (e) => {
    e.preventDefault();
    const currentSermon = this.props.currentSermon.content;
    const series = this.props.series.content;
    this.props.dispatch(audioActions.setPlaying({
      track: {
        ...currentSermon.content.audio[0],
        title: currentSermon.title,
        artist: contentHelpers.speakers(currentSermon),
      },
      album: series,
    }));
  }

  render() {
    const sermonContent = this.props.currentSermon.content;
    const seriesContent = this.props.series.content;

    if (!(sermonContent && seriesContent)) {
      // loading
      return (

            <Loading />

      );
    }

    const currentSermon = sermonContent;

    return (

        <SingleVideoPlayer ooyalaId={currentSermon.content.ooyalaId} />

          <h7 style={{ verticalAlign: "middle" }}>Listen To Audio</h7>

{currentSermon.title}

{contentHelpers.speakers(currentSermon)}

{time.date(currentSermon)}

        <SeriesVideoList id={this.props.params.id} />

    );
  }
}
<|endoftext|>


<commit_before>/*
*  Copyright (c) Codiad & Kent Safranski (codiad.com), distributed
*  as-is and without warranty under the MIT License. See
*  [root]/license.txt for more. This information must remain intact.
*/

//////////////////////////////////////////////////////////////////////
// CTRL Key Bind
//////////////////////////////////////////////////////////////////////

$.ctrl = function(key, callback, args) {
    var isCtrl = false;
    $(document).keydown(function(e) {
        if(!args) args=[];
        isCtrl = e.ctrlKey;
        if(e.keyCode == key && isCtrl) {
            callback.apply(this, args);
            isCtrl = false;
            return false;
        }
    }).keyup(function(e) {
        if(e.ctrlKey) isCtrl = false;
    });
};

$(function(){ keybindings.init(); });

//////////////////////////////////////////////////////////////////////
// Bindings
//////////////////////////////////////////////////////////////////////

var keybindings = {

    init : function(){

        // Close Modals //////////////////////////////////////////////
        $(document).keyup(function(e){ if(e.keyCode == 27){ modal.unload(); } });

        // Save [CTRL+S] /////////////////////////////////////////////
        $.ctrl('83', function(){ active.save(); });

        // Open in browser [CTRL+O] //////////////////////////////////
        $.ctrl('79', function(){ active.open_in_browser(); });

        // Find [CTRL+F] /////////////////////////////////////////////
        $.ctrl('70', function(){ editor.open_search('find'); });

        // Replace [CTRL+R] //////////////////////////////////////////
        $.ctrl('82', function(){ editor.open_search('replace'); });

        // Active List Up ////////////////////////////////////////////
        $.ctrl('38', function(){ active.move('up'); });

        // Active List Down //////////////////////////////////////////
        $.ctrl('40', function(){ active.move('down'); });

    }

};<commit_msg>Minor code cleanup<commit_after>/*
*  Copyright (c) Codiad & Kent Safranski (codiad.com), distributed
*  as-is and without warranty under the MIT License. See
*  [root]/license.txt for more. This information must remain intact.
*/

//////////////////////////////////////////////////////////////////////
// CTRL Key Bind
//////////////////////////////////////////////////////////////////////

$.ctrl = function(key, callback, args) {
    $(document).keydown(function(e) {
        if(!args) args=[];
        if(e.keyCode == key && e.ctrlKey) {
            callback.apply(this, args);
            return false;
        }
    });
};

$(function(){ keybindings.init(); });

//////////////////////////////////////////////////////////////////////
// Bindings
//////////////////////////////////////////////////////////////////////

var keybindings = {

    init : function(){

        // Close Modals //////////////////////////////////////////////
        $(document).keyup(function(e){ if(e.keyCode == 27){ modal.unload(); } });

        // Save [CTRL+S] /////////////////////////////////////////////
        $.ctrl('83', function(){ active.save(); });

        // Open in browser [CTRL+O] //////////////////////////////////
        $.ctrl('79', function(){ active.open_in_browser(); });

        // Find [CTRL+F] /////////////////////////////////////////////
        $.ctrl('70', function(){ editor.open_search('find'); });

        // Replace [CTRL+R] //////////////////////////////////////////
        $.ctrl('82', function(){ editor.open_search('replace'); });

        // Active List Up ////////////////////////////////////////////
        $.ctrl('38', function(){ active.move('up'); });

        // Active List Down //////////////////////////////////////////
        $.ctrl('40', function(){ active.move('down'); });

    }

};<|endoftext|>


<commit_before>import BaseSprite from './basesprite'

import {attr, readonly} from './decorators'
import {parseColorString, createPath, getLinearGradients} from './utils'

import {Effects} from 'sprite-animator'
import pathEffect from './path-effect/index'

Effects.d = pathEffect

function calPathRect(attr) {
  let path = attr.loadObj('path')
  const {
    d, lineCap, lineJoin, lineWidth,
  } = attr

  if(!path) {
    if(d) { // Deserialized sprite may have d value but no path obj
      path = createPath(d)
      attr.saveObj('path', path)
    } else {
      return [0, 0, 0, 0]
    }
  }

  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
  svg.style.visibility = 'hidden'
  svg.style.position = 'absolute'
  svg.style.top = 0
  svg.style.left = 0
  // svg.setAttribute('width', 1000)
  // svg.setAttribute('height', 1000)
  // svg.setAttribute('preserveAspectRatio', 'none')
  path.setAttribute('stroke-width', lineWidth)
  path.setAttribute('stroke', '#f00')
  path.setAttribute('stroke-linecap', lineCap)
  path.setAttribute('stroke-linejoin', lineJoin)
  svg.appendChild(path)
  document.body.appendChild(svg)
  const {x: x0, y: y0} = svg.getBoundingClientRect()
  const {
    x, y, width, height,
  } = path.getBoundingClientRect()
  const [ox, oy] = [x - x0, y - y0]
  document.body.removeChild(svg)

  const pathRect = [ox, oy, width, height]

  attr.saveObj('pathRect', pathRect)

  return pathRect
}

function getBoundingBox(attr, forceUpdate) {
  let pathRect = forceUpdate ? null : attr.loadObj('pathRect')

  if(!pathRect) {
    pathRect = calPathRect(attr)
  }

  const [x, y, width, height] = pathRect,
    lw = Math.ceil(attr.lineWidth / 2)

  return [-lw, -lw, x + width + lw, y + height + lw]
}

export class PathSpriteAttr extends BaseSprite.Attr {
  constructor(subject) {
    super(subject)
    this.merge({
      lineWidth: 1,
      lineCap: 'butt',
      lineJoin: 'miter',
      strokeColor: parseColorString('black'),
      fillColor: '',
      renderMode: 'stroke', // stroke, fill
      // d: path2d,
      boxSize: [0, 0],
    })
  }

  @readonly
  get boundingBox() {
    return getBoundingBox(this)
  }

  @readonly
  get pathRect() {
    const pathRect = this.loadObj('pathRect')
    return pathRect || [0, 0, 0, 0]
  }

  @attr('repaint')
  set d(val) {
    const path = createPath(val)
    this.set('d', path.getAttribute('d'))
    this.saveObj('path', path)

    const box = getBoundingBox(this, true)
    this.set('boxSize', [box[2] - box[0], box[3] - box[1]])

    const [x0, y0] = this.translate
    const offset = this.get('dOffset') || [0, 0]

    this.translate = [x0 + box[0] - offset[0], y0 + box[1] - offset[1]]
    this.set('dOffset', [box[0], box[1]])
  }
  get d() {
    return this.get('d')
  }

  @readonly
  get boxSize() {
    return this.get('boxSize')
  }

  @attr('repaint')
  set lineWidth(val) {
    this.set('lineWidth', Math.round(val))

    if(this.d) {
      const box = getBoundingBox(this, false)
      this.set('boxSize', [box[2] - box[0], box[3] - box[1]])
    }
  }
  get lineWidth() {
    return this.get('lineWidth')
  }

  /**
    lineCap: butt|round|square
   */
  @attr('repaint')
  set lineCap(val) {
    this.set('lineCap', val)
  }
  get lineCap() {
    return this.get('lineCap')
  }

  /**
    lineJoin: miter|round|bevel
   */
  @attr('repaint')
  set lineJoin(val) {
    this.set('lineJoin', val)
  }
  get lineJoin() {
    return this.get('lineJoin')
  }

  @attr('repaint')
  set renderMode(val) {
    this.set('renderMode', val)
  }
  get renderMode() {
    return this.get('renderMode')
  }

  @attr('repaint')
  set color(val) {
    this.strokeColor = val
  }
  get color() {
    return this.strokeColor
  }

  @attr('repaint')
  set strokeColor(val) {
    this.set('strokeColor', parseColorString(val))
  }
  get strokeColor() {
    return this.get('strokeColor')
  }

  @attr('repaint')
  set fillColor(val) {
    this.set('fillColor', parseColorString(val))
  }
  get fillColor() {
    return this.get('fillColor')
  }
}

class Path extends BaseSprite {
  static Attr = PathSpriteAttr

  constructor(d, opts) {
    super(opts)
    if(d) {
      this.attr({d})
    }
  }

  get contentSize() {
    let [width, height] = this.attr('size')

    const boxSize = this.attr('boxSize')

    if(width === '') {
      width = boxSize[0] | 0
    }
    if(height === '') {
      height = boxSize[1] | 0
    }

    return [width, height]
  }

  get pathRect() {
    return this.attr('pathRect')
  }

  getPointAtLength(length) {
    const path = createPath(this.attr('d'))
    const {x, y} = path.getPointAtLength(length)
    return [x, y]
  }

  getPathLength() {
    const path = createPath(this.attr('d'))
    return path.getTotalLength()
  }

  render(t) {
    const context = super.render(t),
      attr = this.attr()

    if(attr.d) {
      const box = attr.boundingBox

      context.translate(-box[0], -box[1])

      const p = this.createPath(attr.d),
        {strokeColor, fillColor} = attr

      context.lineWidth = attr.lineWidth
      context.lineCap = attr.lineCap
      context.lineJoin = attr.lineJoin

      const [width, height] = this.contentSize,
        [borderWidth] = attr.border

      const linearGradients = attr.linearGradients

      if(linearGradients && linearGradients.strokeColor) {
        const rect = linearGradients.strokeColor.rect || [borderWidth, borderWidth,
          width, height]

        context.strokeStyle = getLinearGradients(context, rect, linearGradients.strokeColor)
      } else {
        context.strokeStyle = strokeColor
      }

      context.stroke(p)

      if(attr.renderMode === 'fill') {
        if(linearGradients && linearGradients.fillColor) {
          const rect = linearGradients.fillColor.rect || [borderWidth, borderWidth,
            width, height]

          context.fillStyle = getLinearGradients(context, rect, linearGradients.fillColor)
        } else {
          context.fillStyle = fillColor || strokeColor
        }

        context.fill(p)
      }

      this.path = p
    }

    return context
  }
}

export default Path
<commit_msg>fix: calPathRect<commit_after>import BaseSprite from './basesprite'

import {attr, readonly} from './decorators'
import {parseColorString, createPath, getLinearGradients} from './utils'

import {Effects} from 'sprite-animator'
import pathEffect from './path-effect/index'

Effects.d = pathEffect

function calPathRect(attr) {
  let path = attr.loadObj('path')
  const {
    d, lineCap, lineJoin, lineWidth,
  } = attr

  if(!path) {
    if(d) { // Deserialized sprite may have d value but no path obj
      path = createPath(d)
      attr.saveObj('path', path)
    } else {
      return [0, 0, 0, 0]
    }
  }

  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
  svg.style.visibility = 'hidden'
  svg.style.position = 'absolute'
  svg.style.top = 0
  svg.style.left = 0
  // svg.setAttribute('width', 1000)
  // svg.setAttribute('height', 1000)
  // svg.setAttribute('preserveAspectRatio', 'none')
  path.setAttribute('stroke-width', lineWidth)
  path.setAttribute('stroke', '#f00')
  path.setAttribute('stroke-linecap', lineCap)
  path.setAttribute('stroke-linejoin', lineJoin)
  svg.appendChild(path)
  document.body.appendChild(svg)
  const {left: x0, top: y0} = svg.getBoundingClientRect()
  const {
    left, top, width, height,
  } = path.getBoundingClientRect()
  const [ox, oy] = [left - x0, top - y0]
  document.body.removeChild(svg)

  const pathRect = [ox, oy, width, height]

  attr.saveObj('pathRect', pathRect)

  return pathRect
}

function getBoundingBox(attr, forceUpdate) {
  let pathRect = forceUpdate ? null : attr.loadObj('pathRect')

  if(!pathRect) {
    pathRect = calPathRect(attr)
  }

  const [x, y, width, height] = pathRect,
    lw = Math.ceil(attr.lineWidth / 2)

  return [-lw, -lw, x + width + lw, y + height + lw]
}

export class PathSpriteAttr extends BaseSprite.Attr {
  constructor(subject) {
    super(subject)
    this.merge({
      lineWidth: 1,
      lineCap: 'butt',
      lineJoin: 'miter',
      strokeColor: parseColorString('black'),
      fillColor: '',
      renderMode: 'stroke', // stroke, fill
      // d: path2d,
      boxSize: [0, 0],
    })
  }

  @readonly
  get boundingBox() {
    return getBoundingBox(this)
  }

  @readonly
  get pathRect() {
    const pathRect = this.loadObj('pathRect')
    return pathRect || [0, 0, 0, 0]
  }

  @attr('repaint')
  set d(val) {
    const path = createPath(val)
    this.set('d', path.getAttribute('d'))
    this.saveObj('path', path)

    const box = getBoundingBox(this, true)
    this.set('boxSize', [box[2] - box[0], box[3] - box[1]])

    const [x0, y0] = this.translate
    const offset = this.get('dOffset') || [0, 0]

    this.translate = [x0 + box[0] - offset[0], y0 + box[1] - offset[1]]
    this.set('dOffset', [box[0], box[1]])
  }
  get d() {
    return this.get('d')
  }

  @readonly
  get boxSize() {
    return this.get('boxSize')
  }

  @attr('repaint')
  set lineWidth(val) {
    this.set('lineWidth', Math.round(val))

    if(this.d) {
      const box = getBoundingBox(this, false)
      this.set('boxSize', [box[2] - box[0], box[3] - box[1]])
    }
  }
  get lineWidth() {
    return this.get('lineWidth')
  }

  /**
    lineCap: butt|round|square
   */
  @attr('repaint')
  set lineCap(val) {
    this.set('lineCap', val)
  }
  get lineCap() {
    return this.get('lineCap')
  }

  /**
    lineJoin: miter|round|bevel
   */
  @attr('repaint')
  set lineJoin(val) {
    this.set('lineJoin', val)
  }
  get lineJoin() {
    return this.get('lineJoin')
  }

  @attr('repaint')
  set renderMode(val) {
    this.set('renderMode', val)
  }
  get renderMode() {
    return this.get('renderMode')
  }

  @attr('repaint')
  set color(val) {
    this.strokeColor = val
  }
  get color() {
    return this.strokeColor
  }

  @attr('repaint')
  set strokeColor(val) {
    this.set('strokeColor', parseColorString(val))
  }
  get strokeColor() {
    return this.get('strokeColor')
  }

  @attr('repaint')
  set fillColor(val) {
    this.set('fillColor', parseColorString(val))
  }
  get fillColor() {
    return this.get('fillColor')
  }
}

class Path extends BaseSprite {
  static Attr = PathSpriteAttr

  constructor(d, opts) {
    super(opts)
    if(d) {
      this.attr({d})
    }
  }

  get contentSize() {
    let [width, height] = this.attr('size')

    const boxSize = this.attr('boxSize')

    if(width === '') {
      width = boxSize[0] | 0
    }
    if(height === '') {
      height = boxSize[1] | 0
    }

    return [width, height]
  }

  get pathRect() {
    return this.attr('pathRect')
  }

  getPointAtLength(length) {
    const path = createPath(this.attr('d'))
    const {x, y} = path.getPointAtLength(length)
    return [x, y]
  }

  getPathLength() {
    const path = createPath(this.attr('d'))
    return path.getTotalLength()
  }

  render(t) {
    const context = super.render(t),
      attr = this.attr()

    if(attr.d) {
      const box = attr.boundingBox

      context.translate(-box[0], -box[1])

      const p = this.createPath(attr.d),
        {strokeColor, fillColor} = attr

      context.lineWidth = attr.lineWidth
      context.lineCap = attr.lineCap
      context.lineJoin = attr.lineJoin

      const [width, height] = this.contentSize,
        [borderWidth] = attr.border

      const linearGradients = attr.linearGradients

      if(linearGradients && linearGradients.strokeColor) {
        const rect = linearGradients.strokeColor.rect || [borderWidth, borderWidth,
          width, height]

        context.strokeStyle = getLinearGradients(context, rect, linearGradients.strokeColor)
      } else {
        context.strokeStyle = strokeColor
      }

      context.stroke(p)

      if(attr.renderMode === 'fill') {
        if(linearGradients && linearGradients.fillColor) {
          const rect = linearGradients.fillColor.rect || [borderWidth, borderWidth,
            width, height]

          context.fillStyle = getLinearGradients(context, rect, linearGradients.fillColor)
        } else {
          context.fillStyle = fillColor || strokeColor
        }

        context.fill(p)
      }

      this.path = p
    }

    return context
  }
}

export default Path
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import AceEditor from 'react-ace';

import 'brace/mode/gherkin';
import 'brace/theme/monokai';

class Feature extends Component {
  constructor(props) {
    super();
    this.state = {
      body: props.feature.body
    };
  }

  render() {
    const { feature } = this.props;
    return (

{feature.name}

        <AceEditor
          value={this.state.body}
          onChange={this.onChange}
          width="1024px"
          minLines={15}
          maxLines={300}
          mode="gherkin"
          theme="monokai"
          setOptions={{useSoftTabs: true}}
          tabSize={2}
          editorProps={{$blockScrolling: true}} />

    );
  }

  onChange = (value) => {
    this.setState({text: value});
  };
}

Feature.propTypes = {
  feature: PropTypes.object.isRequired
};

export default Feature;<commit_msg>Fix ace editor onChange<commit_after>import React, { Component, PropTypes } from 'react';
import AceEditor from 'react-ace';

import 'brace/mode/gherkin';
import 'brace/theme/monokai';

class Feature extends Component {
  constructor(props) {
    super();
    this.state = {
      body: props.feature.body
    };
  }

  render() {
    const { feature } = this.props;
    return (

{feature.name}

        <AceEditor
          value={this.state.body}
          onChange={this.onChange}
          width="1024px"
          minLines={15}
          maxLines={300}
          mode="gherkin"
          theme="monokai"
          setOptions={{useSoftTabs: true}}
          tabSize={2}
          editorProps={{$blockScrolling: true}} />

    );
  }

  onChange = (value) => {
    this.setState({body: value});
  };
}

Feature.propTypes = {
  feature: PropTypes.object.isRequired
};

export default Feature;<|endoftext|>


<commit_before>import { undoScrollbar } from '../utils/scrollbarFix.js'
import { undoIOSfix } from '../utils/iosFix.js'
import { undoIEfix } from '../utils/ieFix.js'
import { unsetAriaHidden } from '../utils/aria.js'
import * as dom from '../utils/dom/index.js'
import { swalClasses } from '../utils/classes.js'
import globalState, { restoreActiveElement } from '../globalState.js'
import privateProps from '../privateProps.js'
import privateMethods from '../privateMethods.js'
/*
 * Instance method to close sweetAlert
 */

function removePopupAndResetState (container, isToast, onAfterClose) {
  if (isToast) {
    triggerOnAfterClose(onAfterClose)
  } else {
    restoreActiveElement().then(() => triggerOnAfterClose(onAfterClose))
    globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, { capture: globalState.keydownListenerCapture })
    globalState.keydownHandlerAdded = false
  }

  // Unset globalState props so GC will dispose globalState (#1569)
  delete globalState.keydownHandler
  delete globalState.keydownTarget

  if (container.parentNode) {
    container.parentNode.removeChild(container)
  }
  removeBodyClasses()

  if (dom.isModal()) {
    undoScrollbar()
    undoIOSfix()
    undoIEfix()
    unsetAriaHidden()
  }
}

function removeBodyClasses () {
  dom.removeClass(
    [document.documentElement, document.body],
    [
      swalClasses.shown,
      swalClasses['height-auto'],
      swalClasses['no-backdrop'],
      swalClasses['toast-shown'],
      swalClasses['toast-column']
    ]
  )
}

function swalCloseEventFinished (popup, container, isToast, onAfterClose) {
  if (dom.hasClass(popup, swalClasses.hide)) {
    removePopupAndResetState(container, isToast, onAfterClose)
  }

  // Unset WeakMaps so GC will be able to dispose them (#1569)
  unsetWeakMaps(privateProps)
  unsetWeakMaps(privateMethods)
}

export function close (resolveValue) {
  const container = dom.getContainer()
  const popup = dom.getPopup()

  if (!popup || dom.hasClass(popup, swalClasses.hide)) {
    return
  }

  const innerParams = privateProps.innerParams.get(this)
  const swalPromiseResolve = privateMethods.swalPromiseResolve.get(this)
  const onClose = innerParams.onClose
  const onAfterClose = innerParams.onAfterClose

  dom.removeClass(popup, swalClasses.show)
  dom.addClass(popup, swalClasses.hide)

  // If animation is supported, animate
  if (dom.animationEndEvent && dom.hasCssAnimation(popup)) {
    popup.addEventListener(dom.animationEndEvent, function (e) {
      if (e.target === popup) {
        swalCloseEventFinished(popup, container, dom.isToast(), onAfterClose)
      }
    })
  } else {
    // Otherwise, remove immediately
    removePopupAndResetState(container, dom.isToast(), onAfterClose)
  }

  if (onClose !== null && typeof onClose === 'function') {
    onClose(popup)
  }

  // Resolve Swal promise
  swalPromiseResolve(resolveValue || {})

  // Unset this.params so GC will dispose it (#1569)
  delete this.params
}

const unsetWeakMaps = (obj) => {
  for (const i in obj) {
    obj[i] = new WeakMap()
  }
}

const triggerOnAfterClose = (onAfterClose) => {
  if (onAfterClose !== null && typeof onAfterClose === 'function') {
    setTimeout(() => {
      onAfterClose()
    })
  }
}

export {
  close as closePopup,
  close as closeModal,
  close as closeToast
}
<commit_msg>chore: fix complexity of close() method (#1641)<commit_after>import { undoScrollbar } from '../utils/scrollbarFix.js'
import { undoIOSfix } from '../utils/iosFix.js'
import { undoIEfix } from '../utils/ieFix.js'
import { unsetAriaHidden } from '../utils/aria.js'
import * as dom from '../utils/dom/index.js'
import { swalClasses } from '../utils/classes.js'
import globalState, { restoreActiveElement } from '../globalState.js'
import privateProps from '../privateProps.js'
import privateMethods from '../privateMethods.js'
/*
 * Instance method to close sweetAlert
 */

function removePopupAndResetState (container, isToast, onAfterClose) {
  if (isToast) {
    triggerOnAfterClose(onAfterClose)
  } else {
    restoreActiveElement().then(() => triggerOnAfterClose(onAfterClose))
    globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, { capture: globalState.keydownListenerCapture })
    globalState.keydownHandlerAdded = false
  }

  // Unset globalState props so GC will dispose globalState (#1569)
  delete globalState.keydownHandler
  delete globalState.keydownTarget

  if (container.parentNode) {
    container.parentNode.removeChild(container)
  }
  removeBodyClasses()

  if (dom.isModal()) {
    undoScrollbar()
    undoIOSfix()
    undoIEfix()
    unsetAriaHidden()
  }
}

function removeBodyClasses () {
  dom.removeClass(
    [document.documentElement, document.body],
    [
      swalClasses.shown,
      swalClasses['height-auto'],
      swalClasses['no-backdrop'],
      swalClasses['toast-shown'],
      swalClasses['toast-column']
    ]
  )
}

function swalCloseEventFinished (popup, container, isToast, onAfterClose) {
  if (dom.hasClass(popup, swalClasses.hide)) {
    removePopupAndResetState(container, isToast, onAfterClose)
  }

  // Unset WeakMaps so GC will be able to dispose them (#1569)
  unsetWeakMaps(privateProps)
  unsetWeakMaps(privateMethods)
}

export function close (resolveValue) {

  const popup = dom.getPopup()

  if (!popup || dom.hasClass(popup, swalClasses.hide)) {
    return
  }

  const innerParams = privateProps.innerParams.get(this)
  const swalPromiseResolve = privateMethods.swalPromiseResolve.get(this)
  const { onClose, onAfterClose } = innerParams

  dom.removeClass(popup, swalClasses.show)
  dom.addClass(popup, swalClasses.hide)

  handlePopupAnimation( popup, onAfterClose )

  if (onClose !== null && typeof onClose === 'function') {
    onClose(popup)
  }

  // Resolve Swal promise
  swalPromiseResolve(resolveValue || {})

  // Unset this.params so GC will dispose it (#1569)
  delete this.params
}

const handlePopupAnimation = ( popup,  onAfterClose ) => {
  const container = dom.getContainer()
  // If animation is supported, animate
  const animationIsSupported = dom.animationEndEvent && dom.hasCssAnimation(popup);

  if (animationIsSupported) {
    animatePopup( popup, container, onAfterClose )
  } else {
    // Otherwise, remove immediately
    removePopupAndResetState(container, dom.isToast(), onAfterClose)
  }
}

const animatePopup = ( popup, container, onAfterClose ) => {
  popup.addEventListener(dom.animationEndEvent, function (e) {
    if (e.target === popup) {
      swalCloseEventFinished(popup, container, dom.isToast(), onAfterClose)
    }
  })
}

const unsetWeakMaps = (obj) => {
  for (const i in obj) {
    obj[i] = new WeakMap()
  }
}

const triggerOnAfterClose = (onAfterClose) => {
  if (onAfterClose !== null && typeof onAfterClose === 'function') {
    setTimeout(() => {
      onAfterClose()
    })
  }
}

export {
  close as closePopup,
  close as closeModal,
  close as closeToast
}
<|endoftext|>


<commit_before>import { details as settingDetails } from './settings'

export default {
    status: {
        alertMessage: null,  // reveal status to user
        provider: '4chan',
        boardID: null,
        threadID: null,
    },

    display: {
        isAppReady: false,
        isScrolling: false,  // app scroll
        isHeaderVisible: true,
        isNavbarOpen: false,
        isDrawerOpen: true,
        isThreadOpen: false,
        activeHeaderPanel: null,  // responses to header buttons
    },

    boardList: {
        didInvalidate: false,
        favourites: [],
        items: []
    },

    cache: {
        board: { },
        thread: { }
    },

    board: {
        receivedAt: 0,  // unix timestamp
        isFetching: false,
        didInvalidate: false,
        searchWord: null,
        filterWords: [],
        posts: [],
        limit: 30  // infinite scroll
    },

    thread: {
        receivedAt: 0,  // unix timestamp
        isActive: false,
        isFetching: false,
        didInvalidate: false,
        isBeingWatched: false,
        posts: [],
    },

    threadMonitor: {
        newPosts: 0,
        threads: [
            // e.g. {threadID, boardID, posts}
        ]
    },

    post: {
        isAuthenticated: false,
        type: null,  // thread/comment
        references: [],
        message: null,  // user input
        upload: null
        // canReply
    },

    settings: {
        internal: {
            requestThrottle: 3,
            maxBoardAge: 900,  // 15 mins
            maxThreadAge: 900,  // 15 mins
        },

        external: {
            theme: 'dark',
            homeBoard: 'g',
            nsfw: false,
            downloadLocation: '~/Downloads/Lurka',
            boardUpdateInterval: 15,
            threadUpdateInterval: 5,
            autoMute: false
        },

       details: settingDetails
    }
}
<commit_msg>feat(initialState): Add state.display.activeView<commit_after>import { details as settingDetails } from './settings'

export default {
    status: {
        alertMessage: null,  // reveal status to user
        provider: '4chan',
        boardID: null,
        threadID: null,
    },

    display: {
        isAppReady: false,
        isScrolling: false,  // app scroll
        isHeaderVisible: true,
        isNavbarOpen: false,
        isDrawerOpen: true,
        isThreadOpen: false,
        activeView: "content",  // for toggling home page
        activeHeaderPanel: null,  // responses to header buttons
    },

    boardList: {
        didInvalidate: false,
        favourites: [],
        items: []
    },

    cache: {
        board: { },
        thread: { }
    },

    board: {
        receivedAt: 0,  // unix timestamp
        isFetching: false,
        didInvalidate: false,
        searchWord: null,
        filterWords: [],
        posts: [],
        limit: 30  // infinite scroll
    },

    thread: {
        receivedAt: 0,  // unix timestamp
        isActive: false,
        isFetching: false,
        didInvalidate: false,
        isBeingWatched: false,
        posts: [],
    },

    threadMonitor: {
        newPosts: 0,
        threads: [
            // e.g. {threadID, boardID, posts}
        ]
    },

    post: {
        isAuthenticated: false,
        type: null,  // thread/comment
        references: [],
        message: null,  // user input
        upload: null
        // canReply
    },

    settings: {
        internal: {
            requestThrottle: 3,
            maxBoardAge: 900,  // 15 mins
            maxThreadAge: 900,  // 15 mins
        },

        external: {
            theme: 'dark',
            homeBoard: 'g',
            nsfw: false,
            downloadLocation: '~/Downloads/Lurka',
            boardUpdateInterval: 15,
            threadUpdateInterval: 5,
            autoMute: false
        },

       details: settingDetails
    }
}
<|endoftext|>


<commit_before>import React from 'react';

const Message = () => {
  return (

{this.props.user}

{this.props.message}

  );
};

export default Message;
<commit_msg>Fix `props` references in Message<commit_after>import React from 'react';

const Message = (props) => {
  return (

{props.user}

{props.message}

  );
};

export default Message;
<|endoftext|>