<commit_before>'use strict';

const chalk = require('chalk')
  , cnsl = require('./utils/cnsl')
  , compiler = require('./helpers/compile')
  , fileCache = require('./utils/fileCache')
  , filetype = require('./utils/filetype')
  , fs = require('fs')
  , idResource = require('identify-resource')
  , isEqual = require('lodash/isEqual')
  , path = require('path')
  , series = require('async').series
  , truncate = require('./utils/truncate')

  , RE_BUDDY_BUILT = /\/\* generated by Buddy/
    // Test to determine if file has already been generated by buddy/browserify/component
  , RE_BUILT = /(?:\[function\(require,\s?module,\s?exports\))/

  , debug = cnsl.debug
  , error = cnsl.error
  , hasMultipleVersions = idResource.hasMultipleVersions
  , helpers = {
      write: require('./helpers/write'),
      compile: compiler.compile,
      compress: require('./helpers/compress'),
      concat: require('./helpers/concat'),
      inline: require('./helpers/inline'),
      parse: require('./helpers/parse'),
      replace: require('./helpers/replace'),
      wrap: require('./helpers/wrap')
    }
  , identify = idResource.identify
  , print = cnsl.print
  , resolve = idResource.resolve
  , strong = cnsl.strong
  , warn = cnsl.warn;

/**
 * File instance factory
 * @param {String} filepath
 * @param {Object} options
 * @returns {File}
 */
var factory = module.exports = function factory (filepath, options) {
  let file;

  // Retrieve cached
  if (factory.cache.hasFile(filepath)) {
    file = factory.cache.getFile(filepath);

  // Create new if valid id
  } else {
    const type = filetype(filepath, options.fileExtensions)
      , opts = {
          fileExtensions: options.fileExtensions,
          sources: options.sources,
          type: type
        }
      , id = identify(filepath, opts);

    if (id) {
      file = new File(id, filepath, type, options);
      factory.cache.addFile(file);
      // Warn of multiple versions
      if (hasMultipleVersions(id)) {
        warn('more than one version of '
          + strong(id.split('#')[0])
          + ' exists ('
          + strong(file.relpath)
          + ')', 3);
      }
    } else {
      throw new Error('unable to create file for: ' + filepath);
    }
  }

  return file;
};

/*
 * Default file cache
 */
module.exports.cache = fileCache();

class File {
  /**
   * Constructor
   * @param {String} id
   * @param {String} filepath
   * @param {String} type
   * @param {Object} options
   */
  constructor (id, filepath, type, options) {
    this.content = '';
    this.compiledContent = '';
    this.dependencies = [];
    this.dependencyReferences = [];
    this.fileContent = '';
    this.filepath = filepath;
    this.id = id;
    this.isBuddyBuilt = false;
    this.isBuilt = false;
    this.isDependency = false;
    this.isInline = false;
    this.isLocked = false;
    this.options = options;
    this.type = type;

    this.extension = path.extname(this.filepath).slice(1);
    this.relpath = truncate(path.relative(process.cwd(), filepath));
    this.name = path.basename(this.filepath);

    debug('created File instance ' + strong(this.relpath), 3);
  }

  /**
   * Retrieve writeable state
   * @param {Boolean} isBatch
   * @returns {Boolean}
   */
  isWriteable (isBatch) {
    return isBatch
      // Only writeable if not node_module in batch mode
      ? !~this.filepath.indexOf('node_modules')
      : !this.isDependency;
  }

  /**
   * Retrieve inlineable state
   * @returns {Boolean}
   */
  isInlineable () {
    return this.isInline;
  }

  /**
   * Retrieve flattened dependency tree
   * @param {Boolean} asReferences
   * @returns {Array}
   */
  getAllDependencies (asReferences) {
    const key = asReferences ? 'dependencyReferences' : 'dependencies'
      , self = this;

    let deps = [];

    function add (dependency, dependant) {
      const instance = dependency.instance || dependency;

      if (instance !== self && !~deps.indexOf(dependency)) {
        deps.push(dependency);
        // Add children
        if (key in instance) {
          instance[key].forEach((dep) => {
            // Protect against circular references
            if ((dep.instance || dep) != dependant) add(dep, dependency);
          });
        }
      }
    }

    this[key].forEach(add);

    return deps;
  }

  /**
   * Run 'workflows' tasks in sequence
   * @param {Array} workflow
   * @param {Object} options
   * @param {Function} fn(err, dependencies)
   * @returns {null}
   */
  run (workflow, options, fn) {
    if (!workflow.length) return fn(null, []);

    series(workflow.map((task) => {
      // Memoize
      return this[task].bind(this, options);
    }), (err) => {
      // Return dependencies if parsed
      fn(err, (~workflow.indexOf('parse') || ~workflow.indexOf('parseInline'))
        ? this.dependencies
        : []
      );
    });
  }

  /**
   * Read and store file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  load (options, fn) {
    if (!this.fileContent) {
      const content = fs.readFileSync(this.filepath, (this.type == 'image' && this.extension != 'svg') ? null : 'utf8');

      this.content = this.fileContent = content;
      // Determine if file has already been processed
      this.isBuddyBuilt = (this.type == 'js') && RE_BUDDY_BUILT.test(content);
      this.isBuilt = (this.type == 'js') && RE_BUILT.test(content);
      debug('load: ' + strong(this.relpath), 4);
    } else {
      this.content = this.fileContent;
    }

    fn();
  }

  /**
   * Compile file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  compile (options, fn) {
    // Only compile if not already
    if (!this.compiledContent) {
      // Expose properties for compilers
      let options = {
        id: this.id,
        type: this.type
      };

      if (this.type == 'html') {
        let dataUrl;

        // Gather all dependencies
        options.includes = [];
        this.getAllDependencies()
          .forEach((dependency) => {
            if (dependency.type != 'json') {
              const id = dependency.id
                , idx = id.indexOf('#');

              // Strip version
              if (idx != -1) id = id.slice(0, idx);
              options.includes.push({
                id: id,
                content: dependency.content,
                filepath: dependency.filepath
              });
            // Sidecar data file
            } else {
              try {
                options.data = JSON.parse(dependency.content);
              } catch (err) {
                warn('malformed json file: ' + strong(dependency.filepath));
              }
            }
          });
      } else if (this.type == 'css') {
        // Gather all directories
        options.paths = factory.cache.getDirs();
      }

      helpers.compile(this.filepath, this.content, this.options.compilers, options, (err, content) => {
        if (err) {
          // Don't quit on error
          error(err, 2, false);
          return fn();
        }
        debug('compile: ' + strong(this.relpath), 4);
        this.content = this.compiledContent = content;
        fn();
      });
    } else {
      this.content = this.compiledContent;
      fn();
    }
  }

  /**
   * Parse file contents for dependency references
   * @param {Object} options
   * @param {Function} fn(err)
   */
  parse (options, fn) {
    this._parse(false, options, fn);
  }

  /**
   * Parse file contents for inline dependency references
   * @param {Object} options
   * @param {Function} fn(err)
   */
  parseInline (options, fn) {
    this._parse(true, options, fn);
  }

  /**
   * Parse file contents
   * @param {Boolean} isInline
   * @param {Object} options
   * @param {Function} fn(err)
   */
  _parse (isInline, options, fn) {
    const helper = isInline ? helpers.parse.inline : helpers.parse;

    // Only parse unbuilt files
    if (!this.isBuddyBuilt || !this.isBuilt) {
      helper(this.filepath, this.type, this.content, (err, deps) => {
        if (err) return fn(err);

        debug('parse: ' + strong(this.relpath), 4);

        const options = {
          fileExtensions: this.options.fileExtensions,
          type: this.type,
          sources: this.options.sources
        };

        deps.forEach((dep) => {
          const filepath = resolve(this.filepath, dep.filepath, options);

          let instance;

          if (filepath !== '') {
            // Save context for future inlining
            this.dependencyReferences.push(dep);
            // Handle disabled/ignored
            // Ignore node_modules when watching
            if (filepath === false || (options.watching == 2 && ~filepath.indexOf('node_modules'))) {
              dep.isDisabled = true;
            } else if (instance = factory(filepath, this.options)) {
              // Store instance
              dep.instance = instance;
              // Process if not locked (parent target files are locked)
              if (!instance.isLocked) {
                // Store if not already stored
                if (!~this.dependencies.indexOf(instance)) {
                  instance.isDependency = true;
                  instance.isInline = 'stack' in dep;
                  this.dependencies.push(instance);
                }
              }
            }
          }

          if (!instance) {
            // Unable to resolve filepath
            if (!dep.isDisabled) {
              warn('dependency '
                + strong(dep.filepath)
                + ' for '
                + strong(this.id)
                + ' not found', 3);
            }
          }
        });

        fn();
      });
    }
  }

  /**
   * Inline dependency content
   * @param {Object} options
   * @param {Function} fn(err)
   */
  inline (options, fn) {
    const deps = (this.type != 'css')
      ? this.getAllDependencies(true)
      : this.dependencyReferences;

    helpers.inline(this.type, this.content, deps, this.options.runtimeOptions.compress, (err, content) => {
      if (err) return fn(err);
      debug('inline: ' + strong(this.relpath), 4);
      this.content = content;
      fn();
    });
  }

  /**
   * Replace relative dependency references with fully resolved
   * @param {Object} options
   * @param {Function} fn(err)
   */
  replaceReferences (options, fn) {
    this.content = helpers.replace.references(this.content, this.type, this.dependencyReferences);
    debug('replace dependency references: ' + strong(this.relpath), 4);
    fn();
  }

  /**
   * Replace process.env references with values
   * @param {Object} options
   * @param {Function} fn(err)
   */
  replaceEnvironment (options, fn) {
    this.content = helpers.replace.environment(this.content, this.dependencyReferences);
    debug('replace environment vars: ' + strong(this.relpath), 4);
    fn();
  }

  /**
   * Compress file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  compress (options, fn) {
    helpers.compress(this.type, this.content, this.options.compressors, (err, content) => {
      if (err) return fn(err);
      this.content = content;
      debug('compressed: ' + strong(this.relpath), 4);
      fn();
    });
  }

  /**
   * Wrap JS file contents in a module definition
   * @param {Object} options
   * @param {Function} fn(err)
   */
  wrap (options, fn) {
    // Allow wrapping of browserified bundles
    if (!this.isBuddyBuilt) {
      this.content = helpers.wrap(this.id, this.content);
      debug('wrap: ' + strong(this.relpath), 4);
    }
    fn();
  }

  /**
   * Concatenate file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  concat (options, fn) {
    this.content = helpers.concat(this.type, this.content, this.id, this.getAllDependencies().reverse(), options);
    debug('concat: ' + strong(this.relpath), 4);
    fn();
  }

  /**
   * Write file contents to disk
   * @param {String} filepath
   * @returns {String}
   */
  write (filepath) {
    helpers.write(filepath, this.content);

    print(chalk.green('built'
      + (this.options.runtimeOptions.compress ? ' and compressed ' : ' '))
      + strong(truncate(path.relative(process.cwd(), filepath))), 2);

    return filepath;
  }

  /**
   * Reset content
   * @param {Boolean} hard
   */
  reset (hard) {
    this.isLocked = false;
    this.isDependency = false;
    this.isInline = false;
    this.dependencies = [];
    this.dependencyReferences = [];
    if (this.type != 'js') {
      this.content = this.fileContent;
      this.compiledContent = '';
    } else {
      this.content = this.compiledContent || this.fileContent;
    }
    if (hard) {
      this.content = this.fileContent = this.compiledContent = '';
      // Following are set on load
      this.isBuddyBuilt = false;
      this.isBuilt = false;
    }
    debug('reset' + (hard ? ' (hard)' : '') + ': ' + strong(this.relpath), 4);
  }

  /**
   * Destroy instance
   */
  destroy () {
    this.reset(true);
    this.options = null;
  }
}<commit_msg>fix typo<commit_after>'use strict';

const chalk = require('chalk')
  , cnsl = require('./utils/cnsl')
  , compiler = require('./helpers/compile')
  , fileCache = require('./utils/fileCache')
  , filetype = require('./utils/filetype')
  , fs = require('fs')
  , idResource = require('identify-resource')
  , path = require('path')
  , series = require('async').series
  , truncate = require('./utils/truncate')

  , RE_BUDDY_BUILT = /\/\* generated by Buddy/
    // Test to determine if file has already been generated by buddy/browserify/component
  , RE_BUILT = /(?:\[function\(require,\s?module,\s?exports\))/

  , debug = cnsl.debug
  , error = cnsl.error
  , hasMultipleVersions = idResource.hasMultipleVersions
  , helpers = {
      write: require('./helpers/write'),
      compile: compiler.compile,
      compress: require('./helpers/compress'),
      concat: require('./helpers/concat'),
      inline: require('./helpers/inline'),
      parse: require('./helpers/parse'),
      replace: require('./helpers/replace'),
      wrap: require('./helpers/wrap')
    }
  , identify = idResource.identify
  , print = cnsl.print
  , resolve = idResource.resolve
  , strong = cnsl.strong
  , warn = cnsl.warn;

/**
 * File instance factory
 * @param {String} filepath
 * @param {Object} options
 * @returns {File}
 */
var factory = module.exports = function factory (filepath, options) {
  let file;

  // Retrieve cached
  if (factory.cache.hasFile(filepath)) {
    file = factory.cache.getFile(filepath);

  // Create new if valid id
  } else {
    const type = filetype(filepath, options.fileExtensions)
      , opts = {
          fileExtensions: options.fileExtensions,
          sources: options.sources,
          type: type
        }
      , id = identify(filepath, opts);

    if (id) {
      file = new File(id, filepath, type, options);
      factory.cache.addFile(file);
      // Warn of multiple versions
      if (hasMultipleVersions(id)) {
        warn('more than one version of '
          + strong(id.split('#')[0])
          + ' exists ('
          + strong(file.relpath)
          + ')', 3);
      }
    } else {
      throw new Error('unable to create file for: ' + filepath);
    }
  }

  return file;
};

/*
 * Default file cache
 */
module.exports.cache = fileCache();

class File {
  /**
   * Constructor
   * @param {String} id
   * @param {String} filepath
   * @param {String} type
   * @param {Object} options
   */
  constructor (id, filepath, type, options) {
    this.content = '';
    this.compiledContent = '';
    this.dependencies = [];
    this.dependencyReferences = [];
    this.fileContent = '';
    this.filepath = filepath;
    this.id = id;
    this.isBuddyBuilt = false;
    this.isBuilt = false;
    this.isDependency = false;
    this.isInline = false;
    this.isLocked = false;
    this.options = options;
    this.type = type;

    this.extension = path.extname(this.filepath).slice(1);
    this.relpath = truncate(path.relative(process.cwd(), filepath));
    this.name = path.basename(this.filepath);

    debug('created File instance ' + strong(this.relpath), 3);
  }

  /**
   * Retrieve writeable state
   * @param {Boolean} isBatch
   * @returns {Boolean}
   */
  isWriteable (isBatch) {
    return isBatch
      // Only writeable if not node_module in batch mode
      ? !~this.filepath.indexOf('node_modules')
      : !this.isDependency;
  }

  /**
   * Retrieve inlineable state
   * @returns {Boolean}
   */
  isInlineable () {
    return this.isInline;
  }

  /**
   * Retrieve flattened dependency tree
   * @param {Boolean} asReferences
   * @returns {Array}
   */
  getAllDependencies (asReferences) {
    const key = asReferences ? 'dependencyReferences' : 'dependencies'
      , self = this;

    let deps = [];

    function add (dependency, dependant) {
      const instance = dependency.instance || dependency;

      if (instance !== self && !~deps.indexOf(dependency)) {
        deps.push(dependency);
        // Add children
        if (key in instance) {
          instance[key].forEach((dep) => {
            // Protect against circular references
            if ((dep.instance || dep) != dependant) add(dep, dependency);
          });
        }
      }
    }

    this[key].forEach(add);

    return deps;
  }

  /**
   * Run 'workflows' tasks in sequence
   * @param {Array} workflow
   * @param {Object} options
   * @param {Function} fn(err, dependencies)
   * @returns {null}
   */
  run (workflow, options, fn) {
    if (!workflow.length) return fn(null, []);

    series(workflow.map((task) => {
      // Memoize
      return this[task].bind(this, options);
    }), (err) => {
      // Return dependencies if parsed
      fn(err, (~workflow.indexOf('parse') || ~workflow.indexOf('parseInline'))
        ? this.dependencies
        : []
      );
    });
  }

  /**
   * Read and store file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  load (options, fn) {
    if (!this.fileContent) {
      const content = fs.readFileSync(this.filepath, (this.type == 'image' && this.extension != 'svg') ? null : 'utf8');

      this.content = this.fileContent = content;
      // Determine if file has already been processed
      this.isBuddyBuilt = (this.type == 'js') && RE_BUDDY_BUILT.test(content);
      this.isBuilt = (this.type == 'js') && RE_BUILT.test(content);
      debug('load: ' + strong(this.relpath), 4);
    } else {
      this.content = this.fileContent;
    }

    fn();
  }

  /**
   * Compile file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  compile (options, fn) {
    // Only compile if not already
    if (!this.compiledContent) {
      // Expose properties for compilers
      let options = {
        id: this.id,
        type: this.type
      };

      if (this.type == 'html') {
        let dataUrl;

        // Gather all dependencies
        options.includes = [];
        this.getAllDependencies()
          .forEach((dependency) => {
            if (dependency.type != 'json') {
              const id = dependency.id
                , idx = id.indexOf('#');

              // Strip version
              if (idx != -1) id = id.slice(0, idx);
              options.includes.push({
                id: id,
                content: dependency.content,
                filepath: dependency.filepath
              });
            // Sidecar data file
            } else {
              try {
                options.data = JSON.parse(dependency.content);
              } catch (err) {
                warn('malformed json file: ' + strong(dependency.filepath));
              }
            }
          });
      } else if (this.type == 'css') {
        // Gather all directories
        options.paths = factory.cache.getDirs();
      }

      helpers.compile(this.filepath, this.content, this.options.compilers, options, (err, content) => {
        if (err) {
          // Don't quit on error
          error(err, 2, false);
          return fn();
        }
        debug('compile: ' + strong(this.relpath), 4);
        this.content = this.compiledContent = content;
        fn();
      });
    } else {
      this.content = this.compiledContent;
      fn();
    }
  }

  /**
   * Parse file contents for dependency references
   * @param {Object} options
   * @param {Function} fn(err)
   */
  parse (options, fn) {
    this._parse(false, options, fn);
  }

  /**
   * Parse file contents for inline dependency references
   * @param {Object} options
   * @param {Function} fn(err)
   */
  parseInline (options, fn) {
    this._parse(true, options, fn);
  }

  /**
   * Parse file contents
   * @param {Boolean} isInline
   * @param {Object} options
   * @param {Function} fn(err)
   */
  _parse (isInline, options, fn) {
    const helper = isInline ? helpers.parse.inline : helpers.parse;

    // Only parse unbuilt files
    if (!this.isBuddyBuilt || !this.isBuilt) {
      helper(this.filepath, this.type, this.content, (err, deps) => {
        if (err) return fn(err);

        debug('parse: ' + strong(this.relpath), 4);

        const resolveOptions = {
          fileExtensions: this.options.fileExtensions,
          type: this.type,
          sources: this.options.sources
        };

        deps.forEach((dep) => {
          const filepath = resolve(this.filepath, dep.filepath, resolveOptions);

          let instance;

          if (filepath !== '') {
            // Save context for future inlining
            this.dependencyReferences.push(dep);
            // Handle disabled/ignored
            // Ignore node_modules when watching
            if (filepath === false || (options.watching == 2 && ~filepath.indexOf('node_modules'))) {
              dep.isDisabled = true;
            } else if (instance = factory(filepath, this.options)) {
              // Store instance
              dep.instance = instance;
              // Process if not locked (parent target files are locked)
              if (!instance.isLocked) {
                // Store if not already stored
                if (!~this.dependencies.indexOf(instance)) {
                  instance.isDependency = true;
                  instance.isInline = 'stack' in dep;
                  this.dependencies.push(instance);
                }
              }
            }
          }

          if (!instance) {
            // Unable to resolve filepath
            if (!dep.isDisabled) {
              warn('dependency '
                + strong(dep.filepath)
                + ' for '
                + strong(this.id)
                + ' not found', 3);
            }
          }
        });

        fn();
      });
    }
  }

  /**
   * Inline dependency content
   * @param {Object} options
   * @param {Function} fn(err)
   */
  inline (options, fn) {
    const deps = (this.type != 'css')
      ? this.getAllDependencies(true)
      : this.dependencyReferences;

    helpers.inline(this.type, this.content, deps, this.options.runtimeOptions.compress, (err, content) => {
      if (err) return fn(err);
      debug('inline: ' + strong(this.relpath), 4);
      this.content = content;
      fn();
    });
  }

  /**
   * Replace relative dependency references with fully resolved
   * @param {Object} options
   * @param {Function} fn(err)
   */
  replaceReferences (options, fn) {
    this.content = helpers.replace.references(this.content, this.type, this.dependencyReferences);
    debug('replace dependency references: ' + strong(this.relpath), 4);
    fn();
  }

  /**
   * Replace process.env references with values
   * @param {Object} options
   * @param {Function} fn(err)
   */
  replaceEnvironment (options, fn) {
    this.content = helpers.replace.environment(this.content, this.dependencyReferences);
    debug('replace environment vars: ' + strong(this.relpath), 4);
    fn();
  }

  /**
   * Compress file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  compress (options, fn) {
    helpers.compress(this.type, this.content, this.options.compressors, (err, content) => {
      if (err) return fn(err);
      this.content = content;
      debug('compressed: ' + strong(this.relpath), 4);
      fn();
    });
  }

  /**
   * Wrap JS file contents in a module definition
   * @param {Object} options
   * @param {Function} fn(err)
   */
  wrap (options, fn) {
    // Allow wrapping of browserified bundles
    if (!this.isBuddyBuilt) {
      this.content = helpers.wrap(this.id, this.content);
      debug('wrap: ' + strong(this.relpath), 4);
    }
    fn();
  }

  /**
   * Concatenate file contents
   * @param {Object} options
   * @param {Function} fn(err)
   */
  concat (options, fn) {
    this.content = helpers.concat(this.type, this.content, this.id, this.getAllDependencies().reverse(), options);
    debug('concat: ' + strong(this.relpath), 4);
    fn();
  }

  /**
   * Write file contents to disk
   * @param {String} filepath
   * @returns {String}
   */
  write (filepath) {
    helpers.write(filepath, this.content);

    print(chalk.green('built'
      + (this.options.runtimeOptions.compress ? ' and compressed ' : ' '))
      + strong(truncate(path.relative(process.cwd(), filepath))), 2);

    return filepath;
  }

  /**
   * Reset content
   * @param {Boolean} hard
   */
  reset (hard) {
    this.isLocked = false;
    this.isDependency = false;
    this.isInline = false;
    this.dependencies = [];
    this.dependencyReferences = [];
    if (this.type != 'js') {
      this.content = this.fileContent;
      this.compiledContent = '';
    } else {
      this.content = this.compiledContent || this.fileContent;
    }
    if (hard) {
      this.content = this.fileContent = this.compiledContent = '';
      // Following are set on load
      this.isBuddyBuilt = false;
      this.isBuilt = false;
    }
    debug('reset' + (hard ? ' (hard)' : '') + ': ' + strong(this.relpath), 4);
  }

  /**
   * Destroy instance
   */
  destroy () {
    this.reset(true);
    this.options = null;
  }
}<|endoftext|>


<commit_before>/*
 * fis
 * http://web.baidu.com/
 */

'use strict';

var fis = require('../fis-kernel.js');

function normalizeExt(ext){
    if(ext){
        var rExt = fis.config.get('roadmap.ext' + ext);
        if(rExt){
            ext = (rExt[0] === '.') ? rExt : ('.' + rExt);
        }
    }
    return ext;
}

function ensure(str){
    return str.replace(/[:*?"<>|,; ()&$]/g, '_');
}

function normalizePath(path, reg, rExt){
    return ensure(path.replace(reg, '')) + rExt;
}

function addHash(path, file){
    var rExt  = file.rExt,
        qRExt = fis.util.escapeReg(rExt),
        qExt = fis.util.escapeReg(file.ext),
        hash = file.getHash(),
        reg = new RegExp(qRExt + '$|' + qExt + '$', 'i');
    return path.replace(reg, '') + '_' + hash + rExt;
}

function getDomainsByPath(path){
    var domain = fis.config.get('domain', {}), value = '';
    if(typeof domain === 'string'){
        value = domain;
    } else {
        fis.util.map(domain, function(pattern, domain){
            if(pattern === 'image' && fis.util.isImageFile(path)){
                value = domain;
                return true;
            } else if((new RegExp(pattern, 'i')).test(path)){
                value = domain;
                return true;
            }
        });
    }
    return value.split(/\s*,\s*/);
}

function getDomain(path, hash){
    var domains = getDomainsByPath(path),
        len = domains.length,
        domain = '';
    hash = hash || fis.util.md5(path);
    if(len){
        domain = domains[hash.charCodeAt(0) % len]
            .replace(/\\/g, '/')
            .replace(/\/+$/, '');
    }
    return domain;
}

var File = Object.derive(function(){
    var _ = fis.util,
        info = _.pathinfo(arguments);
    _.map(info, this, true);
    var ext = info.ext,
        rExt = this.rExt = normalizeExt(ext),
        realpath = this.realpath = _.realpathSafe(info.fullname),
        realpathNoExt = this.realpathNoExt = info.rest,
        root = fis.project.getProjectPath(),
        lib = fis.project.getLibPath(), type;

    this.useHash = rExt === '.js' || rExt === '.css' || _.isImageFile(rExt);
    this.isMod = false;
    this.requires = [];

    //lib first
    if(realpath.indexOf(lib + '/') === 0) {
        //e.g. realpath = lib_root/tangram/1.5.0/tangram/base.js
        //libPath = tangram/1.5.0/tangram/base.js
        var libPath = realpath.substring(lib.length + 1);
        var matches = libPath.match(/^([^\/]+)\/([^\/]+)\/.+$/);
        if(matches){
            //islib
            this.isLib = true;
            this.libName = matches[1];
            this.libVersion = matches[2];
            root = lib + '/' + matches[1] + '/' + matches[2];
        }
        type = 'lib';
    } else if(realpath.indexOf(root) === 0){
        type = 'project';
    }

    if(type){
        var len = root.length, subpath;
        this.subpath = subpath = realpath.substring(len);
        this.subdirname = info.dirname.substring(len);
        this.subpathNoExt = realpathNoExt.substring(len);
        fis.uri.roadmap(subpath, type, this);
        var reg = new RegExp(_.escapeReg(ext) + '$|' + _.escapeReg(rExt) + '$', 'i');
        if(!this.release){
            if(this.isLib){
                this.release = '/lib' + this.subpath;
            } else {
                this.release = this.subpath;
            }
        }
        this.release = normalizePath(this.release, reg, rExt);
        this.url = normalizePath(this.url || this.release, reg, rExt);
        this.id = this.id || subpath.replace(/^\//, '');
        var ns = this.isLib ? this.libName : fis.config.get('project.name', '');
        if(ns){
            this.id = ns + ':' + this.id;
        }
    }
}, {
    exists : function(){
        return fis.util.exists(this.realpath);
    },
    isText : function(){
        return fis.util.isTextFile(this.realpath);
    },
    isImage : function(){
        return fis.util.isImageFile(this.realpath);
    },
    toString : function(){
        return this.realpath;
    },
    getMtime : function(){
        return fis.util.mtime(this.realpath);
    },
    isFile : function(){
        return fis.util.isFile(this.realpath);
    },
    isDir : function(){
        return fis.util.isDir(this.realpath);
    },
    setContent : function(c){
        this._content = c;
        return this;
    },
    getContent : function(){
        if(typeof this._content === 'undefined'){
            this._content = fis.util.read(this.realpath);
        }
        return this._content;
    },
    getHash : function(){
        if(typeof this._hash === 'undefined'){
            this._hash = fis.util.md5(this.getContent());
        }
        return this._hash;
    },
    getBase64 : function(prefix){
        prefix = typeof prefix === 'undefined' ? true : prefix;
        if(prefix){
            prefix = 'data:' + fis.util.getMimeType(this.rExt) + ';base64,';
        } else {
            prefix = '';
        }
        return prefix + fis.util.base64(this._content);
    },
    writeTo : function(target){
        var charset = this.isText() ? fis.config.get('project.charset', 'utf-8') : null;
        fis.util.write(target, this._content, charset);
        return this;
    },
    getId : function(){
        return this.id;
    },
    getUrl : function(withHash, withDomain){
        var url;
        if(withHash && this.useHash){
            url = addHash(this.url, this);
            if(withDomain){
                url = getDomain(this.subpath, this.getHash()) + url;
            }
        } else {
            url = this.url;
        }
        return url + this.query;
    },
    getHashRelease : function(){
        if(this.useHash){
            return addHash(this.release, this);
        } else {
            return this.release;
        }
    },
    addRequire : function(id){
        if(id && (id = id.trim())){
            if(this.requires.indexOf(id) < 0){
                this.requires.push(id);
            }
            return id;
        }
        return false;
    },
    addSameNameRequire : function(ext){
        var path;
        if(fis.util.isFile(this.realpathNoExt + ext)){
            path = './' + this.filename + ext;
        } else {
            var map = fis.config.get('roadmap.ext');
            for(var key in map){
                if(map.hasOwnProperty(key)){
                    var rExt = map[key];
                    if(rExt === ext && fis.util.isFile(this.realpathNoExt + rExt)) {
                        path = './' + this.filename + rExt;
                        break;
                    }
                }
            }
        }
        if(path){
            var info = fis.uri.getId(path, this.dirname);
            this.addRequire(info.id);
        }
    },
    removeRequire : function(id){
        var pos = this.requires.indexOf(id);
        if(pos > -1){
            this.requires.splice(pos, 1);
        }
    },
    deliver : function(output, md5){
        var release = this.release;
        if(release){
            if(fis.util.exists(output) && !fis.util.isDir(output)){
                fis.log.error('unable to deliver file['
                    + this.realpath + '] to dir['
                    + output + ']: invalid output dir.');
            }
            this.delivered = [];
            var target;
            if(md5 == 0 || !this.useHash){
                target = fis.util(output, release);
                this.writeTo(target);
                this.delivered.push(target);
            } else if(md5 == 1){
                target = fis.util(output, this.getHashRelease());
                this.writeTo(target);
                this.delivered.push(target);
            } else {
                target = fis.util(output, release);
                this.writeTo(target);
                this.delivered.push(target);

                target = fis.util(output, this.getHashRelease());
                this.writeTo(target);
                this.delivered.push(target);
            }
        }
        return this;
    }
});

module.exports = File.factory();
module.exports.wrap = function(file){
    if(typeof file === 'string'){
        return new File(file);
    } else if(file instanceof File){
        return file;
    } else {
        fis.log.error('unable to convert [' + (typeof file) + '] to [File] object.');
    }
};<commit_msg>支持conf文件不存在<commit_after>/*
 * fis
 * http://web.baidu.com/
 */

'use strict';

var fis = require('../fis-kernel.js');

function normalizeExt(ext){
    if(ext){
        var rExt = fis.config.get('roadmap.ext' + ext);
        if(rExt){
            ext = (rExt[0] === '.') ? rExt : ('.' + rExt);
        }
    }
    return ext;
}

function ensure(str){
    return str.replace(/[:*?"<>|,; ()&$]/g, '_');
}

function normalizePath(path, reg, rExt){
    return ensure(path.replace(reg, '')) + rExt;
}

function addHash(path, file){
    var rExt  = file.rExt,
        qRExt = fis.util.escapeReg(rExt),
        qExt = fis.util.escapeReg(file.ext),
        hash = file.getHash(),
        reg = new RegExp(qRExt + '$|' + qExt + '$', 'i');
    return path.replace(reg, '') + '_' + hash + rExt;
}

function getDomainsByPath(path){
    var domain = fis.config.get('domain', {}), value = '';
    if(typeof domain === 'string'){
        value = domain;
    } else {
        fis.util.map(domain, function(pattern, domain){
            if(pattern === 'image' && fis.util.isImageFile(path)){
                value = domain;
                return true;
            } else if((new RegExp(pattern, 'i')).test(path)){
                value = domain;
                return true;
            }
        });
    }
    return value.split(/\s*,\s*/);
}

function getDomain(path, hash){
    var domains = getDomainsByPath(path),
        len = domains.length,
        domain = '';
    hash = hash || fis.util.md5(path);
    if(len){
        domain = domains[hash.charCodeAt(0) % len]
            .replace(/\\/g, '/')
            .replace(/\/+$/, '');
    }
    return domain;
}

var File = Object.derive(function(){
    var _ = fis.util,
        info = _.pathinfo(arguments);
    _.map(info, this, true);
    var ext = info.ext,
        rExt = this.rExt = normalizeExt(ext),
        realpath = this.realpath = _.realpathSafe(info.fullname),
        realpathNoExt = this.realpathNoExt = info.rest,
        root = fis.project.getProjectPath(),
        lib = fis.project.getLibPath(), type;

    this.useHash = rExt === '.js' || rExt === '.css' || _.isImageFile(rExt);
    this.isMod = false;
    this.requires = [];

    //lib first
    if(realpath.indexOf(lib + '/') === 0) {
        //e.g. realpath = lib_root/tangram/1.5.0/tangram/base.js
        //libPath = tangram/1.5.0/tangram/base.js
        var libPath = realpath.substring(lib.length + 1);
        var matches = libPath.match(/^([^\/]+)\/([^\/]+)\/.+$/);
        if(matches){
            //islib
            this.isLib = true;
            this.libName = matches[1];
            this.libVersion = matches[2];
            root = lib + '/' + matches[1] + '/' + matches[2];
        }
        type = 'lib';
    } else if(realpath.indexOf(root) === 0){
        type = 'project';
    }

    if(type){
        var len = root.length, subpath;
        this.subpath = subpath = realpath.substring(len);
        this.subdirname = info.dirname.substring(len);
        this.subpathNoExt = realpathNoExt.substring(len);
        fis.uri.roadmap(subpath, type, this);
        var reg = new RegExp(_.escapeReg(ext) + '$|' + _.escapeReg(rExt) + '$', 'i');
        if(!this.release){
            if(this.isLib){
                this.release = '/lib' + this.subpath;
            } else {
                this.release = this.subpath;
            }
        }
        this.release = normalizePath(this.release, reg, rExt);
        this.url = normalizePath(this.url || this.release, reg, rExt);
        this.id = this.id || subpath.replace(/^\//, '');
        var ns = this.isLib ? this.libName : fis.config.get('project.name', '');
        if(ns){
            this.id = ns + ':' + this.id;
        }
    }
}, {
    exists : function(){
        return fis.util.exists(this.realpath);
    },
    isText : function(){
        return fis.util.isTextFile(this.realpath);
    },
    isImage : function(){
        return fis.util.isImageFile(this.realpath);
    },
    toString : function(){
        return this.realpath;
    },
    getMtime : function(){
        return fis.util.mtime(this.realpath);
    },
    isFile : function(){
        return fis.util.isFile(this.realpath);
    },
    isDir : function(){
        return fis.util.isDir(this.realpath);
    },
    setContent : function(c){
        this._content = c;
        return this;
    },
    getContent : function(){
        if(typeof this._content === 'undefined'){
            this._content = fis.util.read(this.realpath);
        }
        return this._content;
    },
    getHash : function(){
        if(typeof this._hash === 'undefined'){
            this._hash = fis.util.md5(this.getContent());
        }
        return this._hash;
    },
    getBase64 : function(prefix){
        prefix = typeof prefix === 'undefined' ? true : prefix;
        if(prefix){
            prefix = 'data:' + fis.util.getMimeType(this.rExt) + ';base64,';
        } else {
            prefix = '';
        }
        return prefix + fis.util.base64(this._content);
    },
    writeTo : function(target){
        var charset = this.isText() ? fis.config.get('project.charset', 'utf-8') : null;
        fis.util.write(target, this._content, charset);
        return this;
    },
    getId : function(){
        return this.id;
    },
    getUrl : function(withHash, withDomain){
        var url;
        if(withHash && this.useHash){
            url = addHash(this.url, this);
            if(withDomain){
                url = getDomain(this.subpath, this.getHash()) + url;
            }
        } else {
            url = this.url;
        }
        return url + this.query;
    },
    getHashRelease : function(){
        if(this.useHash){
            return addHash(this.release, this);
        } else {
            return this.release;
        }
    },
    addRequire : function(id){
        if(id && (id = id.trim())){
            if(this.requires.indexOf(id) < 0){
                this.requires.push(id);
            }
            return id;
        }
        return false;
    },
    addSameNameRequire : function(ext){
        var path;
        if(fis.util.isFile(this.realpathNoExt + ext)){
            path = './' + this.filename + ext;
        } else {
            var map = fis.config.get('roadmap.ext');
            for(var key in map){
                if(map.hasOwnProperty(key)){
                    var rExt = map[key];
                    if(rExt === ext && fis.util.isFile(this.realpathNoExt + rExt)) {
                        path = './' + this.filename + rExt;
                        break;
                    }
                }
            }
        }
        if(path){
            var info = fis.uri.getId(path, this.dirname);
            this.addRequire(info.id);
        }
    },
    removeRequire : function(id){
        var pos = this.requires.indexOf(id);
        if(pos > -1){
            this.requires.splice(pos, 1);
        }
    },
    deliver : function(output, md5){
        var release = this.release;
        if(!release){
            fis.log.error('unable get release path of file['
                + this.realpath
                + ']: Maybe this file is not in current project');
        }
        if(fis.util.exists(output) && !fis.util.isDir(output)){
            fis.log.error('unable to deliver file['
                + this.realpath + '] to dir['
                + output + ']: invalid output dir.');
        }
        this.delivered = [];
        var target;
        if(md5 == 0 || !this.useHash){
            target = fis.util(output, release);
            this.writeTo(target);
            this.delivered.push(target);
        } else if(md5 == 1){
            target = fis.util(output, this.getHashRelease());
            this.writeTo(target);
            this.delivered.push(target);
        } else {
            target = fis.util(output, release);
            this.writeTo(target);
            this.delivered.push(target);

            target = fis.util(output, this.getHashRelease());
            this.writeTo(target);
            this.delivered.push(target);
        }
        return this;
    }
});

module.exports = File.factory();
module.exports.wrap = function(file){
    if(typeof file === 'string'){
        return new File(file);
    } else if(file instanceof File){
        return file;
    } else {
        fis.log.error('unable to convert [' + (typeof file) + '] to [File] object.');
    }
};<|endoftext|>


<commit_before>/*
 *  find.js
 *
 *  David Janes
 *  IOTDB.org
 *  2017-08-05
 *
 *  Copyright [2013-2018] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

const _ = require("iotdb-helpers");

const assert = require("assert");

const util = require("./util");

/**
 */
const find = (_self, done) => {
    const self = _.d.clone.shallow(_self)
    const method = "find";

    assert.ok(self.mongo_collection, `${method}: expected self.mongo_collection`)
    assert.ok(_.is.JSON(self.query) || _.is.Nullish(self.query), `${method}: expected self.query to be a JSON-like object or Null`)

    const query = util.restore_ids(self.query || {});
    const find_in = self.find_in || exports.find.DEFAULT;

    const options = Object.assign({
        skip: 0
    }, find_in)

    if (self.pager) {
        options.skip = _.coerce.to.Integer(self.pager, 0)
    }
    if (self.query_limit) {
        options.limit = self.query_limit;
    }
    if (self.projection) {
        if (_.is.Array(self.projection)) {
            options.projection = {}
            self.projection.forEach(key => options.projection[key] = 1)
        } else {
            options.projection = _.d.clone(self.projection)
        }
    }

    self.mongo_collection.find(query, options).toArray((error, mongo_result) => {
        if (error) {
            return done(util.intercept(self)(error))
        }

        self.jsons = util.safe_ids(mongo_result);
        self.json = self.jsons.length ? self.jsons[0] : null;

        self.mongo_result = mongo_result;

        done(null, self)
    })
}

/**
 *  API
 */
exports.find = _.promise.denodeify(find)

exports.find.DEFAULT = {}
<commit_msg>add paging<commit_after>/*
 *  find.js
 *
 *  David Janes
 *  IOTDB.org
 *  2017-08-05
 *
 *  Copyright [2013-2018] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

const _ = require("iotdb-helpers");

const assert = require("assert");

const util = require("./util");

/**
 */
const find = (_self, done) => {
    const self = _.d.clone.shallow(_self)
    const method = "find";

    assert.ok(self.mongo_collection, `${method}: expected self.mongo_collection`)
    assert.ok(_.is.JSON(self.query) || _.is.Nullish(self.query), `${method}: expected self.query to be a JSON-like object or Null`)

    const query = util.restore_ids(self.query || {});
    const find_in = self.find_in || exports.find.DEFAULT;

    const options = Object.assign({
        skip: 0
    }, find_in)

    if (self.pager) {
        options.skip = _.coerce.to.Integer(self.pager, 0)
    }
    if (self.query_limit) {
        options.limit = self.query_limit;
    }
    if (self.projection) {
        if (_.is.Array(self.projection)) {
            options.projection = {}
            self.projection.forEach(key => options.projection[key] = 1)
        } else {
            options.projection = _.d.clone(self.projection)
        }
    }

    self.mongo_collection.find(query, options).toArray((error, mongo_result) => {
        if (error) {
            return done(util.intercept(self)(error))
        }

        self.jsons = util.safe_ids(mongo_result)
        self.json = self.jsons.length ? self.jsons[0] : null

        self.mongo_result = mongo_result

        self.cursor = null

        if (options.limit) {
            self.cursor = {
                first: "0",
                previous: null,
                current: `${options.skip}`,
                next: null,

                is_first: null,
                is_last: null,
                has_next: null,
                has_previous: null,
                is_previous_first: null,

                implements_previous: true,

                n_results: self.jsons.length,
                n_start: options.skip,
            }

            if (self.jsons.length === 0) {
                self.cursor.is_first = true
                self.cursor.is_last = true
            } else {
                self.cursor.next = `${Math.min(options.limit, self.jsons.length) + options.skip}`

                if (self.jsons.length < options.limit) {
                    self.cursor.next = null
                    self.cursor.is_last = true
                    self.cursor.has_next = false
                } else {
                    self.cursor.has_next = true
                }

                if (options.skip === 0) {
                    self.cursor.is_first = true
                }

                const previous = options.skip - options.limit
                self.cursor.previous = `${Math.max(0, previous)}`
                if (previous === 0) {
                    self.cursor.has_previous = true
                    self.cursor.is_previous_first = true
                } else if (previous < 0) {
                    self.cursor.has_previous = false
                } else {
                    self.cursor.has_previous = true
                }

            }
        }

        done(null, self)
    })
}

/**
 *  API
 */
exports.find = _.promise.denodeify(find)

exports.find.DEFAULT = {}
<|endoftext|>


<commit_before>/*****************************************************************************
Hz Form Module

*****************************************************************************/

/*----------------------------------------------------------------------------
Hz Library Boilerplate
----------------------------------------------------------------------------*/
var hz = ( function( ns ) { return ns; } )( hz || {} );

/*----------------------------------------------------------------------------
Module Dependencies
----------------------------------------------------------------------------*/
/*?jspp { "include" : { "hz" : [ "ui", "util" ] } } ?*/

/*----------------------------------------------------------------------------
Additions to Base Types
----------------------------------------------------------------------------*/

/**
 *  Attempts to focus on the best element for keyboard input.
 *
 */
HTMLFormElement.prototype.autoFocus = function() {
    var num_elements = this.elements.length;
    for( var i = 0; i < num_elements; ++i ) {
        if( this.elements[ i ].type != 'hidden' ) {
            this.elements[ i ].focus();
            return;
        }
    }
};

/**
 *  Enable or disable all fields in the form.
 *
 *  @param enable True to enable the form, false to disable it
 */
HTMLFormElement.prototype.enableAll = function() {
    var enable = arguments.length > 0 ? arguments[ 0 ] : true;
    var num_elements = this.elements.length;
    for( var i = 0; i < num_elements; ++i ) {
        this.elements[ i ].disabled = !enable;
    }
};

/**
 *  Copies all the input data contained in the form into an object that is
 *  intended to be submitted to a remote resource (usually as JSON).
 *
 *  @return An object containing data entered into the form
 */
HTMLFormElement.prototype.getData = function() {
    var acount = 0;
    var bind;
    var data = {};
    var f;
    var num_elements = this.elements.length;
    for( var i = 0; i < num_elements; ++i ) {
        f = this.elements.item( i );
        if( hz.form.ignore_types.indexOf( f.type ) != -1 ) {
            continue;
        }
        if( ( 'hz' in f ) && ( 'bind' in f.hz ) ) {
            bind = f.hz.bind;
        }
        else if( f.name ) {
            bind = f.name;
        }
        else if( f.id ) {
            bind = f.id;
        }
        else {
            bind = '_anonymous.' + acount;
            acount += 1;
        }
        hz.util.oset( data, bind, hz.form.get_value( f ) );
    }
    return data;
};

/**
 *  Convenience method to set a value in a form as a hidden input.
 *
 *  @param name  The name of the hidden input
 *  @param value The value to set in the hidden input
 *  @return      The input element that was constructed (or updated)
 */
HTMLFormElement.prototype.setHiddenInput = function( name, value ) {
    var input = this.elements.namedItem( name );
    if( input == null ) {
        input = document.createElement( 'input' );
        this.appendChild( input );
    }
    hz.util.omap(
        input,
        { 'type' : 'hidden', 'name' : name, 'value' : value }
    );
    return input;
};

/*----------------------------------------------------------------------------
Hz Form Submodule
----------------------------------------------------------------------------*/
hz.form = ( function( ns ) {

    /*------------------------------------------------------------------------
    Public Properties
    ------------------------------------------------------------------------*/

    ns.ignore_types = [ 'submit', 'reset' ];
                                    //input types ignored in forms

    /*------------------------------------------------------------------------
    Public Methods
    ------------------------------------------------------------------------*/

    /**
     *  Consistently builds complex document structure to house an input in
     *  a form.
     *
     *  @param label The content of the field's label
     *  @param input Optional input element (defaults to un-typed input)
     *  @param help  Optional user assistance information
     *  @return      The container to append to the form
     */
    ns.create_field = function( label ) {
        var input = arguments.length > 1 ? arguments[ 1 ] : null;
        var help  = arguments.length > 2 ? arguments[ 2 ] : null;

        //the field's container element
        var div = document.createElement( 'div' );
        div.className = 'field';

        //the field's label
        var lbl = document.createElement( 'label' );
        hz.util.append( lbl, label );
        div.appendChild( lbl );

        var span;

        //check for a specified help message
        if( help != null ) {
            span = document.createElement( 'span' );
            hz.util.append( span, help );
            lbl.appendChild( span );
        }

        //see if the user wants a default input element
        if( input == null ) {
            input = document.createElement( 'input' );
        }

        //see if this input needs a name
        if( input.name == '' ) {
            input.name = hz.util.slug( label );
        }

        //see if the input needs an ID
        if( input.id == '' ) {
            input.id = input.name;
        }

        //set the label's "for" attribute for proper interaction
        lbl.setAttribute( 'for', input.id );

        //set up and append the input's container to the field container
        span = document.createElement( 'span' );
        span.appendChild( input );
        div.appendChild( span );

        //decorate the element for future access
        div.hz = { 'input' : input };

        //return the form field structure
        return div;
    };

    /**
     *  Default form construction.  Creates the form in a controlled way to
     *  prevent default browser behavior (these forms are not meant to be
     *  submitted by the browser to a remote resource).
     *
     *  @param handler Optional callback handler for submit events
     *  @return        The form element that was created
     */
    ns.create_form = function() {
        var handler = arguments.length > 0 ? arguments[ 0 ] : null;
        var form = document.createElement( 'form' );
        //handle all submit events ourselves
        form.onsubmit = function( event ) {
            event.preventDefault();
            if( handler instanceof Function ) {
                handler( event );
            }
        };
        //this sets up a redirect for calling submit() directly on the form
        var event = new Event(
            'trap_submit',
            { 'bubbles' : false, 'cancelable' : true }
        );
        form.addEventListener( 'trap_submit', form.onsubmit );
        form.submit = function() { return form.dispatchEvent( event ); };
        //attach magical form builder method
        form.build = _build;
        return form;
    };

    /**
     *  Deals with the complexities of fetching a value from an input.  The
     *  given input is not always an input element.  It may also be a known
     *  container for composite inputs (e.g. "check" inputs from the hz.ui
     *  module).
     *
     *  @param input The input element from which to get it's current value
     *  @return      The value of the input.  This is usually a string, but
     *               may also be an array of values (e.g. inclusive select
     *               inputs).  If a value could not be resolved, returns null.
     */
    ns.get_value = function( input ) {
        var values = [];
        if( input.tagName == 'UL' ) {
            var inp, item;
            var num_items = input.childNodes.length;
            if( input.className.indexOf( 'exclusive' ) != -1 ) {
                for( var i = 0; i < num_items; ++i ) {
                    item = input.childNodes[ i ];
                    inp = item.getElementsByTagName( 'input' ).item( 0 );
                    if( inp.checked == true ) {
                        return inp.value;
                    }
                }
                return null;
            }
            for( var i = 0; i < num_items; ++i ) {
                item = input.childNodes[ i ];
                inp = item.getElementsByTagName( 'input' ).item( 0 );
                if( inp.checked == true ) {
                    values.push( inp.value );
                }
            }
            return values;
        }
        else if( input.tagName == 'SELECT' ) {
            if( input.multiple == true ) {
                for( var i = 0; i < input.options.legth; ++i ) {
                    if( input.options[ i ].selected == true ) {
                        values.push( input.options[ i ].value );
                    }
                }
                return values;
            }
            return input.options[ input.selectedIndex ].value;
        }
        return input.value;
    };

    /*------------------------------------------------------------------------
    Classes
    ------------------------------------------------------------------------*/

    /*------------------------------------------------------------------------
    Private Properties
    ------------------------------------------------------------------------*/

    var _build_keys = [ 'args', 'help', 'label', 'type', 'value' ];
                                    //desired form field building keys

    /*------------------------------------------------------------------------
    Private Methods
    ------------------------------------------------------------------------*/

    /**
     *  Convenience function that gets added to internally-constructed forms.
     *
     *  @param formspec A form specification object (object of fields)
     *  @param sublabel The label of the submit button in the form
     */
    function _build( formspec ) {
        var sublabel = arguments.length > 1 ? arguments[ 1 ] : 'Submit';

        var field, spec;
        for( var key in formspec ) {
            spec = formspec[ key ];
            hz.util.odef( spec, _build_keys );
            if( spec.type == 'hidden' ) {
                this.setHiddenInput( key, spec.value );
                continue;
            }
            if( spec.type  == null ) { spec.type  = 'entry'; }
            if( spec.label == null ) { spec.label = key;     }
            if( spec.args  == null ) {
                if( spec.value != null ) {
                    spec.args = [ spec.value ];
                }
                else {
                    spec.args = [];
                }
            }
            field = ns.create_field(
                spec.label,
                hz.ui.create[ spec.type ].apply( null, spec.args ),
                spec.help
            );
            this.appendChild( field );
        }

        if( sublabel != null ) {
            var button = hz.ui.create.button( sublabel );
            button.type = 'submit';
            var subfield = document.createElement( 'div' );
            subfield.className = 'control';
            subfield.appendChild( button );
            this.appendChild( subfield );
        }

    };

    return ns;
} )( hz.form || {} );
<commit_msg>Small improvements, and added documentation.<commit_after>/*****************************************************************************
Hz Form Module

*****************************************************************************/

/*----------------------------------------------------------------------------
Hz Library Boilerplate
----------------------------------------------------------------------------*/
var hz = ( function( ns ) { return ns; } )( hz || {} );

/*----------------------------------------------------------------------------
Module Dependencies
----------------------------------------------------------------------------*/
/*?jspp { "include" : { "hz" : [ "ui", "util" ] } } ?*/

/*----------------------------------------------------------------------------
Additions to Base Types
----------------------------------------------------------------------------*/

/**
 *  Attempts to focus on the best element for keyboard input.
 *
 */
HTMLFormElement.prototype.autoFocus = function() {
    var num_elements = this.elements.length;
    for( var i = 0; i < num_elements; ++i ) {
        if( this.elements[ i ].type != 'hidden' ) {
            this.elements[ i ].focus();
            return;
        }
    }
};

/**
 *  Enable or disable all fields in the form.
 *
 *  @param enable True to enable the form, false to disable it
 */
HTMLFormElement.prototype.enableAll = function() {
    var enable = arguments.length > 0 ? arguments[ 0 ] : true;
    var num_elements = this.elements.length;
    for( var i = 0; i < num_elements; ++i ) {
        this.elements[ i ].disabled = !enable;
    }
};

/**
 *  Copies all the input data contained in the form into an object that is
 *  intended to be submitted to a remote resource (usually as JSON).
 *
 *  ZIH - this doesn't currently cope with "check" input lists
 *
 *  @return An object containing data entered into the form
 */
HTMLFormElement.prototype.getData = function() {
    var acount = 0;
    var bind;
    var data = {};
    var f;
    var num_elements = this.elements.length;
    for( var i = 0; i < num_elements; ++i ) {
        f = this.elements.item( i );
        if( hz.form.ignore_types.indexOf( f.type ) != -1 ) {
            continue;
        }
        if( ( 'hz' in f ) && ( 'bind' in f.hz ) ) {
            bind = f.hz.bind;
        }
        else if( f.name ) {
            bind = f.name;
        }
        else if( f.id ) {
            bind = f.id;
        }
        else {
            bind = '_anonymous.' + acount;
            acount += 1;
        }
        hz.util.oset( data, bind, hz.form.get_value( f ) );
    }
    return data;
};

/**
 *  Convenience method to set a value in a form as a hidden input.
 *
 *  @param name  The name of the hidden input
 *  @param value The value to set in the hidden input
 *  @return      The input element that was constructed (or updated)
 */
HTMLFormElement.prototype.setHiddenInput = function( name, value ) {
    var input = this.elements.namedItem( name );
    if( input == null ) {
        input = document.createElement( 'input' );
        this.appendChild( input );
    }
    hz.util.omap(
        input,
        { 'type' : 'hidden', 'name' : name, 'value' : value }
    );
    return input;
};

/*----------------------------------------------------------------------------
Hz Form Submodule
----------------------------------------------------------------------------*/
hz.form = ( function( ns ) {

    /*------------------------------------------------------------------------
    Public Properties
    ------------------------------------------------------------------------*/

    ns.ignore_types = [ 'submit', 'reset', 'button', 'image' ];
                                    //input types ignored in forms

    /*------------------------------------------------------------------------
    Public Methods
    ------------------------------------------------------------------------*/

    /**
     *  Consistently builds complex document structure to house an input in
     *  a form.
     *
     *  @param label The content of the field's label
     *  @param input Optional input element (defaults to un-typed input)
     *  @param help  Optional user assistance information
     *  @return      The container to append to the form
     */
    ns.create_field = function( label ) {
        var input = arguments.length > 1 ? arguments[ 1 ] : null;
        var help  = arguments.length > 2 ? arguments[ 2 ] : null;

        //the field's container element
        var div = document.createElement( 'div' );
        div.className = 'field';

        //the field's label
        var lbl = document.createElement( 'label' );
        hz.util.append( lbl, label );
        div.appendChild( lbl );

        var span;

        //check for a specified help message
        if( help != null ) {
            span = document.createElement( 'span' );
            hz.util.append( span, help );
            lbl.appendChild( span );
        }

        //see if the user wants a default input element
        if( input == null ) {
            input = document.createElement( 'input' );
        }

        //see if this input needs a name
        if( input.name == '' ) {
            input.name = hz.util.slug( label );
        }

        //see if the input needs an ID
        if( input.id == '' ) {
            input.id = input.name;
        }

        //set the label's "for" attribute for proper interaction
        lbl.setAttribute( 'for', input.id );

        //set up and append the input's container to the field container
        span = document.createElement( 'span' );
        span.appendChild( input );
        div.appendChild( span );

        //decorate the element for future access
        div.hz = { 'input' : input };

        //return the form field structure
        return div;
    };

    /**
     *  Default form construction.  Creates the form in a controlled way to
     *  prevent default browser behavior (these forms are not meant to be
     *  submitted by the browser to a remote resource).
     *
     *  @param handler Optional callback handler for submit events
     *  @return        The form element that was created
     */
    ns.create_form = function() {
        var handler = arguments.length > 0 ? arguments[ 0 ] : null;

        //create a form element
        var form = document.createElement( 'form' );
        form.className = 'form';

        //handle all submit events ourselves (the user can override this)
        form.onsubmit = function( event ) {
            event.preventDefault();
            if( handler instanceof Function ) {
                handler( event );
            }
        };

        //this sets up a redirect for calling submit() directly on the form
        var event = new Event(
            'trap_submit',
            { 'bubbles' : false, 'cancelable' : true }
        );
        form.addEventListener( 'trap_submit', form.onsubmit );
        form.submit = function() { return form.dispatchEvent( event ); };

        //attach magical form builder method
        form.build = _build;

        //return the constructed form element
        return form;
    };

    /**
     *  Deals with the complexities of fetching a value from an input.  The
     *  given input is not always an input element.  It may also be a known
     *  container for composite inputs (e.g. "check" inputs from the hz.ui
     *  module).
     *
     *  @param input The input element from which to get it's current value
     *  @return      The value of the input.  This is usually a string, but
     *               may also be an array of values (e.g. inclusive select
     *               inputs).  If a value could not be resolved, returns null.
     */
    ns.get_value = function( input ) {

        //a container for extracting multiple values from some elements
        var values = [];

        //special-case form elements may be a list of checkbox|radio inputs
        if( input.tagName == 'UL' ) {
            var inp, item;
            var num_items = input.childNodes.length;
            if( input.className.indexOf( 'exclusive' ) != -1 ) {
                for( var i = 0; i < num_items; ++i ) {
                    item = input.childNodes[ i ];
                    inp = item.getElementsByTagName( 'input' ).item( 0 );
                    if( inp.checked == true ) {
                        return inp.value;
                    }
                }
                return null;
            }
            for( var i = 0; i < num_items; ++i ) {
                item = input.childNodes[ i ];
                inp = item.getElementsByTagName( 'input' ).item( 0 );
                if( inp.checked == true ) {
                    values.push( inp.value );
                }
            }
            return values;
        }

        //handle select inputs (that may contain multiple values)
        else if( input.tagName == 'SELECT' ) {
            if( input.multiple == true ) {
                for( var i = 0; i < input.options.legth; ++i ) {
                    if( input.options[ i ].selected == true ) {
                        values.push( input.options[ i ].value );
                    }
                }
                return values;
            }
            return input.options[ input.selectedIndex ].value;
        }

        //return the value of all other form elements
        return input.value;
    };

    /*------------------------------------------------------------------------
    Classes
    ------------------------------------------------------------------------*/

    /*------------------------------------------------------------------------
    Private Properties
    ------------------------------------------------------------------------*/

    var _build_keys = [ 'args', 'help', 'label', 'type', 'value' ];
                                    //desired form field building keys

    /*------------------------------------------------------------------------
    Private Methods
    ------------------------------------------------------------------------*/

    /**
     *  Convenience function that gets added to internally-constructed forms.
     *
     *  @param formspec A form specification object (object of fields)
     *  @param sublabel The label of the submit button in the form
     */
    function _build( formspec ) {
        var sublabel = arguments.length > 1 ? arguments[ 1 ] : 'Submit';

        var field, spec;
        for( var key in formspec ) {
            spec = formspec[ key ];
            hz.util.odef( spec, _build_keys );
            if( spec.type == 'hidden' ) {
                this.setHiddenInput( key, spec.value );
                continue;
            }
            if( spec.type  == null ) { spec.type  = 'entry'; }
            if( spec.label == null ) { spec.label = key;     }
            if( spec.args  == null ) {
                if( spec.value != null ) {
                    spec.args = [ spec.value ];
                }
                else {
                    spec.args = [];
                }
            }
            field = ns.create_field(
                spec.label,
                hz.ui.create[ spec.type ].apply( null, spec.args ),
                spec.help
            );
            this.appendChild( field );
        }

        if( sublabel != null ) {
            var button = hz.ui.create.button( sublabel );
            button.type = 'submit';
            var subfield = document.createElement( 'div' );
            subfield.className = 'control';
            subfield.appendChild( button );
            this.appendChild( subfield );
        }

    };

    return ns;
} )( hz.form || {} );
<|endoftext|>


<commit_before>const Map = require('./map');
const Player = require('./player');
const SuperGhost = require('./superGhost');
const Ghost = require('./ghost');

const canvas = document.getElementById("main-canvas");
const ctx = canvas.getContext('2d');

const realMap = [
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,-1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,-1,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,-1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,-1,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,-1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,-1,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,-1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,-1,0,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,0,-1,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,-1,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];

const map = new Map({
  ctx: ctx,
  width: realMap[0].length,
  height: realMap.length,
  grid: realMap
})

const player = new Player(ctx);

const superGhost = new SuperGhost({map: map, ctx: ctx});

const ghosts = [
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
];

var drawMap = function() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  console.log("Points: " + player.pillCount);

  map.drawWalls();

  player.update(map);
  superGhost.update(player, map);
  ghosts.forEach(function(ghost){
    ghost.update(player, map);
  });
  if(superGhost.gameEnd === true){
    clearTimeout();
    gameEnd(player.pillCount);
  }
}

function gameEnd(pillCount){
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.font = "20px Comic Sans MS"
  ctx.fillStyle = "#000000"
  ctx.textAlign = "center";

  var hiScore = localStorage.getItem("highScorePM") || 0
  if(hiScore < pillCount){
    localStorage.setItem("highScorePM", pillCount);
    hiScore = pillCount
    ctx.fillText("YOU HAVE THE NEW HI SCORE!", canvas.width / 2, canvas.width / 2 + 90)
  }
  ctx.fillText("GAME OVER", canvas.width / 2, canvas.height / 2 + 10)
  ctx.fillText("POINTS: " + pillCount, canvas.width / 2, canvas.height / 2 + 35)
  ctx.fillText("HIGH SCORE: " + hiScore, canvas.width / 2, canvas.height / 2 + 60)
  restartButton();
}

var offset = canvas.getBoundingClientRect();
var rectx = {x:20, y:20, w:150, h:40}
var hCenteredx = canvas.width / 2 - rectx.w / 2
var vCenteredx = canvas.height / 2 - rectx.h / 2  - 35

function restartButton(){
  ctx.fillStyle = "#5B0000";
  ctx.fillRect(hCenteredx,vCenteredx,rectx.w,rectx.h)
  ctx.font = "20px Comic Sans MS";
  ctx.fillStyle = "#FFFFFF";
  ctx.textAlign = "center";
  ctx.fillText("PLAY AGAIN!", canvas.width / 2, canvas.height / 2 - 25)

  canvas.addEventListener('click', function(e){
    var mousePos = getMousePos(canvas, e);
    if(mousePos.x >= hCentered && mousePos.x <= (hCentered + rect.w) && mousePos.y >= vCentered && mousePos.y <= (vCentered + rect.h)){
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      setTimeout(init(), 1);
      // console.log('restart button')
    }
  } , false);
  // ctx.fillStyle = "#5B0000";
  // ctx.fillRect
}

function getMousePos(canvas, e) {
  var rect = canvas.getBoundingClientRect();
  return {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top
  };
}

var bindKeys = function() {
  // custom onkeydown parser <- returns true only for keydown on 37-40
  document.onkeydown = function(e) {
    if (e.keyCode === 38) { player.speed = 1 };
    if (e.keyCode === 40) { player.speed = -1 };
    if (e.keyCode === 37) { player.direction = -1 };
    if (e.keyCode === 39) { player.direction = 1 };
  }

  document.onkeyup = function(e) {
    if (e.keyCode === 38 || e.keyCode === 40) { player.speed = 0 };
    if (e.keyCode === 37 || e.keyCode === 39) { player.direction = 0 };
  }
}

var gameLoop = function() {
  var loop = setTimeout(gameLoop, 1000 / 30);
  drawMap(loop);
}

function Game(){
}

var init = function(){

  bindKeys();
  gameLoop();
}

var offset = canvas.getBoundingClientRect();
var rect = {x:20, y:20, w:100, h:40}
var hCentered = canvas.width / 2 - rect.w / 2
var vCentered = canvas.height / 2 - rect.h / 2 - 35

Game.prototype.startButton = function(){
	ctx.fillStyle = "#5B0000";
	ctx.fillRect(hCentered,vCentered,rect.w,rect.h)
	ctx.font = "20px Comic Sans MS";
	ctx.fillStyle = "#FFFFFF";
	ctx.textAlign = "center";
	ctx.fillText("START", canvas.width / 2, canvas.height / 2 - 25)

		canvas.addEventListener('click', function(e) {
			var mousePos = getMousePos(canvas, e);
			if(mousePos.x >= hCentered && mousePos.x <= (hCentered + rect.w) && mousePos.y >= vCentered && mousePos.y <= (vCentered + rect.h)){
	  			setTimeout(init(), 1);
			}
		}, false);
}

function getMousePos(canvas, e) {
	var rect = canvas.getBoundingClientRect();
	return {
		x: e.clientX - rect.left,
		y: e.clientY - rect.top
	};
}

module.exports = Game;
<commit_msg>update restart button<commit_after>const Map = require('./map');
const Player = require('./player');
const SuperGhost = require('./superGhost');
const Ghost = require('./ghost');

const canvas = document.getElementById("main-canvas");
const ctx = canvas.getContext('2d');

const realMap = [
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,-1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,-1,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,-1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,-1,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,-1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,-1,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,-1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,-1,0,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1],
  [1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
  [1,0,0,0,-1,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,-1,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];

const map = new Map({
  ctx: ctx,
  width: realMap[0].length,
  height: realMap.length,
  grid: realMap
})

const player = new Player(ctx);

const superGhost = new SuperGhost({map: map, ctx: ctx});

const ghosts = [
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
  new Ghost({map: map, ctx: ctx}),
];

var drawMap = function(loop) {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  console.log("Points: " + player.pillCount);

  map.drawWalls();

  player.update(map);
  superGhost.update(player, map);
  ghosts.forEach(function(ghost){
    ghost.update(player, map);
  });
  if(superGhost.gameEnd === true){
    clearTimeout(loop);
    gameEnd(player.pillCount);
  }
}

function gameEnd(pillCount){
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.font = "20px Comic Sans MS"
  ctx.fillStyle = "#000000"
  ctx.textAlign = "center";

  var hiScore = localStorage.getItem("highScorePM") || 0
  if(hiScore < pillCount){
    localStorage.setItem("highScorePM", pillCount);
    hiScore = pillCount
    ctx.fillText("YOU HAVE THE NEW HI SCORE!", canvas.width / 2, canvas.width / 2 + 90)
  }
  ctx.fillText("GAME OVER", canvas.width / 2, canvas.height / 2 + 10)
  ctx.fillText("POINTS: " + pillCount, canvas.width / 2, canvas.height / 2 + 35)
  ctx.fillText("HIGH SCORE: " + hiScore, canvas.width / 2, canvas.height / 2 + 60)
  restartButton();
}

var offset = canvas.getBoundingClientRect();
var rectx = {x:20, y:20, w:150, h:40}
var hCenteredx = canvas.width / 2 - rectx.w / 2
var vCenteredx = canvas.height / 2 - rectx.h / 2  - 35

function restartButton(){
  ctx.fillStyle = "#5B0000";
  ctx.fillRect(hCenteredx,vCenteredx,rectx.w,rectx.h)
  ctx.font = "20px Comic Sans MS";
  ctx.fillStyle = "#FFFFFF";
  ctx.textAlign = "center";
  ctx.fillText("PLAY AGAIN!", canvas.width / 2, canvas.height / 2 - 25)

  canvas.addEventListener('click', function(e){
    var mousePos = getMousePos(canvas, e);
    if(mousePos.x >= hCentered && mousePos.x <= (hCentered + rect.w) && mousePos.y >= vCentered && mousePos.y <= (vCentered + rect.h)){
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      // IM NOT SURE HOW TO RESTART THE GAME
      // setTimeout(init(), 1);
    }
  } , false);
}

function getMousePos(canvas, e) {
  var rect = canvas.getBoundingClientRect();
  return {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top
  };
}

var bindKeys = function() {
  // custom onkeydown parser <- returns true only for keydown on 37-40
  document.onkeydown = function(e) {
    if (e.keyCode === 38) { player.speed = 1 };
    if (e.keyCode === 40) { player.speed = -1 };
    if (e.keyCode === 37) { player.direction = -1 };
    if (e.keyCode === 39) { player.direction = 1 };
  }

  document.onkeyup = function(e) {
    if (e.keyCode === 38 || e.keyCode === 40) { player.speed = 0 };
    if (e.keyCode === 37 || e.keyCode === 39) { player.direction = 0 };
  }
}

var gameLoop = function() {
  var loop = setTimeout(gameLoop, 1000 / 30);
  drawMap(loop);
}

function Game(){
}

var init = function(){
  bindKeys();
  gameLoop();
}

var offset = canvas.getBoundingClientRect();
var rect = {x:20, y:20, w:100, h:40}
var hCentered = canvas.width / 2 - rect.w / 2
var vCentered = canvas.height / 2 - rect.h / 2 - 35

Game.prototype.startButton = function(){
	ctx.fillStyle = "#5B0000";
	ctx.fillRect(hCentered,vCentered,rect.w,rect.h)
	ctx.font = "20px Comic Sans MS";
	ctx.fillStyle = "#FFFFFF";
	ctx.textAlign = "center";
	ctx.fillText("START", canvas.width / 2, canvas.height / 2 - 25)

		canvas.addEventListener('click', function(e) {
			var mousePos = getMousePos(canvas, e);
			if(mousePos.x >= hCentered && mousePos.x <= (hCentered + rect.w) && mousePos.y >= vCentered && mousePos.y <= (vCentered + rect.h)){
	  			setTimeout(init(), 1);
			}
		}, false);
}

function getMousePos(canvas, e) {
	var rect = canvas.getBoundingClientRect();
	return {
		x: e.clientX - rect.left,
		y: e.clientY - rect.top
	};
}

module.exports = Game;
<|endoftext|>


<commit_before>/*
* Game Constructor
*
* @class Roguelike.game
* @classdesc The heart of this roguelike game! In here we provide access to
* all the other objects and function, and we handle the startup of the game
*
* @param {object} userSettings - The settings that the user provides
*/
Roguelike.Game = function(userSettings){

    /*
    * @property {bool} isInitialized - Boolean to see if the game is allready initialized
    */
    this.isInitialized = false;

    /*
    * @property {Roguelike.Controls} controls - The object that handles user input
    */
    this.controls = null;

    /*
    * @property {Roguelike.Map} map - Reference to the current map
    */
    this.map = null;

    /*
    * @property {Roguelike.Player} player - Reference to the player object
    */
    this.player = null;

    /*
    * @property {Roguelike.Renderer} map - Reference to the current renderer
    */
    this.renderer = null;

    /*
    * @property {object} settings - The default settings
    */
    this.settings = {
        canvas: null,
        tilesX: 60,
        tilesY: 40,
        tileSize: 20,
        maxRooms: 10,
        minRoomWidth: 6,
        maxRoomWidth: 12,
        minRoomHeight: 6,
        maxRoomHeight: 12,
        debug: false
    };

    //Extend the default settings with those of the user
    this.settings = Roguelike.Utils.extend(this.settings, userSettings);

    //Initialize itself
    this.initialize();

};

Roguelike.Game.prototype = {

    /*
    * Initialize the game, create all objects
    * @protected
    *
    * @param {int} tileX - The number of tiles on the X axis
    * @param {int} tileY - The number of tiles on the Y axis
    * @param {int} maxRooms - The maximum number of rooms in a map
    */
    initialize: function(){

        //Check if the game is allready initialized
        if(this.isInitialized){
            return;
        }

        //Check if the debugged has to be initialized
        if(this.settings.debug === true){
            canvas.addEventListener('mousemove', Roguelike.Utils.debugTiles, false);
        }

        //Initialize the tracking of the players input
        this.controls = new Roguelike.Controls();
        window.addEventListener('keydown', this.controls.keydown, false);
        window.addEventListener('keyup', this.controls.keyup, false);

        //Initialize the map
        this.map = new Roguelike.Map(this);
        this.map.initialize();

        //Generate rooms for this map
        this.map.roomFactory.generateRooms(this);
        this.map.addRooms();

        //Add the player in the mix
        this.player = new Roguelike.Player(this, {x: 10, y: 10});

        //Draw the map on canvas
        this.renderer = new Roguelike.Renderer(this);
        this.renderer.drawMap();
        this.renderer.drawPlayer();

        //Call the gameloop
        var update = this.update();
        updateInterval = setInterval(function(){
            update();
        }, 1000/(1000/30));

        //The game is fully initialized!
        this.isInitialized = true;

    },

    /*
    * All the functions that need to be executed everytime the game updates
    * Basically the game loop
    * @protected
    *
    */
    update: function(){

        //Update the player
        this.player.update();

        //Update the camera
        this.camera.update();

        //Render the changes
        this.renderer.update();

    },

};<commit_msg>Had to create a variable that stores the current game, this way EventListeners and Intervals can also access this variable.<commit_after>/*
* Game Constructor
*
* @class Roguelike.game
* @classdesc The heart of this roguelike game! In here we provide access to
* all the other objects and function, and we handle the startup of the game
*
* @param {object} userSettings - The settings that the user provides
*/
Roguelike.Game = function(userSettings){

    /*
    * @property {bool} isInitialized - Boolean to see if the game is allready initialized
    */
    this.isInitialized = false;

    /*
    * @property {Roguelike.Controls} controls - The object that handles user input
    */
    this.controls = null;

    /*
    * @property {Roguelike.Map} map - Reference to the current map
    */
    this.map = null;

    /*
    * @property {Roguelike.Camera} camera - Reference to the camera
    */
    this.camera = null;

    /*
    * @property {Roguelike.Player} player - Reference to the player object
    */
    this.player = null;

    /*
    * @property {Roguelike.Renderer} map - Reference to the current renderer
    */
    this.renderer = null;

    /*
    * @property {object} settings - The default settings
    */
    this.settings = {
        canvas: null,
        tilesX: 60,
        tilesY: 40,
        tileSize: 20,
        maxRooms: 10,
        minRoomWidth: 6,
        maxRoomWidth: 12,
        minRoomHeight: 6,
        maxRoomHeight: 12,
        debug: false
    };

    //Extend the default settings with those of the user
    this.settings = Roguelike.Utils.extend(this.settings, userSettings);

    //Initialize itself
    this.initialize();

};

Roguelike.Game.prototype = {

    /*
    * Initialize the game, create all objects
    * @protected
    *
    * @param {int} tileX - The number of tiles on the X axis
    * @param {int} tileY - The number of tiles on the Y axis
    * @param {int} maxRooms - The maximum number of rooms in a map
    */
    initialize: function(){

        //Check if the game is allready initialized
        if(this.isInitialized){
            return;
        }

        //Check if the debugged has to be initialized
        if(this.settings.debug === true){
            canvas.addEventListener('mousemove', Roguelike.Utils.debugTiles, false);
        }

        //Set this to the currentgame variable, so all the stupid Event Listeners
        //and Intervals know what is going on
        var currentGame = this;

        //Initialize the tracking of the players input
        this.controls = new Roguelike.Controls();

        function keyDownFunction(e){
            currentGame.controls.keyDown(e);
        }
        function keyUpFunction(e){
            currentGame.controls.keyUp(e);
        }
        window.addEventListener('keydown', keyDownFunction, false);
        window.addEventListener('keyup', keyUpFunction, false);

        //Initialize the map
        this.map = new Roguelike.Map(this);
        this.map.initialize();

        //Generate rooms for this map
        this.map.roomFactory.generateRooms(this);
        this.map.addRooms();

        //Create the camera object
        this.camera = new Roguelike.Camera(this, {x: 0, y: 0});
        //TODO: Let the camera follow the player
        //this.camera.follow(this.player, )

        //Add the player in the mix
        this.player = new Roguelike.Player(this, {x: 10, y: 10});

        //Draw the map on canvas
        this.renderer = new Roguelike.Renderer(this);
        this.renderer.drawMap();
        this.renderer.drawPlayer();

        //Call the gameloop
        updateInterval = setInterval(function(){
            currentGame.update();
        }, 1000/(1000/30));

        //The game is fully initialized!
        this.isInitialized = true;

    },

    /*
    * All the functions that need to be executed everytime the game updates
    * Basically the game loop
    * @protected
    *
    */
    update: function(){

        //Update the player
        this.player.update();

        //Update the camera
        this.camera.update();

        //Render the changes
        this.renderer.update();

    },

};<|endoftext|>