<commit_before>var async = require('async');
var ReadPreference = require('mongodb-read-preference');
var isNotAuthorized = require('mongodb-js-errors').isNotAuthorized;
var toNS = require('mongodb-ns');
var security = require('mongodb-security');
var _ = require('lodash');

var debug = require('debug')('mongodb-instance-model:autofetch');

/**
 * aggregates stats across all found databases
 * @param  {Function} done      callback
 * @param  {Object}   results   async.auto results
 */
function getStats(done, results) {
  var databases = results.databases;

  var keys = [
    'document_count',
    'storage_size',
    'index_count',
    'index_size'
  ];
  var stats = {};
  keys.map(function(k) {
    stats[k] = 0;
  });
  databases.map(function(db) {
    keys.map(function(k) {
      stats[k] += db[k];
    });
  });
  done(null, stats);
}

/**
 * @example
 *
 * @param {Object} resp - Result of `db.admin().command({buildInfo: 1})`.
 * @return {Object}
 */
function parseBuildInfo(resp) {
  var res = {
    version: resp.version,
    commit: resp.gitVersion,
    commit_url: 'https://github.com/mongodb/mongo/commit/' + resp.gitVersion,
    flags_loader: resp.loaderFlags,
    flags_compiler: resp.compilerFlags,
    allocator: resp.allocator,
    javascript_engine: resp.javascriptEngine,
    debug: resp.debug,
    for_bits: resp.bits,
    max_bson_object_size: resp.maxBsonObjectSize,
    enterprise_module: false
  };
  if (resp.modules && resp.modules.indexOf('enterprise') !== -1) {
    res.enterprise_module = true;
  }
  return res;
}

function getBuildInfo(done, results) {
  var db = results.db;

  debug('checking we can get buildInfo...');
  db.admin().buildInfo(function(err, res) {
    if (err) {
      debug('buildInfo failed!', err);
      err.command = 'buildInfo';
      return done(err);
    }
    done(null, parseBuildInfo(res));
  });
}

/**
 * @param {Object} resp - Result of `db.admin().command({hostInfo: 1})`.
 * @return {Object}
 */
function parseHostInfo(resp) {
  return {
    system_time: resp.system.currentTime,
    hostname: resp.system.hostname,
    os: resp.os.name,
    os_family: resp.os.type.toLowerCase(),
    kernel_version: resp.os.version,
    kernel_version_string: resp.extra.versionString,
    memory_bits: resp.system.memSizeMB * 1024 * 1024,
    memory_page_size: resp.extra.pageSize,
    arch: resp.system.cpuArch,
    cpu_cores: resp.system.numCores,
    cpu_cores_physical: resp.extra.physicalCores,
    cpu_scheduler: resp.extra.scheduler,
    cpu_frequency: resp.extra.cpuFrequencyMHz * 1000000,
    cpu_string: resp.extra.cpuString,
    cpu_bits: resp.system.cpuAddrSize,
    machine_model: resp.extra.model,
    feature_numa: resp.system.numaEnabled,
    /* `alwaysFullSync` seen as synchronous :p */
    /* eslint no-sync: 0 */
    feature_always_full_sync: resp.extra.alwaysFullSync,
    feature_nfs_async: resp.extra.nfsAsync
  };
}

/**
 * @param {Function} done
 * @param {mongo.Db} results
 *
 * @example
 *   { system_time: Sun Nov 08 2015 19:40:59 GMT-0500 (EST),
 *     hostname: 'lucas.local',
 *     os: 'Mac OS X',
 *     os_family: 'darwin',
 *     kernel_version: '14.5.0\u0000',
 *     kernel_version_string: 'Darwin Kernel Version 14.5.0: Tue Sep  1 21:23:09 PDT 2015; root:xnu-2782.50.1~1/RELEASE_X86_64\u0000',
 *     memory_bits: 17179869184,
 *     memory_page_size: 4096,
 *     arch: 'x86_64\u0000',
 *     cpu_cores: 4,
 *     cpu_cores_physical: 2,
 *     cpu_scheduler: 'multiq\u0000',
 *     cpu_frequency: 2800000000,
 *     cpu_string: 'Intel(R) Core(TM) i7-4558U CPU @ 2.80GHz\u0000',
 *     cpu_bits: 64,
 *     machine_model: 'MacBookPro11,1\u0000',
 *     feature_numa: false,
 *     feature_always_full_sync: 0,
 *     feature_nfs_async: 0 }
 */
function getHostInfo(done, results) {
  var db = results.db;

  debug('checking we can get hostInfo...');
  var spec = {
    hostInfo: 1
  };
  var options = {};
  db.admin().command(spec, options, function(err, res) {
    if (err) {
      if (/^not authorized/.test(err.message)) {
        debug('hostInfo unavailable for this user and thats ok!');
        done(null, {});
        return;
      }
      debug('driver error', err);
      err.command = 'hostInfo';
      done(err);
      return;
    }
    debug('got hostInfo successully!');
    done(null, parseHostInfo(res));
  });
}

function listDatabases(done, results) {
  var db = results.db;
  var userInfo = results.userInfo;

  var cluster = security.getResourcesWithActions(
    userInfo, ['listDatabases'], 'special').filter(function(resource) {
      return resource.cluster;
    });

  if (!cluster) {
    // user does not have privilege to run listDatabases, don't even try
    done(null, []);
  }

  var options = {
    readPreference: ReadPreference.nearest
  };

  var spec = {
    listDatabases: 1
  };

  db.admin().command(spec, options, function(err, res) {
    if (err) {
      if (isNotAuthorized(err)) {
        debug('listDatabases failed. returning empty list []');
        done(null, []);
        return;
      }

      debug('listDatabases failed', err);
      err.command = 'listDatabases';
      done(err);
      return;
    }

    var names = res.databases
      .map(function(d) {
        return d.name;
      });
      // .filter(function(name) {
      //   if (name === 'admin') {
      //     return false;
      //   }
      //   return true;
      // });

    debug('listDatabases succeeded!', {
      res: JSON.stringify(res),
      names: names
    });
    done(null, names);
  });
}

function parseDatabase(resp) {
  return {
    _id: resp.db,
    name: resp.db,
    document_count: resp.objects || 0,
    storage_size: resp.storageSize || 0,
    index_count: resp.indexes || 0,
    index_size: resp.indexSize || 0
  };
}

/**
 * @param {mongo.Db} db
 * @param {String} [name]
 * @param {Function} done

 * @example
 *   {
 *     _id: 'yelp',
 *     name: 'yelp',
 *     document_count: 656617,
 *     storage_size: 200200192,
 *     index_count: 3,
 *     index_size: 5496832
 *   }
 */
function getDatabase(db, name, done) {
  if (typeof name === 'function') {
    done = name;
    name = db.databaseName;
  }

  var spec = {
    dbStats: 1
  };

  var options = {};
  debug('running dbStats for `%s`...', name);
  db.db(name).command(spec, options, function(err, res) {
    if (err) {
      debug('dbStats failed. Falling back to no stats.');
      res = {};
    } else {
      debug('got dbStats for `%s`', name, res);
    }
    res.db = res.db || name;
    done(null, parseDatabase(res));
  });
}

function getDatabases(done, results) {
  var db = results.db;

  // merge and de-dupe databases
  var dbnames = _.union(results.listDatabases, results.allowedDatabases);

  async.parallel(_.map(dbnames, function(name) {
    var result = _.partial(getDatabase, db, name);
    return result;
  }), function(err, res) {
    done(err, res);
  });
}

function getUserInfo(done, results) {
  var db = results.db;

  // get the user privileges
  db.command({
    connectionStatus: 1,
    showPrivileges: true
  }, function(err, res) {
    if (err) {
      done(err);
    }
    var user = res.authInfo.authenticatedUsers[0];
    if (!user) {
      debug('no logged in user, returning empty document');
      return done(null, {});
    }

    db.command({
      usersInfo: user,
      showPrivileges: true
    }, function(_err, _res) {
      if (_err) {
        done(_err);
      }
      done(null, _res.users[0]);
    });
  });
}

function getAllowedDatabases(done, results) {
  var userInfo = results.userInfo;

  // get databases on which the user is allowed to call listCollections
  var databases = security.getResourcesWithActions(
    userInfo, ['listCollections'], 'database').map(function(resource) {
      return resource.db;
    });

  done(null, databases);
}

function parseCollection(resp) {
  var ns = toNS(resp.db + '.' + resp.name);
  return {
    _id: ns.toString(),
    name: ns.collection,
    database: ns.database
  };
}

function getAllowedCollections(done, results) {
  var userInfo = results.userInfo;

  // get collections on which the user is allowed to call find and collStats
  var compassActions = ['find', 'collStats'];
  var collections = security.getResourcesWithActions(
    userInfo, compassActions, 'collection').map(function(resource) {
      return {
        db: resource.db,
        name: resource.collection
      };
    });

  collections = _.map(collections, parseCollection);
  debug('allowed collections', collections);
  done(null, collections);
}

function getDatabaseCollections(db, done) {
  debug('getDatabaseCollections...');

  var options = {
    readPreference: ReadPreference.nearest
  };

  var spec = {};

  db.listCollections(spec, options).toArray(function(err, res) {
    if (err) {
      err.command = 'listCollections';
      return done(err);
    }
    done(null, _.map(res, function(d) {
      d.db = db.databaseName;
      return parseCollection(d);
    }));
  });
}

function getCollections(done, results) {
  // var db = results.db;

  // concat
  var collections = [].concat.apply(
    results.listCollections, results.allowedCollections);
  // de-dupe based on _id
  collections = _.uniq(collections, '_id');

  // @todo filter the ones that we can "count on"
  // async.filter(collections, function(collection, callback) {
  //   db.db(collection.database).collection(collection.name).count(function(err) {
  //     if (err) {
  //       return callback(false);
  //     }
  //     callback(true);
  //   });
  // }, function(countableCollections) {
  //   done(null, countableCollections);
  // });
  done(null, collections);
}

function listCollections(done, results) {
  var db = results.db;
  var databases = results.databases;

  // merge and de-dupe databases
  var dbnames = _.pluck(databases, 'name');
  var tasks = _.map(dbnames, function(name) {
    return getDatabaseCollections.bind(null, db.db(name));
  });

  async.parallel(tasks, function(err, res) {
    if (err) {
      debug('listCollections failed', err);
      return done(err);
    }
    done(null, _.flatten(res));
  });
}

function attach(anything, done) {
  done(null, anything);
}

/**
 * pass in a connection model and get instance details back.
 * @param  {Mongo.db}   db     database handle from the node driver
 * @param  {Function} done     callback
 */
function getInstanceDetail(db, done) {
  var tasks = {
    db: attach.bind(null, db),
    userInfo: ['db', getUserInfo],

    host: ['db', getHostInfo],
    build: ['db', getBuildInfo],

    listDatabases: ['db', 'userInfo', listDatabases],
    allowedDatabases: ['userInfo', getAllowedDatabases],
    databases: ['db', 'listDatabases', 'allowedDatabases', getDatabases],

    listCollections: ['db', 'databases', listCollections],
    allowedCollections: ['userInfo', getAllowedCollections],
    collections: ['db', 'listCollections', 'allowedCollections', getCollections],

    stats: ['databases', getStats]
  };

  async.auto(tasks, function(err, results) {
    if (err) {
      // report error
      return done(err);
    }
    // cleanup
    results = _.omit(results, ['db', 'listDatabases', 'allowedDatabases',
      'userInfo', 'listCollections', 'allowedCollections']);
    return done(null, results);
  });
}

module.exports = getInstanceDetail;

// module.exports.getCollections = getCollections;
// module.exports.getDatabaseCollections = getDatabaseCollections;
// module.exports.getDatabases = getDatabases;
// module.exports.getDatabase = getDatabase;
// module.exports.getBuildInfo = getBuildInfo;
// module.exports.getHostInfo = getHostInfo;
<commit_msg>INT-831 additionally list collections under databases<commit_after>var async = require('async');
var ReadPreference = require('mongodb-read-preference');
var isNotAuthorized = require('mongodb-js-errors').isNotAuthorized;
var toNS = require('mongodb-ns');
var security = require('mongodb-security');
var _ = require('lodash');

var debug = require('debug')('mongodb-instance-model:fetch');

/**
 * aggregates stats across all found databases
 * @param  {Function} done      callback
 * @param  {Object}   results   async.auto results
 */
function getStats(done, results) {
  var databases = results.databases;

  var keys = [
    'document_count',
    'storage_size',
    'index_count',
    'index_size'
  ];
  var stats = {};
  keys.map(function(k) {
    stats[k] = 0;
  });
  databases.map(function(db) {
    keys.map(function(k) {
      stats[k] += db[k];
    });
  });
  done(null, stats);
}

/**
 * @example
 *
 * @param {Object} resp - Result of `db.admin().command({buildInfo: 1})`.
 * @return {Object}
 */
function parseBuildInfo(resp) {
  var res = {
    version: resp.version,
    commit: resp.gitVersion,
    commit_url: 'https://github.com/mongodb/mongo/commit/' + resp.gitVersion,
    flags_loader: resp.loaderFlags,
    flags_compiler: resp.compilerFlags,
    allocator: resp.allocator,
    javascript_engine: resp.javascriptEngine,
    debug: resp.debug,
    for_bits: resp.bits,
    max_bson_object_size: resp.maxBsonObjectSize,
    enterprise_module: false
  };
  if (resp.modules && resp.modules.indexOf('enterprise') !== -1) {
    res.enterprise_module = true;
  }
  return res;
}

function getBuildInfo(done, results) {
  var db = results.db;

  debug('checking we can get buildInfo...');
  db.admin().buildInfo(function(err, res) {
    if (err) {
      debug('buildInfo failed!', err);
      err.command = 'buildInfo';
      return done(err);
    }
    done(null, parseBuildInfo(res));
  });
}

/**
 * @param {Object} resp - Result of `db.admin().command({hostInfo: 1})`.
 * @return {Object}
 */
function parseHostInfo(resp) {
  return {
    system_time: resp.system.currentTime,
    hostname: resp.system.hostname,
    os: resp.os.name,
    os_family: resp.os.type.toLowerCase(),
    kernel_version: resp.os.version,
    kernel_version_string: resp.extra.versionString,
    memory_bits: resp.system.memSizeMB * 1024 * 1024,
    memory_page_size: resp.extra.pageSize,
    arch: resp.system.cpuArch,
    cpu_cores: resp.system.numCores,
    cpu_cores_physical: resp.extra.physicalCores,
    cpu_scheduler: resp.extra.scheduler,
    cpu_frequency: resp.extra.cpuFrequencyMHz * 1000000,
    cpu_string: resp.extra.cpuString,
    cpu_bits: resp.system.cpuAddrSize,
    machine_model: resp.extra.model,
    feature_numa: resp.system.numaEnabled,
    /* `alwaysFullSync` seen as synchronous :p */
    /* eslint no-sync: 0 */
    feature_always_full_sync: resp.extra.alwaysFullSync,
    feature_nfs_async: resp.extra.nfsAsync
  };
}

/**
 * @param {Function} done
 * @param {mongo.Db} results
 *
 * @example
 *   { system_time: Sun Nov 08 2015 19:40:59 GMT-0500 (EST),
 *     hostname: 'lucas.local',
 *     os: 'Mac OS X',
 *     os_family: 'darwin',
 *     kernel_version: '14.5.0\u0000',
 *     kernel_version_string: 'Darwin Kernel Version 14.5.0: Tue Sep  1 21:23:09 PDT 2015; root:xnu-2782.50.1~1/RELEASE_X86_64\u0000',
 *     memory_bits: 17179869184,
 *     memory_page_size: 4096,
 *     arch: 'x86_64\u0000',
 *     cpu_cores: 4,
 *     cpu_cores_physical: 2,
 *     cpu_scheduler: 'multiq\u0000',
 *     cpu_frequency: 2800000000,
 *     cpu_string: 'Intel(R) Core(TM) i7-4558U CPU @ 2.80GHz\u0000',
 *     cpu_bits: 64,
 *     machine_model: 'MacBookPro11,1\u0000',
 *     feature_numa: false,
 *     feature_always_full_sync: 0,
 *     feature_nfs_async: 0 }
 */
function getHostInfo(done, results) {
  var db = results.db;

  debug('checking we can get hostInfo...');
  var spec = {
    hostInfo: 1
  };
  var options = {};
  db.admin().command(spec, options, function(err, res) {
    if (err) {
      if (/^not authorized/.test(err.message)) {
        debug('hostInfo unavailable for this user and thats ok!');
        done(null, {});
        return;
      }
      debug('driver error', err);
      err.command = 'hostInfo';
      done(err);
      return;
    }
    debug('got hostInfo successully!');
    done(null, parseHostInfo(res));
  });
}

function listDatabases(done, results) {
  var db = results.db;
  var userInfo = results.userInfo;

  var cluster = security.getResourcesWithActions(
    userInfo, ['listDatabases'], 'special').filter(function(resource) {
      return resource.cluster;
    });

  if (!cluster) {
    // user does not have privilege to run listDatabases, don't even try
    done(null, []);
  }

  var options = {
    readPreference: ReadPreference.nearest
  };

  var spec = {
    listDatabases: 1
  };

  db.admin().command(spec, options, function(err, res) {
    if (err) {
      if (isNotAuthorized(err)) {
        debug('listDatabases failed. returning empty list []');
        done(null, []);
        return;
      }

      debug('listDatabases failed', err);
      err.command = 'listDatabases';
      done(err);
      return;
    }

    var names = res.databases
      .map(function(d) {
        return d.name;
      });
      // .filter(function(name) {
      //   if (name === 'admin') {
      //     return false;
      //   }
      //   return true;
      // });

    debug('listDatabases succeeded!', {
      res: JSON.stringify(res),
      names: names
    });
    done(null, names);
  });
}

function parseDatabase(resp) {
  return {
    _id: resp.db,
    name: resp.db,
    document_count: resp.objects || 0,
    storage_size: resp.storageSize || 0,
    index_count: resp.indexes || 0,
    index_size: resp.indexSize || 0
  };
}

/**
 * @param {mongo.Db} db
 * @param {String} [name]
 * @param {Function} done

 * @example
 *   {
 *     _id: 'yelp',
 *     name: 'yelp',
 *     document_count: 656617,
 *     storage_size: 200200192,
 *     index_count: 3,
 *     index_size: 5496832
 *   }
 */
function getDatabase(db, name, done) {
  if (typeof name === 'function') {
    done = name;
    name = db.databaseName;
  }

  var spec = {
    dbStats: 1
  };

  var options = {};
  debug('running dbStats for `%s`...', name);
  db.db(name).command(spec, options, function(err, res) {
    if (err) {
      debug('dbStats failed. Falling back to no stats.');
      res = {};
    } else {
      debug('got dbStats for `%s`', name, res);
    }
    res.db = res.db || name;
    done(null, parseDatabase(res));
  });
}

function getDatabases(done, results) {
  var db = results.db;

  // merge and de-dupe databases
  var dbnames = _.union(results.listDatabases, results.allowedDatabases);

  async.parallel(_.map(dbnames, function(name) {
    var result = _.partial(getDatabase, db, name);
    return result;
  }), function(err, res) {
    done(err, res);
  });
}

function getUserInfo(done, results) {
  var db = results.db;

  // get the user privileges
  db.command({
    connectionStatus: 1,
    showPrivileges: true
  }, function(err, res) {
    if (err) {
      done(err);
    }
    var user = res.authInfo.authenticatedUsers[0];
    if (!user) {
      debug('no logged in user, returning empty document');
      return done(null, {});
    }

    db.command({
      usersInfo: user,
      showPrivileges: true
    }, function(_err, _res) {
      if (_err) {
        done(_err);
      }
      done(null, _res.users[0]);
    });
  });
}

function getAllowedDatabases(done, results) {
  var userInfo = results.userInfo;

  // get databases on which the user is allowed to call listCollections
  var databases = security.getResourcesWithActions(
    userInfo, ['listCollections'], 'database').map(function(resource) {
      return resource.db;
    });

  done(null, databases);
}

function parseCollection(resp) {
  var ns = toNS(resp.db + '.' + resp.name);
  return {
    _id: ns.toString(),
    name: ns.collection,
    database: ns.database
  };
}

function getAllowedCollections(done, results) {
  var userInfo = results.userInfo;

  // get collections on which the user is allowed to call find and collStats
  var compassActions = ['find', 'collStats'];
  var collections = security.getResourcesWithActions(
    userInfo, compassActions, 'collection').map(function(resource) {
      return {
        db: resource.db,
        name: resource.collection
      };
    });

  collections = _.map(collections, parseCollection);
  debug('allowed collections', collections);
  done(null, collections);
}

function getDatabaseCollections(db, done) {
  debug('getDatabaseCollections...');

  var options = {
    readPreference: ReadPreference.nearest
  };

  var spec = {};

  db.listCollections(spec, options).toArray(function(err, res) {
    if (err) {
      err.command = 'listCollections';
      return done(err);
    }
    done(null, _.map(res, function(d) {
      d.db = db.databaseName;
      return parseCollection(d);
    }));
  });
}

function getCollections(done, results) {
  // var db = results.db;

  // concat
  var collections = [].concat.apply(
    results.listCollections, results.allowedCollections);
  // de-dupe based on _id
  collections = _.uniq(collections, '_id');

  // @todo filter the ones that we can "count on"
  // async.filter(collections, function(collection, callback) {
  //   db.db(collection.database).collection(collection.name).count(function(err) {
  //     if (err) {
  //       return callback(false);
  //     }
  //     callback(true);
  //   });
  // }, function(countableCollections) {
  //   done(null, countableCollections);
  // });
  done(null, collections);
}

function listCollections(done, results) {
  var db = results.db;
  var databases = results.databases;

  // merge and de-dupe databases
  var dbnames = _.pluck(databases, 'name');
  var tasks = _.map(dbnames, function(name) {
    return getDatabaseCollections.bind(null, db.db(name));
  });

  async.parallel(tasks, function(err, res) {
    if (err) {
      debug('listCollections failed', err);
      return done(err);
    }
    done(null, _.flatten(res));
  });
}

function getHierarchy(done, results) {
  var databases = results.databases;
  var collections = _.groupBy(results.collections, function(collection) {
    return collection.database;
  });
  _.each(databases, function(db) {
    db.collections = collections[db.name] || [];
  });
  done();
}

function attach(anything, done) {
  done(null, anything);
}

/**
 * pass in a connection model and get instance details back.
 * @param  {Mongo.db}   db     database handle from the node driver
 * @param  {Function} done     callback
 */
function getInstanceDetail(db, done) {
  var tasks = {
    db: attach.bind(null, db),
    userInfo: ['db', getUserInfo],

    host: ['db', getHostInfo],
    build: ['db', getBuildInfo],

    listDatabases: ['db', 'userInfo', listDatabases],
    allowedDatabases: ['userInfo', getAllowedDatabases],
    databases: ['db', 'listDatabases', 'allowedDatabases', getDatabases],

    listCollections: ['db', 'databases', listCollections],
    allowedCollections: ['userInfo', getAllowedCollections],
    collections: ['db', 'listCollections', 'allowedCollections', getCollections],

    hierarchy: ['databases', 'collections', getHierarchy],

    stats: ['databases', getStats]
  };

  async.auto(tasks, function(err, results) {
    if (err) {
      // report error
      return done(err);
    }
    // cleanup
    results = _.omit(results, ['db', 'listDatabases', 'allowedDatabases',
      'userInfo', 'listCollections', 'allowedCollections']);
    return done(null, results);
  });
}

module.exports = getInstanceDetail;

// module.exports.getCollections = getCollections;
// module.exports.getDatabaseCollections = getDatabaseCollections;
// module.exports.getDatabases = getDatabases;
// module.exports.getDatabase = getDatabase;
// module.exports.getBuildInfo = getBuildInfo;
// module.exports.getHostInfo = getHostInfo;
<|endoftext|>


<commit_before>/*
    (c) 2014 Digital Humanities Lab, Faculty of Humanities, Utrecht University
    Author: Julian Gonggrijp, j.gonggrijp@uu.nl

    Automated text scraper for Spanish text corpora:
    http://corpus.rae.es/cordenet.html
    http://corpus.rae.es/creanet.html
    http://www.corpusdelespanol.org/
*/

(function ( ) {
    'use strict';

    var target,
        parser = new DOMParser(),
        data = [],
        progress_steps,
        progress = 0;

    function create_statusbar ( ) {
        var statuswidget = document.createElement('div');
        statuswidget.setAttribute('style', 'background: #fff; padding: 20px; border-radius: 10px; z-index: 10; position: fixed; top: 50px; right: 50px;');
        statuswidget.innerHTML = (
            '
'
            + '
'
            + '
'
        );
        target.document.body.appendChild(statuswidget);
    }

    function update_statusbar ( ) {
        var percentage = ++progress / progress_steps * 100;
        target.document.querySelector('#progress-fill').style.width = percentage + '%';
    }

    function retrieveAndProceed (href, continuation) {
        window.jQuery.get(href, function (data) {
            var docElem = parser.parseFromString(data, 'text/html');
            continuation(docElem);
        });
    }

    var domain = {
        'www.corpusdelespanol.org': {
            init: function ( ) {
                target = frames[6];
                var navtable = target.document.querySelectorAll('#zabba table')[1],
                    navrow = navtable.querySelectorAll('td')[2];
                progress_steps = Number(navrow.childNodes[4].nodeValue.split('/')[1]);
            },
            turnpage_then: function (doc, continuation, alternative) {
                var navtable = doc.querySelectorAll('#zabba table')[1];
                if (! navtable) {
                    alternative();
                    return;
                }
                var navrow = navtable.querySelectorAll('td')[2],
                    anchor = navrow.querySelectorAll('a')[2],
                    progress = navrow.childNodes[4].nodeValue.split('/');
                if (Number(progress[0]) < Number(progress[1])) {
                    retrieveAndProceed(anchor.href, continuation);
                } else {
                    alternative();
                }
            },
            scrape1page: function (doc) {
                var row, anchors, field, rowdata;
                for (var i = 1; i <= 100; ++i) {
                    row = doc.querySelector('#t' + i);
                    if (! row) continue;
                    anchors = row.querySelectorAll('a');
                    field = row.querySelector('#texto_' + i);
                    rowdata = [];
                    if (!anchors || !field || !field.value) continue;
                    for (var j = 0; j < 3; ++j) {
                        rowdata.push(anchors[j].childNodes[0].nodeValue);
                    }
                    data.push(rowdata.concat(field.value.split(/|<\/u><\/b>/)));
                }
                update_statusbar();
            }
        },
        'corpus.rae.es': {
            init: function ( ) {
                target = window;
                var navnode = document.querySelector('td.texto[align="center"]');
                progress_steps = Number(navnode.textContent.split(/[^0,1-9]+/)[2]) + 1;
            },
            turnpage_then: function (doc, continuation, alternative) {
                var anchor = doc.querySelector('td > a');
                if (! anchor || anchor.textContent != 'Siguiente') {
                    alternative();
                    return;
                }
                retrieveAndProceed(anchor.href, continuation);
            },
            scrape1page: function (doc) {
                var lines = doc.querySelector('tt').innerHTML.split('\n'),
                    pieces, century, rowdata;
                for (var l = lines.length, i = 1; i < l; ++i) {
                    pieces = lines[i].split(/|<\/a>/);
                    if (pieces.length < 3) continue;
                    century = Number(pieces[2].substr(52,15).match(/\d\d/));
                    rowdata = [
                        pieces[0].substr(0, 5),
                        century + 1,
                        pieces[2].substr(109, 61),
                        pieces[0].substr(5),
                        pieces[1],
                        pieces[2].substr(0, 48)
                    ];
                    data.push(rowdata);
                }
                update_statusbar();
            }
        }
    }[window.location.hostname];

    if (! domain) return;

    function insert_jquery_then (continuation) {
        var scriptnode = document.createElement('script');
        scriptnode.setAttribute('src', '//code.jquery.com/jquery-2.1.1.min.js');
        document.head.appendChild(scriptnode);
        scriptnode.addEventListener('load', continuation);
    }

    function data2csv ( ) {
        console.log(data);
        // step below removes mysterious undefined elements that
        // creep into the array
        data = data.filter(function (elem) { return elem; });
        var text = '',
            aLength;
        for (var l = data.length, i = 0; i < l; ++i) {
            var a = data[i];
            aLength = a.length;
            if (aLength != 6) {
                console.log('Error: subarray of incorrect length.\n', a);
                continue;
            }
            text += String(a[0]).trim() + ';' + String(a[1]).trim();
            for (var j = 2; j < aLength; ++j) {
                text += ';"' + String(a[j]).trim() + '"';
            }
            text += '\n';
        }
        document.write(
            'Scraping complete. Please copy the contents below ' +
            'into a plaintext document and give it a .csv extension.
' +
            '<textarea id="output" style="width: 50ex; height: 10em;">' +
            'number;century;text;contextLeft;sample;contextRight\n' +
            text +
            '</textarea>'
        );
        window.jQuery('#output').focus().select();
    }

    function scrape (doc) {
        domain.turnpage_then(doc, function (next_doc) {
            domain.scrape1page(next_doc);
            scrape(next_doc);
        }, data2csv);
    }

    domain.init();
    create_statusbar();
    domain.scrape1page(target.document);
    insert_jquery_then(function ( ) {
		update_statusbar();
		scrape(target.document);
	});
}());
<commit_msg>Make further adjustments because of JSLint<commit_after>/*
    (c) 2014 Digital Humanities Lab, Faculty of Humanities, Utrecht University
    Author: Julian Gonggrijp, j.gonggrijp@uu.nl

    Automated text scraper for Spanish text corpora:
    http://corpus.rae.es/cordenet.html
    http://corpus.rae.es/creanet.html
    http://www.corpusdelespanol.org/
*/

(function ( ) {
    'use strict';

    var target,
        parser = new DOMParser(),
        data = [],
        progress_steps,
        progress = 0;

    function create_statusbar ( ) {
        var statuswidget = document.createElement('div');
        statuswidget.setAttribute('style', 'background: #fff; padding: 20px; border-radius: 10px; z-index: 10; position: fixed; top: 50px; right: 50px;');
        statuswidget.innerHTML = (
            '
'
            + '
'
            + '
'
        );
        target.document.body.appendChild(statuswidget);
    }

    function update_statusbar ( ) {
        var percentage = ++progress / progress_steps * 100;
        target.document.querySelector('#progress-fill').style.width = percentage + '%';
    }

    function retrieveAndProceed (href, continuation) {
        window.jQuery.get(href, function (data) {
            var docElem = parser.parseFromString(data, 'text/html');
            continuation(docElem);
        });
    }

    var domain = ({
        'www.corpusdelespanol.org': {
            init: function ( ) {
                target = frames[6];
                var navtable = target.document.querySelectorAll('#zabba table')[1],
                    navrow = navtable.querySelectorAll('td')[2];
                progress_steps = Number(navrow.childNodes[4].nodeValue.split('/')[1]);
            },
            turnpage_then: function (doc, continuation, alternative) {
                var navtable = doc.querySelectorAll('#zabba table')[1];
                if (!navtable) {
                    alternative();
                    return;
                }
                var navrow = navtable.querySelectorAll('td')[2],
                    anchor = navrow.querySelectorAll('a')[2],
                    progress = navrow.childNodes[4].nodeValue.split('/');
                if (Number(progress[0]) < Number(progress[1])) {
                    retrieveAndProceed(anchor.href, continuation);
                } else {
                    alternative();
                }
            },
            scrape1page: function (doc) {
                var row, anchors, field, rowdata;
                for (var i = 1; i <= 100; ++i) {
                    row = doc.querySelector('#t' + i);
                    if (!row) continue;
                    anchors = row.querySelectorAll('a');
                    field = row.querySelector('#texto_' + i);
                    rowdata = [];
                    if (!anchors || !field || !field.value) continue;
                    for (var j = 0; j < 3; ++j) {
                        rowdata.push(anchors[j].childNodes[0].nodeValue);
                    }
                    data.push(rowdata.concat(field.value.split(/|<\/u><\/b>/)));
                }
                update_statusbar();
            }
        },
        'corpus.rae.es': {
            init: function ( ) {
                target = window;
                var navnode = document.querySelector('td.texto[align="center"]');
                progress_steps = Number(navnode.textContent.split(/[^0,1-9]+/)[2]) + 1;
            },
            turnpage_then: function (doc, continuation, alternative) {
                var anchor = doc.querySelector('td > a');
                if (!anchor || anchor.textContent !== 'Siguiente') {
                    alternative();
                    return;
                }
                retrieveAndProceed(anchor.href, continuation);
            },
            scrape1page: function (doc) {
                var lines = doc.querySelector('tt').innerHTML.split('\n'),
                    pieces, century, rowdata;
                for (var l = lines.length, i = 1; i < l; ++i) {
                    pieces = lines[i].split(/|<\/a>/);
                    if (pieces.length < 3) continue;
                    century = Number(pieces[2].substr(52, 15).match(/\d\d/));
                    rowdata = [
                        pieces[0].substr(0, 5),
                        century + 1,
                        pieces[2].substr(109, 61),
                        pieces[0].substr(5),
                        pieces[1],
                        pieces[2].substr(0, 48)
                    ];
                    data.push(rowdata);
                }
                update_statusbar();
            }
        }
    }[window.location.hostname]);

    if (!domain) return;

    function insert_jquery_then (continuation) {
        var scriptnode = document.createElement('script');
        scriptnode.setAttribute('src', '//code.jquery.com/jquery-2.1.1.min.js');
        document.head.appendChild(scriptnode);
        scriptnode.addEventListener('load', continuation);
    }

    function data2csv ( ) {
        console.log(data);
        // step below removes mysterious undefined elements that
        // creep into the array
        data = data.filter(function (elem) { return elem; });
        var text = '',
            aLength;
        for (var l = data.length, i = 0; i < l; ++i) {
            var a = data[i];
            aLength = a.length;
            if (aLength !== 6) {
                console.log('Error: subarray of incorrect length.\n', a);
                continue;
            }
            text += String(a[0]).trim() + ';' + String(a[1]).trim();
            for (var j = 2; j < aLength; ++j) {
                text += ';"' + String(a[j]).trim() + '"';
            }
            text += '\n';
        }
        document.write(
            'Scraping complete. Please copy the contents below ' +
            'into a plaintext document and give it a .csv extension.
' +
            '<textarea id="output" style="width: 50ex; height: 10em;">' +
            'number;century;text;contextLeft;sample;contextRight\n' +
            text +
            '</textarea>'
        );
        window.jQuery('#output').focus().select();
    }

    function scrape (doc) {
        domain.turnpage_then(doc, function (next_doc) {
            domain.scrape1page(next_doc);
            scrape(next_doc);
        }, data2csv);
    }

    domain.init();
    create_statusbar();
    domain.scrape1page(target.document);
    insert_jquery_then(function ( ) {
        update_statusbar();
        scrape(target.document);
    });
}());
<|endoftext|>


<commit_before>var semver = require("semver")
var parseGitHubURL = require("github-url-from-git")
var depTypes = ["dependencies","devDependencies","optionalDependencies"]
var extractDescription = require("./extract_description")
var url = require("url")
var typos = require("./typos")

var fixer = module.exports = {
  // default warning function
  warn: function() {},

  fixRepositoryField: function(data) {
    if (data.repositories) {
      this.warn("'repositories' (plural) Not supported.\n" +
           "Please pick one as the 'repository' field");
      data.repository = data.repositories[0]
    }
    if (!data.repository) return this.warn('No repository field.')
    if (typeof data.repository === "string") {
      data.repository = {
        type: "git",
        url: data.repository
      }
    }
    var r = data.repository.url || ""
    if (r) {
      var ghurl = parseGitHubURL(r)
      if (ghurl) {
        r = ghurl.replace(/^https?:\/\//, 'git://')
      }
    }

    if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) {
      this.warn("Probably broken git url: " + r)
    }
  }

, fixTypos: function(data) {
    Object.keys(typos.topLevel).forEach(function (d) {
      if (data.hasOwnProperty(d)) {
        this.warn(makeTypoWarning(d, typos.topLevel[d]))
      }
    }, this)
  }

, fixScriptsField: function(data) {
    if (!data.scripts) return
    if (typeof data.scripts !== "object") {
      this.warn("scripts must be an object")
      delete data.scripts
    }
    Object.keys(data.scripts).forEach(function (k) {
      if (typeof data.scripts[k] !== "string") {
        this.warn("script values must be string commands")
        delete data.scripts[k]
      } else if (typos.script[k]) {
        this.warn(makeTypoWarning(k, typos.script[k], "scripts"))
      }
    }, this)
  }

, fixFilesField: function(data) {
    var files = data.files
    if (files && !Array.isArray(files)) {
      this.warn("Invalid 'files' member")
      delete data.files
    } else if (data.files) {
      data.files = data.files.filter(function(file) {
        if (!file || typeof file !== "string") {
          this.warn("Invalid filename in 'files' list: " + file)
          return false
        } else {
          return true
        }
      }, this)
    }
  }

, fixBinField: function(data) {
    if (!data.bin) return;
    if (typeof data.bin === "string") {
      var b = {}
      b[data.name] = data.bin
      data.bin = b
    }
  }

, fixManField: function(data) {
    if (!data.man) return;
    if (typeof data.man === "string") {
      data.man = [ data.man ]
    }
  }
, fixBundleDependenciesField: function(data) {
    var bdd = "bundledDependencies"
    var bd = "bundleDependencies"
    if (data[bdd] && !data[bd]) {
      data[bd] = data[bdd]
      delete data[bdd]
    }
    if (data[bd] && !Array.isArray(data[bd])) {
      this.warn("Invalid 'bundleDependencies' list. " +
                "Must be array of package names")
      delete data[bd]
    } else if (data[bd]) {
      data[bd] = data[bd].filter(function(bd) {
        if (!bd || typeof bd !== 'string') {
          this.warn("Invalid bundleDependencies member: " + bd)
          return false
        } else {
          return true
        }
      }, this)
    }
  }

, fixDependencies: function(data, strict) {
    var loose = !strict
    objectifyDeps(data, this.warn)
    addOptionalDepsToDeps(data, this.warn)
    this.fixBundleDependenciesField(data)

    ;['dependencies','devDependencies'].forEach(function(deps) {
      if (!(deps in data)) return
      if (!data[deps] || typeof data[deps] !== "object") {
        this.warn(deps + " field must be an object")
        delete data[deps]
        return
      }
      Object.keys(data[deps]).forEach(function (d) {
        var r = data[deps][d]
        if (typeof r !== 'string') {
          this.warn('Invalid dependency: ' + d + ' ' + JSON.stringify(r))
          delete data[deps][d]
        } else {
          var v = semver.validRange(data[deps][d], loose)
          if (v === null) {
            var m = 'Invalid dependency: ' + d + '@' + JSON.stringify(r)
            if (strict) throw new Error(m)
            this.warn(m)
            delete data[deps][d]
          }
        }
      }, this)
    }, this)
  }

, fixModulesField: function (data) {
    if (data.modules) {
      this.warn("modules field is deprecated")
      delete data.modules
    }
  }

, fixKeywordsField: function (data) {
    if (typeof data.keywords === "string") {
      data.keywords = data.keywords.split(/,\s+/)
    }
    if (data.keywords && !Array.isArray(data.keywords)) {
      delete data.keywords
      this.warn("keywords should be an array of strings")
    } else if (data.keywords) {
      data.keywords = data.keywords.filter(function(kw) {
        if (typeof kw !== "string" || !kw) {
          this.warn("keywords should be an array of strings");
          return false
        } else {
          return true
        }
      }, this)
    }
  }

, fixVersionField: function(data, strict) {
    // allow "loose" semver 1.0 versions in non-strict mode
    // enforce strict semver 2.0 compliance in strict mode
    var loose = !strict
    if (!data.version) {
      data.version = ""
      return true
    }
    if (!semver.valid(data.version, loose)) {
      throw new Error('Invalid version: "'+ data.version + '"')
    }
    data.version = semver.clean(data.version, loose)
    return true
  }

, fixPeople: function(data) {
    modifyPeople(data, unParsePerson)
    modifyPeople(data, parsePerson)
  }

, fixNameField: function(data, strict) {
    if (!data.name && !strict) {
      data.name = ""
      return
    }
    if (typeof data.name !== "string") {
      throw new Error("name field must be a string.")
    }
    if (!strict)
      data.name = data.name.trim()
    ensureValidName(data.name, strict)
  }

, fixDescriptionField: function (data) {
    if (data.description && typeof data.description !== 'string') {
      this.warn("'description' field should be a string")
      delete data.description
    }
    if (data.readme && !data.description)
      data.description = extractDescription(data.readme)
    if (!data.description) this.warn('No description')
  }

, fixReadmeField: function (data) {
    if (!data.readme) {
      this.warn("No README data")
      data.readme = "ERROR: No README data found!"
    }
  }

, fixBugsField: function(data) {
    if (!data.bugs && data.repository && data.repository.url) {
      var gh = parseGitHubURL(data.repository.url)
      if(gh) {
        if(gh.match(/^https:\/\/github.com\//))
          data.bugs = {url: gh + "/issues"}
        else // gist url
          data.bugs = {url: gh}
      }
    }
    else if(data.bugs) {
      var emailRe = /^.+@.*\..+$/
      if(typeof data.bugs == "string") {
        if(emailRe.test(data.bugs))
          data.bugs = {email:data.bugs}
        else if(url.parse(data.bugs).protocol)
          data.bugs = {url: data.bugs}
        else
          this.warn("Bug string field must be url, email, or {email,url}")
      }
      else {
        bugsTypos(data.bugs, this.warn)
        var oldBugs = data.bugs
        data.bugs = {}
        if(oldBugs.url) {
          if(typeof(oldBugs.url) == "string" && url.parse(oldBugs.url).protocol)
            data.bugs.url = oldBugs.url
          else
            this.warn("bugs.url field must be a string url. Deleted.")
        }
        if(oldBugs.email) {
          if(typeof(oldBugs.email) == "string" && emailRe.test(oldBugs.email))
            data.bugs.email = oldBugs.email
          else
            this.warn("bugs.email field must be a string email. Deleted.")
        }
      }
      if(!data.bugs.email && !data.bugs.url) {
        delete data.bugs
        this.warn("Normalized value of bugs field is an empty object. Deleted.")
      }
    }
  }

,  fixHomepageField: function(data) {
    if(!data.homepage) return true;
    if(typeof data.homepage !== "string") {
      this.warn("homepage field must be a string url. Deleted.")
      return delete data.homepage
    }
    if(!url.parse(data.homepage).protocol) {
      this.warn("homepage field must start with a protocol.")
      data.homepage = "http://" + data.homepage
    }
  }
}

function ensureValidName (name, strict) {
  if (name.charAt(0) === "." ||
      name.match(/[\/@\s\+%:]/) ||
      name !== encodeURIComponent(name) ||
      (strict && name !== name.toLowerCase()) ||
      name.toLowerCase() === "node_modules" ||
      name.toLowerCase() === "favicon.ico") {
        throw new Error("Invalid name: " + JSON.stringify(name))
  }
}

function modifyPeople (data, fn) {
  if (data.author) data.author = fn(data.author)
  ;["maintainers", "contributors"].forEach(function (set) {
    if (!Array.isArray(data[set])) return;
    data[set] = data[set].map(fn)
  })
  return data
}

function unParsePerson (person) {
  if (typeof person === "string") return person
  var name = person.name || ""
  var u = person.url || person.web
  var url = u ? (" ("+u+")") : ""
  var e = person.email || person.mail
  var email = e ? (" <"+e+">") : ""
  return name+email+url
}

function parsePerson (person) {
  if (typeof person !== "string") return person
  var name = person.match(/^([^\(<]+)/)
  var url = person.match(/\(([^\)]+)\)/)
  var email = person.match(/<([^>]+)>/)
  var obj = {}
  if (name && name[0].trim()) obj.name = name[0].trim()
  if (email) obj.email = email[1];
  if (url) obj.url = url[1];
  return obj
}

function addOptionalDepsToDeps (data, warn) {
  var o = data.optionalDependencies
  if (!o) return;
  var d = data.dependencies || {}
  Object.keys(o).forEach(function (k) {
    d[k] = o[k]
  })
  data.dependencies = d
}

function depObjectify (deps) {
  if (!deps) return {}
  if (typeof deps === "string") {
    deps = deps.trim().split(/[\n\r\s\t ,]+/)
  }
  if (!Array.isArray(deps)) return deps
  var o = {}
  deps.filter(function (d) {
    return typeof d === "string"
  }).forEach(function(d) {
    d = d.trim().split(/(:?[@\s><=])/)
    var dn = d.shift()
    var dv = d.join("")
    dv = dv.trim()
    dv = dv.replace(/^@/, "")
    o[dn] = dv
  })
  return o
}

function objectifyDeps (data, warn) {
  depTypes.forEach(function (type) {
    if (!data[type]) return;
    data[type] = depObjectify(data[type])
  })
}

function bugsTypos(bugs, warn) {
  if (!bugs) return
  Object.keys(bugs).forEach(function (k) {
    if (typos.bugs[k]) {
      warn(makeTypoWarning(k, typos.bugs[k], "bugs"))
      bugs[typos.bugs[k]] = bugs[k]
      delete bugs[k]
    }
  })
}

function makeTypoWarning (providedName, probableName, field) {
  if (field) {
    providedName = field + "['" + providedName + "']"
    probableName = field + "['" + probableName + "']"
  }
  return providedName + " should probably be " + probableName + "."
}
<commit_msg>Allow urls as dependencies<commit_after>var semver = require("semver")
var parseGitHubURL = require("github-url-from-git")
var depTypes = ["dependencies","devDependencies","optionalDependencies"]
var extractDescription = require("./extract_description")
var url = require("url")
var typos = require("./typos")

var fixer = module.exports = {
  // default warning function
  warn: function() {},

  fixRepositoryField: function(data) {
    if (data.repositories) {
      this.warn("'repositories' (plural) Not supported.\n" +
           "Please pick one as the 'repository' field");
      data.repository = data.repositories[0]
    }
    if (!data.repository) return this.warn('No repository field.')
    if (typeof data.repository === "string") {
      data.repository = {
        type: "git",
        url: data.repository
      }
    }
    var r = data.repository.url || ""
    if (r) {
      var ghurl = parseGitHubURL(r)
      if (ghurl) {
        r = ghurl.replace(/^https?:\/\//, 'git://')
      }
    }

    if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) {
      this.warn("Probably broken git url: " + r)
    }
  }

, fixTypos: function(data) {
    Object.keys(typos.topLevel).forEach(function (d) {
      if (data.hasOwnProperty(d)) {
        this.warn(makeTypoWarning(d, typos.topLevel[d]))
      }
    }, this)
  }

, fixScriptsField: function(data) {
    if (!data.scripts) return
    if (typeof data.scripts !== "object") {
      this.warn("scripts must be an object")
      delete data.scripts
    }
    Object.keys(data.scripts).forEach(function (k) {
      if (typeof data.scripts[k] !== "string") {
        this.warn("script values must be string commands")
        delete data.scripts[k]
      } else if (typos.script[k]) {
        this.warn(makeTypoWarning(k, typos.script[k], "scripts"))
      }
    }, this)
  }

, fixFilesField: function(data) {
    var files = data.files
    if (files && !Array.isArray(files)) {
      this.warn("Invalid 'files' member")
      delete data.files
    } else if (data.files) {
      data.files = data.files.filter(function(file) {
        if (!file || typeof file !== "string") {
          this.warn("Invalid filename in 'files' list: " + file)
          return false
        } else {
          return true
        }
      }, this)
    }
  }

, fixBinField: function(data) {
    if (!data.bin) return;
    if (typeof data.bin === "string") {
      var b = {}
      b[data.name] = data.bin
      data.bin = b
    }
  }

, fixManField: function(data) {
    if (!data.man) return;
    if (typeof data.man === "string") {
      data.man = [ data.man ]
    }
  }
, fixBundleDependenciesField: function(data) {
    var bdd = "bundledDependencies"
    var bd = "bundleDependencies"
    if (data[bdd] && !data[bd]) {
      data[bd] = data[bdd]
      delete data[bdd]
    }
    if (data[bd] && !Array.isArray(data[bd])) {
      this.warn("Invalid 'bundleDependencies' list. " +
                "Must be array of package names")
      delete data[bd]
    } else if (data[bd]) {
      data[bd] = data[bd].filter(function(bd) {
        if (!bd || typeof bd !== 'string') {
          this.warn("Invalid bundleDependencies member: " + bd)
          return false
        } else {
          return true
        }
      }, this)
    }
  }

, fixDependencies: function(data, strict) {
    var loose = !strict
    objectifyDeps(data, this.warn)
    addOptionalDepsToDeps(data, this.warn)
    this.fixBundleDependenciesField(data)

    ;['dependencies','devDependencies'].forEach(function(deps) {
      if (!(deps in data)) return
      if (!data[deps] || typeof data[deps] !== "object") {
        this.warn(deps + " field must be an object")
        delete data[deps]
        return
      }
      Object.keys(data[deps]).forEach(function (d) {
        var r = data[deps][d]
        if (typeof r !== 'string') {
          this.warn('Invalid dependency: ' + d + ' ' + JSON.stringify(r))
          delete data[deps][d]
        } else {
          var v = semver.validRange(data[deps][d], loose)
          if (v === null) {
            // if it's null, then it MUST be either a name/project or a url
            v = url.parse(data[deps][d])
            if (v.protocol) return
            else if (data[deps][d].match(/^[^\/]+\/[^\/]+$/)) return
            var m = 'Invalid dependency: ' + d + '@' + JSON.stringify(r)
            if (strict) throw new Error(m)
            this.warn(m)
            delete data[deps][d]
          }
        }
      }, this)
    }, this)
  }

, fixModulesField: function (data) {
    if (data.modules) {
      this.warn("modules field is deprecated")
      delete data.modules
    }
  }

, fixKeywordsField: function (data) {
    if (typeof data.keywords === "string") {
      data.keywords = data.keywords.split(/,\s+/)
    }
    if (data.keywords && !Array.isArray(data.keywords)) {
      delete data.keywords
      this.warn("keywords should be an array of strings")
    } else if (data.keywords) {
      data.keywords = data.keywords.filter(function(kw) {
        if (typeof kw !== "string" || !kw) {
          this.warn("keywords should be an array of strings");
          return false
        } else {
          return true
        }
      }, this)
    }
  }

, fixVersionField: function(data, strict) {
    // allow "loose" semver 1.0 versions in non-strict mode
    // enforce strict semver 2.0 compliance in strict mode
    var loose = !strict
    if (!data.version) {
      data.version = ""
      return true
    }
    if (!semver.valid(data.version, loose)) {
      throw new Error('Invalid version: "'+ data.version + '"')
    }
    data.version = semver.clean(data.version, loose)
    return true
  }

, fixPeople: function(data) {
    modifyPeople(data, unParsePerson)
    modifyPeople(data, parsePerson)
  }

, fixNameField: function(data, strict) {
    if (!data.name && !strict) {
      data.name = ""
      return
    }
    if (typeof data.name !== "string") {
      throw new Error("name field must be a string.")
    }
    if (!strict)
      data.name = data.name.trim()
    ensureValidName(data.name, strict)
  }

, fixDescriptionField: function (data) {
    if (data.description && typeof data.description !== 'string') {
      this.warn("'description' field should be a string")
      delete data.description
    }
    if (data.readme && !data.description)
      data.description = extractDescription(data.readme)
    if (!data.description) this.warn('No description')
  }

, fixReadmeField: function (data) {
    if (!data.readme) {
      this.warn("No README data")
      data.readme = "ERROR: No README data found!"
    }
  }

, fixBugsField: function(data) {
    if (!data.bugs && data.repository && data.repository.url) {
      var gh = parseGitHubURL(data.repository.url)
      if(gh) {
        if(gh.match(/^https:\/\/github.com\//))
          data.bugs = {url: gh + "/issues"}
        else // gist url
          data.bugs = {url: gh}
      }
    }
    else if(data.bugs) {
      var emailRe = /^.+@.*\..+$/
      if(typeof data.bugs == "string") {
        if(emailRe.test(data.bugs))
          data.bugs = {email:data.bugs}
        else if(url.parse(data.bugs).protocol)
          data.bugs = {url: data.bugs}
        else
          this.warn("Bug string field must be url, email, or {email,url}")
      }
      else {
        bugsTypos(data.bugs, this.warn)
        var oldBugs = data.bugs
        data.bugs = {}
        if(oldBugs.url) {
          if(typeof(oldBugs.url) == "string" && url.parse(oldBugs.url).protocol)
            data.bugs.url = oldBugs.url
          else
            this.warn("bugs.url field must be a string url. Deleted.")
        }
        if(oldBugs.email) {
          if(typeof(oldBugs.email) == "string" && emailRe.test(oldBugs.email))
            data.bugs.email = oldBugs.email
          else
            this.warn("bugs.email field must be a string email. Deleted.")
        }
      }
      if(!data.bugs.email && !data.bugs.url) {
        delete data.bugs
        this.warn("Normalized value of bugs field is an empty object. Deleted.")
      }
    }
  }

,  fixHomepageField: function(data) {
    if(!data.homepage) return true;
    if(typeof data.homepage !== "string") {
      this.warn("homepage field must be a string url. Deleted.")
      return delete data.homepage
    }
    if(!url.parse(data.homepage).protocol) {
      this.warn("homepage field must start with a protocol.")
      data.homepage = "http://" + data.homepage
    }
  }
}

function ensureValidName (name, strict) {
  if (name.charAt(0) === "." ||
      name.match(/[\/@\s\+%:]/) ||
      name !== encodeURIComponent(name) ||
      (strict && name !== name.toLowerCase()) ||
      name.toLowerCase() === "node_modules" ||
      name.toLowerCase() === "favicon.ico") {
        throw new Error("Invalid name: " + JSON.stringify(name))
  }
}

function modifyPeople (data, fn) {
  if (data.author) data.author = fn(data.author)
  ;["maintainers", "contributors"].forEach(function (set) {
    if (!Array.isArray(data[set])) return;
    data[set] = data[set].map(fn)
  })
  return data
}

function unParsePerson (person) {
  if (typeof person === "string") return person
  var name = person.name || ""
  var u = person.url || person.web
  var url = u ? (" ("+u+")") : ""
  var e = person.email || person.mail
  var email = e ? (" <"+e+">") : ""
  return name+email+url
}

function parsePerson (person) {
  if (typeof person !== "string") return person
  var name = person.match(/^([^\(<]+)/)
  var url = person.match(/\(([^\)]+)\)/)
  var email = person.match(/<([^>]+)>/)
  var obj = {}
  if (name && name[0].trim()) obj.name = name[0].trim()
  if (email) obj.email = email[1];
  if (url) obj.url = url[1];
  return obj
}

function addOptionalDepsToDeps (data, warn) {
  var o = data.optionalDependencies
  if (!o) return;
  var d = data.dependencies || {}
  Object.keys(o).forEach(function (k) {
    d[k] = o[k]
  })
  data.dependencies = d
}

function depObjectify (deps) {
  if (!deps) return {}
  if (typeof deps === "string") {
    deps = deps.trim().split(/[\n\r\s\t ,]+/)
  }
  if (!Array.isArray(deps)) return deps
  var o = {}
  deps.filter(function (d) {
    return typeof d === "string"
  }).forEach(function(d) {
    d = d.trim().split(/(:?[@\s><=])/)
    var dn = d.shift()
    var dv = d.join("")
    dv = dv.trim()
    dv = dv.replace(/^@/, "")
    o[dn] = dv
  })
  return o
}

function objectifyDeps (data, warn) {
  depTypes.forEach(function (type) {
    if (!data[type]) return;
    data[type] = depObjectify(data[type])
  })
}

function bugsTypos(bugs, warn) {
  if (!bugs) return
  Object.keys(bugs).forEach(function (k) {
    if (typos.bugs[k]) {
      warn(makeTypoWarning(k, typos.bugs[k], "bugs"))
      bugs[typos.bugs[k]] = bugs[k]
      delete bugs[k]
    }
  })
}

function makeTypoWarning (providedName, probableName, field) {
  if (field) {
    providedName = field + "['" + providedName + "']"
    probableName = field + "['" + probableName + "']"
  }
  return providedName + " should probably be " + probableName + "."
}
<|endoftext|>


<commit_before>! function(window, angular, undefined) {
  var defaultMapSettings = {
      center: {
        lat: 12.94206,
        lng: 77.62229
      },
      zoom: 8
    },
    setMarkerFunc = undefined;

  function setMarkerCurryFunc(placesMap) {
    if (placesMap) {
      var map = placesMap;
    }
    return function(marker) {
      if (!map) {
        throw "Please pass a map reference before setting marker";
      }
      if (Object.prototype.toString.call(marker) == "[object Object]" && marker.position !== undefined) {
        marker.map = map;
        var newMarker = new google.maps.Marker(marker);
        newMarker.setMap(map);
        return newMarker;
      }
    }
  }

  angular.module('ds.map', []).
  factory('dsMapFactory', ['$http', function($http) {
    var obj = {
      getDefaultMapSettings: getDefaultMapSettingsFunc,
      setDefaultMapSettings: setDefaultMapSettingsFunc,
      setDirectionsToLocation: function() {}
    };

    function getDefaultMapSettingsFunc() {
      return defaultMapSettings;
    }

    function setDefaultMapSettingsFunc(settingsObj) {
      defaultMapSettings = settingsObj;
    }

    return obj;
  }]).
  directive('dsMap', ['$q', '$timeout', 'dsMapFactory', function($q, $timeout, dsMapFactory) {
    var deferred = $q.defer(),
      obj = {
        scope: {
          mapSettings: "="
        },
        restrict: "E",
        link: function(scope) {

        },
        controller: ['$scope', '$element', '$attrs', function($scope, $element, $attr) {
          var _deferred = $q.defer(),
            placesDeferred = $q.defer(),
            dsMapElements = 0,
            deferredArray = [],
            that = this,
            mapSettings = that.mapSettings = $scope.mapSettings || dsMapFactory.getDefaultMapSettings(),
            map = new google.maps.Map(document.createElement('div'), mapSettings);

          function getLocationFunc() {
            return that.dsPlacesLocation;
          }

          function setLocationFunc(center) {
            that.dsPlacesLocation = center || that.getMap().getCenter();
          }

          function getMapFunc() {
            return that.map;
          }

          function setMapFunc(map) {
            dsMapFactory.map = that.map = map;
            that.setLocation(that.map.getCenter());
          }

          function iterateAllMarkersFunc(callback) {
            for (var groups in that.placesGroupHash) {
              var eachGroupRef = that.placesGroupHash[groups];
              for (var _count = 0, len = eachGroupRef.length; _count < len; _count++) {
                var allIteratedMarkers = eachGroupRef[_count];
                callback.call(allIteratedMarkers);
              }
            }
          }

          function iterateMarkersMatchingBooleanFunc(bool, callback) {
            that.iterateAllMarkers(function() {
              var eachPreviousMarker = this;
              if (eachPreviousMarker.visible === bool) {
                callback.call(eachPreviousMarker);
                eachPreviousMarker.setMap(eachPreviousMarker.getMap());
              }
            });
          }

          function setGroupMarkerVisibilityFunc(group, bool, setVisibleMarkerBounds) {
            var currentGroupRef = that.placesGroupHash[group],
              previousGroup = undefined;
            if (previousGroup != group) {
              that.bounds = new google.maps.LatLngBounds();
              for (var currentGroupCount = 0, len = currentGroupRef.length; currentGroupCount < len; currentGroupCount++) {
                var eachCurrentMarker = currentGroupRef[currentGroupCount];
                if (eachCurrentMarker.visible !== bool) {
                  eachCurrentMarker.visible = bool;
                  eachCurrentMarker.setMap(eachCurrentMarker.getMap());
                  if (setVisibleMarkerBounds && eachCurrentMarker.visible) {
                    that.bounds.extend(eachCurrentMarker.position);
                  }
                }
              }

              if (setVisibleMarkerBounds) {
                that.changeMapBounds();
              }
            }

            previousGroup = group;
          }

          function hideAllVisibleMarkersFunc() {
            that.iterateMarkersMatchingBoolean(true, function() {
              var visibleMarker = this;
              visibleMarker.visible = false;
            });
          }

          function changeMapBoundsFunc() {
            that.getMap().fitBounds(that.bounds);
            that.getMap().setCenter(that.getLocation());
            that.getMap().setZoom(that.getMap().getZoom() - 1);
          }

          that.mapDeferred = deferred;
          that.mapRendered = deferred.promise;
          that.placesDeferred = placesDeferred;
          that.placesRendered = placesDeferred.promise;
          that.placesGroupHash = {};
          that.placesCount = 0;
          that.getLocation = getLocationFunc;
          that.setLocation = setLocationFunc;
          that.getMap = getMapFunc;
          that.setMap = setMapFunc;
          that.iterateAllMarkers = iterateAllMarkersFunc;
          that.iterateMarkersMatchingBoolean = iterateMarkersMatchingBooleanFunc;
          that.setGroupMarkerVisibility = setGroupMarkerVisibilityFunc;
          that.hideAllVisibleMarkers = hideAllVisibleMarkersFunc;
          that.changeMapBounds = changeMapBoundsFunc;

          that.setMap(map);

          that.placesRendered.then(function() {
            that.changeMapBounds();
          });

          (function(dfrd, _dfrd) {
            that.mapRendered.then(function() {
              dsMapFactory.setMarker = setMarkerFunc = setMarkerCurryFunc(that.getMap());
            });
          })(deferred, _deferred);

          that.nextDefer = deferred = _deferred;
        }]
      };
    return obj;
  }]).
  directive('dsMapView', function() {
      var obj = {
        scope: {
          mapSettings: "=",
          originOptions: "=",
          destinationOptions: "="
        },
        restrict: "E",
        replace: true,
        template: "

",
        require: "^?dsMap",
        link: function(scope, element, attrs, dsMapController) {
          var map = new google.maps.Map(element[0], scope.mapSettings || dsMapController.mapSettings),
            clonedOriginMarkerObject = angular.copy(scope.originOptions || {});
          dsMapController.setMap(map);
          dsMapController.isViewSet = true;
          dsMapController.destinationOptions = angular.copy(scope.destinationOptions || {});
          dsMapController.mapRendered.then(function() {
            clonedOriginMarkerObject.position = dsMapController.getLocation();
            setMarkerFunc(clonedOriginMarkerObject);
          });
        },
        controller: ['$scope', function($scope) {
          var that = this;
          that.originOptions = $scope.originOptions;
          that.destinationOptions = angular.copy($scope.destinationOptions);
        }]
      };
      return obj;
    }).directive('dsMapPlaces', ['$q', 'dsMapFactory', function($q, dsMapFactory) {
      var initialdsMapView = true,
        deferred = $q.defer(),
        obj = {
          scope: {
            options: "="
          },
          restrict: "E",
          replace: true,
          require: "^?dsMap",
          link: function(scope, element, attrs, dsMapController) {
            var rankByDistance = google.maps.places.RankBy.DISTANCE;

            if (initialdsMapView) {
              dsMapController.mapDeferred.resolve();
              initialdsMapView = false;
            }

            dsMapController.mapRendered.then(function() {
              dsMapController.placesService = new google.maps.places.PlacesService(dsMapController.getMap());
              dsMapController.directionsService = new google.maps.DirectionsService();
              scope.options.map = dsMapController.getMap();
              dsMapController.directionsDisplay = new google.maps.DirectionsRenderer(scope.options);
              dsMapController.bounds = new google.maps.LatLngBounds();
              deferred.resolve(dsMapController.placesService);
            });
          },
          controller: ['$scope', '$element', '$attrs', function($scope, $element, $attr) {
            var that = this;

            function dsMapPlacesDeferredFunc() {
              var _deferred = $q.defer();
              return function() {
                return _deferred;
              };
            }

            function dsMapPlacesLoadedFunc(callback) {
              that.dsMapPlacesDeferred().promise.then(callback);
            }

            that.placesServiceSet = deferred.promise;
            that.dsMapPlacesDeferred = dsMapPlacesDeferredFunc();
            dsMapFactory.dsMapPlacesLoaded = that.dsMapPlacesLoaded = dsMapPlacesLoadedFunc;
          }]
        };
      return obj;
    }])
    .directive('dsGroup', ['dsMapFactory', function(dsMapFactory) {
      var obj = {
        scope: true,
        restrict: "E",
        require: ["^?dsMap", "^?dsMapPlaces"],
        link: function(scope, element, attrs, dsMapControllers) {
          previousGroup = undefined;
          dsMapController = dsMapControllers[0];
          dsMapPlacesController = dsMapControllers[1];

          (function(previousGroup, dsMapController, dsMapPlacesController) {
            var isMapPlacesLoaded = false;

            function onToggleClickFunc(group) {
              dsMapController.directionsDisplay.setMap(null);
              if (previousGroup !== undefined) {
                if (previousGroup == group) {
                  return;
                }
                dsMapController.setGroupMarkerVisibility(previousGroup, false, true);
              } else {
                dsMapController.hideAllVisibleMarkers();
              }

              dsMapController.setGroupMarkerVisibility(group, true, true);

              previousGroup = group;
            }

            function toggleGroupVisibilityFunc(group) {
              dsMapPlacesController.dsMapPlacesLoaded(function() {
                isMapPlacesLoaded = true;
              });
              if (isMapPlacesLoaded) {
                onToggleClickFunc(group);
              }
            }

            function setDirectionsToLocationFunc(location, mode) {
              var request = {
                origin: dsMapController.getLocation(),
                destination: location,
                travelMode: google.maps.TravelMode[mode]
              };

              dsMapController.destinationOptions.position = location;

              dsMapPlacesController.dsMapPlacesLoaded(function() {
                dsMapController.hideAllVisibleMarkers();
                dsMapController.directionsDisplay.setMap(dsMapController.getMap());
                dsMapController.directionsService.route(request, function(response, status) {
                  if (status == "OK") {
                    var destinationMarker = setMarkerFunc(dsMapController.destinationOptions);
                    dsMapController.directionsDisplay.setDirections(response);
                    dsMapController.bounds = new google.maps.LatLngBounds();
                    dsMapController.bounds.extend(dsMapController.getLocation());
                    dsMapController.bounds.extend(location);
                    dsMapController.placesGroupHash['searchedLocation'] = [destinationMarker];
                    dsMapController.changeMapBounds();
                  }
                });
              });
            }

            scope.toggleGroupVisibility = toggleGroupVisibilityFunc;

            if (dsMapController.isViewSet) {
              dsMapFactory.setDirectionsToLocation = setDirectionsToLocationFunc;
            }
          })(previousGroup, dsMapController, dsMapPlacesController);
        },
        controller: ['$scope', function($scope) {
          var that = undefined;
          that = this;
          that.isGroupSet = true;
        }]
      };

      return obj;
    }])
    .directive('dsPlacesType', ['$q', '$timeout', function($q, $timeout) {
      var deferred = $q.defer(),
        placesServiceCount = 0,
        obj = {
          scope: {
            group: "@",
            types: "=",
            options: "=",
            sortOption: "@",
            radius: "="
          },
          templateUrl: "dsMapPlaces.html",
          restrict: "E",
          replace: true,
          require: ["^?dsMap", "^?dsMapPlaces", "^?dsGroup"],
          link: function(scope, element, attrs, dsMapControllers) {
            var dsMapController = dsMapControllers[0],
              dsMapPlacesController = dsMapControllers[1],
              dsGroupController = dsMapControllers[2],
              obj = {
                location: dsMapController.getMap().getCenter(),
                types: []
              },
              showDirectionTimeoutId = undefined;

            function callPlacesServiceFunc(dfrd, _dfrd, type) {
              dsMapController.placesCount++;
              dsMapController.mapRendered.then(function() {
                dfrd.promise.then(function() {
                  dsMapPlacesController.placesServiceSet.then(function(placesService) {
                    if (type) {
                      obj.types = [type];
                    }

                    placesService.nearbySearch(obj, function(results, status) {
                      if (status == "OK" && Object.prototype.toString.call(results) == "[object Array]") {
                        scope.Places = scope.Places.concat(type === undefined ? results : results.slice(0, scope.types[type]));
                      }
                      $timeout(function() {
                        if ((--dsMapController.placesCount) === 0) {
                          dsMapController.nextDefer.resolve();
                          dsMapController.nextDefer.promise.then(function() {
                            _dfrd.resolve();
                            dsMapPlacesController.dsMapPlacesDeferred().resolve();
                          });
                          dsMapController.placesDeferred.resolve();
                        } else {
                          _dfrd.resolve();
                        }

                        // @TODO : Increases performance drastically but fails for more than one map
                      }, (dsMapController.placesCount % 10 !== 0 ? 0 : 1000));
                    });
                  });
                });
              });
            }

            function showDirectionFunc() {
              var that = this;
              $timeout.cancel(showDirectionTimeoutId);
              showDirectionTimeoutId = $timeout(function() {
                var request = {
                  origin: dsMapController.getLocation(),
                  destination: that.place.geometry.location,
                  travelMode: google.maps.TravelMode["DRIVING"]
                };

                if (dsGroupController && dsMapController.placesGroupHash[scope.group][0].visible === false) {
                  dsMapController.setGroupMarkerVisibility(scope.group, true, true);
                }

                dsMapController.directionsDisplay.setMap(dsMapController.getMap());

                try {
                  if (that.isDirectionResponseCallMade) {
                    if (that.directionResponse) {
                      dsMapController.directionsDisplay.setDirections(that.directionResponse);
                    }
                    return;
                  }
                  that.isDirectionResponseCallMade = true;
                  dsMapController.directionsService.route(request, function(response, status) {
                    if (status == "OK") {
                      dsMapController.directionsDisplay.setDirections(that.directionResponse = response);
                    }
                  });
                } catch (err) {}
              }, 300)
            }

            if (scope.group !== undefined) {
              dsMapController.placesGroupHash[scope.group] = [];
            }

            if (scope.sortOption && scope.sortOption == "radius") {
              obj.radius = scope.radius || 5000;
            } else {
              obj.rankBy = google.maps.places.RankBy.DISTANCE;
            }

            if (Object.prototype.toString.call(scope.types) == "[object Array]") {
              var _deferred = $q.defer();
              obj.types = scope.types;
              callPlacesServiceFunc(deferred, _deferred);
              deferred = _deferred;
            } else if (Object.prototype.toString.call(scope.types) == "[object Object]") {
              for (var _types in scope.types) {
                (function(_deferred) {
                  _deferred = $q.defer();
                  callPlacesServiceFunc(deferred, _deferred, _types);
                  deferred = _deferred;
                })(_deferred);
              }
            } else {
              throw "'types' attribute must be an array or object"
            }

            if (dsMapController.isViewSet) {
              dsMapPlacesController.dsMapPlacesLoaded(function() {
                scope.showDirection = showDirectionFunc;
              });
            }
          },
          controller: ['$scope', '$element', '$attrs', function($scope, $element, $attr) {
            $scope.Places = [];
          }]
        };

      deferred.resolve();

      return obj;
    }]).directive('dsEachPlace', [function() {
      var obj = {
        require: ["^?dsMap", "^?dsMapPlaces", "^?dsPlacesType"],
        link: function(scope, element, attrs, dsMapControllers) {
          var dsMapController = dsMapControllers[0],
            dsMapPlacesController = dsMapControllers[1],
            dsPlacesTypeController = dsMapControllers[2];

          if (scope.place) {
            var placeLocation = scope.place.geometry.location,
              clonedOptions = angular.copy(scope.options),
              distanceThis = parseFloat(google.maps.geometry.spherical.computeDistanceBetween(dsMapController.getLocation(), placeLocation) / 1000).toFixed(1),
              durationThis = parseInt(distanceThis * 42 / 3.5, 10);

            if (clonedOptions === undefined) {
              clonedOptions = {
                position: placeLocation,
                visible: true
              };
            } else if (Object.prototype.toString.call(clonedOptions) == "[object Object]") {
              clonedOptions.position = placeLocation;
              clonedOptions.visible = clonedOptions.visible === undefined ? true : clonedOptions.visible;
            }

            if (clonedOptions.visible) {
              dsMapController.bounds.extend(placeLocation);
            }

            scope.place.distance = distanceThis;
            scope.place.duration = durationThis;

            dsMapController.mapRendered.then(function() {
              var marker = setMarkerFunc(clonedOptions);
              try {
                dsMapController.placesGroupHash[scope.group].push(marker);
              } catch (err) {
                console.warn(err);
              }
            });
          }
        }
      };

      return obj;
    }]);
}(window, window.angular);
<commit_msg>Update dsMapService.js<commit_after>! function(window, angular, undefined) {
  var defaultMapSettings = {
      center: {
        lat: 12.94206,
        lng: 77.62229
      },
      zoom: 8
    },
    setMarkerFunc = undefined;

  function setMarkerCurryFunc(placesMap) {
    if (placesMap) {
      var map = placesMap;
    }
    return function(marker) {
      if (!map) {
        throw "Please pass a map reference before setting marker";
      }
      if (Object.prototype.toString.call(marker) == "[object Object]" && marker.position !== undefined) {
        marker.map = map;
        var newMarker = new google.maps.Marker(marker);
        newMarker.setMap(map);
        return newMarker;
      }
    }
  }

  angular.module('ds.map', []).
  factory('dsMapFactory', ['$http', function($http) {
    var obj = {
      getDefaultMapSettings: getDefaultMapSettingsFunc,
      setDefaultMapSettings: setDefaultMapSettingsFunc,
      setDirectionsToLocation: function() {}
    };

    function getDefaultMapSettingsFunc() {
      return defaultMapSettings;
    }

    function setDefaultMapSettingsFunc(settingsObj) {
      defaultMapSettings = settingsObj;
    }

    return obj;
  }]).
  directive('dsMap', ['$q', '$timeout', 'dsMapFactory', function($q, $timeout, dsMapFactory) {
    var deferred = $q.defer(),
      obj = {
        scope: {
          mapSettings: "="
        },
        restrict: "E",
        link: function(scope) {

        },
        controller: ['$scope', '$element', '$attrs', function($scope, $element, $attr) {
          var _deferred = $q.defer(),
            placesDeferred = $q.defer(),
            dsMapElements = 0,
            deferredArray = [],
            that = this,
            mapSettings = that.mapSettings = $scope.mapSettings || dsMapFactory.getDefaultMapSettings(),
            map = new google.maps.Map(document.createElement('div'), mapSettings);

          function getLocationFunc() {
            return that.dsPlacesLocation;
          }

          function setLocationFunc(center) {
            that.dsPlacesLocation = center || that.getMap().getCenter();
          }

          function getMapFunc() {
            return that.map;
          }

          function setMapFunc(map) {
            dsMapFactory.map = that.map = map;
            that.setLocation(that.map.getCenter());
          }

          function iterateAllMarkersFunc(callback) {
            for (var groups in that.placesGroupHash) {
              var eachGroupRef = that.placesGroupHash[groups];
              for (var _count = 0, len = eachGroupRef.length; _count < len; _count++) {
                var allIteratedMarkers = eachGroupRef[_count];
                callback.call(allIteratedMarkers);
              }
            }
          }

          function iterateMarkersMatchingBooleanFunc(bool, callback) {
            that.iterateAllMarkers(function() {
              var eachPreviousMarker = this;
              if (eachPreviousMarker.visible === bool) {
                callback.call(eachPreviousMarker);
                eachPreviousMarker.setMap(eachPreviousMarker.getMap());
              }
            });
          }

          function setGroupMarkerVisibilityFunc(group, bool, setVisibleMarkerBounds) {
            var currentGroupRef = that.placesGroupHash[group],
              previousGroup = undefined;
            if (previousGroup != group) {
              that.bounds = new google.maps.LatLngBounds();
              for (var currentGroupCount = 0, len = currentGroupRef.length; currentGroupCount < len; currentGroupCount++) {
                var eachCurrentMarker = currentGroupRef[currentGroupCount];
                if (eachCurrentMarker.visible !== bool) {
                  eachCurrentMarker.visible = bool;
                  eachCurrentMarker.setMap(eachCurrentMarker.getMap());
                  if (setVisibleMarkerBounds && eachCurrentMarker.visible) {
                    that.bounds.extend(eachCurrentMarker.position);
                  }
                }
              }

              if (setVisibleMarkerBounds) {
                that.changeMapBounds();
              }
            }

            previousGroup = group;
          }

          function hideAllVisibleMarkersFunc() {
            that.iterateMarkersMatchingBoolean(true, function() {
              var visibleMarker = this;
              visibleMarker.visible = false;
            });
          }

          function changeMapBoundsFunc() {
            that.getMap().fitBounds(that.bounds);
            that.getMap().setCenter(that.getLocation());
            that.getMap().setZoom(that.getMap().getZoom() - 1);
          }

          that.mapDeferred = deferred;
          that.mapRendered = deferred.promise;
          that.placesDeferred = placesDeferred;
          that.placesRendered = placesDeferred.promise;
          that.placesGroupHash = {};
          that.placesCount = 0;
          that.getLocation = getLocationFunc;
          that.setLocation = setLocationFunc;
          that.getMap = getMapFunc;
          that.setMap = setMapFunc;
          that.iterateAllMarkers = iterateAllMarkersFunc;
          that.iterateMarkersMatchingBoolean = iterateMarkersMatchingBooleanFunc;
          that.setGroupMarkerVisibility = setGroupMarkerVisibilityFunc;
          that.hideAllVisibleMarkers = hideAllVisibleMarkersFunc;
          that.changeMapBounds = changeMapBoundsFunc;

          that.setMap(map);

          that.placesRendered.then(function() {
            that.changeMapBounds();
          });

          (function(dfrd, _dfrd) {
            that.mapRendered.then(function() {
              dsMapFactory.setMarker = setMarkerFunc = setMarkerCurryFunc(that.getMap());
            });
          })(deferred, _deferred);

          that.nextDefer = deferred = _deferred;
        }]
      };
    return obj;
  }]).
  directive('dsMapView', function() {
      var obj = {
        scope: {
          mapSettings: "=",
          originOptions: "=",
          destinationOptions: "="
        },
        restrict: "E",
        replace: true,
        template: "

",
        require: "^?dsMap",
        link: function(scope, element, attrs, dsMapController) {
          var map = new google.maps.Map(element[0], scope.mapSettings || dsMapController.mapSettings),
            clonedOriginMarkerObject = angular.copy(scope.originOptions || {});
          dsMapController.setMap(map);
          dsMapController.isViewSet = true;
          dsMapController.destinationOptions = angular.copy(scope.destinationOptions || {});
          dsMapController.mapRendered.then(function() {
            clonedOriginMarkerObject.position = dsMapController.getLocation();
            setMarkerFunc(clonedOriginMarkerObject);
          });
        },
        controller: ['$scope', function($scope) {
          var that = this;
          that.originOptions = $scope.originOptions;
          that.destinationOptions = angular.copy($scope.destinationOptions);
        }]
      };
      return obj;
    }).directive('dsMapPlaces', ['$q', 'dsMapFactory', function($q, dsMapFactory) {
      var initialdsMapView = true,
        deferred = $q.defer(),
        obj = {
          scope: {
            options: "="
          },
          restrict: "E",
          replace: true,
          require: "^?dsMap",
          link: function(scope, element, attrs, dsMapController) {
            var rankByDistance = google.maps.places.RankBy.DISTANCE;

            if (initialdsMapView) {
              dsMapController.mapDeferred.resolve();
              initialdsMapView = false;
            }

            dsMapController.mapRendered.then(function() {
              dsMapController.placesService = new google.maps.places.PlacesService(dsMapController.getMap());
              dsMapController.directionsService = new google.maps.DirectionsService();
              scope.options.map = dsMapController.getMap();
              dsMapController.directionsDisplay = new google.maps.DirectionsRenderer(scope.options);
              dsMapController.bounds = new google.maps.LatLngBounds();
              deferred.resolve(dsMapController.placesService);
            });
          },
          controller: ['$scope', '$element', '$attrs', function($scope, $element, $attr) {
            var that = this;

            function dsMapPlacesDeferredFunc() {
              var _deferred = $q.defer();
              return function() {
                return _deferred;
              };
            }

            function dsMapPlacesLoadedFunc(callback) {
              that.dsMapPlacesDeferred().promise.then(callback);
            }

            that.placesServiceSet = deferred.promise;
            that.dsMapPlacesDeferred = dsMapPlacesDeferredFunc();
            dsMapFactory.dsMapPlacesLoaded = that.dsMapPlacesLoaded = dsMapPlacesLoadedFunc;
          }]
        };
      return obj;
    }])
    .directive('dsGroup', ['dsMapFactory', function(dsMapFactory) {
      var obj = {
        scope: true,
        restrict: "E",
        require: ["^?dsMap", "^?dsMapPlaces"],
        link: function(scope, element, attrs, dsMapControllers) {
          previousGroup = undefined;
          dsMapController = dsMapControllers[0];
          dsMapPlacesController = dsMapControllers[1];

          (function(previousGroup, dsMapController, dsMapPlacesController) {
            var isMapPlacesLoaded = false;

            function onToggleClickFunc(group) {
              dsMapController.directionsDisplay.setMap(null);
              if (previousGroup !== undefined) {
                if (previousGroup == group) {
                  return;
                }
                dsMapController.setGroupMarkerVisibility(previousGroup, false, true);
              } else {
                dsMapController.hideAllVisibleMarkers();
              }

              dsMapController.setGroupMarkerVisibility(group, true, true);

              previousGroup = group;
            }

            function toggleGroupVisibilityFunc(group) {
              dsMapPlacesController.dsMapPlacesLoaded(function() {
                isMapPlacesLoaded = true;
              });
              if (isMapPlacesLoaded) {
                onToggleClickFunc(group);
              }
            }

            function setDirectionsToLocationFunc(location, mode) {
              var request = {
                origin: dsMapController.getLocation(),
                destination: location,
                travelMode: google.maps.TravelMode[mode]
              };

              dsMapController.destinationOptions.position = location;

              dsMapPlacesController.dsMapPlacesLoaded(function() {
                dsMapController.hideAllVisibleMarkers();
                dsMapController.directionsDisplay.setMap(dsMapController.getMap());
                dsMapController.directionsService.route(request, function(response, status) {
                  if (status == "OK") {
                    var destinationMarker = setMarkerFunc(dsMapController.destinationOptions);
                    dsMapController.directionsDisplay.setDirections(response);
                    dsMapController.bounds = new google.maps.LatLngBounds();
                    dsMapController.bounds.extend(dsMapController.getLocation());
                    dsMapController.bounds.extend(location);
                    dsMapController.placesGroupHash['searchedLocation'] = [destinationMarker];
                    dsMapController.changeMapBounds();
                  }
                });
              });
            }

            scope.toggleGroupVisibility = toggleGroupVisibilityFunc;

            if (dsMapController.isViewSet) {
              dsMapFactory.setDirectionsToLocation = setDirectionsToLocationFunc;
            }
          })(previousGroup, dsMapController, dsMapPlacesController);
        },
        controller: ['$scope', function($scope) {
          var that = undefined;
          that = this;
          that.isGroupSet = true;
        }]
      };

      return obj;
    }])
    .directive('dsPlacesType', ['$q', '$timeout', function($q, $timeout) {
      var deferred = $q.defer(),
        placesServiceCount = 0,
        obj = {
          scope: {
            group: "@",
            types: "=",
            options: "=",
            sortOption: "@",
            radius: "="
          },
          templateUrl: "dsMapPlaces.html",
          restrict: "E",
          replace: true,
          require: ["^?dsMap", "^?dsMapPlaces", "^?dsGroup"],
          link: function(scope, element, attrs, dsMapControllers) {
            var dsMapController = dsMapControllers[0],
              dsMapPlacesController = dsMapControllers[1],
              dsGroupController = dsMapControllers[2],
              obj = {
                location: dsMapController.getMap().getCenter(),
                types: []
              },
              showDirectionTimeoutId = undefined;

            function callPlacesServiceFunc(dfrd, _dfrd, type) {
              dsMapController.placesCount++;
              dsMapController.mapRendered.then(function() {
                dfrd.promise.then(function() {
                  dsMapPlacesController.placesServiceSet.then(function(placesService) {
                    if (type) {
                      obj.types = [type];
                    }

                    placesService.nearbySearch(obj, function(results, status) {
                      if (status == "OK" && Object.prototype.toString.call(results) == "[object Array]") {
                        scope.Places = scope.Places.concat(type === undefined ? results : results.slice(0, scope.types[type]));
                      }
                      $timeout(function() {
                        if ((--dsMapController.placesCount) === 0) {
                          dsMapController.nextDefer.resolve();
                          dsMapController.nextDefer.promise.then(function() {
                            _dfrd.resolve();
                            dsMapPlacesController.dsMapPlacesDeferred().resolve();
                          });
                          dsMapController.placesDeferred.resolve();
                        } else {
                          _dfrd.resolve();
                        }
                      }, 300);
                    });
                  });
                });
              });
            }

            function showDirectionFunc() {
              var that = this;
              $timeout.cancel(showDirectionTimeoutId);
              showDirectionTimeoutId = $timeout(function() {
                var request = {
                  origin: dsMapController.getLocation(),
                  destination: that.place.geometry.location,
                  travelMode: google.maps.TravelMode["DRIVING"]
                };

                if (dsGroupController && dsMapController.placesGroupHash[scope.group][0].visible === false) {
                  dsMapController.setGroupMarkerVisibility(scope.group, true, true);
                }

                dsMapController.directionsDisplay.setMap(dsMapController.getMap());

                try {
                  if (that.isDirectionResponseCallMade) {
                    if (that.directionResponse) {
                      dsMapController.directionsDisplay.setDirections(that.directionResponse);
                    }
                    return;
                  }
                  that.isDirectionResponseCallMade = true;
                  dsMapController.directionsService.route(request, function(response, status) {
                    if (status == "OK") {
                      dsMapController.directionsDisplay.setDirections(that.directionResponse = response);
                    }
                  });
                } catch (err) {}
              }, 300)
            }

            if (scope.group !== undefined) {
              dsMapController.placesGroupHash[scope.group] = [];
            }

            if (scope.sortOption && scope.sortOption == "radius") {
              obj.radius = scope.radius || 5000;
            } else {
              obj.rankBy = google.maps.places.RankBy.DISTANCE;
            }

            if (Object.prototype.toString.call(scope.types) == "[object Array]") {
              var _deferred = $q.defer();
              obj.types = scope.types;
              callPlacesServiceFunc(deferred, _deferred);
              deferred = _deferred;
            } else if (Object.prototype.toString.call(scope.types) == "[object Object]") {
              for (var _types in scope.types) {
                (function(_deferred) {
                  _deferred = $q.defer();
                  callPlacesServiceFunc(deferred, _deferred, _types);
                  deferred = _deferred;
                })(_deferred);
              }
            } else {
              throw "'types' attribute must be an array or object"
            }

            if (dsMapController.isViewSet) {
              dsMapPlacesController.dsMapPlacesLoaded(function() {
                scope.showDirection = showDirectionFunc;
              });
            }
          },
          controller: ['$scope', '$element', '$attrs', function($scope, $element, $attr) {
            $scope.Places = [];
          }]
        };

      deferred.resolve();

      return obj;
    }]).directive('dsEachPlace', [function() {
      var obj = {
        require: ["^?dsMap", "^?dsMapPlaces", "^?dsPlacesType"],
        link: function(scope, element, attrs, dsMapControllers) {
          var dsMapController = dsMapControllers[0],
            dsMapPlacesController = dsMapControllers[1],
            dsPlacesTypeController = dsMapControllers[2];

          if (scope.place) {
            var placeLocation = scope.place.geometry.location,
              clonedOptions = angular.copy(scope.options),
              distanceThis = parseFloat(google.maps.geometry.spherical.computeDistanceBetween(dsMapController.getLocation(), placeLocation) / 1000).toFixed(1),
              durationThis = parseInt(distanceThis * 42 / 3.5, 10);

            if (clonedOptions === undefined) {
              clonedOptions = {
                position: placeLocation,
                visible: true
              };
            } else if (Object.prototype.toString.call(clonedOptions) == "[object Object]") {
              clonedOptions.position = placeLocation;
              clonedOptions.visible = clonedOptions.visible === undefined ? true : clonedOptions.visible;
            }

            if (clonedOptions.visible) {
              dsMapController.bounds.extend(placeLocation);
            }

            scope.place.distance = distanceThis;
            scope.place.duration = durationThis;

            dsMapController.mapRendered.then(function() {
              var marker = setMarkerFunc(clonedOptions);
              try {
                dsMapController.placesGroupHash[scope.group].push(marker);
              } catch (err) {
                console.warn(err);
              }
            });
          }
        }
      };

      return obj;
    }]);
}(window, window.angular);
<|endoftext|>