<commit_before>"use strict";

//Load dependencies
var request = require("request");
var _ = require('lodash');
var cheerio = require("cheerio");
var async = require("async");
var bnetAPI = process.require("core/api/bnet.js");
var updateModel = process.require("updates/updateModel.js");

//Configuration
var applicationStorage = process.require("core/applicationStorage.js");

module.exports.getWoWProgressPage = function (path, callback) {
    var url = "http://www.wowprogress.com" + path;
    var logger = applicationStorage.logger;
    logger.verbose("GET wowprogress URL %s", url);
    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            callback(null, body);
        } else {
            callback(new Error("Error HTTP " + error.message + " on fetching wowprogress api " + url));
        }
    });
};

module.exports.getGuildsUrlsOnPage = function (number, callback) {
    var self = this;
    async.waterfall([
        function (callback) {
            if (number == -1) {
                var url = "/pve/rating/next";
            } else {
                var url = "/pve/rating/next/" + number + "/rating";
            }
            self.getWoWProgressPage(url, function (error, body) {
                callback(error, body)
            })
        },
        function (body, callback) {
            var $ = cheerio.load(body);

            var urls = [];
            $('body').find('.ratingContainer table.rating tr a.guild').each(function (i, elem) {
                urls.push($(this).attr('href'));
            });

            callback(null, urls);
        }
    ], function (error, urls) {
        callback(error, urls);
    })

};

module.exports.getKills = function (url, callback) {
    var self = this;
    var logger = applicationStorage.logger;

    async.waterfall([
            function (callback) {
                self.getWoWProgressPage(url, function (error, body) {
                    callback(error, body)
                })
            },
            function (body, callback) {

                var $ = cheerio.load(body);

                var armoryUrl = decodeURIComponent(($('.armoryLink').attr('href')));

                if (armoryUrl == "undefined") {
                    return callback(new Error('Armory link undefined'));
                }
                try {
                    var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                    var realm = armoryUrl.match('battle.net/wow/guild/(.*)/(.*)/')[1];
                    var region = armoryUrl.match('http://(.*).battle.net/wow/guild/')[1];
                } catch (e) {
                    logger.info("Parsing error, trying CN");
                    try {
                        var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                        var realm = $('.realm').text().substring(3);
                        //var realm = armoryUrl.match('www.battlenet.com.cn/wow/guild/(.*)/(.*)/')[1];
                        var region = armoryUrl.match('http://www.battlenet.com.(.*)/wow/guild/')[1];
                    } catch (e) {
                        logger.error("Error on page parsing %s", url);
                        callback(true);
                    }
                }

                bnetAPI.getGuild(region, realm, name, [], function (error, guild) {
                    if (!guild || !guild.realm || !guild.name) {
                        logger.warn("Bnet return empty guild so try to update %s-%s-%s, skip it", region, realm, name);

                        //Bug on chinese ...
                        if (region == "cn") {
                            updateModel.insert("wp_pu", region, realm, name, 3, function () {
                                logger.verbose("Insert GuildProgress to update %s-%s-%s with priority %s", region, realm, name, 3);
                                callback(true);
                            });
                        } else {
                            callback(true);
                        }
                    } else {
                        callback(error, $, region, guild.realm, guild.name);
                    }
                });

            }
            ,
            function ($, region, realm, name, callback) {
                var kills = [];

                var bestTimestamp = 999999999999999999999;
                var normalCount = 0;
                var heroicCount = 0;
                var mythicCount = 0;
                var normalArchimonde = false;
                var heroicArchimonde = false;
                var mythicArchimonde = false;
                var normalArchimondeTimestamp = 0;
                var heroicArchimondeTimestamp = 0;
                var mythicArchimondeTimestamp = 0;

                $('table.rating a.boss_kills_link').each(function (i, elem) {

                    var kill = {region: region.toLowerCase(), realm: realm, name: name};

                    var bossName = $(this).text();

                    var bossArray = bossName.replace(/(^\+)/g, "").trim().split(':');
                    kill["boss"] = bossArray[1].trim();

                    if (bossArray[0] == 'N') {
                        kill["difficulty"] = 'normal';
                        normalCount++;
                    } else if (bossArray[0] == 'H') {
                        kill["difficulty"] = 'heroic'
                        heroicCount++;
                    } else if (bossArray[0] == 'M') {
                        kill["difficulty"] = 'mythic';
                        mythicCount++;
                    } else {
                        callback(new Error("WOWPROGRESS_PARSING_ERROR"));
                    }

                    if (kill["boss"] == "Xhul'horac") {
                        kill["boss"] = "Xhul'horac";
                    }
                    var date = $(this).parent().next().text();
                    var timestamp = new Date(date + " GMT+0000").getTime();

                    if (isNaN(timestamp)) {
                        timestamp = new Date(date.substring(1, date.length) + " GMT+0000").getTime();
                    }

                    if (isNaN(timestamp) || timestamp < 1388534400000) {
                        timestamp = parseInt($(this).parent().next().find('.datetime').attr('data-ts'), 10) * 1000;
                    }

                    //Fix incorrect kill ...
                    if (kill["boss"] == "Archimonde") {
                        bestTimestamp = timestamp;

                        if (kill["difficulty"] == "normal") {
                            normalArchimonde = true;
                            normalArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "heroic") {
                            heroicArchimonde = true;
                            heroicArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "mythic") {
                            mythicArchimonde = true;
                            mythicArchimondeTimestamp = timestamp;
                        }

                    }

                    if (timestamp > bestTimestamp) {
                        kill["timestamp"] = bestTimestamp;
                    } else {
                        kill["timestamp"] = timestamp;
                    }

                    if (kill["timestamp"] < new Date("Jun 01 2016 GMT+0000").getTime()) {
                        kills.push(kill);
                    }

                });

                var bossList = [
                    "Hellfire Assault",
                    "Iron Reaver",
                    "Kormrok", "Hellfire High Council",
                    "Kilrogg Deadeye",
                    "Gorefiend",
                    "Shadow-Lord Iskar",
                    "Socrethar the Eternal",
                    "Tyrant Velhari",
                    "Fel Lord Zakuun",
                    "Xhul'horac",
                    "Mannoroth",
                    "Archimonde"];

                if (normalArchimonde && normalCount < 13) {
                    var normalKills = [];
                    async.forEach(bossList, function (boss) {
                        var normalKill = {};
                        normalKill.boss = boss;
                        normalKill.difficulty = "normal";
                        normalKill.region = region.toLowerCase();
                        normalKill.realm = realm;
                        normalKill.name = name;
                        normalKill.timestamp = normalArchimonde;
                        normalKills.push(normalKill);

                    });
                    kills.concat(normalKills);

                }

                if (heroicArchimonde && heroicCount < 13) {
                    var heroicKills = [];
                    async.forEach(bossList, function (boss) {
                        var heroicKill = {};
                        heroicKill.boss = boss;
                        heroicKill.difficulty = "heroic";
                        heroicKill.region = region.toLowerCase();
                        heroicKill.realm = realm;
                        heroicKill.name = name;
                        heroicKill.timestamp = heroicArchimondeTimestamp;
                        heroicKills.push(heroicKill);

                    });
                    kills = kills.concat(heroicKills);
                }
                if (mythicArchimonde && mythicCount < 13) {
                    var mythicKills = [];
                    async.forEach(bossList, function (boss) {
                        var mythicKill = {};
                        mythicKill.boss = boss;
                        mythicKill.difficulty = "mythic";
                        mythicKill.region = region.toLowerCase();
                        mythicKill.realm = realm;
                        mythicKill.name = name;
                        mythicKill.timestamp = mythicArchimondeTimestamp;
                        mythicKills.push(mythicKill);
                    });
                    kills = kills.concat(mythicKills);
                }
                kills = _.uniqBy(kills, function (elem) {
                    return elem.boss + elem.difficulty;
                });
                callback(null, kills);
            }

        ],
        function (error, kills) {
            callback(error, kills);
        }
    );

};

<commit_msg>Fix bug on realm name for chinese<commit_after>"use strict";

//Load dependencies
var request = require("request");
var _ = require('lodash');
var cheerio = require("cheerio");
var async = require("async");
var bnetAPI = process.require("core/api/bnet.js");
var updateModel = process.require("updates/updateModel.js");

//Configuration
var applicationStorage = process.require("core/applicationStorage.js");

module.exports.getWoWProgressPage = function (path, callback) {
    var url = "http://www.wowprogress.com" + path;
    var logger = applicationStorage.logger;
    logger.verbose("GET wowprogress URL %s", url);
    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            callback(null, body);
        } else {
            callback(new Error("Error HTTP " + error.message + " on fetching wowprogress api " + url));
        }
    });
};

module.exports.getGuildsUrlsOnPage = function (number, callback) {
    var self = this;
    async.waterfall([
        function (callback) {
            if (number == -1) {
                var url = "/pve/rating/next";
            } else {
                var url = "/pve/rating/next/" + number + "/rating";
            }
            self.getWoWProgressPage(url, function (error, body) {
                callback(error, body)
            })
        },
        function (body, callback) {
            var $ = cheerio.load(body);

            var urls = [];
            $('body').find('.ratingContainer table.rating tr a.guild').each(function (i, elem) {
                urls.push($(this).attr('href'));
            });

            callback(null, urls);
        }
    ], function (error, urls) {
        callback(error, urls);
    })

};

module.exports.getKills = function (url, callback) {
    var self = this;
    var logger = applicationStorage.logger;

    async.waterfall([
            function (callback) {
                self.getWoWProgressPage(url, function (error, body) {
                    callback(error, body)
                })
            },
            function (body, callback) {

                var $ = cheerio.load(body);

                var armoryUrl = decodeURIComponent(($('.armoryLink').attr('href')));

                if (armoryUrl == "undefined") {
                    return callback(new Error('Armory link undefined'));
                }
                try {
                    var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                    var realm = armoryUrl.match('battle.net/wow/guild/(.*)/(.*)/')[1];
                    var region = armoryUrl.match('http://(.*).battle.net/wow/guild/')[1];
                } catch (e) {
                    logger.info("Parsing error, trying CN");
                    try {
                        var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                        var realm = $('.realm').text().substring(3);
                        //var realm = armoryUrl.match('www.battlenet.com.cn/wow/guild/(.*)/(.*)/')[1];
                        var region = armoryUrl.match('http://www.battlenet.com.(.*)/wow/guild/')[1];
                    } catch (e) {
                        logger.error("Error on page parsing %s", url);
                        callback(true);
                    }
                }

                //For Chinese, force update only // PATCH
                if (region == "cn") {
                    updateModel.insert("wp_pu", region, realm, name, 3, function () {
                        logger.verbose("Insert GuildProgress to update %s-%s-%s with priority %s", region, realm, name, 3);
                        callback(true);
                    });
                }else{
                    //Speed don't parse other ...
                    callback(true);
                }

                bnetAPI.getGuild(region, realm, name, [], function (error, guild) {
                    if (!guild || !guild.realm || !guild.name) {
                        logger.warn("Bnet return empty guild %s-%s-%s, skip it", region, realm, name);
                            callback(true);

                    } else {
                        callback(error, $, region, guild.realm, guild.name);
                    }
                });

            }
            ,
            function ($, region, realm, name, callback) {
                var kills = [];

                var bestTimestamp = 999999999999999999999;
                var normalCount = 0;
                var heroicCount = 0;
                var mythicCount = 0;
                var normalArchimonde = false;
                var heroicArchimonde = false;
                var mythicArchimonde = false;
                var normalArchimondeTimestamp = 0;
                var heroicArchimondeTimestamp = 0;
                var mythicArchimondeTimestamp = 0;

                $('table.rating a.boss_kills_link').each(function (i, elem) {

                    var kill = {region: region.toLowerCase(), realm: realm, name: name};

                    var bossName = $(this).text();

                    var bossArray = bossName.replace(/(^\+)/g, "").trim().split(':');
                    kill["boss"] = bossArray[1].trim();

                    if (bossArray[0] == 'N') {
                        kill["difficulty"] = 'normal';
                        normalCount++;
                    } else if (bossArray[0] == 'H') {
                        kill["difficulty"] = 'heroic'
                        heroicCount++;
                    } else if (bossArray[0] == 'M') {
                        kill["difficulty"] = 'mythic';
                        mythicCount++;
                    } else {
                        callback(new Error("WOWPROGRESS_PARSING_ERROR"));
                    }

                    if (kill["boss"] == "Xhul'horac") {
                        kill["boss"] = "Xhul'horac";
                    }
                    var date = $(this).parent().next().text();
                    var timestamp = new Date(date + " GMT+0000").getTime();

                    if (isNaN(timestamp)) {
                        timestamp = new Date(date.substring(1, date.length) + " GMT+0000").getTime();
                    }

                    if (isNaN(timestamp) || timestamp < 1388534400000) {
                        timestamp = parseInt($(this).parent().next().find('.datetime').attr('data-ts'), 10) * 1000;
                    }

                    //Fix incorrect kill ...
                    if (kill["boss"] == "Archimonde") {
                        bestTimestamp = timestamp;

                        if (kill["difficulty"] == "normal") {
                            normalArchimonde = true;
                            normalArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "heroic") {
                            heroicArchimonde = true;
                            heroicArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "mythic") {
                            mythicArchimonde = true;
                            mythicArchimondeTimestamp = timestamp;
                        }

                    }

                    if (timestamp > bestTimestamp) {
                        kill["timestamp"] = bestTimestamp;
                    } else {
                        kill["timestamp"] = timestamp;
                    }

                    if (kill["timestamp"] < new Date("Jun 01 2016 GMT+0000").getTime()) {
                        kills.push(kill);
                    }

                });

                var bossList = [
                    "Hellfire Assault",
                    "Iron Reaver",
                    "Kormrok", "Hellfire High Council",
                    "Kilrogg Deadeye",
                    "Gorefiend",
                    "Shadow-Lord Iskar",
                    "Socrethar the Eternal",
                    "Tyrant Velhari",
                    "Fel Lord Zakuun",
                    "Xhul'horac",
                    "Mannoroth",
                    "Archimonde"];

                if (normalArchimonde && normalCount < 13) {
                    var normalKills = [];
                    async.forEach(bossList, function (boss) {
                        var normalKill = {};
                        normalKill.boss = boss;
                        normalKill.difficulty = "normal";
                        normalKill.region = region.toLowerCase();
                        normalKill.realm = realm;
                        normalKill.name = name;
                        normalKill.timestamp = normalArchimonde;
                        normalKills.push(normalKill);

                    });
                    kills.concat(normalKills);

                }

                if (heroicArchimonde && heroicCount < 13) {
                    var heroicKills = [];
                    async.forEach(bossList, function (boss) {
                        var heroicKill = {};
                        heroicKill.boss = boss;
                        heroicKill.difficulty = "heroic";
                        heroicKill.region = region.toLowerCase();
                        heroicKill.realm = realm;
                        heroicKill.name = name;
                        heroicKill.timestamp = heroicArchimondeTimestamp;
                        heroicKills.push(heroicKill);

                    });
                    kills = kills.concat(heroicKills);
                }
                if (mythicArchimonde && mythicCount < 13) {
                    var mythicKills = [];
                    async.forEach(bossList, function (boss) {
                        var mythicKill = {};
                        mythicKill.boss = boss;
                        mythicKill.difficulty = "mythic";
                        mythicKill.region = region.toLowerCase();
                        mythicKill.realm = realm;
                        mythicKill.name = name;
                        mythicKill.timestamp = mythicArchimondeTimestamp;
                        mythicKills.push(mythicKill);
                    });
                    kills = kills.concat(mythicKills);
                }
                kills = _.uniqBy(kills, function (elem) {
                    return elem.boss + elem.difficulty;
                });
                callback(null, kills);
            }

        ],
        function (error, kills) {
            callback(error, kills);
        }
    );

};

<|endoftext|>


<commit_before>'use strict';

var _       = require('lodash');
var del     = require('del');
var gear    = require('gear');
var path    = require('path');
var fs      = require('fs');
var utility = require('./utility');

var parseHeader   = utility.parseHeader;
var getStyleNames = utility.getStyleNames;
var tasks         = require('gear-lib');

tasks.clean = function(directories, blobs, done) {
  directories = _.isString(directories) ? [directories] : directories;

  return del(directories, _.partial(done, _, blobs));
};
tasks.clean.type = 'collect';

// Depending on the languages required for the current language being
// processed, this task reorders it's dependencies first then include the
// language.
tasks.reorderDeps = function(options, blobs, done) {
  var buffer       = {},
      newBlobOrder = [];

  _.each(blobs, function(blob) {
    var basename = path.basename(blob.name),
        fileInfo = parseHeader(blob.result),
        extra = {blob: blob, processed: false};

    buffer[basename] = _.merge(extra, fileInfo || {});
  });

  function pushInBlob(object) {
    if(!object.processed) {
      object.processed = true;
      newBlobOrder.push(object.blob);
    }
  }

  _.each(buffer, function(buf) {
    var object;

    if(buf.Requires) {
      _.each(buf.Requires, function(language) {
        object = buffer[language];
        pushInBlob(object);
      });
    }

    pushInBlob(buf);
  });

  done(null, newBlobOrder);
};
tasks.reorderDeps.type = 'collect';

tasks.template = function(template, blob, done) {
  template = template || '';

  var filename = path.basename(blob.name),
      basename = path.basename(filename, '.js'),
      content  = _.template(template)({
        name: basename,
        filename: filename,
        content: blob.result.trim()
      });

  return done(null, new gear.Blob(content, blob));
};

tasks.templateAll = function(template, blobs, done) {
  var names, content;

  names = _.map(blobs, function(blob) {
    return path.basename(blob.name, '.js');
  });

  content = _.template(template)({ names: names });

  return done(null, [new blobs[0].constructor(content, blobs)]);
};
tasks.templateAll.type = 'collect';

tasks.rename = function(options, blob, done) {
  options = options || {};

  var name = blob.name,
      ext  = new RegExp(path.extname(name) + '$');

  name = name.replace(ext, options.extname);

  return done(null, new gear.Blob(blob.result, {name: name}));
};

// Adds the contributors from `AUTHORS.en.txt` onto the `package.json` file
// and moves the result into the `build` directory.
tasks.buildPackage = function(json, blob, done) {
  var result,
      lines = blob.result.split(/\r?\n/),
      regex = /^- (.*) <(.*)>$/;

  json.contributors = _.transform(lines, function(result, line) {
    var matches = line.match(regex);

    if(matches) {
      result.push({
        name: matches[1],
        email: matches[2]
      });
    }
  }, []);

  result = JSON.stringify(json, null, '  ');

  return done(null, new gear.Blob(result, blob));
};

// Mainly for replacing the keys of `utility.REPLACES` for it's values while
// skipping over strings, regular expressions, or comments. However, this is
// pretty generic so long as you use the `utility.replace` function, you can
// replace a regular expression with a string.
tasks.replaceSkippingStrings = function(params, blob, done) {
  var content = blob.result,
      length  = content.length,
      offset  = 0,

      replace = params.replace || '',
      regex   = params.regex,
      starts  = /\/\/|['"\/]/,

      result  = [],
      chunk, end, match, start, terminator;

  while(offset < length) {
    chunk = content.slice(offset);
    match = chunk.match(starts);
    end   = match ? match.index : length;

    chunk = content.slice(offset, end + offset);
    result.push(chunk.replace(regex, replace));

    offset += end;

    if(match) {
      // We found a starter sequence: either a `//` or a "quote"
      // In the case of `//` our terminator is the end of line.
      // Otherwise it's either a matching quote or an escape symbol.
      terminator = match[0] !== '//' ? new RegExp('[' + match[0] + '\\\\]') : /$/m;
      start      = offset;
      offset    += 1;

      while(true) {
        chunk = content.slice(offset);
        match = chunk.match(terminator);

        if(!match) {
          return done('Unmatched quote');
        }

        if(match[0] === '\\') {
          offset += match.index + 2;
        } else {
          offset += match.index + 1;
          result.push(content.slice(start, offset));
          break;
        }
      }
    }
  }

  return done(null, new gear.Blob(result.join(''), blob));
};

tasks.filter = function(callback, blobs, done) {
  var filteredBlobs = _.filter(blobs, callback);

  // Re-add in blobs required from header definition
  _.each(filteredBlobs, function(blob) {
    var dirname  = path.dirname(blob.name),
        content  = blob.result,
        fileInfo = parseHeader(content);

    if(fileInfo && fileInfo.Requires) {
      _.each(fileInfo.Requires, function(language) {
        var filename  = dirname + '/' + language,
            fileFound = _.find(filteredBlobs, { name: filename });

        if(!fileFound) {
          filteredBlobs.push(
            _.find(blobs, { name: filename }));
        }
      });
    }
  });

  return done(null, filteredBlobs);
};
tasks.filter.type = 'collect';

tasks.readSnippet = function(options, blob, done) {
  var name        = path.basename(blob.name, '.js'),
      fileInfo    = parseHeader(blob.result),
      snippetName = path.join('test', 'detect', name, 'default.txt');

  function onRead(error, blob) {
    if (error !== null) return done(null, null); // ignore missing snippets
    var meta = {name: name + '.js', fileInfo: fileInfo};
    blob = new gear.Blob(blob.result, meta);
    return done(error, blob);
  }

  gear.Blob.readFile(snippetName, 'utf8', onRead, false);
};

// Translate the template for the demo in `demo/index.html` to a usable HTML
// file.
tasks.templateDemo = function(options, blobs, done) {
  var name = path.join('demo', 'index.html');

  fs.readFile(name, function(err, template) {
    if(err) return done(err, null);

    getStyleNames(function(err, styles) {
      var content = _.template(template)({
                      path: path,
                      blobs: _.compact(blobs),
                      styles: styles
                    });

      return done(err, [new gear.Blob(content)]);
    });
  });
};
tasks.templateDemo.type = 'collect';

// Packages up included languages into the core `highlight.js` and moves the
// result into the `build` directory.
tasks.packageFiles = function(options, blobs, done) {
  var content,
      coreFile  = _.head(blobs),
      languages = _.tail(blobs),

      lines     = coreFile.result
                    .replace(utility.regex.header, '')
                    .split('\n\n'),
      lastLine  = _.last(lines),
      langStr   = _.foldl(languages, function(str, language) {
                    return str + language.result + '\n';
                  }, '');

  lines[lines.length - 1] = langStr.trim();

  lines   = lines.concat(lastLine);
  content = lines.join('\n\n');

  return done(null, [new gear.Blob(content)]);
};
tasks.packageFiles.type = 'collect';

module.exports = new gear.Registry({ tasks: tasks });
<commit_msg>Change templateDemo to use promises<commit_after>'use strict';

var _        = require('lodash');
var bluebird = require('bluebird');
var del      = require('del');
var fs       = bluebird.promisifyAll(require('fs'));
var gear     = require('gear');
var path     = require('path');
var utility  = require('./utility');

var parseHeader   = utility.parseHeader;
var getStyleNames = utility.getStyleNames;
var tasks         = require('gear-lib');

tasks.clean = function(directories, blobs, done) {
  directories = _.isString(directories) ? [directories] : directories;

  return del(directories, _.partial(done, _, blobs));
};
tasks.clean.type = 'collect';

// Depending on the languages required for the current language being
// processed, this task reorders it's dependencies first then include the
// language.
tasks.reorderDeps = function(options, blobs, done) {
  var buffer       = {},
      newBlobOrder = [];

  _.each(blobs, function(blob) {
    var basename = path.basename(blob.name),
        fileInfo = parseHeader(blob.result),
        extra = {blob: blob, processed: false};

    buffer[basename] = _.merge(extra, fileInfo || {});
  });

  function pushInBlob(object) {
    if(!object.processed) {
      object.processed = true;
      newBlobOrder.push(object.blob);
    }
  }

  _.each(buffer, function(buf) {
    var object;

    if(buf.Requires) {
      _.each(buf.Requires, function(language) {
        object = buffer[language];
        pushInBlob(object);
      });
    }

    pushInBlob(buf);
  });

  done(null, newBlobOrder);
};
tasks.reorderDeps.type = 'collect';

tasks.template = function(template, blob, done) {
  template = template || '';

  var filename = path.basename(blob.name),
      basename = path.basename(filename, '.js'),
      content  = _.template(template)({
        name: basename,
        filename: filename,
        content: blob.result.trim()
      });

  return done(null, new gear.Blob(content, blob));
};

tasks.templateAll = function(template, blobs, done) {
  var names, content;

  names = _.map(blobs, function(blob) {
    return path.basename(blob.name, '.js');
  });

  content = _.template(template)({ names: names });

  return done(null, [new blobs[0].constructor(content, blobs)]);
};
tasks.templateAll.type = 'collect';

tasks.rename = function(options, blob, done) {
  options = options || {};

  var name = blob.name,
      ext  = new RegExp(path.extname(name) + '$');

  name = name.replace(ext, options.extname);

  return done(null, new gear.Blob(blob.result, {name: name}));
};

// Adds the contributors from `AUTHORS.en.txt` onto the `package.json` file
// and moves the result into the `build` directory.
tasks.buildPackage = function(json, blob, done) {
  var result,
      lines = blob.result.split(/\r?\n/),
      regex = /^- (.*) <(.*)>$/;

  json.contributors = _.transform(lines, function(result, line) {
    var matches = line.match(regex);

    if(matches) {
      result.push({
        name: matches[1],
        email: matches[2]
      });
    }
  }, []);

  result = JSON.stringify(json, null, '  ');

  return done(null, new gear.Blob(result, blob));
};

// Mainly for replacing the keys of `utility.REPLACES` for it's values while
// skipping over strings, regular expressions, or comments. However, this is
// pretty generic so long as you use the `utility.replace` function, you can
// replace a regular expression with a string.
tasks.replaceSkippingStrings = function(params, blob, done) {
  var content = blob.result,
      length  = content.length,
      offset  = 0,

      replace = params.replace || '',
      regex   = params.regex,
      starts  = /\/\/|['"\/]/,

      result  = [],
      chunk, end, match, start, terminator;

  while(offset < length) {
    chunk = content.slice(offset);
    match = chunk.match(starts);
    end   = match ? match.index : length;

    chunk = content.slice(offset, end + offset);
    result.push(chunk.replace(regex, replace));

    offset += end;

    if(match) {
      // We found a starter sequence: either a `//` or a "quote"
      // In the case of `//` our terminator is the end of line.
      // Otherwise it's either a matching quote or an escape symbol.
      terminator = match[0] !== '//' ? new RegExp('[' + match[0] + '\\\\]') : /$/m;
      start      = offset;
      offset    += 1;

      while(true) {
        chunk = content.slice(offset);
        match = chunk.match(terminator);

        if(!match) {
          return done('Unmatched quote');
        }

        if(match[0] === '\\') {
          offset += match.index + 2;
        } else {
          offset += match.index + 1;
          result.push(content.slice(start, offset));
          break;
        }
      }
    }
  }

  return done(null, new gear.Blob(result.join(''), blob));
};

tasks.filter = function(callback, blobs, done) {
  var filteredBlobs = _.filter(blobs, callback);

  // Re-add in blobs required from header definition
  _.each(filteredBlobs, function(blob) {
    var dirname  = path.dirname(blob.name),
        content  = blob.result,
        fileInfo = parseHeader(content);

    if(fileInfo && fileInfo.Requires) {
      _.each(fileInfo.Requires, function(language) {
        var filename  = dirname + '/' + language,
            fileFound = _.find(filteredBlobs, { name: filename });

        if(!fileFound) {
          filteredBlobs.push(
            _.find(blobs, { name: filename }));
        }
      });
    }
  });

  return done(null, filteredBlobs);
};
tasks.filter.type = 'collect';

tasks.readSnippet = function(options, blob, done) {
  var name        = path.basename(blob.name, '.js'),
      fileInfo    = parseHeader(blob.result),
      snippetName = path.join('test', 'detect', name, 'default.txt');

  function onRead(error, blob) {
    if (error !== null) return done(null, null); // ignore missing snippets
    var meta = {name: name + '.js', fileInfo: fileInfo};
    blob = new gear.Blob(blob.result, meta);
    return done(error, blob);
  }

  gear.Blob.readFile(snippetName, 'utf8', onRead, false);
};

// Translate the template for the demo in `demo/index.html` to a usable HTML
// file.
tasks.templateDemo = function(options, blobs, done) {
  var name        = path.join('demo', 'index.html'),
      getTemplate = fs.readFileAsync(name);

  bluebird.join(getTemplate, getStyleNames(), function(template, styles) {
    var content = _.template(template)({
                    path: path,
                    blobs: _.compact(blobs),
                    styles: styles
                  });

    return done(null, [new gear.Blob(content)]);
  })
  .catch(done);
};
tasks.templateDemo.type = 'collect';

// Packages up included languages into the core `highlight.js` and moves the
// result into the `build` directory.
tasks.packageFiles = function(options, blobs, done) {
  var content,
      coreFile  = _.head(blobs),
      languages = _.tail(blobs),

      lines     = coreFile.result
                    .replace(utility.regex.header, '')
                    .split('\n\n'),
      lastLine  = _.last(lines),
      langStr   = _.foldl(languages, function(str, language) {
                    return str + language.result + '\n';
                  }, '');

  lines[lines.length - 1] = langStr.trim();

  lines   = lines.concat(lastLine);
  content = lines.join('\n\n');

  return done(null, [new gear.Blob(content)]);
};
tasks.packageFiles.type = 'collect';

module.exports = new gear.Registry({ tasks: tasks });
<|endoftext|>


<commit_before><commit_msg>Remove any sort of transport security<commit_after><|endoftext|>


<commit_before>// Word cloud layout by Jason Davies, http://www.jasondavies.com/word-cloud/
// Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf
(function(exports) {
  function cloud() {
    var size = [256, 256],
        text = cloudText,
        font = cloudFont,
        fontSize = cloudFontSize,
        fontStyle = cloudFontNormal,
        fontWeight = cloudFontNormal,
        rotate = cloudRotate,
        padding = cloudPadding,
        spiral = archimedeanSpiral,
        words = [],
        timeInterval = Infinity,
        event = d3.dispatch("word", "end"),
        timer = null,
        cloud = {};

    cloud.start = function() {
      var board = zeroArray((size[0] >> 5) * size[1]),
          bounds = null,
          n = words.length,
          i = -1,
          tags = [],
          data = words.map(function(d, i) {
            d.text = text.call(this, d, i);
            d.font = font.call(this, d, i);
            d.style = fontStyle.call(this, d, i);
            d.weight = fontWeight.call(this, d, i);
            d.rotate = rotate.call(this, d, i);
            d.size = ~~fontSize.call(this, d, i);
            d.padding = padding.call(this, d, i);
            return d;
          }).sort(function(a, b) { return b.size - a.size; });

      if (timer) clearInterval(timer);
      timer = setInterval(step, 0);
      step();

      return cloud;

      function step() {
        var start = +new Date,
            d;
        while (+new Date - start < timeInterval && ++i < n && timer) {
          d = data[i];
          d.x = (size[0] * (Math.random() + .5)) >> 1;
          d.y = (size[1] * (Math.random() + .5)) >> 1;
          cloudSprite(d, data, i);
          if (d.hasText && place(board, d, bounds)) {
            tags.push(d);
            event.word(d);
            if (bounds) cloudBounds(bounds, d);
            else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}];
            // Temporary hack
            d.x -= size[0] >> 1;
            d.y -= size[1] >> 1;
          }
        }
        if (i >= n) {
          cloud.stop();
          event.end(tags, bounds);
        }
      }
    }

    cloud.stop = function() {
      if (timer) {
        clearInterval(timer);
        timer = null;
      }
      return cloud;
    };

    cloud.timeInterval = function(x) {
      if (!arguments.length) return timeInterval;
      timeInterval = x == null ? Infinity : x;
      return cloud;
    };

    function place(board, tag, bounds) {
      var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}],
          startX = tag.x,
          startY = tag.y,
          maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]),
          s = spiral(size),
          dt = Math.random() < .5 ? 1 : -1,
          t = -dt,
          dxdy,
          dx,
          dy;

      while (dxdy = s(t += dt)) {
        dx = ~~dxdy[0];
        dy = ~~dxdy[1];

        if (Math.min(dx, dy) > maxDelta) break;

        tag.x = startX + dx;
        tag.y = startY + dy;

        if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 ||
            tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue;
        // TODO only check for collisions within current bounds.
        if (!bounds || !cloudCollide(tag, board, size[0])) {
          if (!bounds || collideRects(tag, bounds)) {
            var sprite = tag.sprite,
                w = tag.width >> 5,
                sw = size[0] >> 5,
                lx = tag.x - (w << 4),
                sx = lx & 0x7f,
                msx = 32 - sx,
                h = tag.y1 - tag.y0,
                x = (tag.y + tag.y0) * sw + (lx >> 5),
                last;
            for (var j = 0; j < h; j++) {
              last = 0;
              for (var i = 0; i <= w; i++) {
                board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0);
              }
              x += sw;
            }
            delete tag.sprite;
            return true;
          }
        }
      }
      return false;
    }

    cloud.words = function(x) {
      if (!arguments.length) return words;
      words = x;
      return cloud;
    };

    cloud.size = function(x) {
      if (!arguments.length) return size;
      size = [+x[0], +x[1]];
      return cloud;
    };

    cloud.font = function(x) {
      if (!arguments.length) return font;
      font = d3.functor(x);
      return cloud;
    };

    cloud.fontStyle = function(x) {
      if (!arguments.length) return fontStyle;
      fontStyle = d3.functor(x);
      return cloud;
    };

    cloud.fontWeight = function(x) {
      if (!arguments.length) return fontWeight;
      fontWeight = d3.functor(x);
      return cloud;
    };

    cloud.rotate = function(x) {
      if (!arguments.length) return rotate;
      rotate = d3.functor(x);
      return cloud;
    };

    cloud.text = function(x) {
      if (!arguments.length) return text;
      text = d3.functor(x);
      return cloud;
    };

    cloud.spiral = function(x) {
      if (!arguments.length) return spiral;
      spiral = spirals[x + ""] || x;
      return cloud;
    };

    cloud.fontSize = function(x) {
      if (!arguments.length) return fontSize;
      fontSize = d3.functor(x);
      return cloud;
    };

    cloud.padding = function(x) {
      if (!arguments.length) return padding;
      padding = d3.functor(x);
      return cloud;
    };

    return d3.rebind(cloud, event, "on");
  }

  function cloudText(d) {
    return d.text;
  }

  function cloudFont() {
    return "serif";
  }

  function cloudFontNormal() {
    return "normal";
  }

  function cloudFontSize(d) {
    return Math.sqrt(d.value);
  }

  function cloudRotate() {
    return (~~(Math.random() * 6) - 3) * 30;
  }

  function cloudPadding() {
    return 1;
  }

  // Fetches a monochrome sprite bitmap for the specified text.
  // Load in batches for speed.
  function cloudSprite(d, data, di) {
    if (d.sprite) return;
    c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
    var x = 0,
        y = 0,
        maxh = 0,
        n = data.length;
    --di;
    while (++di < n) {
      d = data[di];
      c.save();
      c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font;
      var w = c.measureText(d.text + "m").width * ratio,
          h = d.size << 1;
      if (d.rotate) {
        var sr = Math.sin(d.rotate * cloudRadians),
            cr = Math.cos(d.rotate * cloudRadians),
            wcr = w * cr,
            wsr = w * sr,
            hcr = h * cr,
            hsr = h * sr;
        w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5;
        h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr));
      } else {
        w = (w + 0x1f) >> 5 << 5;
      }
      if (h > maxh) maxh = h;
      if (x + w >= (cw << 5)) {
        x = 0;
        y += maxh;
        maxh = 0;
      }
      if (y + h >= ch) break;
      c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio);
      if (d.rotate) c.rotate(d.rotate * cloudRadians);
      c.fillText(d.text, 0, 0);
      if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0);
      c.restore();
      d.width = w;
      d.height = h;
      d.xoff = x;
      d.yoff = y;
      d.x1 = w >> 1;
      d.y1 = h >> 1;
      d.x0 = -d.x1;
      d.y0 = -d.y1;
      d.hasText = true;
      x += w;
    }
    var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data,
        sprite = [];
    while (--di >= 0) {
      d = data[di];
      if (!d.hasText) continue;
      var w = d.width,
          w32 = w >> 5,
          h = d.y1 - d.y0;
      // Zero the buffer
      for (var i = 0; i < h * w32; i++) sprite[i] = 0;
      x = d.xoff;
      if (x == null) return;
      y = d.yoff;
      var seen = 0,
          seenRow = -1;
      for (var j = 0; j < h; j++) {
        for (var i = 0; i < w; i++) {
          var k = w32 * j + (i >> 5),
              m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0;
          sprite[k] |= m;
          seen |= m;
        }
        if (seen) seenRow = j;
        else {
          d.y0++;
          h--;
          j--;
          y++;
        }
      }
      d.y1 = d.y0 + seenRow;
      d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32);
    }
  }

  // Use mask-based collision detection.
  function cloudCollide(tag, board, sw) {
    sw >>= 5;
    var sprite = tag.sprite,
        w = tag.width >> 5,
        lx = tag.x - (w << 4),
        sx = lx & 0x7f,
        msx = 32 - sx,
        h = tag.y1 - tag.y0,
        x = (tag.y + tag.y0) * sw + (lx >> 5),
        last;
    for (var j = 0; j < h; j++) {
      last = 0;
      for (var i = 0; i <= w; i++) {
        if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0))
            & board[x + i]) return true;
      }
      x += sw;
    }
    return false;
  }

  function cloudBounds(bounds, d) {
    var b0 = bounds[0],
        b1 = bounds[1];
    if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0;
    if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0;
    if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1;
    if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1;
  }

  function collideRects(a, b) {
    return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y;
  }

  function archimedeanSpiral(size) {
    var e = size[0] / size[1];
    return function(t) {
      return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)];
    };
  }

  function rectangularSpiral(size) {
    var dy = 4,
        dx = dy * size[0] / size[1],
        x = 0,
        y = 0;
    return function(t) {
      var sign = t < 0 ? -1 : 1;
      // See triangular numbers: T_n = n * (n + 1) / 2.
      switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) {
        case 0:  x += dx; break;
        case 1:  y += dy; break;
        case 2:  x -= dx; break;
        default: y -= dy; break;
      }
      return [x, y];
    };
  }

  // TODO reuse arrays?
  function zeroArray(n) {
    var a = [],
        i = -1;
    while (++i < n) a[i] = 0;
    return a;
  }

  var cloudRadians = Math.PI / 180,
      cw = 1 << 11 >> 5,
      ch = 1 << 11,
      canvas,
      ratio = 1;

  if (typeof document !== "undefined") {
    canvas = document.createElement("canvas");
    canvas.width = 1;
    canvas.height = 1;
    ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2);
    canvas.width = (cw << 5) / ratio;
    canvas.height = ch / ratio;
  } else {
    // node-canvas support
    var Canvas = require("canvas");
    canvas = new Canvas(cw << 5, ch);
  }

  var c = canvas.getContext("2d"),
      spirals = {
        archimedean: archimedeanSpiral,
        rectangular: rectangularSpiral
      };
  c.fillStyle = c.strokeStyle = "red";
  c.textAlign = "center";

  exports.cloud = cloud;
})(typeof exports === "undefined" ? d3.layout || (d3.layout = {}) : exports);
<commit_msg>Add a random_placement flag to d3.layout.cloud<commit_after>// Word cloud layout by Jason Davies, http://www.jasondavies.com/word-cloud/
// Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf
// modifications:
// 2013-10-12 Very minor dumbing-down tweak by Andrew Goldstone:
//      random_placement flag
(function(exports) {
  function cloud() {
    var size = [256, 256],
        text = cloudText,
        font = cloudFont,
        fontSize = cloudFontSize,
        fontStyle = cloudFontNormal,
        fontWeight = cloudFontNormal,
        rotate = cloudRotate,
        padding = cloudPadding,
        spiral = archimedeanSpiral,
        words = [],
        timeInterval = Infinity,
        event = d3.dispatch("word", "end"),
        timer = null,
        random_placement = true,
        cloud = {};

    cloud.start = function() {
      var board = zeroArray((size[0] >> 5) * size[1]),
          bounds = null,
          n = words.length,
          i = -1,
          tags = [],
          data = words.map(function(d, i) {
            d.text = text.call(this, d, i);
            d.font = font.call(this, d, i);
            d.style = fontStyle.call(this, d, i);
            d.weight = fontWeight.call(this, d, i);
            d.rotate = rotate.call(this, d, i);
            d.size = ~~fontSize.call(this, d, i);
            d.padding = padding.call(this, d, i);
            return d;
          }).sort(function(a, b) { return b.size - a.size; });

      if (timer) clearInterval(timer);
      timer = setInterval(step, 0);
      step();

      return cloud;

      function step() {
        var start = +new Date,
            d;
        while (+new Date - start < timeInterval && ++i < n && timer) {
          d = data[i];
          if (random_placement) {
              d.x = (size[0] * (Math.random() + .5)) >> 1;
              d.y = (size[1] * (Math.random() + .5)) >> 1;
          } else {
              d.x = size[0] >> 1;
              d.y = size[1] >> 1;
          }
          cloudSprite(d, data, i);
          if (d.hasText && place(board, d, bounds)) {
            tags.push(d);
            event.word(d);
            if (bounds) cloudBounds(bounds, d);
            else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}];
            // Temporary hack
            d.x -= size[0] >> 1;
            d.y -= size[1] >> 1;
          }
        }
        if (i >= n) {
          cloud.stop();
          event.end(tags, bounds);
        }
      }
    }

    cloud.stop = function() {
      if (timer) {
        clearInterval(timer);
        timer = null;
      }
      return cloud;
    };

    cloud.timeInterval = function(x) {
      if (!arguments.length) return timeInterval;
      timeInterval = x == null ? Infinity : x;
      return cloud;
    };

    function place(board, tag, bounds) {
      var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}],
          startX = tag.x,
          startY = tag.y,
          maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]),
          s = spiral(size),
          dt = Math.random() < .5 ? 1 : -1,
          t = -dt,
          dxdy,
          dx,
          dy;

      while (dxdy = s(t += dt)) {
        dx = ~~dxdy[0];
        dy = ~~dxdy[1];

        if (Math.min(dx, dy) > maxDelta) break;

        tag.x = startX + dx;
        tag.y = startY + dy;

        if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 ||
            tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue;
        // TODO only check for collisions within current bounds.
        if (!bounds || !cloudCollide(tag, board, size[0])) {
          if (!bounds || collideRects(tag, bounds)) {
            var sprite = tag.sprite,
                w = tag.width >> 5,
                sw = size[0] >> 5,
                lx = tag.x - (w << 4),
                sx = lx & 0x7f,
                msx = 32 - sx,
                h = tag.y1 - tag.y0,
                x = (tag.y + tag.y0) * sw + (lx >> 5),
                last;
            for (var j = 0; j < h; j++) {
              last = 0;
              for (var i = 0; i <= w; i++) {
                board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0);
              }
              x += sw;
            }
            delete tag.sprite;
            return true;
          }
        }
      }
      return false;
    }

    cloud.words = function(x) {
      if (!arguments.length) return words;
      words = x;
      return cloud;
    };

    cloud.size = function(x) {
      if (!arguments.length) return size;
      size = [+x[0], +x[1]];
      return cloud;
    };

    cloud.font = function(x) {
      if (!arguments.length) return font;
      font = d3.functor(x);
      return cloud;
    };

    cloud.fontStyle = function(x) {
      if (!arguments.length) return fontStyle;
      fontStyle = d3.functor(x);
      return cloud;
    };

    cloud.fontWeight = function(x) {
      if (!arguments.length) return fontWeight;
      fontWeight = d3.functor(x);
      return cloud;
    };

    cloud.rotate = function(x) {
      if (!arguments.length) return rotate;
      rotate = d3.functor(x);
      return cloud;
    };

    cloud.text = function(x) {
      if (!arguments.length) return text;
      text = d3.functor(x);
      return cloud;
    };

    cloud.spiral = function(x) {
      if (!arguments.length) return spiral;
      spiral = spirals[x + ""] || x;
      return cloud;
    };

    cloud.fontSize = function(x) {
      if (!arguments.length) return fontSize;
      fontSize = d3.functor(x);
      return cloud;
    };

    cloud.padding = function(x) {
      if (!arguments.length) return padding;
      padding = d3.functor(x);
      return cloud;
    };

    cloud.random_placement = function (x) {
      if (!arguments.length) return random_placement;
      random_placement = x;
      return cloud;
    };

    return d3.rebind(cloud, event, "on");
  }

  function cloudText(d) {
    return d.text;
  }

  function cloudFont() {
    return "serif";
  }

  function cloudFontNormal() {
    return "normal";
  }

  function cloudFontSize(d) {
    return Math.sqrt(d.value);
  }

  function cloudRotate() {
    return (~~(Math.random() * 6) - 3) * 30;
  }

  function cloudPadding() {
    return 1;
  }

  // Fetches a monochrome sprite bitmap for the specified text.
  // Load in batches for speed.
  function cloudSprite(d, data, di) {
    if (d.sprite) return;
    c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
    var x = 0,
        y = 0,
        maxh = 0,
        n = data.length;
    --di;
    while (++di < n) {
      d = data[di];
      c.save();
      c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font;
      var w = c.measureText(d.text + "m").width * ratio,
          h = d.size << 1;
      if (d.rotate) {
        var sr = Math.sin(d.rotate * cloudRadians),
            cr = Math.cos(d.rotate * cloudRadians),
            wcr = w * cr,
            wsr = w * sr,
            hcr = h * cr,
            hsr = h * sr;
        w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5;
        h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr));
      } else {
        w = (w + 0x1f) >> 5 << 5;
      }
      if (h > maxh) maxh = h;
      if (x + w >= (cw << 5)) {
        x = 0;
        y += maxh;
        maxh = 0;
      }
      if (y + h >= ch) break;
      c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio);
      if (d.rotate) c.rotate(d.rotate * cloudRadians);
      c.fillText(d.text, 0, 0);
      if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0);
      c.restore();
      d.width = w;
      d.height = h;
      d.xoff = x;
      d.yoff = y;
      d.x1 = w >> 1;
      d.y1 = h >> 1;
      d.x0 = -d.x1;
      d.y0 = -d.y1;
      d.hasText = true;
      x += w;
    }
    var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data,
        sprite = [];
    while (--di >= 0) {
      d = data[di];
      if (!d.hasText) continue;
      var w = d.width,
          w32 = w >> 5,
          h = d.y1 - d.y0;
      // Zero the buffer
      for (var i = 0; i < h * w32; i++) sprite[i] = 0;
      x = d.xoff;
      if (x == null) return;
      y = d.yoff;
      var seen = 0,
          seenRow = -1;
      for (var j = 0; j < h; j++) {
        for (var i = 0; i < w; i++) {
          var k = w32 * j + (i >> 5),
              m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0;
          sprite[k] |= m;
          seen |= m;
        }
        if (seen) seenRow = j;
        else {
          d.y0++;
          h--;
          j--;
          y++;
        }
      }
      d.y1 = d.y0 + seenRow;
      d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32);
    }
  }

  // Use mask-based collision detection.
  function cloudCollide(tag, board, sw) {
    sw >>= 5;
    var sprite = tag.sprite,
        w = tag.width >> 5,
        lx = tag.x - (w << 4),
        sx = lx & 0x7f,
        msx = 32 - sx,
        h = tag.y1 - tag.y0,
        x = (tag.y + tag.y0) * sw + (lx >> 5),
        last;
    for (var j = 0; j < h; j++) {
      last = 0;
      for (var i = 0; i <= w; i++) {
        if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0))
            & board[x + i]) return true;
      }
      x += sw;
    }
    return false;
  }

  function cloudBounds(bounds, d) {
    var b0 = bounds[0],
        b1 = bounds[1];
    if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0;
    if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0;
    if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1;
    if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1;
  }

  function collideRects(a, b) {
    return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y;
  }

  function archimedeanSpiral(size) {
    var e = size[0] / size[1];
    return function(t) {
      return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)];
    };
  }

  function rectangularSpiral(size) {
    var dy = 4,
        dx = dy * size[0] / size[1],
        x = 0,
        y = 0;
    return function(t) {
      var sign = t < 0 ? -1 : 1;
      // See triangular numbers: T_n = n * (n + 1) / 2.
      switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) {
        case 0:  x += dx; break;
        case 1:  y += dy; break;
        case 2:  x -= dx; break;
        default: y -= dy; break;
      }
      return [x, y];
    };
  }

  // TODO reuse arrays?
  function zeroArray(n) {
    var a = [],
        i = -1;
    while (++i < n) a[i] = 0;
    return a;
  }

  var cloudRadians = Math.PI / 180,
      cw = 1 << 11 >> 5,
      ch = 1 << 11,
      canvas,
      ratio = 1;

  if (typeof document !== "undefined") {
    canvas = document.createElement("canvas");
    canvas.width = 1;
    canvas.height = 1;
    ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2);
    canvas.width = (cw << 5) / ratio;
    canvas.height = ch / ratio;
  } else {
    // node-canvas support
    var Canvas = require("canvas");
    canvas = new Canvas(cw << 5, ch);
  }

  var c = canvas.getContext("2d"),
      spirals = {
        archimedean: archimedeanSpiral,
        rectangular: rectangularSpiral
      };
  c.fillStyle = c.strokeStyle = "red";
  c.textAlign = "center";

  exports.cloud = cloud;
})(typeof exports === "undefined" ? d3.layout || (d3.layout = {}) : exports);
<|endoftext|>


<commit_before>/**
 * @file:   Implementation of Myers longest common subsequence algorithm using
 *          an edit graph.
 * @see:
 * * http://dx.doi.org/10.1007/BF01840446
 * * http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
 */

(function(exports){
    function Editgraph(a, b, x, y, N, M, dmax) {
        this.a = a;
        this.b = b;

        this.x = (typeof x === 'undefined') ? 0 : x;
        this.y = (typeof y === 'undefined') ? 0 : y;
        this.N = (typeof N === 'undefined') ? a.length : N;
        this.M = (typeof M === 'undefined') ? b.length : M;
        this.dmax = (typeof dmax === 'undefined') ? this.N + this.M : dmax;
    }

    Editgraph.prototype.equals = function(a, b) {
        return (a === b);
    };

    /**
     * Call a function for each D-Path identified using myers basic greedy lcs
     * algorithm.
     *
     * Returns the length of the shortest edit script, i.e. the minimal number
     * of insert and delete operations required to turn a into b.
     *
     * @param onpath function(dpath, precedessor)
     */
    Editgraph.prototype.forEachDpath = function (onpath, T) {
        var d, k, path, prev, end;
        var V = {};

        V[1] = new Dpath(Dpath.FORWARD, 1, 0);
        for (d = 0; d <= this.dmax; d++) {
            for (k = -d; k <= d; k+=2) {
                path = new Dpath(Dpath.FORWARD, k);
                prev = this.extendDpathFw(path, -d, d, V);
                if (onpath) {
                    onpath.call(T, path, prev, d);
                }

                // check if we are done
                end = path.getSnakeEnd();
                if (end.x >= this.N && end.y >= this.M) {
                    return d;
                }
            }
        }
    };

    /**
     * Call a function for each reverse D-Path identified using myers basic
     * greedy lcs algorithm.
     *
     * Returns the length of the shortest edit script, i.e. the minimal number
     * of insert and delete operations required to turn a into b.
     *
     * @param onpath function(dpath, precedessor)
     */
    Editgraph.prototype.forEachReverseDpath = function (onpath, T) {
        var delta = this.N - this.M;
        var d, k, path, prev, end;
        var V = {};

        V[delta-1] = new Dpath(Dpath.BACKWARD, delta-1, this.N);
        for (d = 0; d <= this.dmax; d++) {
            for (k = -d+delta; k <= d+delta; k+=2) {
                path = new Dpath(Dpath.BACKWARD, k);
                prev = this.extendDpathBw(path, -d+delta, d+delta, V);
                if (onpath) {
                    onpath.call(T, path, prev, d);
                }

                // check if we are done
                end = path.getSnakeEnd();
                if (end.x <= 0 && end.y <= 0) {
                    return d;
                }
            }
        }
    };

    Editgraph.prototype.extendDpathFw = function(path, kmin, kmax, V) {
        var prev;
        var k = path.k;

        // Determine the preceeding d-path segment. Pick the one whose furthest
        // reaching x value is greatest.
        if (k === kmin || (k !== kmax && V[k-1].x < V[k+1].x)) {
            // Furthest reaching d-path segment is above (k+1), move down.
            prev = V[k+1];
            path.xo = prev.x;
        }
        else {
            // Furthest reaching d-path segment is left (k-1), move right.
            prev = V[k-1];
            path.xo = prev.x+1;
        }

        // Compute the snake: Move along the diagonal as long as there are
        // common values in a and b.
        path.x = path.xo;
        while (path.x < this.N && path.x - k < this.M && this.equals(
                    this.a[this.x + path.x], this.b[this.y + path.x - k])) {
            path.x++;
        }

        // Memozie computed path
        V[k] = path;

        // Return preceeding path
        return prev;
    };

    Editgraph.prototype.extendDpathBw = function(path, kmin, kmax, V) {
        var prev;
        var k = path.k;

        // Determine the preceeding d-path segment. Pick the one whose furthest
        // reaching x value is greatest.
        if (k === kmax || (k !== kmin && V[k-1].x < V[k+1].x)) {
            // Furthest reaching d-path segment is underneath (k-1), move up.
            prev = V[k-1];
            path.xo = prev.x;
        }
        else {
            // Furthest reaching d-path segment is left (k-1), move right.
            prev = V[k+1];
            path.xo = prev.x-1;
        }

        // Compute the snake: Move along the diagonal as long as there are
        // common values in a and b.
        path.x = path.xo;
        while (path.x > 0 && path.x - k > 0 && this.equals(
                    this.a[this.x + path.x - 1], this.b[this.y + path.x - k - 1])) {
            path.x--;
        }

        // Memozie computed path
        V[k] = path;

        // Return preceeding path
        return prev;
    };

    /**
     * Middle snake
     */
    Editgraph.prototype.middleSnake = function (onpath) {
        var delta = this.N - this.M;
        var d, k;
        var Vf = {'1': 0};
        var Vb = {'1': 0};
        var result;

        for (d = 0; d <= this.dmax/2; d++) {
            for (k = -d; k <= d; k+=2) {
                result = this.extendDpathFw(k, -d, d, Vf, function(x, x0, k0) {
                    var y = x-k;
                    var y0 = x0-k;

                    if (onpath) onpath(d, x, k, x0, k0);

                    // check for overlap
                    if (delta % 2 === 1 && (k === delta - (d-1) || k === delta + (d+1))) {
                        if (Vf[k] >= Vb[k]) {
                            // return last forward snake
                            return [this.x + x, this.y + y, this.x + x0, this.y + y0]
                        }
                    }
                });
                if (result) {
                    return result;
                }
            }

            for (k = -d; k <= d; k+=2) {
                result = this.extendDpathBw(k+delta, -d+delta, d+delta, Vb, function(d, x, k, x0, k0) {
                    var y = x-k;
                    var y0 = x0-k;

                    if (onpath) onpath(d, x, k, x0, k0);

                    // check for overlap
                    if (delta % 2 === 0 && (k + delta === -d || k + delta === d)) {
                        if (Vf[k+delta] >= Vb[k+delta]) {
                            // return last forward snake
                            return [this.x + x, this.y + y, this.x + x0, this.y + y0]
                        }
                    }
                });
                if (result) {
                    return result;
                }
            }
        }
    }

    /**
     * Calculates the longest common subsequence from arrays a and b. Returns the
     * length of the shortest edit script, i.e. the minimal number of insert and
     * delete operations required to turn a into b.
     */
    Editgraph.prototype.editgraph_simple = function (builder) {
        var d;

        d = this.forEachDpath(function(dpath) {
            var vertical = (k0 > k);
            if (vertical) {
                builder.down(x0, x0-k);
            }
            else {
                builder.right(x0, x0-k);
            }

            while(x0++ < x) {
                builder.diag(x0, x0-k);
            }
        });

        return d;
    };

    /**
     * Calculates the longest common subsequence of a and b.
     */
    Editgraph.prototype.lcs_simple = function () {
        var paths = [];
        var last_path;
        var path;
        var result;
        var x;

        this.forEachDpath(function(dpath) {
            var prev_path = paths[k0];
            last_path = {
                'prev'  : prev_path,
                'x0'    : x0,
                'x'     : x,
            };
            paths[k] = last_path;
        });

        if (last_path.x !== this.a.length) {
            throw new Error('Programming error: end-point of last path must match the length of the input array.\n');
        }

        result = [];
        for (path = last_path; path; path = path.prev) {
            for (x=path.x; x > path.x0; x--) {
                result.unshift(this.a[x-1]);
            }
        }

        return result;
    };

    /**
     * Create the shortest edit script turning a into b. Editor should be an
     * object with the following two methods:
     *
     * * insert(idx, symbol)
     * * remove(idx)
     */
    Editgraph.prototype.ses_simple = function (editor) {
        var paths = [];
        var last_path;
        var path;

        this.forEachDpath(function(d, x, k, x0, k0) {
            var prev_path = paths[k0];
            last_path = {
                'prev'  : prev_path,
                'insert': (k0 > k),
                'x0'    : x0,
                'y0'    : x0-k,
            };
            paths[k] = last_path;
        });

        result = [];
        for (path = last_path; path.prev; path = path.prev) {
            if (path.insert) {
                editor.insert(path.x0, this.b[path.y0-1]);
            }
            else {
                editor.remove(path.x0-1);
            }
        }
    };

    /**
     * Dpath
     */
    function Dpath(dir, k, x, xo) {
        /**
         * The direction of this Dpath.
         */
        this.dir = dir;

        /**
         * The k-line on which the snake-part is located at.
         */
        this.k = k;

        /**
         * Furthest reaching x coordinate, when dir=FORWARD x >= xo otherwise
         * x <= xo. X marks the end of the snake.
         */
        this.x = x;

        /**
         * X coordinate of the operation and at the same time the other end of
         * the snake.
         */
        this.xo = (typeof xo === 'undefined') ? x : xo;

        /**
         * The precedessor of this Dpath.
         */
        this.previous = undefined;
    }

    Dpath.FORWARD = 1;
    Dpath.BACKWARD = -1;

    /**
     * Call appropriate editor commands in order to turn a into b.
     *
     * Editor should implement:
     * * remove(seq, idx, symbol);
     * * delete(seq, idx, symbol);
     * * skip(seq, idx, symbol);
     */
    Dpath.prototype.edit = function(editor, a, b, edit_dir, T) {
    };

    /**
     * Call callback for each (x,y) representing a symbol which both of the
     * sequences have in common. Proceed from the point of the snake which is
     * nearest to (0,0) to the point of the snake which is nearest to (N,M).
     */
    Dpath.prototype.forEachCommonSymbolForward = function(callback, T) {
        var end = this.getSnakeHigh();
        var cur = this.getSnakeLow();

        for (cur.x++, cur.y++; cur.x <= end.x && cur.y <= end.y; cur.x++, cur.y++) {
            callback.call(T, cur.x, cur.y);
        }
    };

    /**
     * Call callback for each (x,y) representing a symbol which both of the
     * sequences have in common. Proceed from the point of the snake which is
     * nearest to (N,M) to the point of the snake which is nearest to (0,0).
     */
    Dpath.prototype.forEachCommonSymbolBackward = function(callback, T) {
        var end = this.getSnakeLow();
        var cur;

        for (cur = this.getSnakeHigh(); cur.x > end.x && cur.y > end.y; cur.x--, cur.y--) {
            callback.call(T, cur.x, cur.y);
        }
    };

    /**
     * Call appropriate plotting commands in order to visualize the Dpath.
     */
    Dpath.prototype.forEachEdge = function(callback, T) {
        // Orthogonal line (corresponds to insert/remove operations)
        if (this.previous) {
            callback.call(T, this.previous.getSnakeEnd(), this.getSnakeStart());
        }
        // Diagonal line (corresponds to skip operations/equal symbols)
        if (this.x !== this.xo) {
            callback.call(T, this.getSnakeStart(), this.getSnakeEnd());
        }
    };

    /**
     * Return the start of the snake of this d-path.
     *
     * This point also represents the point of action for operations.
     */
    Dpath.prototype.getSnakeStart = function() {
        return {
            'x': this.xo,
            'y': this.xo-this.k
        };
    };

    /**
     * Return the point of the d-path which is furthest away from the origin of
     * the current direction.
     *
     * For forward-paths this returns the point which is furthest away from
     * (0,0), for reverse-paths (N,M).
     */
    Dpath.prototype.getSnakeEnd = function() {
        return {
            'x': this.x,
            'y': this.x-this.k
        };
    };

    /**
     * Return the point of the d-path which is nearest to (0,0)
     */
    Dpath.prototype.getSnakeLow = function() {
        return (this.dir === Dpath.FORWARD) ? this.getSnakeStart() : this.getSnakeEnd();
    }

    /**
     * Return the point of the d-path which is furthest away from (0,0)
     */
    Dpath.prototype.getSnakeHigh = function() {
        return (this.dir === Dpath.FORWARD) ? this.getSnakeEnd() : this.getSnakeStart();
    }

    /**
     * Set the the given dpath as the predecessor of this one.
     */
    Dpath.prototype.prepend = function(predecessor) {
        if (predecessor.dir !== this.dir) {
            throw new Error('Directions must match');
        }
        this.previous = predecessor;
    };

    // CommonJS exports
    exports.Editgraph = Editgraph;
    exports.Dpath = Dpath;

}(typeof exports === 'undefined' ? (this.editgraph={}) : exports));
<commit_msg>Convert ses_simple, lcs_simple, editgraph_simple<commit_after>/**
 * @file:   Implementation of Myers longest common subsequence algorithm using
 *          an edit graph.
 * @see:
 * * http://dx.doi.org/10.1007/BF01840446
 * * http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
 */

(function(exports){
    function Editgraph(a, b, x, y, N, M, dmax) {
        this.a = a;
        this.b = b;

        this.x = (typeof x === 'undefined') ? 0 : x;
        this.y = (typeof y === 'undefined') ? 0 : y;
        this.N = (typeof N === 'undefined') ? a.length : N;
        this.M = (typeof M === 'undefined') ? b.length : M;
        this.dmax = (typeof dmax === 'undefined') ? this.N + this.M : dmax;
    }

    Editgraph.prototype.equals = function(a, b) {
        return (a === b);
    };

    /**
     * Call a function for each D-Path identified using myers basic greedy lcs
     * algorithm.
     *
     * Returns the length of the shortest edit script, i.e. the minimal number
     * of insert and delete operations required to turn a into b.
     *
     * @param onpath function(dpath, precedessor)
     */
    Editgraph.prototype.forEachDpath = function (onpath, T) {
        var d, k, path, prev, end;
        var V = {};

        V[1] = new Dpath(Dpath.FORWARD, 1, 0);
        for (d = 0; d <= this.dmax; d++) {
            for (k = -d; k <= d; k+=2) {
                path = new Dpath(Dpath.FORWARD, k);
                prev = this.extendDpathFw(path, -d, d, V);
                if (onpath) {
                    onpath.call(T, path, prev, d);
                }

                // check if we are done
                end = path.getSnakeEnd();
                if (end.x >= this.N && end.y >= this.M) {
                    return d;
                }
            }
        }
    };

    /**
     * Call a function for each reverse D-Path identified using myers basic
     * greedy lcs algorithm.
     *
     * Returns the length of the shortest edit script, i.e. the minimal number
     * of insert and delete operations required to turn a into b.
     *
     * @param onpath function(dpath, precedessor)
     */
    Editgraph.prototype.forEachReverseDpath = function (onpath, T) {
        var delta = this.N - this.M;
        var d, k, path, prev, end;
        var V = {};

        V[delta-1] = new Dpath(Dpath.BACKWARD, delta-1, this.N);
        for (d = 0; d <= this.dmax; d++) {
            for (k = -d+delta; k <= d+delta; k+=2) {
                path = new Dpath(Dpath.BACKWARD, k);
                prev = this.extendDpathBw(path, -d+delta, d+delta, V);
                if (onpath) {
                    onpath.call(T, path, prev, d);
                }

                // check if we are done
                end = path.getSnakeEnd();
                if (end.x <= 0 && end.y <= 0) {
                    return d;
                }
            }
        }
    };

    Editgraph.prototype.extendDpathFw = function(path, kmin, kmax, V) {
        var prev;
        var k = path.k;

        // Determine the preceeding d-path segment. Pick the one whose furthest
        // reaching x value is greatest.
        if (k === kmin || (k !== kmax && V[k-1].x < V[k+1].x)) {
            // Furthest reaching d-path segment is above (k+1), move down.
            prev = V[k+1];
            path.xo = prev.x;
        }
        else {
            // Furthest reaching d-path segment is left (k-1), move right.
            prev = V[k-1];
            path.xo = prev.x+1;
        }

        // Compute the snake: Move along the diagonal as long as there are
        // common values in a and b.
        path.x = path.xo;
        while (path.x < this.N && path.x - k < this.M && this.equals(
                    this.a[this.x + path.x], this.b[this.y + path.x - k])) {
            path.x++;
        }

        // Memozie computed path
        V[k] = path;

        // Return preceeding path
        return prev;
    };

    Editgraph.prototype.extendDpathBw = function(path, kmin, kmax, V) {
        var prev;
        var k = path.k;

        // Determine the preceeding d-path segment. Pick the one whose furthest
        // reaching x value is greatest.
        if (k === kmax || (k !== kmin && V[k-1].x < V[k+1].x)) {
            // Furthest reaching d-path segment is underneath (k-1), move up.
            prev = V[k-1];
            path.xo = prev.x;
        }
        else {
            // Furthest reaching d-path segment is left (k-1), move right.
            prev = V[k+1];
            path.xo = prev.x-1;
        }

        // Compute the snake: Move along the diagonal as long as there are
        // common values in a and b.
        path.x = path.xo;
        while (path.x > 0 && path.x - k > 0 && this.equals(
                    this.a[this.x + path.x - 1], this.b[this.y + path.x - k - 1])) {
            path.x--;
        }

        // Memozie computed path
        V[k] = path;

        // Return preceeding path
        return prev;
    };

    /**
     * Middle snake
     */
    Editgraph.prototype.middleSnake = function (onpath) {
        var delta = this.N - this.M;
        var d, k;
        var Vf = {'1': 0};
        var Vb = {'1': 0};
        var result;

        for (d = 0; d <= this.dmax/2; d++) {
            for (k = -d; k <= d; k+=2) {
                result = this.extendDpathFw(k, -d, d, Vf, function(x, x0, k0) {
                    var y = x-k;
                    var y0 = x0-k;

                    if (onpath) onpath(d, x, k, x0, k0);

                    // check for overlap
                    if (delta % 2 === 1 && (k === delta - (d-1) || k === delta + (d+1))) {
                        if (Vf[k] >= Vb[k]) {
                            // return last forward snake
                            return [this.x + x, this.y + y, this.x + x0, this.y + y0]
                        }
                    }
                });
                if (result) {
                    return result;
                }
            }

            for (k = -d; k <= d; k+=2) {
                result = this.extendDpathBw(k+delta, -d+delta, d+delta, Vb, function(d, x, k, x0, k0) {
                    var y = x-k;
                    var y0 = x0-k;

                    if (onpath) onpath(d, x, k, x0, k0);

                    // check for overlap
                    if (delta % 2 === 0 && (k + delta === -d || k + delta === d)) {
                        if (Vf[k+delta] >= Vb[k+delta]) {
                            // return last forward snake
                            return [this.x + x, this.y + y, this.x + x0, this.y + y0]
                        }
                    }
                });
                if (result) {
                    return result;
                }
            }
        }
    }

    /**
     * Calculates the longest common subsequence from arrays a and b. Returns the
     * length of the shortest edit script, i.e. the minimal number of insert and
     * delete operations required to turn a into b.
     */
    Editgraph.prototype.editgraph_simple = function (builder) {
        var d;

        d = this.forEachDpath(function(path, prev) {
            var vertical = (prev.k > path.k);
            var start = path.getSnakeStart();
            if (vertical) {
                builder.down(start.x, start.y);
            }
            else {
                builder.right(start.x, start.y);
            }

            path.forEachCommonSymbolForward(function(x, y) {
                builder.diag(x, y);
            });
        });

        return d;
    };

    /**
     * Calculates the longest common subsequence of a and b.
     */
    Editgraph.prototype.lcs_simple = function () {
        var last_path;
        var path;
        var result;
        var x;

        this.forEachDpath(function(path, prev) {
            if (prev) {
                path.prepend(prev);
            }
            last_path = path;
        });

        if (last_path.x !== this.a.length) {
            throw new Error('Programming error: end-point of last path must match the length of the input array.\n');
        }

        result = [];
        for (path = last_path; path; path = path.previous) {
            for (x=path.x; x > path.xo; x--) {
                result.unshift(this.a[x-1]);
            }
        }

        return result;
    };

    /**
     * Create the shortest edit script turning a into b. Editor should be an
     * object with the following two methods:
     *
     * * insert(idx, symbol)
     * * remove(idx)
     */
    Editgraph.prototype.ses_simple = function (editor) {
        var last_path;
        var path;
        var pos;

        this.forEachDpath(function(dpath, prev) {
            if (prev) {
                dpath.prepend(prev);
            }
            last_path = dpath;
        });

        result = [];
        for (path = last_path; path.previous; path = path.previous) {
            pos = path.getSnakeStart();
            if (path.previous.k > path.k) {
                editor.insert(pos.x, this.b[pos.y-1]);
            }
            else {
                editor.remove(pos.x-1);
            }
        }
    };

    /**
     * Dpath
     */
    function Dpath(dir, k, x, xo) {
        /**
         * The direction of this Dpath.
         */
        this.dir = dir;

        /**
         * The k-line on which the snake-part is located at.
         */
        this.k = k;

        /**
         * Furthest reaching x coordinate, when dir=FORWARD x >= xo otherwise
         * x <= xo. X marks the end of the snake.
         */
        this.x = x;

        /**
         * X coordinate of the operation and at the same time the other end of
         * the snake.
         */
        this.xo = (typeof xo === 'undefined') ? x : xo;

        /**
         * The precedessor of this Dpath.
         */
        this.previous = undefined;
    }

    Dpath.FORWARD = 1;
    Dpath.BACKWARD = -1;

    /**
     * Call appropriate editor commands in order to turn a into b.
     *
     * Editor should implement:
     * * remove(seq, idx, symbol);
     * * delete(seq, idx, symbol);
     * * skip(seq, idx, symbol);
     */
    Dpath.prototype.edit = function(editor, a, b, edit_dir, T) {
    };

    /**
     * Call callback for each (x,y) representing a symbol which both of the
     * sequences have in common. Proceed from the point of the snake which is
     * nearest to (0,0) to the point of the snake which is nearest to (N,M).
     */
    Dpath.prototype.forEachCommonSymbolForward = function(callback, T) {
        var end = this.getSnakeHigh();
        var cur = this.getSnakeLow();

        for (cur.x++, cur.y++; cur.x <= end.x && cur.y <= end.y; cur.x++, cur.y++) {
            callback.call(T, cur.x, cur.y);
        }
    };

    /**
     * Call callback for each (x,y) representing a symbol which both of the
     * sequences have in common. Proceed from the point of the snake which is
     * nearest to (N,M) to the point of the snake which is nearest to (0,0).
     */
    Dpath.prototype.forEachCommonSymbolBackward = function(callback, T) {
        var end = this.getSnakeLow();
        var cur;

        for (cur = this.getSnakeHigh(); cur.x > end.x && cur.y > end.y; cur.x--, cur.y--) {
            callback.call(T, cur.x, cur.y);
        }
    };

    /**
     * Call appropriate plotting commands in order to visualize the Dpath.
     */
    Dpath.prototype.forEachEdge = function(callback, T) {
        // Orthogonal line (corresponds to insert/remove operations)
        if (this.previous) {
            callback.call(T, this.previous.getSnakeEnd(), this.getSnakeStart());
        }
        // Diagonal line (corresponds to skip operations/equal symbols)
        if (this.x !== this.xo) {
            callback.call(T, this.getSnakeStart(), this.getSnakeEnd());
        }
    };

    /**
     * Return the start of the snake of this d-path.
     *
     * This point also represents the point of action for operations.
     */
    Dpath.prototype.getSnakeStart = function() {
        return {
            'x': this.xo,
            'y': this.xo-this.k
        };
    };

    /**
     * Return the point of the d-path which is furthest away from the origin of
     * the current direction.
     *
     * For forward-paths this returns the point which is furthest away from
     * (0,0), for reverse-paths (N,M).
     */
    Dpath.prototype.getSnakeEnd = function() {
        return {
            'x': this.x,
            'y': this.x-this.k
        };
    };

    /**
     * Return the point of the d-path which is nearest to (0,0)
     */
    Dpath.prototype.getSnakeLow = function() {
        return (this.dir === Dpath.FORWARD) ? this.getSnakeStart() : this.getSnakeEnd();
    }

    /**
     * Return the point of the d-path which is furthest away from (0,0)
     */
    Dpath.prototype.getSnakeHigh = function() {
        return (this.dir === Dpath.FORWARD) ? this.getSnakeEnd() : this.getSnakeStart();
    }

    /**
     * Set the the given dpath as the predecessor of this one.
     */
    Dpath.prototype.prepend = function(predecessor) {
        if (predecessor.dir !== this.dir) {
            throw new Error('Directions must match');
        }
        this.previous = predecessor;
    };

    // CommonJS exports
    exports.Editgraph = Editgraph;
    exports.Dpath = Dpath;

}(typeof exports === 'undefined' ? (this.editgraph={}) : exports));
<|endoftext|>


<commit_before>var _ = require('lodash');
var BPromise = require('bluebird');
var mongodb = BPromise.promisifyAll(require('mongodb'));
BPromise.promisifyAll(mongodb.Cursor.prototype);

var objectifyID = function( obj ) {
	if ( obj._id && !(obj._id instanceof mongodb.ObjectID) )
	{
		if ( obj._id.length != 12 && obj._id.length != 24 )
			throw new Error("Invalid object ID " + obj._id );
		obj._id = mongodb.ObjectID(obj._id);
	}
};
var stringifyID = function( obj ) {
	if ( obj._id && (obj._id instanceof mongodb.ObjectID) )
		obj._id = obj._id.toString();
};

var MongoCursor = function(original) {
	this.original = original;
};
MongoCursor.prototype.limit = function(count) {
	this.original.limit(count);
	return this;
};
MongoCursor.prototype.skip = function(count) {
	this.original.skip(count);
	return this;
};
MongoCursor.prototype.sort = function(order) {
	this.original.sort(order);
	return this;
};
MongoCursor.prototype.toArray = function(callback) {
	return this.original.toArrayAsync()
	.then(function(arr) {
		_.forEach(arr, function(obj) {
			stringifyID(obj);
		});
		return arr;
	})
	.nodeify(callback);
};
MongoCursor.prototype.stream = function() {
	var stream = this.original.stream();
	stream.on('data', function(obj) {
		stringifyID(obj);
	});
	return stream;
};

var MongoCollection = function(engine, name) {
	this.db = engine.db.collection(name);
};

MongoCollection.prototype.findOne = function(query, projection, callback) {
	objectifyID( query );
	return this.db.findOneAsync(query, projection)
		.then(function(item) {
			if ( item )
				stringifyID(item);
			return item;
		})
		.nodeify(callback);
};
MongoCollection.prototype.find = function(query, projection) {
	objectifyID( query );
	return new MongoCursor( this.db.find(query, projection) );
};
MongoCollection.prototype.insert = function(obj, opt, callback) {
	return this.db.insertAsync(obj, opt)
		.then(function(arr) {
			_.forEach(arr, function(item) {
				stringifyID(item);
			});
			return arr;
		})
		.nodeify(callback);
};
MongoCollection.prototype.update = function(query, obj, opt, callback) {
	objectifyID( query );
	return this.db.updateAsync(query, obj, opt)
		.nodeify(callback);
};
MongoCollection.prototype.remove = function(query, opt, callback) {
	objectifyID( query );
	console.log(query);
	return this.db.removeAsync(query, opt)
		.then(function(result) {
			return result[0];
		})
		.nodeify(callback);
};

var MongoEngine = function(options) {
	this.options = _.clone(options);
	this.db = this.options.db || null;
	this.collections = [];
};
MongoEngine.prototype.connect = function() {
	console.log('connect');
	if ( this.db )
		return BPromise.delay(this, 1);

	this.db = new mongodb.Db(this.options.dbname, new mongodb.Server(this.options.host, this.options.port), {safe:true});
	return this.db.openAsync()
	.catch(function(e) {
		throw new Error("Failed to connect to MongoDB.");
	})
	.bind(this)
	.then(function(db) {
		BPromise.map( _.keys(this.options.resources), function(colName) {
		  return db.collectionAsync(colName, {strict:true})
		  .catch( function(e) {
		  	return db.createCollectionAsync(colName);
		  })
		  .catch( function(e) {
		  	throw new Error("Failed to create collection '" + colName + "'.");
		  });
		})
		.then(function() {
			console.log('connected');
		})
		.return(this);
	});
};
MongoEngine.prototype.collection = function(name) {
	if ( !this.collections[name] )
		this.collections[name] = new MongoCollection(this, name);
	return this.collections[name];
};

module.exports = function(options) {
	return new MongoEngine(options);
};<commit_msg>Try another delete workaround.<commit_after>var _ = require('lodash');
var BPromise = require('bluebird');
var mongodb = BPromise.promisifyAll(require('mongodb'));
BPromise.promisifyAll(mongodb.Cursor.prototype);

var objectifyID = function( obj ) {
	if ( obj._id && !(obj._id instanceof mongodb.ObjectID) )
	{
		if ( obj._id.length != 12 && obj._id.length != 24 )
			throw new Error("Invalid object ID " + obj._id );
		obj._id = mongodb.ObjectID(obj._id);
	}
};
var stringifyID = function( obj ) {
	if ( obj._id && (obj._id instanceof mongodb.ObjectID) )
		obj._id = obj._id.toString();
};

var MongoCursor = function(original) {
	this.original = original;
};
MongoCursor.prototype.limit = function(count) {
	this.original.limit(count);
	return this;
};
MongoCursor.prototype.skip = function(count) {
	this.original.skip(count);
	return this;
};
MongoCursor.prototype.sort = function(order) {
	this.original.sort(order);
	return this;
};
MongoCursor.prototype.toArray = function(callback) {
	return this.original.toArrayAsync()
	.then(function(arr) {
		_.forEach(arr, function(obj) {
			stringifyID(obj);
		});
		return arr;
	})
	.nodeify(callback);
};
MongoCursor.prototype.stream = function() {
	var stream = this.original.stream();
	stream.on('data', function(obj) {
		stringifyID(obj);
	});
	return stream;
};

var MongoCollection = function(engine, name) {
	this.db = engine.db.collection(name);
};

MongoCollection.prototype.findOne = function(query, projection, callback) {
	objectifyID( query );
	return this.db.findOneAsync(query, projection)
		.then(function(item) {
			if ( item )
				stringifyID(item);
			return item;
		})
		.nodeify(callback);
};
MongoCollection.prototype.find = function(query, projection) {
	objectifyID( query );
	return new MongoCursor( this.db.find(query, projection) );
};
MongoCollection.prototype.insert = function(obj, opt, callback) {
	return this.db.insertAsync(obj, opt)
		.then(function(arr) {
			_.forEach(arr, function(item) {
				stringifyID(item);
			});
			return arr;
		})
		.nodeify(callback);
};
MongoCollection.prototype.update = function(query, obj, opt, callback) {
	objectifyID( query );
	return this.db.updateAsync(query, obj, opt)
		.nodeify(callback);
};
MongoCollection.prototype.remove = function(query, opt, callback) {
	objectifyID( query );
	console.log(query);
	return this.db.removeAsync(query, opt)
		.then(function(result) {
			console.log(result);
			if _.isArray(result)
				result = result[0];
			return result;
		})
		.nodeify(callback);
};

var MongoEngine = function(options) {
	this.options = _.clone(options);
	this.db = this.options.db || null;
	this.collections = [];
};
MongoEngine.prototype.connect = function() {
	console.log('connect');
	if ( this.db )
		return BPromise.delay(this, 1);

	this.db = new mongodb.Db(this.options.dbname, new mongodb.Server(this.options.host, this.options.port), {safe:true});
	return this.db.openAsync()
	.catch(function(e) {
		throw new Error("Failed to connect to MongoDB.");
	})
	.bind(this)
	.then(function(db) {
		BPromise.map( _.keys(this.options.resources), function(colName) {
		  return db.collectionAsync(colName, {strict:true})
		  .catch( function(e) {
		  	return db.createCollectionAsync(colName);
		  })
		  .catch( function(e) {
		  	throw new Error("Failed to create collection '" + colName + "'.");
		  });
		})
		.then(function() {
			console.log('connected');
		})
		.return(this);
	});
};
MongoEngine.prototype.collection = function(name) {
	if ( !this.collections[name] )
		this.collections[name] = new MongoCollection(this, name);
	return this.collections[name];
};

module.exports = function(options) {
	return new MongoEngine(options);
};<|endoftext|>


<commit_before>var contentPattern = '&&<>&&'

function contentFor(contentName) {
  return contentPattern + contentName + contentPattern
}

function parseContents(locals) {
  var str = locals.body
  var regex = new RegExp('\n?' + contentPattern + '.+?' + contentPattern + '\n?', 'g')
  var split = str.split(regex)
  var matches = str.match(regex)

  locals.body = split[0]
  var i = 1;

  if (matches != null) {
    matches.forEach(function(match) {
      var name = match.split(contentPattern)[1]
      locals[name] = split[i];
      i++;
    });
  }
}

function parseScripts(locals) {
  var str = locals.body
  locals.script = ''

  var regex = /\<script(.|\n)*?\>(.|\n)*?\<\/script\>/g
  if (regex.test(str)) {
    locals.body = str.replace(regex, '');
    locals.script = str.match(regex).join('\n');
  }
}

module.exports = exports = function(req, res, next) {
  var render = res.render;

  res.render = function(view, options, fn) {

    var options = options || {};

    // support callback function as second arg
    if ('function' == typeof options) {
      fn = options, options = {};
    }

    var app = req.app
    var defaultLayout = app.get('layout')
    var specifiedLayout = options.layout

    if (specifiedLayout === false || ((specifiedLayout || defaultLayout) === false) ) {
      render.call(res, view, options, fn)
      return
    }

    var layout = specifiedLayout || defaultLayout
    if (layout === true || layout === undefined)
      layout = 'layout'

    options.contentFor = contentFor

    var self = this;
    render.call(res, view, options, function(err, str){

      if (err) {
        if (fn)
          return fn(err)
        else
          throw err
      }

      var locals = { body: str }
      for (var l in options) {
        if (options.hasOwnProperty(l)
          && l != 'layout'
          && l != 'contentFor')
          locals[l] = options[l];
      }

      if (options.extractScripts === true
        || (options.extractScripts === undefined && app.get('layout extractScripts') === true))
        parseScripts(locals)

      parseContents(locals)

      render.call(self, layout, locals, fn);
    });
  };

  next();
};
<commit_msg>Fixes: === > ==, ; where needed, remove extra refrence and some lint cosmetics<commit_after>/*jslint sloppy:true indent:2 plusplus:true regexp:true*/

var contentPattern = '&&<>&&';

function contentFor(contentName) {
  return contentPattern + contentName + contentPattern;
}

function parseContents(locals) {
  var name, i = 1, str = locals.body,
    regex = new RegExp('\n?' + contentPattern + '.+?' + contentPattern + '\n?', 'g'),
    split = str.split(regex),
    matches = str.match(regex);

  locals.body = split[0];

  if (matches !== null) {
    matches.forEach(function (match) {
      name = match.split(contentPattern)[1];
      locals[name] = split[i];
      i++;
    });
  }
}

function parseScripts(locals) {
  var str = locals.body, regex = /\<script(.|\n)*?\>(.|\n)*?\<\/script\>/g;
  locals.script = '';

  if (regex.test(str)) {
    locals.body = str.replace(regex, '');
    locals.script = str.match(regex).join('\n');
  }
}

module.exports = function (req, res, next) {
  var render = res.render;

  res.render = function (view, options, fn) {
    var layout, self = this, app = req.app,
      defaultLayout = app.get('layout');

    options = options || {};
    if (typeof options === 'function') {
      fn = options;
      options = {};
    }

    if (options.layout === false || ((options.layout || defaultLayout) === false)) {
      render.call(res, view, options, fn);
      return;
    }

    layout = options.layout || defaultLayout;
    if (layout === true || layout === undefined) {
      layout = 'layout';
    }

    options.contentFor = contentFor;
    render.call(res, view, options, function (err, str) {
      var l, locals;

      if (err) { return fn ? fn(err) : next(err); }

      locals = { body: str };
      for (l in options) {
        if (options.hasOwnProperty(l) && l !== 'layout' && l !== 'contentFor') {
          locals[l] = options[l];
        }
      }

      if (options.extractScripts === true || (options.extractScripts === undefined && app.get('layout extractScripts') === true)) {
        parseScripts(locals);
      }

      parseContents(locals);
      render.call(self, layout, locals, fn);
    });
  };
  next();
};
<|endoftext|>