<commit_before>const fs = require('fs');
const fsextra = require('fs-extra');
const path = require('path');
const util = require('util');
const logger = require('winston');

const sanitizeHtml = require('sanitize-html');

const request = require('request-promise');
const toml = require('toml');
const tomlify = require('tomlify-j0.4');
const blackfriday = require('./blackfriday.js');

const del = require('delete');
const exec = require('sync-exec');

const fsPathCode = './citra-games-wiki/games';
const fsPathWiki = './citra-games-wiki.wiki';
const fsPathHugoContent = '../../site/content/game';
const fsPathHugoBoxart = '../../site/static/images/game/boxart';
const fsPathHugoIcon = '../../site/static/images/game/icons';
const fsPathHugoScreenshots = '../../site/static/images/screenshots0';
const fsPathHugoSavefiles = '../../site/static/savefiles/';

function gitPull(directory, repository) {
  if (fs.existsSync(directory)) {
      logger.info(`Fetching latest from Github : ${directory}`);
      logger.info(`git --git-dir=${directory} pull`);
      exec(`git --git-dir=${directory} pull`);
  } else {
      logger.info(`Cloning repository from Github : ${directory}`);
      logger.info(`git clone ${repository}`);
      exec(`git clone ${repository}`);
  }
}

function getDirectories (srcpath) {
  return fs.readdirSync(srcpath)
    .filter(file => fs.lstatSync(path.join(srcpath, file)).isDirectory())
}

async function getGithubIssues() {
  var results = [];

  // Only loop through the first x pages to prevent API limiting.
  for (var page = 0; page <= 15; page++) {
    let options = {
      url: `https://api.github.com/repos/citra-emu/citra/issues?per_page=99&page=${page}`,
      headers: { 'User-Agent': 'citrabot' }
    };

    logger.verbose(`Requesting Github Issue Page ${page}.`);
    var body = await request.get(options);
    var obj = JSON.parse(body);

    if (obj == null || obj.length == 0) {
      logger.verbose(`No more Github issues found -- on page ${page}.`);
      break;
    } else {
      results = results.concat(obj);
    }
  }

  return results;
}

async function getTestcases() {
  let options = {
    url: 'https://api.citra-emu.org/telemetry/testcase/',
    headers: { 'User-Agent': 'citrabot' },
  };

  var body = await request.get(options);
  return JSON.parse(body).map(x => {
    return {
      title: x.title,
      compatibility: x.compatibility.toString(),
      date: x.date,
      version: x.version,
      buildName: x.buildName,
      buildDate: x.buildDate,
      cpu: x.cpu,
      gpu: x.gpu,
      os: x.os,
      author: x.author
    }
  });
}

// Fetch game information stored in Github repository.
gitPull('./citra-games-wiki', 'https://github.com/citra-emu/citra-games-wiki.git');

// Fetch game articles stored in Github wiki.
gitPull('./citra-games-wiki.wiki', 'https://github.com/citra-emu/citra-games-wiki.wiki.git');

// Fetch all issues from Github.
var githubIssues = [];
var wikiEntries = {};
var testcases = [];

async function setup() {
  //githubIssues = await getGithubIssues();
  //logger.info(`Imported ${githubIssues.length} issues from Github.`);

  testcases = await getTestcases();
  logger.info(`Obtained ${testcases.length} testcases from Telemetry API.`);
};

setup().then(function() {
  // Make sure the output directories in Hugo exist.
  [fsPathHugoContent, fsPathHugoBoxart, fsPathHugoIcon, fsPathHugoSavefiles, fsPathHugoScreenshots].forEach(function (path) {
    if (fs.existsSync(path) == false) {
        logger.info(`Creating Hugo output directory: ${path}`);
        fs.mkdirSync(path);
    }
  });
})
.then(function() {
  // Transform wiki entries to lowercase
  const files = fs.readdirSync(fsPathWiki);

  logger.info(`Generating wiki database...`);
  files.forEach((file) => {
      wikiEntries[file.toLowerCase()] = file;
  });

  // Loop through each game and process it.
  getDirectories(fsPathCode).forEach(function(game) {
    processGame(game);
  });
})
.catch(function(err) {
  logger.error(err);
});

function processGame(game) {
  try {
    if (game == '.git' || game == '_validation') { return; }

    logger.info(`Processing game: ${game}`);

    // Copy the boxart for the game.
    fsextra.copySync(`${fsPathCode}/${game}/boxart.png`, `${fsPathHugoBoxart}/${game}.png`);

    // Copy the icon for the game.
    fsextra.copySync(`${fsPathCode}/${game}/icon.png`, `${fsPathHugoIcon}/${game}.png`);

    var model = toml.parse(fs.readFileSync(`${fsPathCode}/${game}/game.dat`, 'utf8'));
    let currentDate = new Date();
    model.date = `${currentDate.toISOString()}`;

    // Look up all testcases associated with the Title IDs.
    let releases = model.releases.map(y => y.title);
    let foundTestcases = testcases.filter(x => {
      return releases.includes(x.title);
    });

    logger.info(`Found ${foundTestcases.length} testcases from telemetry, found ${model.testcases.length} in toml file.`);

    model.testcases = model.testcases.concat(foundTestcases);

    // Sort the testcases from most recent to least recent.
    model.testcases.sort(function(a, b){
      // Turn your strings into dates, and then subtract them
      // to get a value that is either negative, positive, or zero.
      return new Date(b.date) - new Date(a.date);
    });

    // SHORTCUTS BLOCK
    // Parse testcase information out of the dat to reinject as shortcut values.
    if (model.testcases == null || model.testcases.length == 0) {
      model.compatibility = "99";
      model.testcase_date = "2000-01-01";
    } else {
      let recent = model.testcases[0];
      model.compatibility = recent.compatibility;
      model.testcase_date = recent.date;
    }

    const toTrim = ["the", "a", "an"];

    let trimmedTitle = model.title.toLowerCase();
    toTrim.forEach(trim => {
        if (trimmedTitle.startsWith(trim + " ")) {
            trimmedTitle = trimmedTitle.substr(trim.length + 1);
        }
    });

    let section_id = `${trimmedTitle[0]}`;
    if (!section_id.match(/[a-z]+/)) {
        section_id = "#";
    }

    model.section_id = section_id;
    // END SHORTCUTS BLOCK

    // SAVEFILE BLOCK
    var fsPathCodeSavefilesGame = `${fsPathCode}/${game}/savefiles/`;
    var fsPathHugoSavefilesGame = `${fsPathHugoSavefiles}/${game}/`;
    if (fs.existsSync(fsPathCodeSavefilesGame)) {
      // Create the savefile directory for the game.
      if (fs.existsSync(fsPathHugoSavefilesGame) == false) {
          fs.mkdirSync(fsPathHugoSavefilesGame);
      }

      // Copy all savefiles into the output folder, and read their data.
      model.savefiles = [];
      fs.readdirSync(fsPathCodeSavefilesGame).forEach(file => {
        if (path.extname(file) == '.zip') {
          fsextra.copySync(`${fsPathCodeSavefilesGame}/${file}`, `${fsPathHugoSavefilesGame}/${file}`);
        } else if (path.extname(file) == '.dat') {
          // Read the data file into an object.
          let savefile = toml.parse(fs.readFileSync(`${fsPathCodeSavefilesGame}/${file}`, 'utf8'));

          let stats = fs.statSync(`${fsPathCodeSavefilesGame}/${file}`);

          // Store the contents of the file in memory for adding it into the markdown later.
          model.savefiles.push({
            date: new Date(util.inspect(stats.mtime)),
            filename: file.replace('.dat', '.zip'),
            title: savefile.title,
            description: savefile.description,
            author: savefile.author,
            title_id: savefile.title_id
          });
        }
      });
      // Finished copying all savefiles into the output folder, and reading their data.
    }
    // END SAVEFILE BLOCK

    // GITHUB ISSUES BLOCK
    model.issues = [];
    model.closed_issues = [];

    if (model.github_issues != null && model.github_issues.length > 0) {
      model.github_issues.forEach(function(number) {
        let issue = githubIssues.find(x => x.number == number);
        if (issue == null) {
          model.closed_issues.push(number);
        } else {
          model.issues.push({
            number: issue.number.toString(),
            title: issue.title,
            state: issue.state,
            created_at: issue.created_at,
            updated_at: issue.updated_at,
            labels: issue.labels.map(function(x) { return { name: x.name, color: x.color } })
          });
        }
      });

      github_issues = null;
    }
    // END GITHUB ISSUES BLOCK

    // Copy the screenshots for the game.
    let fsPathScreenshotInputGame = `${fsPathCode}/${game}/screenshots/`;
    let fsPathScreenshotOutputGame = `${fsPathHugoScreenshots}/${game}/`;
    if (fs.existsSync(fsPathScreenshotInputGame)) {
      // Create the savefile directory for each game.
      if (fs.existsSync(fsPathScreenshotOutputGame) == false) {
          fs.mkdirSync(fsPathScreenshotOutputGame);
      }

      // Copy all screenshots into the output folder.
      fs.readdirSync(fsPathScreenshotInputGame).forEach(file => {
        if (path.extname(file) == '.png') {
          fsextra.copySync(`${fsPathScreenshotInputGame}/${file}`, `${fsPathScreenshotOutputGame}/${file}`);
        }
      });
    }

    // WIKI BLOCK
    var wikiText = "";
    let fsPathWikiGame = `${fsPathWiki}/${wikiEntries[game.toLowerCase() + ".md"]}`;
    if (fs.existsSync(fsPathWikiGame)) {
      wikiText = fs.readFileSync(fsPathWikiGame, 'utf8');

      // Fix Blackfriday markdown rendering differences.
      wikiText = blackfriday.fixLists(wikiText);
      wikiText = blackfriday.fixLinks(wikiText);
    } else {
      wikiText = "## No wiki exists yet for this game.";
    }
    // END WIKI BLOCK

    let modelText = tomlify(model, null, 2);

    let contentOutput = `+++\r\n${modelText}\r\n+++\r\n\r\n${wikiText}\r\n`;
    fs.writeFileSync(`${fsPathHugoContent}/${game}.md`, contentOutput);
  } catch (ex) {
    logger.warn(`${game} failed to generate: ${ex}`);
    logger.error(ex);
  }
}
<commit_msg>Removed comments for debugging.<commit_after>const fs = require('fs');
const fsextra = require('fs-extra');
const path = require('path');
const util = require('util');
const logger = require('winston');

const sanitizeHtml = require('sanitize-html');

const request = require('request-promise');
const toml = require('toml');
const tomlify = require('tomlify-j0.4');
const blackfriday = require('./blackfriday.js');

const del = require('delete');
const exec = require('sync-exec');

const fsPathCode = './citra-games-wiki/games';
const fsPathWiki = './citra-games-wiki.wiki';
const fsPathHugoContent = '../../site/content/game';
const fsPathHugoBoxart = '../../site/static/images/game/boxart';
const fsPathHugoIcon = '../../site/static/images/game/icons';
const fsPathHugoScreenshots = '../../site/static/images/screenshots0';
const fsPathHugoSavefiles = '../../site/static/savefiles/';

function gitPull(directory, repository) {
  if (fs.existsSync(directory)) {
      logger.info(`Fetching latest from Github : ${directory}`);
      logger.info(`git --git-dir=${directory} pull`);
      exec(`git --git-dir=${directory} pull`);
  } else {
      logger.info(`Cloning repository from Github : ${directory}`);
      logger.info(`git clone ${repository}`);
      exec(`git clone ${repository}`);
  }
}

function getDirectories (srcpath) {
  return fs.readdirSync(srcpath)
    .filter(file => fs.lstatSync(path.join(srcpath, file)).isDirectory())
}

async function getGithubIssues() {
  var results = [];

  // Only loop through the first x pages to prevent API limiting.
  for (var page = 0; page <= 15; page++) {
    let options = {
      url: `https://api.github.com/repos/citra-emu/citra/issues?per_page=99&page=${page}`,
      headers: { 'User-Agent': 'citrabot' }
    };

    logger.verbose(`Requesting Github Issue Page ${page}.`);
    var body = await request.get(options);
    var obj = JSON.parse(body);

    if (obj == null || obj.length == 0) {
      logger.verbose(`No more Github issues found -- on page ${page}.`);
      break;
    } else {
      results = results.concat(obj);
    }
  }

  return results;
}

async function getTestcases() {
  let options = {
    url: 'https://api.citra-emu.org/telemetry/testcase/',
    headers: { 'User-Agent': 'citrabot' },
  };

  var body = await request.get(options);
  return JSON.parse(body).map(x => {
    return {
      title: x.title,
      compatibility: x.compatibility.toString(),
      date: x.date,
      version: x.version,
      buildName: x.buildName,
      buildDate: x.buildDate,
      cpu: x.cpu,
      gpu: x.gpu,
      os: x.os,
      author: x.author
    }
  });
}

// Fetch game information stored in Github repository.
gitPull('./citra-games-wiki', 'https://github.com/citra-emu/citra-games-wiki.git');

// Fetch game articles stored in Github wiki.
gitPull('./citra-games-wiki.wiki', 'https://github.com/citra-emu/citra-games-wiki.wiki.git');

// Fetch all issues from Github.
var githubIssues = [];
var wikiEntries = {};
var testcases = [];

async function setup() {
  githubIssues = await getGithubIssues();
  logger.info(`Imported ${githubIssues.length} issues from Github.`);

  testcases = await getTestcases();
  logger.info(`Obtained ${testcases.length} testcases from Telemetry API.`);
};

setup().then(function() {
  // Make sure the output directories in Hugo exist.
  [fsPathHugoContent, fsPathHugoBoxart, fsPathHugoIcon, fsPathHugoSavefiles, fsPathHugoScreenshots].forEach(function (path) {
    if (fs.existsSync(path) == false) {
        logger.info(`Creating Hugo output directory: ${path}`);
        fs.mkdirSync(path);
    }
  });
})
.then(function() {
  // Transform wiki entries to lowercase
  const files = fs.readdirSync(fsPathWiki);

  logger.info(`Generating wiki database...`);
  files.forEach((file) => {
      wikiEntries[file.toLowerCase()] = file;
  });

  // Loop through each game and process it.
  getDirectories(fsPathCode).forEach(function(game) {
    processGame(game);
  });
})
.catch(function(err) {
  logger.error(err);
});

function processGame(game) {
  try {
    if (game == '.git' || game == '_validation') { return; }

    logger.info(`Processing game: ${game}`);

    // Copy the boxart for the game.
    fsextra.copySync(`${fsPathCode}/${game}/boxart.png`, `${fsPathHugoBoxart}/${game}.png`);

    // Copy the icon for the game.
    fsextra.copySync(`${fsPathCode}/${game}/icon.png`, `${fsPathHugoIcon}/${game}.png`);

    var model = toml.parse(fs.readFileSync(`${fsPathCode}/${game}/game.dat`, 'utf8'));
    let currentDate = new Date();
    model.date = `${currentDate.toISOString()}`;

    // Look up all testcases associated with the Title IDs.
    let releases = model.releases.map(y => y.title);
    let foundTestcases = testcases.filter(x => {
      return releases.includes(x.title);
    });

    logger.info(`Found ${foundTestcases.length} testcases from telemetry, found ${model.testcases.length} in toml file.`);

    model.testcases = model.testcases.concat(foundTestcases);

    // Sort the testcases from most recent to least recent.
    model.testcases.sort(function(a, b){
      // Turn your strings into dates, and then subtract them
      // to get a value that is either negative, positive, or zero.
      return new Date(b.date) - new Date(a.date);
    });

    // SHORTCUTS BLOCK
    // Parse testcase information out of the dat to reinject as shortcut values.
    if (model.testcases == null || model.testcases.length == 0) {
      model.compatibility = "99";
      model.testcase_date = "2000-01-01";
    } else {
      let recent = model.testcases[0];
      model.compatibility = recent.compatibility;
      model.testcase_date = recent.date;
    }

    const toTrim = ["the", "a", "an"];

    let trimmedTitle = model.title.toLowerCase();
    toTrim.forEach(trim => {
        if (trimmedTitle.startsWith(trim + " ")) {
            trimmedTitle = trimmedTitle.substr(trim.length + 1);
        }
    });

    let section_id = `${trimmedTitle[0]}`;
    if (!section_id.match(/[a-z]+/)) {
        section_id = "#";
    }

    model.section_id = section_id;
    // END SHORTCUTS BLOCK

    // SAVEFILE BLOCK
    var fsPathCodeSavefilesGame = `${fsPathCode}/${game}/savefiles/`;
    var fsPathHugoSavefilesGame = `${fsPathHugoSavefiles}/${game}/`;
    if (fs.existsSync(fsPathCodeSavefilesGame)) {
      // Create the savefile directory for the game.
      if (fs.existsSync(fsPathHugoSavefilesGame) == false) {
          fs.mkdirSync(fsPathHugoSavefilesGame);
      }

      // Copy all savefiles into the output folder, and read their data.
      model.savefiles = [];
      fs.readdirSync(fsPathCodeSavefilesGame).forEach(file => {
        if (path.extname(file) == '.zip') {
          fsextra.copySync(`${fsPathCodeSavefilesGame}/${file}`, `${fsPathHugoSavefilesGame}/${file}`);
        } else if (path.extname(file) == '.dat') {
          // Read the data file into an object.
          let savefile = toml.parse(fs.readFileSync(`${fsPathCodeSavefilesGame}/${file}`, 'utf8'));

          let stats = fs.statSync(`${fsPathCodeSavefilesGame}/${file}`);

          // Store the contents of the file in memory for adding it into the markdown later.
          model.savefiles.push({
            date: new Date(util.inspect(stats.mtime)),
            filename: file.replace('.dat', '.zip'),
            title: savefile.title,
            description: savefile.description,
            author: savefile.author,
            title_id: savefile.title_id
          });
        }
      });
      // Finished copying all savefiles into the output folder, and reading their data.
    }
    // END SAVEFILE BLOCK

    // GITHUB ISSUES BLOCK
    model.issues = [];
    model.closed_issues = [];

    if (model.github_issues != null && model.github_issues.length > 0) {
      model.github_issues.forEach(function(number) {
        let issue = githubIssues.find(x => x.number == number);
        if (issue == null) {
          model.closed_issues.push(number);
        } else {
          model.issues.push({
            number: issue.number.toString(),
            title: issue.title,
            state: issue.state,
            created_at: issue.created_at,
            updated_at: issue.updated_at,
            labels: issue.labels.map(function(x) { return { name: x.name, color: x.color } })
          });
        }
      });

      github_issues = null;
    }
    // END GITHUB ISSUES BLOCK

    // Copy the screenshots for the game.
    let fsPathScreenshotInputGame = `${fsPathCode}/${game}/screenshots/`;
    let fsPathScreenshotOutputGame = `${fsPathHugoScreenshots}/${game}/`;
    if (fs.existsSync(fsPathScreenshotInputGame)) {
      // Create the savefile directory for each game.
      if (fs.existsSync(fsPathScreenshotOutputGame) == false) {
          fs.mkdirSync(fsPathScreenshotOutputGame);
      }

      // Copy all screenshots into the output folder.
      fs.readdirSync(fsPathScreenshotInputGame).forEach(file => {
        if (path.extname(file) == '.png') {
          fsextra.copySync(`${fsPathScreenshotInputGame}/${file}`, `${fsPathScreenshotOutputGame}/${file}`);
        }
      });
    }

    // WIKI BLOCK
    var wikiText = "";
    let fsPathWikiGame = `${fsPathWiki}/${wikiEntries[game.toLowerCase() + ".md"]}`;
    if (fs.existsSync(fsPathWikiGame)) {
      wikiText = fs.readFileSync(fsPathWikiGame, 'utf8');

      // Fix Blackfriday markdown rendering differences.
      wikiText = blackfriday.fixLists(wikiText);
      wikiText = blackfriday.fixLinks(wikiText);
    } else {
      wikiText = "## No wiki exists yet for this game.";
    }
    // END WIKI BLOCK

    let modelText = tomlify(model, null, 2);

    let contentOutput = `+++\r\n${modelText}\r\n+++\r\n\r\n${wikiText}\r\n`;
    fs.writeFileSync(`${fsPathHugoContent}/${game}.md`, contentOutput);
  } catch (ex) {
    logger.warn(`${game} failed to generate: ${ex}`);
    logger.error(ex);
  }
}
<|endoftext|>


<commit_before>var shell = require('electron').shell;
var clipboard = require('electron').clipboard;
var exec = require('child_process').exec;
var settings = require('electron-settings');
var fs = require('fs');

angular
  .module('app')
  .controller('HomeCtrl', function($scope, $http, $httpParamSerializerJQLike, $mdDialog, $timeout, $q, $filter, torrentDb) {
    $scope.search = {
      text: ''
    };

    $scope.currentPage = 1;
    $scope.totalPages = 1;
    $scope.results = [];
    $scope.savedTorrents = [];
    $scope.loading = false;

    /**
     * Reset the search.
     */
    $scope.resetSearch = function(){
      $scope.search = {
        text: ''
      };
    };

    /**
     * Search by the search object.
     */
    $scope.launchSearch = function(){
      $scope.results = [];
      var data = {
        cat: 0,
        page: $scope.currentPage,
        srcrel: $scope.search.text
      };

      $scope.executeSearch(data);
    };

    /**
     * Execute the search.
     * @param  {Object} params The data to use for search.
     */
    $scope.executeSearch = function(data){
      $scope.loading = true;
      $http({
          method: 'POST',
          url: 'http://www.tntvillage.scambioetico.org/src/releaselist.php',
          data: $httpParamSerializerJQLike(data),
          headers: {'Content-Type': 'application/x-www-form-urlencoded'},
          paramSerializer: '$httpParamSerializerJQLike'
      }).then(function(result){
        console.log('All ok');
        $('table tr', result.data).each(function(index, tr){
          //console.log(index, 'tr', tr);
          if(index > 0){
            var magnet = $('td:eq(1) a',tr).attr('href');
            var l = $('td:eq(3)',tr).text();
            var s = $('td:eq(4)',tr).text();
            var c = $('td:eq(5)',tr).text();
            var title = $('td:eq(6)',tr).text();
            torrentDb.exists(magnet)
              .then(function(isSaved){
                $scope.results.push({
                  magnet: magnet,
                  l: l,
                  s: s,
                  c: c,
                  title: title,
                  isSaved: isSaved
                });
            });
          }
        });
        var pagination = $(".total", result.data);
        $scope.totalPages = pagination.attr('a');
        $scope.loading = false;
      }, function(error){
        console.error('Error', error);
        $scope.loading = false;
      });
    };

    /**
     * Load more items.
     */
    $scope.loadMore = function(){
      $scope.currentPage++;
      var data = {
        cat: 0,
        page: $scope.currentPage,
        srcrel: $scope.search.text
      };
      $scope.executeSearch(data);
    };

    /**
     * Check if the are more pages to show.
     * @return {boolean} Returns true if there are more pages, false otherwise.
     */
    $scope.hasMorePages = function(){
      return ($scope.totalPages - $scope.currentPage) > 0;
    };

    /**
     * Open the given magnet using the default OS application.
     * @param magnet The magnet URL to open.
     * @param title The title of the torrent.
     */
    $scope.openMagnet = function(magnet, title){
      $scope.saveTorrent(magnet, title);
      shell.openExternal(magnet);
    };

    /**
     * Open the given magnet using WebTorrent application.
     * @param magnet The magnet URL to launch in WebTorrent.
     * @param title The title of the torrent.
     */
    $scope.launchInWebTorrent = function(magnet, title){
      $scope.saveTorrent(magnet, title);
      var electronPath = settings.get('preferences.electronPath');
      if(electronPath){
        if (fs.existsSync(electronPath)) {
          var command = electronPath + ' "' + magnet + '"';
          exec(command, function(error, stdout, stderr){
            if(error){
              $scope.showAlert('Error executing the command', error + ' ', stderr);
            } else {
              console.log('Commad executed!', stdout);
            }
          });
        } else {
          $scope.showAlert('WebTorrent not found', 'The file ' + electronPath + ' doesn\'t exist. Please set it in the Preferences page');
        }
      } else {
        $scope.showAlert('WebTorrent path is not set', 'WebTorrent path is not set, please set it in the Preferences page');
      }
    };

    /**
     * Set the torrent with the given magnet to saved.
     * @param  {string} magnet The magnet url that identifies the torrent.
     * @param  {boolean} isSaved Tells if the torrent should be showed as saved or not.
     */
    $scope.setSaved = function(magnet, isSaved){
      var torrents = $filter('filter')($scope.results, {magnet: magnet});
      if(torrents.length > 0){
        torrents[0].isSaved = isSaved;
      }
    };

    /**
     * Save the given torrent.
     * @param {string} magnet The magnet url of the torrent.
     * @param {string} title The title of the torrent.
     */
    $scope.saveTorrent = function(magnet, title){
      var doc = {
        magnet: magnet,
        title: title
      };
      torrentDb.insert(doc);

      // Set the current torrent to isSaved true
      $scope.setSaved(magnet, true);

      // Reload saved torrents
      $scope.loadSavedTorrents();
    };

    /**
     * Remove the torrent identified by the given magnet.
     * @param  {string} magnet The magnet to use to remove the torrent.
     */
    $scope.removeTorrent = function(magnet){
      torrentDb.remove(magnet);

      // Set the current torrent to isSaved false
      $scope.setSaved(magnet, false);

      // Reload saved torrents
      $scope.loadSavedTorrents();
    };

    /**
     * Get all saved torrents.
     * @return {Array} All saved torrents.
     */
    $scope.loadSavedTorrents = function(){
      torrentDb.getAllSavedTorrents()
        .then(function(torrents){
        $scope.savedTorrents = torrents;
      });
    };

    //Load all saved torrents
    $scope.loadSavedTorrents();

    /**
     * Copy to clipboard.
     * @param  {string} magnet Copy the given magnet to clipboard.
     */
    $scope.copyToClipboard = function(magnet){
      clipboard.writeText(magnet);
    };

    /**
     * Show the alert with the given title and error.
     * @param  {string} title The title to display in the dialog.
     * @param  {string} error The error to display in the dialog
     */
    $scope.showAlert = function(title, error){
      $mdDialog.show(
        $mdDialog.alert()
          .parent(angular.element(document.querySelector('#home-page')))
          .clickOutsideToClose(true)
          .title(title)
          .textContent(error)
          .ariaLabel(title)
          .ok('Ok')
      );
    };
  });
<commit_msg>Show toast on clipboard copy<commit_after>var shell = require('electron').shell;
var clipboard = require('electron').clipboard;
var exec = require('child_process').exec;
var settings = require('electron-settings');
var fs = require('fs');

angular
  .module('app')
  .controller('HomeCtrl', function($scope, $http, $httpParamSerializerJQLike, $mdDialog, $timeout, $q, $filter, $mdToast, torrentDb) {
    $scope.search = {
      text: ''
    };

    $scope.currentPage = 1;
    $scope.totalPages = 1;
    $scope.results = [];
    $scope.savedTorrents = [];
    $scope.loading = false;

    /**
     * Reset the search.
     */
    $scope.resetSearch = function(){
      $scope.search = {
        text: ''
      };
    };

    /**
     * Search by the search object.
     */
    $scope.launchSearch = function(){
      $scope.results = [];
      var data = {
        cat: 0,
        page: $scope.currentPage,
        srcrel: $scope.search.text
      };

      $scope.executeSearch(data);
    };

    /**
     * Execute the search.
     * @param  {Object} params The data to use for search.
     */
    $scope.executeSearch = function(data){
      $scope.loading = true;
      $http({
          method: 'POST',
          url: 'http://www.tntvillage.scambioetico.org/src/releaselist.php',
          data: $httpParamSerializerJQLike(data),
          headers: {'Content-Type': 'application/x-www-form-urlencoded'},
          paramSerializer: '$httpParamSerializerJQLike'
      }).then(function(result){
        console.log('All ok');
        $('table tr', result.data).each(function(index, tr){
          //console.log(index, 'tr', tr);
          if(index > 0){
            var magnet = $('td:eq(1) a',tr).attr('href');
            var l = $('td:eq(3)',tr).text();
            var s = $('td:eq(4)',tr).text();
            var c = $('td:eq(5)',tr).text();
            var title = $('td:eq(6)',tr).text();
            torrentDb.exists(magnet)
              .then(function(isSaved){
                $scope.results.push({
                  magnet: magnet,
                  l: l,
                  s: s,
                  c: c,
                  title: title,
                  isSaved: isSaved
                });
            });
          }
        });
        var pagination = $(".total", result.data);
        $scope.totalPages = pagination.attr('a');
        $scope.loading = false;
      }, function(error){
        console.error('Error', error);
        $scope.loading = false;
      });
    };

    /**
     * Load more items.
     */
    $scope.loadMore = function(){
      $scope.currentPage++;
      var data = {
        cat: 0,
        page: $scope.currentPage,
        srcrel: $scope.search.text
      };
      $scope.executeSearch(data);
    };

    /**
     * Check if the are more pages to show.
     * @return {boolean} Returns true if there are more pages, false otherwise.
     */
    $scope.hasMorePages = function(){
      return ($scope.totalPages - $scope.currentPage) > 0;
    };

    /**
     * Open the given magnet using the default OS application.
     * @param magnet The magnet URL to open.
     * @param title The title of the torrent.
     */
    $scope.openMagnet = function(magnet, title){
      $scope.saveTorrent(magnet, title);
      shell.openExternal(magnet);
    };

    /**
     * Open the given magnet using WebTorrent application.
     * @param magnet The magnet URL to launch in WebTorrent.
     * @param title The title of the torrent.
     */
    $scope.launchInWebTorrent = function(magnet, title){
      $scope.saveTorrent(magnet, title);
      var electronPath = settings.get('preferences.electronPath');
      if(electronPath){
        if (fs.existsSync(electronPath)) {
          var command = electronPath + ' "' + magnet + '"';
          exec(command, function(error, stdout, stderr){
            if(error){
              $scope.showAlert('Error executing the command', error + ' ', stderr);
            } else {
              console.log('Commad executed!', stdout);
            }
          });
        } else {
          $scope.showAlert('WebTorrent not found', 'The file ' + electronPath + ' doesn\'t exist. Please set it in the Preferences page');
        }
      } else {
        $scope.showAlert('WebTorrent path is not set', 'WebTorrent path is not set, please set it in the Preferences page');
      }
    };

    /**
     * Set the torrent with the given magnet to saved.
     * @param  {string} magnet The magnet url that identifies the torrent.
     * @param  {boolean} isSaved Tells if the torrent should be showed as saved or not.
     */
    $scope.setSaved = function(magnet, isSaved){
      var torrents = $filter('filter')($scope.results, {magnet: magnet});
      if(torrents.length > 0){
        torrents[0].isSaved = isSaved;
      }
    };

    /**
     * Save the given torrent.
     * @param {string} magnet The magnet url of the torrent.
     * @param {string} title The title of the torrent.
     */
    $scope.saveTorrent = function(magnet, title){
      var doc = {
        magnet: magnet,
        title: title
      };
      torrentDb.insert(doc);

      // Set the current torrent to isSaved true
      $scope.setSaved(magnet, true);

      // Reload saved torrents
      $scope.loadSavedTorrents();
    };

    /**
     * Remove the torrent identified by the given magnet.
     * @param  {string} magnet The magnet to use to remove the torrent.
     */
    $scope.removeTorrent = function(magnet){
      torrentDb.remove(magnet);

      // Set the current torrent to isSaved false
      $scope.setSaved(magnet, false);

      // Reload saved torrents
      $scope.loadSavedTorrents();
    };

    /**
     * Get all saved torrents.
     * @return {Array} All saved torrents.
     */
    $scope.loadSavedTorrents = function(){
      torrentDb.getAllSavedTorrents()
        .then(function(torrents){
        $scope.savedTorrents = torrents;
      });
    };

    //Load all saved torrents
    $scope.loadSavedTorrents();

    /**
     * Copy to clipboard.
     * @param  {string} magnet Copy the given magnet to clipboard.
     */
    $scope.copyToClipboard = function(magnet){
      clipboard.writeText(magnet);

      // Show toast
      $mdToast.show(
        $mdToast.simple()
          .textContent('Magnet url copied to clipboard')
          .position('top right')
          .hideDelay(3000)
      );
    };

    /**
     * Show the alert with the given title and error.
     * @param  {string} title The title to display in the dialog.
     * @param  {string} error The error to display in the dialog
     */
    $scope.showAlert = function(title, error){
      $mdDialog.show(
        $mdDialog.alert()
          .parent(angular.element(document.querySelector('#home-page')))
          .clickOutsideToClose(true)
          .title(title)
          .textContent(error)
          .ariaLabel(title)
          .ok('Ok')
      );
    };
  });
<|endoftext|>


<commit_before><commit_msg>got distances showing up..need to match up rows though<commit_after><|endoftext|>


<commit_before><commit_msg>Only run snowyHeader in December<commit_after><|endoftext|>


<commit_before><commit_msg>bug fix lang/deepClone — remove space in string<commit_after><|endoftext|>


<commit_before>/**
 * Module Dependencies
 */

var mongoose = require('mongoose');

var applicantSchema   = require('./schema.applicant')
  , employerSchema    = require('./schema.employer')
  , jobPostingSchema  = require('./schema.jobPosting');

var dbRef = undefined;

/**
 * Register models and sets local db reference
 * @param db {Connection} Mongoose connection
 * @return none
 */

function register (db) {
    dbRef = db;
    dbRef.model('Applicant', applicantSchema);
    dbRef.model('Employer' , employerSchema);
    dbRef.model('JobPosting' , jobPostingSchema);
}

/**
 * creates an applicant document constructor IE a model
 * @param none
 * @return {Model}
 */

function applicant () {
	if (dbRef === undefined)
		throw new error('Need to register models first!');
	return dbRef.model('Applicant');
}

/**
 * creates an employer document constructor IE a model
 * @param none
 * @return {Model}
 */

function employer () {
	if (dbRef === undefined)
		throw new error('Need to register models first!');
	return dbRef.model('Employer');
}

/**
 * creates a job posting model constructor
 * @param none
 * @return {Model}
 */

function jobPosting () {
	if (dbRef === undefined)
		throw new error('Need to register models first!');
	return dbRef.model('JobPosting');
}

/**
 * Exports
 */

module.exports = {
    register:  register
  , applicant: applicant
  , employer:  employer
  , jobPosting:  jobPosting
};
<commit_msg>Tabbing and spacing fixed<commit_after>/**
 * Module Dependencies
 */

var mongoose = require('mongoose');

var applicantSchema   = require('./schema.applicant')
  , employerSchema    = require('./schema.employer')
  , jobPostingSchema  = require('./schema.jobPosting');

var dbRef = undefined;

/**
 * Register models and sets local db reference
 * @param db {Connection} Mongoose connection
 * @return none
 */

function register (db) {
    dbRef = db;
    dbRef.model('Applicant', applicantSchema);
    dbRef.model('Employer' , employerSchema);
    dbRef.model('JobPosting' , jobPostingSchema);
}

/**
 * creates an applicant document constructor IE a model
 * @param none
 * @return {Model}
 */

function applicant () {
    if (dbRef === undefined)
        throw new error('Need to register models first!');
    return dbRef.model('Applicant');
}

/**
 * creates an employer document constructor IE a model
 * @param none
 * @return {Model}
 */

function employer () {
    if (dbRef === undefined)
        throw new error('Need to register models first!');
    return dbRef.model('Employer');
}

/**
 * creates a job posting model constructor
 * @param none
 * @return {Model}
 */

function jobPosting () {
    if (dbRef === undefined)
        throw new error('Need to register models first!');
    return dbRef.model('JobPosting');
}

/**
 * Exports
 */

module.exports = {
    register:   register
  , applicant:  applicant
  , employer:   employer
  , jobPosting: jobPosting
};
<|endoftext|>


<commit_before><commit_msg>Update ko.js<commit_after><|endoftext|>


<commit_before>/*global define,document,location,require */
/*jslint sloppy:true,nomen:true,plusplus:true */
define([
    "dojo/Evented",
    "dojo/_base/declare",
    "dojo/_base/kernel",
    "dojo/_base/array",
    "dojo/_base/lang",
    "dojo/dom-class",
    "dojo/Deferred",
    "dojo/promise/all",
    "esri/arcgis/utils",
    "esri/urlUtils",
    "esri/request",
    "esri/config",
    "esri/lang",
    "esri/IdentityManager",
    "esri/tasks/GeometryService",
    "config/defaults",
    "application/OAuthHelper"
], function (
    Evented,
    declare,
    kernel,
    array,
    lang,
    domClass,
    Deferred,
    all,
    arcgisUtils,
    urlUtils,
    esriRequest,
    esriConfig,
    esriLang,
    IdentityManager,
    GeometryService,
    defaults,
    OAuthHelper
) {
    return declare([Evented], {
        config: {},
        orgConfig: {},
        appConfig: {},
        urlConfig: {},
        customUrlConfig: {},
        constructor: function () {
            // config will contain application and user defined info for the application such as i18n strings,
            // the web map id and application id, any url parameters and any application specific configuration
            // information.
            this.config = defaults;
        },
        startup: function () {
            var deferred = this._init();
            deferred.then(lang.hitch(this, function (config) {
                // optional ready event to listen to
                this.emit("ready", config);
            }), lang.hitch(this, function (error) {
                // optional error event to listen to
                this.emit("error", error);
            }));
            return deferred;
        },
        // Get URL parameters and set application defaults needed to query arcgis.com for
        // an application and to see if the app is running in Portal or an Org
        _init: function () {
            var deferred, paramItems, mixinParams;

            deferred = new Deferred();
            // Set the web map, group and appid if they exist but ignore other url params.
            // Additional url parameters may be defined by the application but they need to be mixed in
            // to the config object after we retrieve the application configuration info. As an example,
            // we'll mix in some commonly used url parameters in the _queryUrlParams function after
            // the application configuration has been applied so that the url parameters overwrite any
            // configured settings. It's up to the application developer to update the application to take
            // advantage of these parameters.
            paramItems = ["webmap", "appid", "group", "oauthappid"];
            this.urlConfig = this._createUrlParamsObject(paramItems);
            // config defaults <- standard url params
            // we need the webmap, appid, group and oauthappid to query for the data
            lang.mixin(this.config, this.urlConfig);
            // Define the sharing url and other default values like the proxy.
            // The sharing url defines where to search for the web map and application content. The
            // default value is arcgis.com.
            this._initializeApplication();
            // execute these async
            all({
                // check if signed in
                auth: this._checkSignIn(),
                // get localization
                i18n: this._getLocalization(),
                // get application data
                app: this._queryApplicationConfiguration()
            }).then(lang.hitch(this, function () {
                // then execute these async
                all({
                    // get item data
                    item: this._queryDisplayItem(),
                    // get org data
                    org: this._queryOrganizationInformation()
                }).then(lang.hitch(this, function () {
                    // Get any custom url params
                    this._queryUrlParams();
                    // mix in all the settings we got!
                    // defaults <- organization <- application id config <- custom url params
                    lang.mixin(this.config, this.orgConfig, this.appConfig, this.customUrlConfig, this.urlConfig);
                    // Set the geometry helper service to be the app default.
                    if (this.config.helperServices && this.config.helperServices.geometry && this.config.helperServices.geometry.url) {
                        esriConfig.defaults.geometryService = new GeometryService(this.config.helperServices.geometry.url);
                    }
                    // setup OAuth if oauth appid exists_initializeApplication
                    if (this.config.oauthappid) {
                        this._setupOAuth(this.config.oauthappid, this.config.sharinghost);
                    }
                    deferred.resolve(this.config);
                }), deferred.reject);
            }), deferred.reject);
            // return promise
            return deferred.promise;
        },
        _createUrlParamsObject: function (items) {
            var urlObject, obj = {}, i;

            // retrieve url parameters. Templates all use url parameters to determine which arcgis.com
            // resource to work with.
            // Map templates use the webmap param to define the webmap to display
            // Group templates use the group param to provide the id of the group to display.
            // appid is the id of the application based on the template. We use this
            // id to retrieve application specific configuration information. The configuration
            // information will contain the values the  user selected on the template configuration
            // panel.
            urlObject = urlUtils.urlToObject(document.location.href);
            urlObject.query = urlObject.query || {};
            if (urlObject.query && items && items.length) {
                for (i = 0; i < items.length; i++) {
                    if (urlObject.query[items[i]]) {
                        obj[items[i]] = urlObject.query[items[i]];
                    }
                }
            }
            return obj;
        },
        _initializeApplication: function () {
            var appLocation, instance;

            // Check to see if the app is hosted or a portal. If the app is hosted or a portal set the
            // sharing url and the proxy. Otherwise use the sharing url set it to arcgis.com.
            // We know app is hosted (or portal) if it has /apps/ or /home/ in the url.
            appLocation = location.pathname.indexOf("/apps/");
            if (appLocation === -1) {
                appLocation = location.pathname.indexOf("/home/");
            }
            // app is hosted and no sharing url is defined so let's figure it out.
            if (appLocation !== -1) {
                // hosted or portal
                instance = location.pathname.substr(0, appLocation); //get the portal instance name
                this.config.sharinghost = location.protocol + "//" + location.host + instance;
                this.config.proxyurl = location.protocol + "//" + location.host + instance + "/sharing/proxy";
            } else {
                // setup OAuth if oauth appid exists. If we don't call it here before querying for appid
                // the identity manager dialog will appear if the appid isn't publicly shared.
                if (this.config.oauthappid) {
                    this._setupOAuth(this.config.oauthappid, this.config.sharinghost);
                }
            }
            arcgisUtils.arcgisUrl = this.config.sharinghost + "/sharing/rest/content/items";
            // Define the proxy url for the app
            if (this.config.proxyurl) {
                esriConfig.defaults.io.proxyUrl = this.config.proxyurl;
                esriConfig.defaults.io.alwaysUseProxy = false;
            }
        },
        _checkSignIn: function () {
            var deferred, signedIn;

            deferred = new Deferred();
            // check sign-in status
            signedIn = IdentityManager.checkSignInStatus(this.config.sharinghost + "/sharing");
            // resolve regardless of signed in or not.
            signedIn.promise.always(function () {
                deferred.resolve(true);
            });
            return deferred.promise;
        },
        _setupOAuth: function (id, portal) {
            OAuthHelper.init({
                appId: id,
                portal: portal,
                expiration: (14 * 24 * 60) //2 weeks (in minutes)
            });
        },
        _getLocalization: function () {
            var deferred, dirNode, classes, rtlClasses;

            deferred = new Deferred();
            if (this.config.localize) {
                require(["dojo/i18n!application/nls/resources"], lang.hitch(this, function (appBundle) {
                    // Get the localization strings for the template and store in an i18n variable. Also determine if the
                    // application is in a right-to-left language like Arabic or Hebrew.
                    this.config.i18n = appBundle || {};
                    // Bi-directional language support added to support right-to-left languages like Arabic and Hebrew
                    // Note: The map must stay ltr
                    this.config.i18n.direction = "ltr";
                    array.some(["ar", "he"], lang.hitch(this, function (l) {
                        if (kernel.locale.indexOf(l) !== -1) {
                            this.config.i18n.direction = "rtl";
                            return true;
                        }
                        return false;
                    }));
                    // add a dir attribute to the html tag. Then you can add special css classes for rtl languages
                    dirNode = document.getElementsByTagName("html")[0];
                    classes = dirNode.className;
                    if (this.config.i18n.direction === "rtl") {
                        // need to add support for dj_rtl.
                        // if the dir node is set when the app loads dojo will handle.
                        dirNode.setAttribute("dir", "rtl");
                        rtlClasses = " esriRTL dj_rtl dijitRtl " + classes.replace(/ /g, "-rtl ");
                        dirNode.className = lang.trim(classes + rtlClasses);
                    } else {
                        dirNode.setAttribute("dir", "ltr");
                        domClass.add(dirNode, "esriLTR");
                    }
                    deferred.resolve(true);
                }));
            } else {
                deferred.resolve(true);
            }
            return deferred.promise;
        },
        _queryDisplayItem: function () {
            var deferred, itemId, error;

            // Get details about the specified web map or group. If the group or web map is not shared publicly users will
            // be prompted to log-in by the Identity Manager.
            deferred = new Deferred();
            if (this.config.webmap || this.config.group) {
                itemId = this.config.webmap || this.config.group;
                arcgisUtils.getItem(itemId).then(lang.hitch(this, function (itemInfo) {
                    // ArcGIS.com allows you to set an application extent on the application item. Overwrite the
                    // existing web map extent with the application item extent when set.
                    if (this.config.appid && this.config.application_extent.length > 0 && itemInfo.item.extent) {
                        itemInfo.item.extent = [
                            [
                                parseFloat(this.config.application_extent[0][0]),
                                parseFloat(this.config.application_extent[0][1])
                            ],
                            [
                                parseFloat(this.config.application_extent[1][0]),
                                parseFloat(this.config.application_extent[1][1])
                            ]
                        ];
                    }
                    // Set the itemInfo config option. This can be used when calling createMap instead of the webmap or group id
                    this.config.itemInfo = itemInfo;
                    deferred.resolve(true);
                }), function (error) {
                    if (!error) {
                        error = new Error("ApplicationBoilerplate:: Error retrieving display item.");
                    }
                    deferred.reject(error);
                });
            } else {
                error = new Error("ApplicationBoilerplate:: webmap or group undefined.");
                deferred.reject(error);
            }
            return deferred.promise;
        },
        _queryApplicationConfiguration: function () {
            // Get the application configuration details using the application id. When the response contains
            // itemData.values then we know the app contains configuration information. We'll use these values
            // to overwrite the application defaults.
            var deferred = new Deferred();
            if (this.config.appid) {
                arcgisUtils.getItem(this.config.appid).then(lang.hitch(this, function (response) {
                    if (response.item && response.itemData && response.itemData.values) {
                        // get app config values - we'll merge them with config later.
                        this.appConfig = response.itemData.values;
                        // save response
                        this.appResponse = response;
                    }
                    // get the extent for the application item. This can be used to override the default web map extent
                    if (response.item && response.item.extent) {
                        this.config.application_extent = response.item.extent;
                    }
                    deferred.resolve(true);
                }), function (error) {
                    if (!error) {
                        error = new Error("ApplicationBoilerplate:: Error retrieving application configuration.");
                    }
                    deferred.reject(error);
                });
            } else {
                deferred.resolve(true);
            }
            return deferred.promise;
        },
        _queryOrganizationInformation: function () {
            var deferred = new Deferred();
            if (this.config.queryForOrg) {
                // Query the ArcGIS.com organization. This is defined by the sharinghost that is specified. For example if you
                // are a member of an org you'll want to set the sharinghost to be http://<your org name>.arcgis.com. We query
                // the organization by making a self request to the org url which returns details specific to that organization.
                // Examples of the type of information returned are custom roles, units settings, helper services and more.
                // If this fails, the application will continue to function
                esriRequest({
                    url: this.config.sharinghost + "/sharing/rest/portals/self",
                    content: {
                        "f": "json"
                    },
                    callbackParamName: "callback"
                }).then(lang.hitch(this, function (response) {

                    //get the bing key if the organization has one
                    if(response.bingKey){
                        this.orgConfig.bingmapskey = response.bingKey;
                    }
                    // get units defined by the org or the org user
                    this.orgConfig.units = "metric";
                    if (response.user && response.user.units) { //user defined units
                        this.orgConfig.units = response.user.units;
                    } else if (response.units) { //org level units
                        this.orgConfig.units = response.units;
                    } else if ((response.user && response.user.region && response.user.region === "US") || (response.user && !response.user.region && response.region === "US") || (response.user && !response.user.region && !response.region) || (!response.user && response.ipCntryCode === "US") || (!response.user && !response.ipCntryCode && kernel.locale === "en-us")) {
                        // use feet/miles only for the US and if nothing is set for a user
                        this.orgConfig.units = "english";
                    }
                    // Get the helper servcies (routing, print, locator etc)
                    this.orgConfig.helperServices = response.helperServices;
                    // are any custom roles defined in the organization?
                    if (response.user && esriLang.isDefined(response.user.roleId)) {
                        if (response.user.privileges) {
                            this.orgConfig.userPrivileges = response.user.privileges;
                        }
                    }
                    deferred.resolve(true);
                }), function (error) {
                    if (!error) {
                        error = new Error("ApplicationBoilerplate:: Error retrieving organization information.");
                    }
                    deferred.reject(error);
                });
            } else {
                deferred.resolve(true);
            }
            return deferred.promise;
        },
        _queryUrlParams: function () {
            // This function demonstrates how to handle additional custom url parameters. For example
            // if you want users to be able to specify lat/lon coordinates that define the map's center or
            // specify an alternate basemap via a url parameter.
            // If these options are also configurable these updates need to be added after any
            // application default and configuration info has been applied. Currently these values
            // (center, basemap, theme) are only here as examples and can be removed if you don't plan on
            // supporting additional url parameters in your application.
            this.customUrlConfig = this._createUrlParamsObject(this.config.urlItems);
        }
    });
});
<commit_msg>remove unused var<commit_after>/*global define,document,location,require */
/*jslint sloppy:true,nomen:true,plusplus:true */
define([
    "dojo/Evented",
    "dojo/_base/declare",
    "dojo/_base/kernel",
    "dojo/_base/array",
    "dojo/_base/lang",
    "dojo/dom-class",
    "dojo/Deferred",
    "dojo/promise/all",
    "esri/arcgis/utils",
    "esri/urlUtils",
    "esri/request",
    "esri/config",
    "esri/lang",
    "esri/IdentityManager",
    "esri/tasks/GeometryService",
    "config/defaults",
    "application/OAuthHelper"
], function (
    Evented,
    declare,
    kernel,
    array,
    lang,
    domClass,
    Deferred,
    all,
    arcgisUtils,
    urlUtils,
    esriRequest,
    esriConfig,
    esriLang,
    IdentityManager,
    GeometryService,
    defaults,
    OAuthHelper
) {
    return declare([Evented], {
        config: {},
        orgConfig: {},
        appConfig: {},
        urlConfig: {},
        customUrlConfig: {},
        constructor: function () {
            // config will contain application and user defined info for the application such as i18n strings,
            // the web map id and application id, any url parameters and any application specific configuration
            // information.
            this.config = defaults;
        },
        startup: function () {
            var deferred = this._init();
            deferred.then(lang.hitch(this, function (config) {
                // optional ready event to listen to
                this.emit("ready", config);
            }), lang.hitch(this, function (error) {
                // optional error event to listen to
                this.emit("error", error);
            }));
            return deferred;
        },
        // Get URL parameters and set application defaults needed to query arcgis.com for
        // an application and to see if the app is running in Portal or an Org
        _init: function () {
            var deferred, paramItems;

            deferred = new Deferred();
            // Set the web map, group and appid if they exist but ignore other url params.
            // Additional url parameters may be defined by the application but they need to be mixed in
            // to the config object after we retrieve the application configuration info. As an example,
            // we'll mix in some commonly used url parameters in the _queryUrlParams function after
            // the application configuration has been applied so that the url parameters overwrite any
            // configured settings. It's up to the application developer to update the application to take
            // advantage of these parameters.
            paramItems = ["webmap", "appid", "group", "oauthappid"];
            this.urlConfig = this._createUrlParamsObject(paramItems);
            // config defaults <- standard url params
            // we need the webmap, appid, group and oauthappid to query for the data
            lang.mixin(this.config, this.urlConfig);
            // Define the sharing url and other default values like the proxy.
            // The sharing url defines where to search for the web map and application content. The
            // default value is arcgis.com.
            this._initializeApplication();
            // execute these async
            all({
                // check if signed in
                auth: this._checkSignIn(),
                // get localization
                i18n: this._getLocalization(),
                // get application data
                app: this._queryApplicationConfiguration()
            }).then(lang.hitch(this, function () {
                // then execute these async
                all({
                    // get item data
                    item: this._queryDisplayItem(),
                    // get org data
                    org: this._queryOrganizationInformation()
                }).then(lang.hitch(this, function () {
                    // Get any custom url params
                    this._queryUrlParams();
                    // mix in all the settings we got!
                    // defaults <- organization <- application id config <- custom url params
                    lang.mixin(this.config, this.orgConfig, this.appConfig, this.customUrlConfig, this.urlConfig);
                    // Set the geometry helper service to be the app default.
                    if (this.config.helperServices && this.config.helperServices.geometry && this.config.helperServices.geometry.url) {
                        esriConfig.defaults.geometryService = new GeometryService(this.config.helperServices.geometry.url);
                    }
                    // setup OAuth if oauth appid exists_initializeApplication
                    if (this.config.oauthappid) {
                        this._setupOAuth(this.config.oauthappid, this.config.sharinghost);
                    }
                    deferred.resolve(this.config);
                }), deferred.reject);
            }), deferred.reject);
            // return promise
            return deferred.promise;
        },
        _createUrlParamsObject: function (items) {
            var urlObject, obj = {}, i;

            // retrieve url parameters. Templates all use url parameters to determine which arcgis.com
            // resource to work with.
            // Map templates use the webmap param to define the webmap to display
            // Group templates use the group param to provide the id of the group to display.
            // appid is the id of the application based on the template. We use this
            // id to retrieve application specific configuration information. The configuration
            // information will contain the values the  user selected on the template configuration
            // panel.
            urlObject = urlUtils.urlToObject(document.location.href);
            urlObject.query = urlObject.query || {};
            if (urlObject.query && items && items.length) {
                for (i = 0; i < items.length; i++) {
                    if (urlObject.query[items[i]]) {
                        obj[items[i]] = urlObject.query[items[i]];
                    }
                }
            }
            return obj;
        },
        _initializeApplication: function () {
            var appLocation, instance;

            // Check to see if the app is hosted or a portal. If the app is hosted or a portal set the
            // sharing url and the proxy. Otherwise use the sharing url set it to arcgis.com.
            // We know app is hosted (or portal) if it has /apps/ or /home/ in the url.
            appLocation = location.pathname.indexOf("/apps/");
            if (appLocation === -1) {
                appLocation = location.pathname.indexOf("/home/");
            }
            // app is hosted and no sharing url is defined so let's figure it out.
            if (appLocation !== -1) {
                // hosted or portal
                instance = location.pathname.substr(0, appLocation); //get the portal instance name
                this.config.sharinghost = location.protocol + "//" + location.host + instance;
                this.config.proxyurl = location.protocol + "//" + location.host + instance + "/sharing/proxy";
            } else {
                // setup OAuth if oauth appid exists. If we don't call it here before querying for appid
                // the identity manager dialog will appear if the appid isn't publicly shared.
                if (this.config.oauthappid) {
                    this._setupOAuth(this.config.oauthappid, this.config.sharinghost);
                }
            }
            arcgisUtils.arcgisUrl = this.config.sharinghost + "/sharing/rest/content/items";
            // Define the proxy url for the app
            if (this.config.proxyurl) {
                esriConfig.defaults.io.proxyUrl = this.config.proxyurl;
                esriConfig.defaults.io.alwaysUseProxy = false;
            }
        },
        _checkSignIn: function () {
            var deferred, signedIn;

            deferred = new Deferred();
            // check sign-in status
            signedIn = IdentityManager.checkSignInStatus(this.config.sharinghost + "/sharing");
            // resolve regardless of signed in or not.
            signedIn.promise.always(function () {
                deferred.resolve(true);
            });
            return deferred.promise;
        },
        _setupOAuth: function (id, portal) {
            OAuthHelper.init({
                appId: id,
                portal: portal,
                expiration: (14 * 24 * 60) //2 weeks (in minutes)
            });
        },
        _getLocalization: function () {
            var deferred, dirNode, classes, rtlClasses;

            deferred = new Deferred();
            if (this.config.localize) {
                require(["dojo/i18n!application/nls/resources"], lang.hitch(this, function (appBundle) {
                    // Get the localization strings for the template and store in an i18n variable. Also determine if the
                    // application is in a right-to-left language like Arabic or Hebrew.
                    this.config.i18n = appBundle || {};
                    // Bi-directional language support added to support right-to-left languages like Arabic and Hebrew
                    // Note: The map must stay ltr
                    this.config.i18n.direction = "ltr";
                    array.some(["ar", "he"], lang.hitch(this, function (l) {
                        if (kernel.locale.indexOf(l) !== -1) {
                            this.config.i18n.direction = "rtl";
                            return true;
                        }
                        return false;
                    }));
                    // add a dir attribute to the html tag. Then you can add special css classes for rtl languages
                    dirNode = document.getElementsByTagName("html")[0];
                    classes = dirNode.className;
                    if (this.config.i18n.direction === "rtl") {
                        // need to add support for dj_rtl.
                        // if the dir node is set when the app loads dojo will handle.
                        dirNode.setAttribute("dir", "rtl");
                        rtlClasses = " esriRTL dj_rtl dijitRtl " + classes.replace(/ /g, "-rtl ");
                        dirNode.className = lang.trim(classes + rtlClasses);
                    } else {
                        dirNode.setAttribute("dir", "ltr");
                        domClass.add(dirNode, "esriLTR");
                    }
                    deferred.resolve(true);
                }));
            } else {
                deferred.resolve(true);
            }
            return deferred.promise;
        },
        _queryDisplayItem: function () {
            var deferred, itemId, error;

            // Get details about the specified web map or group. If the group or web map is not shared publicly users will
            // be prompted to log-in by the Identity Manager.
            deferred = new Deferred();
            if (this.config.webmap || this.config.group) {
                itemId = this.config.webmap || this.config.group;
                arcgisUtils.getItem(itemId).then(lang.hitch(this, function (itemInfo) {
                    // ArcGIS.com allows you to set an application extent on the application item. Overwrite the
                    // existing web map extent with the application item extent when set.
                    if (this.config.appid && this.config.application_extent.length > 0 && itemInfo.item.extent) {
                        itemInfo.item.extent = [
                            [
                                parseFloat(this.config.application_extent[0][0]),
                                parseFloat(this.config.application_extent[0][1])
                            ],
                            [
                                parseFloat(this.config.application_extent[1][0]),
                                parseFloat(this.config.application_extent[1][1])
                            ]
                        ];
                    }
                    // Set the itemInfo config option. This can be used when calling createMap instead of the webmap or group id
                    this.config.itemInfo = itemInfo;
                    deferred.resolve(true);
                }), function (error) {
                    if (!error) {
                        error = new Error("ApplicationBoilerplate:: Error retrieving display item.");
                    }
                    deferred.reject(error);
                });
            } else {
                error = new Error("ApplicationBoilerplate:: webmap or group undefined.");
                deferred.reject(error);
            }
            return deferred.promise;
        },
        _queryApplicationConfiguration: function () {
            // Get the application configuration details using the application id. When the response contains
            // itemData.values then we know the app contains configuration information. We'll use these values
            // to overwrite the application defaults.
            var deferred = new Deferred();
            if (this.config.appid) {
                arcgisUtils.getItem(this.config.appid).then(lang.hitch(this, function (response) {
                    if (response.item && response.itemData && response.itemData.values) {
                        // get app config values - we'll merge them with config later.
                        this.appConfig = response.itemData.values;
                        // save response
                        this.appResponse = response;
                    }
                    // get the extent for the application item. This can be used to override the default web map extent
                    if (response.item && response.item.extent) {
                        this.config.application_extent = response.item.extent;
                    }
                    deferred.resolve(true);
                }), function (error) {
                    if (!error) {
                        error = new Error("ApplicationBoilerplate:: Error retrieving application configuration.");
                    }
                    deferred.reject(error);
                });
            } else {
                deferred.resolve(true);
            }
            return deferred.promise;
        },
        _queryOrganizationInformation: function () {
            var deferred = new Deferred();
            if (this.config.queryForOrg) {
                // Query the ArcGIS.com organization. This is defined by the sharinghost that is specified. For example if you
                // are a member of an org you'll want to set the sharinghost to be http://<your org name>.arcgis.com. We query
                // the organization by making a self request to the org url which returns details specific to that organization.
                // Examples of the type of information returned are custom roles, units settings, helper services and more.
                // If this fails, the application will continue to function
                esriRequest({
                    url: this.config.sharinghost + "/sharing/rest/portals/self",
                    content: {
                        "f": "json"
                    },
                    callbackParamName: "callback"
                }).then(lang.hitch(this, function (response) {

                    //get the bing key if the organization has one
                    if(response.bingKey){
                        this.orgConfig.bingmapskey = response.bingKey;
                    }
                    // get units defined by the org or the org user
                    this.orgConfig.units = "metric";
                    if (response.user && response.user.units) { //user defined units
                        this.orgConfig.units = response.user.units;
                    } else if (response.units) { //org level units
                        this.orgConfig.units = response.units;
                    } else if ((response.user && response.user.region && response.user.region === "US") || (response.user && !response.user.region && response.region === "US") || (response.user && !response.user.region && !response.region) || (!response.user && response.ipCntryCode === "US") || (!response.user && !response.ipCntryCode && kernel.locale === "en-us")) {
                        // use feet/miles only for the US and if nothing is set for a user
                        this.orgConfig.units = "english";
                    }
                    // Get the helper servcies (routing, print, locator etc)
                    this.orgConfig.helperServices = response.helperServices;
                    // are any custom roles defined in the organization?
                    if (response.user && esriLang.isDefined(response.user.roleId)) {
                        if (response.user.privileges) {
                            this.orgConfig.userPrivileges = response.user.privileges;
                        }
                    }
                    deferred.resolve(true);
                }), function (error) {
                    if (!error) {
                        error = new Error("ApplicationBoilerplate:: Error retrieving organization information.");
                    }
                    deferred.reject(error);
                });
            } else {
                deferred.resolve(true);
            }
            return deferred.promise;
        },
        _queryUrlParams: function () {
            // This function demonstrates how to handle additional custom url parameters. For example
            // if you want users to be able to specify lat/lon coordinates that define the map's center or
            // specify an alternate basemap via a url parameter.
            // If these options are also configurable these updates need to be added after any
            // application default and configuration info has been applied. Currently these values
            // (center, basemap, theme) are only here as examples and can be removed if you don't plan on
            // supporting additional url parameters in your application.
            this.customUrlConfig = this._createUrlParamsObject(this.config.urlItems);
        }
    });
});
<|endoftext|>


<commit_before>/*

Copyright (C) 2011 by Yehuda Katz

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

// lib/handlebars/browser-prefix.js
var Handlebars = {};

(function(Handlebars, undefined) {
;
// lib/handlebars/base.js

Handlebars.VERSION = "1.0.0";
Handlebars.COMPILER_REVISION = 4;

Handlebars.REVISION_CHANGES = {
  1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
  2: '== 1.0.0-rc.3',
  3: '== 1.0.0-rc.4',
  4: '>= 1.0.0'
};

Handlebars.helpers  = {};
Handlebars.partials = {};

var toString = Object.prototype.toString,
    functionType = '[object Function]',
    objectType = '[object Object]';

Handlebars.registerHelper = function(name, fn, inverse) {
  if (toString.call(name) === objectType) {
    if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
    Handlebars.Utils.extend(this.helpers, name);
  } else {
    if (inverse) { fn.not = inverse; }
    this.helpers[name] = fn;
  }
};

Handlebars.registerPartial = function(name, str) {
  if (toString.call(name) === objectType) {
    Handlebars.Utils.extend(this.partials,  name);
  } else {
    this.partials[name] = str;
  }
};

Handlebars.registerHelper('helperMissing', function(arg) {
  if(arguments.length === 2) {
    return undefined;
  } else {
    throw new Error("Missing helper: '" + arg + "'");
  }
});

Handlebars.registerHelper('blockHelperMissing', function(context, options) {
  var inverse = options.inverse || function() {}, fn = options.fn;

  var type = toString.call(context);

  if(type === functionType) { context = context.call(this); }

  if(context === true) {
    return fn(this);
  } else if(context === false || context == null) {
    return inverse(this);
  } else if(type === "[object Array]") {
    if(context.length > 0) {
      return Handlebars.helpers.each(context, options);
    } else {
      return inverse(this);
    }
  } else {
    return fn(context);
  }
});

Handlebars.K = function() {};

Handlebars.createFrame = Object.create || function(object) {
  Handlebars.K.prototype = object;
  var obj = new Handlebars.K();
  Handlebars.K.prototype = null;
  return obj;
};

Handlebars.logger = {
  DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,

  methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},

  // can be overridden in the host environment
  log: function(level, obj) {
    if (Handlebars.logger.level <= level) {
      var method = Handlebars.logger.methodMap[level];
      if (typeof console !== 'undefined' && console[method]) {
        console[method].call(console, obj);
      }
    }
  }
};

Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };

Handlebars.registerHelper('each', function(context, options) {
  var fn = options.fn, inverse = options.inverse;
  var i = 0, ret = "", data;

  var type = toString.call(context);
  if(type === functionType) { context = context.call(this); }

  if (options.data) {
    data = Handlebars.createFrame(options.data);
  }

  if(context && typeof context === 'object') {
    if(context instanceof Array){
      for(var j = context.length; i<j; i++) {
        if (data) { data.index = i; }
        ret = ret + fn(context[i], { data: data });
      }
    } else {
      for(var key in context) {
        if(context.hasOwnProperty(key)) {
          if(data) { data.key = key; }
          ret = ret + fn(context[key], {data: data});
          i++;
        }
      }
    }
  }

  if(i === 0){
    ret = inverse(this);
  }

  return ret;
});

Handlebars.registerHelper('if', function(conditional, options) {
  var type = toString.call(conditional);
  if(type === functionType) { conditional = conditional.call(this); }

  if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
    return options.inverse(this);
  } else {
    return options.fn(this);
  }
});

Handlebars.registerHelper('unless', function(conditional, options) {
  return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
});

Handlebars.registerHelper('with', function(context, options) {
  var type = toString.call(context);
  if(type === functionType) { context = context.call(this); }

  if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
});

Handlebars.registerHelper('log', function(context, options) {
  var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
  Handlebars.log(level, context);
});
;
// lib/handlebars/utils.js

var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];

Handlebars.Exception = function(message) {
  var tmp = Error.prototype.constructor.apply(this, arguments);

  // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
  for (var idx = 0; idx < errorProps.length; idx++) {
    this[errorProps[idx]] = tmp[errorProps[idx]];
  }
};
Handlebars.Exception.prototype = new Error();

// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
  this.string = string;
};
Handlebars.SafeString.prototype.toString = function() {
  return this.string.toString();
};

var escape = {
  "&": "&",
  "<": "<",
  ">": ">",
  '"': """,
  "'": "'",
  "`": "`"
};

var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;

var escapeChar = function(chr) {
  return escape[chr] || "&";
};

Handlebars.Utils = {
  extend: function(obj, value) {
    for(var key in value) {
      if(value.hasOwnProperty(key)) {
        obj[key] = value[key];
      }
    }
  },

  escapeExpression: function(string) {
    // don't escape SafeStrings, since they're already safe
    if (string instanceof Handlebars.SafeString) {
      return string.toString();
    } else if (string == null || string === false) {
      return "";
    }

    // Force a string conversion as this will be done by the append regardless and
    // the regex test will do this transparently behind the scenes, causing issues if
    // an object's to string has escaped characters in it.
    string = string.toString();

    if(!possible.test(string)) { return string; }
    return string.replace(badChars, escapeChar);
  },

  isEmpty: function(value) {
    if (!value && value !== 0) {
      return true;
    } else if(toString.call(value) === "[object Array]" && value.length === 0) {
      return true;
    } else {
      return false;
    }
  }
};
;
// lib/handlebars/runtime.js

Handlebars.VM = {
  template: function(templateSpec) {
    // Just add water
    var container = {
      escapeExpression: Handlebars.Utils.escapeExpression,
      invokePartial: Handlebars.VM.invokePartial,
      programs: [],
      program: function(i, fn, data) {
        var programWrapper = this.programs[i];
        if(data) {
          programWrapper = Handlebars.VM.program(i, fn, data);
        } else if (!programWrapper) {
          programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
        }
        return programWrapper;
      },
      merge: function(param, common) {
        var ret = param || common;

        if (param && common) {
          ret = {};
          Handlebars.Utils.extend(ret, common);
          Handlebars.Utils.extend(ret, param);
        }
        return ret;
      },
      programWithDepth: Handlebars.VM.programWithDepth,
      noop: Handlebars.VM.noop,
      compilerInfo: null
    };

    return function(context, options) {
      options = options || {};
      var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);

      var compilerInfo = container.compilerInfo || [],
          compilerRevision = compilerInfo[0] || 1,
          currentRevision = Handlebars.COMPILER_REVISION;

      if (compilerRevision !== currentRevision) {
        if (compilerRevision < currentRevision) {
          var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
              compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
          throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
                "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
        } else {
          // Use the embedded version info since the runtime doesn't know about this revision yet
          throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
                "Please update your runtime to a newer version ("+compilerInfo[1]+").";
        }
      }

      return result;
    };
  },

  programWithDepth: function(i, fn, data /*, $depth */) {
    var args = Array.prototype.slice.call(arguments, 3);

    var program = function(context, options) {
      options = options || {};

      return fn.apply(this, [context, options.data || data].concat(args));
    };
    program.program = i;
    program.depth = args.length;
    return program;
  },
  program: function(i, fn, data) {
    var program = function(context, options) {
      options = options || {};

      return fn(context, options.data || data);
    };
    program.program = i;
    program.depth = 0;
    return program;
  },
  noop: function() { return ""; },
  invokePartial: function(partial, name, context, helpers, partials, data) {
    var options = { helpers: helpers, partials: partials, data: data };

    if(partial === undefined) {
      throw new Handlebars.Exception("The partial " + name + " could not be found");
    } else if(partial instanceof Function) {
      return partial(context, options);
    } else if (!Handlebars.compile) {
      throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
    } else {
      partials[name] = Handlebars.compile(partial, {data: data !== undefined});
      return partials[name](context, options);
    }
  }
};

Handlebars.template = Handlebars.VM.template;
;
// lib/handlebars/browser-suffix.js
})(Handlebars);
;
<commit_msg>add module.exports again<commit_after>/*

Copyright (C) 2011 by Yehuda Katz

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

// lib/handlebars/browser-prefix.js
var Handlebars = {};
module.exports = Handlebars;

(function(Handlebars, undefined) {
;
// lib/handlebars/base.js

Handlebars.VERSION = "1.0.0";
Handlebars.COMPILER_REVISION = 4;

Handlebars.REVISION_CHANGES = {
  1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
  2: '== 1.0.0-rc.3',
  3: '== 1.0.0-rc.4',
  4: '>= 1.0.0'
};

Handlebars.helpers  = {};
Handlebars.partials = {};

var toString = Object.prototype.toString,
    functionType = '[object Function]',
    objectType = '[object Object]';

Handlebars.registerHelper = function(name, fn, inverse) {
  if (toString.call(name) === objectType) {
    if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
    Handlebars.Utils.extend(this.helpers, name);
  } else {
    if (inverse) { fn.not = inverse; }
    this.helpers[name] = fn;
  }
};

Handlebars.registerPartial = function(name, str) {
  if (toString.call(name) === objectType) {
    Handlebars.Utils.extend(this.partials,  name);
  } else {
    this.partials[name] = str;
  }
};

Handlebars.registerHelper('helperMissing', function(arg) {
  if(arguments.length === 2) {
    return undefined;
  } else {
    throw new Error("Missing helper: '" + arg + "'");
  }
});

Handlebars.registerHelper('blockHelperMissing', function(context, options) {
  var inverse = options.inverse || function() {}, fn = options.fn;

  var type = toString.call(context);

  if(type === functionType) { context = context.call(this); }

  if(context === true) {
    return fn(this);
  } else if(context === false || context == null) {
    return inverse(this);
  } else if(type === "[object Array]") {
    if(context.length > 0) {
      return Handlebars.helpers.each(context, options);
    } else {
      return inverse(this);
    }
  } else {
    return fn(context);
  }
});

Handlebars.K = function() {};

Handlebars.createFrame = Object.create || function(object) {
  Handlebars.K.prototype = object;
  var obj = new Handlebars.K();
  Handlebars.K.prototype = null;
  return obj;
};

Handlebars.logger = {
  DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,

  methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},

  // can be overridden in the host environment
  log: function(level, obj) {
    if (Handlebars.logger.level <= level) {
      var method = Handlebars.logger.methodMap[level];
      if (typeof console !== 'undefined' && console[method]) {
        console[method].call(console, obj);
      }
    }
  }
};

Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };

Handlebars.registerHelper('each', function(context, options) {
  var fn = options.fn, inverse = options.inverse;
  var i = 0, ret = "", data;

  var type = toString.call(context);
  if(type === functionType) { context = context.call(this); }

  if (options.data) {
    data = Handlebars.createFrame(options.data);
  }

  if(context && typeof context === 'object') {
    if(context instanceof Array){
      for(var j = context.length; i<j; i++) {
        if (data) { data.index = i; }
        ret = ret + fn(context[i], { data: data });
      }
    } else {
      for(var key in context) {
        if(context.hasOwnProperty(key)) {
          if(data) { data.key = key; }
          ret = ret + fn(context[key], {data: data});
          i++;
        }
      }
    }
  }

  if(i === 0){
    ret = inverse(this);
  }

  return ret;
});

Handlebars.registerHelper('if', function(conditional, options) {
  var type = toString.call(conditional);
  if(type === functionType) { conditional = conditional.call(this); }

  if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
    return options.inverse(this);
  } else {
    return options.fn(this);
  }
});

Handlebars.registerHelper('unless', function(conditional, options) {
  return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
});

Handlebars.registerHelper('with', function(context, options) {
  var type = toString.call(context);
  if(type === functionType) { context = context.call(this); }

  if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
});

Handlebars.registerHelper('log', function(context, options) {
  var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
  Handlebars.log(level, context);
});
;
// lib/handlebars/utils.js

var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];

Handlebars.Exception = function(message) {
  var tmp = Error.prototype.constructor.apply(this, arguments);

  // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
  for (var idx = 0; idx < errorProps.length; idx++) {
    this[errorProps[idx]] = tmp[errorProps[idx]];
  }
};
Handlebars.Exception.prototype = new Error();

// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
  this.string = string;
};
Handlebars.SafeString.prototype.toString = function() {
  return this.string.toString();
};

var escape = {
  "&": "&",
  "<": "<",
  ">": ">",
  '"': """,
  "'": "'",
  "`": "`"
};

var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;

var escapeChar = function(chr) {
  return escape[chr] || "&";
};

Handlebars.Utils = {
  extend: function(obj, value) {
    for(var key in value) {
      if(value.hasOwnProperty(key)) {
        obj[key] = value[key];
      }
    }
  },

  escapeExpression: function(string) {
    // don't escape SafeStrings, since they're already safe
    if (string instanceof Handlebars.SafeString) {
      return string.toString();
    } else if (string == null || string === false) {
      return "";
    }

    // Force a string conversion as this will be done by the append regardless and
    // the regex test will do this transparently behind the scenes, causing issues if
    // an object's to string has escaped characters in it.
    string = string.toString();

    if(!possible.test(string)) { return string; }
    return string.replace(badChars, escapeChar);
  },

  isEmpty: function(value) {
    if (!value && value !== 0) {
      return true;
    } else if(toString.call(value) === "[object Array]" && value.length === 0) {
      return true;
    } else {
      return false;
    }
  }
};
;
// lib/handlebars/runtime.js

Handlebars.VM = {
  template: function(templateSpec) {
    // Just add water
    var container = {
      escapeExpression: Handlebars.Utils.escapeExpression,
      invokePartial: Handlebars.VM.invokePartial,
      programs: [],
      program: function(i, fn, data) {
        var programWrapper = this.programs[i];
        if(data) {
          programWrapper = Handlebars.VM.program(i, fn, data);
        } else if (!programWrapper) {
          programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
        }
        return programWrapper;
      },
      merge: function(param, common) {
        var ret = param || common;

        if (param && common) {
          ret = {};
          Handlebars.Utils.extend(ret, common);
          Handlebars.Utils.extend(ret, param);
        }
        return ret;
      },
      programWithDepth: Handlebars.VM.programWithDepth,
      noop: Handlebars.VM.noop,
      compilerInfo: null
    };

    return function(context, options) {
      options = options || {};
      var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);

      var compilerInfo = container.compilerInfo || [],
          compilerRevision = compilerInfo[0] || 1,
          currentRevision = Handlebars.COMPILER_REVISION;

      if (compilerRevision !== currentRevision) {
        if (compilerRevision < currentRevision) {
          var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
              compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
          throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
                "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
        } else {
          // Use the embedded version info since the runtime doesn't know about this revision yet
          throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
                "Please update your runtime to a newer version ("+compilerInfo[1]+").";
        }
      }

      return result;
    };
  },

  programWithDepth: function(i, fn, data /*, $depth */) {
    var args = Array.prototype.slice.call(arguments, 3);

    var program = function(context, options) {
      options = options || {};

      return fn.apply(this, [context, options.data || data].concat(args));
    };
    program.program = i;
    program.depth = args.length;
    return program;
  },
  program: function(i, fn, data) {
    var program = function(context, options) {
      options = options || {};

      return fn(context, options.data || data);
    };
    program.program = i;
    program.depth = 0;
    return program;
  },
  noop: function() { return ""; },
  invokePartial: function(partial, name, context, helpers, partials, data) {
    var options = { helpers: helpers, partials: partials, data: data };

    if(partial === undefined) {
      throw new Handlebars.Exception("The partial " + name + " could not be found");
    } else if(partial instanceof Function) {
      return partial(context, options);
    } else if (!Handlebars.compile) {
      throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
    } else {
      partials[name] = Handlebars.compile(partial, {data: data !== undefined});
      return partials[name](context, options);
    }
  }
};

Handlebars.template = Handlebars.VM.template;
;
// lib/handlebars/browser-suffix.js
})(Handlebars);
;
<|endoftext|>


<commit_before><commit_msg>Switched category and type on network timing objects.<commit_after><|endoftext|>