<commit_before>
process.title = "npm"

// FIXME there really ought to be a path.split in node core
require("path").SPLIT_CHAR = process.platform === "win32" ? "\\" : "/"

var EventEmitter = require("events").EventEmitter
  , npm = module.exports = new EventEmitter
  , config = require("./lib/config.js")
  , set = require("./lib/utils/set.js")
  , get = require("./lib/utils/get.js")
  , ini = require("./lib/utils/ini.js")
  , log = require("./lib/utils/log.js")
  , fs = require("graceful-fs")
  , path = require("path")
  , abbrev = require("abbrev")
  , which = require("which")
  , semver = require("semver")
  , findPrefix = require("./lib/utils/find-prefix.js")
  , getUid = require("./lib/utils/uid-number.js")
  , mkdir = require("./lib/utils/mkdir-p.js")

npm.commands = {}
npm.ELIFECYCLE = {}
npm.E404 = {}
npm.EPUBLISHCONFLICT = {}
npm.EJSONPARSE = {}
npm.EISGIT = {}
npm.ECYCLE = {}
npm.ENOTSUP = {}

// HACK for windows
if (process.platform === "win32") {
  // stub in unavailable methods from process and fs binding
  if (!process.getuid) process.getuid = function() {}
  if (!process.getgid) process.getgid = function() {}
  var fsBinding = process.binding("fs")
  if (!fsBinding.chown) fsBinding.chown = function() {
    var cb = arguments[arguments.length - 1]
    if (typeof cb == "function") cb()
  }

  // patch rename/renameSync, but this should really be fixed in node
  var _fsRename = fs.rename
    , _fsPathPatch
  _fsPathPatch = function(p) {
    return p && p.replace(/\\/g, "/") || p;
  }
  fs.rename = function(p1, p2) {
    arguments[0] = _fsPathPatch(p1)
    arguments[1] = _fsPathPatch(p2)
    return _fsRename.apply(fs, arguments);
  }
}

try {
  // startup, ok to do this synchronously
  var j = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"))+"")
  npm.version = j.version
  npm.nodeVersionRequired = j.engines.node
  if (!semver.satisfies(process.version, j.engines.node)) {
    log.error([""
              ,"npm requires node version: "+j.engines.node
              ,"And you have: "+process.version
              ,"which is not satisfactory."
              ,""
              ,"Bad things will likely happen.  You have been warned."
              ,""].join("\n"), "unsupported version")
  }
} catch (ex) {
  try {
    log(ex, "error reading version")
  } catch (er) {}
  npm.version = ex
}

var commandCache = {}
  // short names for common things
  , aliases = { "rm" : "uninstall"
              , "r" : "uninstall"
              , "un" : "uninstall"
              , "unlink" : "uninstall"
              , "remove" : "uninstall"
              , "rb" : "rebuild"
              , "list" : "ls"
              , "la" : "ls"
              , "ll" : "ls"
              , "ln" : "link"
              , "i" : "install"
              , "up" : "update"
              , "c" : "config"
              , "info" : "view"
              , "find" : "search"
              , "s" : "search"
              , "se" : "search"
              , "author" : "owner"
              , "home" : "docs"
              , "unstar": "star" // same function
              , "apihelp" : "help"
              }

  , aliasNames = Object.keys(aliases)
  // these are filenames in ./lib
  , cmdList = [ "install"
              , "uninstall"
              , "cache"
              , "config"
              , "set"
              , "get"
              , "update"
              , "outdated"
              , "prune"
              , "submodule"
              , "pack"

              , "rebuild"
              , "link"

              , "publish"
              , "star"
              , "tag"
              , "adduser"
              , "unpublish"
              , "owner"
              , "deprecate"

              , "help"
              , "help-search"
              , "ls"
              , "search"
              , "view"
              , "init"
              , "version"
              , "edit"
              , "explore"
              , "docs"
              , "bugs"
              , "faq"
              , "root"
              , "prefix"
              , "bin"
              , "whoami"

              , "test"
              , "stop"
              , "start"
              , "restart"
              , "run-script"
              , "completion"
              ]
  , plumbing = [ "build"
               , "unbuild"
               , "xmas"
               ]
  , fullList = npm.fullList = cmdList.concat(aliasNames).filter(function (c) {
      return plumbing.indexOf(c) === -1
    })
  , abbrevs = abbrev(fullList)

Object.keys(abbrevs).concat(plumbing).forEach(function addCommand (c) {
  Object.defineProperty(npm.commands, c, { get : function () {
    if (!loaded) throw new Error(
      "Call npm.load(conf, cb) before using this command.\n"+
      "See the README.md or cli.js for example usage.")
    var a = npm.deref(c)
    if (c === "la" || c === "ll") {
      npm.config.set("long", true)
    }
    npm.command = c
    if (commandCache[a]) return commandCache[a]
    var cmd = require(__dirname+"/lib/"+a+".js")
    commandCache[a] = function () {
      var args = Array.prototype.slice.call(arguments, 0)
      if (typeof args[args.length - 1] !== "function") {
        args.push(defaultCb)
      }
      if (args.length === 1) args.unshift([])
      cmd.apply(npm, args)
    }
    Object.keys(cmd).forEach(function (k) {
      commandCache[a][k] = cmd[k]
    })
    return commandCache[a]
  }, enumerable: fullList.indexOf(c) !== -1 })

  // make css-case commands callable via camelCase as well
  if (c.match(/\-([a-z])/)) {
    addCommand(c.replace(/\-([a-z])/g, function (a, b) {
      return b.toUpperCase()
    }))
  }
})

function defaultCb (er, data) {
  if (er) console.error(er.stack || er.message)
  else console.log(data)
}

npm.deref = function (c) {
  if (!c) return ""
  if (c.match(/[A-Z]/)) c = c.replace(/([A-Z])/g, function (m) {
    return "-" + m.toLowerCase()
  })
  if (plumbing.indexOf(c) !== -1) return c
  var a = abbrevs[c]
  if (aliases[a]) a = aliases[a]
  return a
}

var loaded = false
  , loading = false
  , loadErr = null
  , loadListeners = []

function loadCb (er) {
  loadListeners.forEach(function (cb) {
    process.nextTick(cb.bind(npm, er, npm))
  })
  loadListeners.length = 0
}

npm.load = function (conf, cb_) {
  if (!cb_ && typeof conf === "function") cb_ = conf , conf = {}
  if (!cb_) cb_ = function () {}
  if (!conf) conf = {}
  loadListeners.push(cb_)
  if (loaded || loadErr) return cb(loadErr)
  if (loading) return
  loading = true
  var onload = true

  function cb (er) {
    if (loadErr) return
    loaded = true
    loadCb(loadErr = er)
    if (onload = onload && npm.config.get("onload-script")) {
      require(onload)
      onload = false
    }
  }

  log.waitForConfig()

  load(npm, conf, cb)
}

function load (npm, conf, cb) {
  which(process.argv[0], function (er, node) {
    //console.error("back from which")
    if (!er && node.toUpperCase() !== process.execPath.toUpperCase()) {
      log.verbose("node symlink", node)
      process.execPath = node
      process.installPrefix = path.resolve(node, "..", "..")
    }

    // look up configs
    //console.error("about to look up configs")

    ini.resolveConfigs(conf, function (er) {
      //console.error("back from config lookup", er && er.stack)
      if (er) return cb(er)

      var n = 2
        , errState

      loadPrefix(npm, conf, next)
      loadUid(npm, conf, next)

      function next (er) {
        //console.error("next", er && er.stack)
        if (errState) return
        if (er) return cb(errState = er)
        if (-- n <= 0) return cb()
      }
    })
  })
}

function loadPrefix (npm, conf, cb) {
  // try to guess at a good node_modules location.
  var p
  if (!npm.config.get("global")
      && !conf.hasOwnProperty("prefix")) {
    p = process.cwd()
  } else {
    p = npm.config.get("prefix")
  }

  findPrefix(p, function (er, p) {
    //console.log("Back from findPrefix", er && er.stack, p)
    Object.defineProperty(npm, "prefix",
      { get : function () { return p }
      , set : function (r) { return p = r }
      , enumerable : true
      })
    // the prefix MUST exist, or else nothing works.
    mkdir(p, cb)
  })
}

function loadUid (npm, conf, cb) {
  // if we're not in unsafe-perm mode, then figure out who
  // to run stuff as.  Do this first, to support `npm update npm -g`
  if (!npm.config.get("unsafe-perm")) {
    getUid(npm.config.get("user"), npm.config.get("group"), cb)
  } else {
    //console.error("skipping loadUid")
    process.nextTick(cb)
  }
}

npm.config =
  { get : function (key) { return ini.get(key) }
  , set : function (key, val) { return ini.set(key, val, "cli") }
  , del : function (key, val) { return ini.del(key, val, "cli") }
  }

Object.defineProperty(npm, "dir",
  { get : function () {
      if (npm.config.get("global")) {
        return path.resolve(npm.prefix, "lib", "node_modules")
      } else {
        return path.resolve(npm.prefix, "node_modules")
      }
    }
  , enumerable : true
  })

Object.defineProperty(npm, "root",
  { get : function () { return npm.dir } })

Object.defineProperty(npm, "cache",
  { get : function () { return npm.config.get("cache") }
  , set : function (r) { return npm.config.set("cache", r) }
  , enumerable : true
  })

var tmpFolder
Object.defineProperty(npm, "tmp",
  { get : function () {
      if (!tmpFolder) tmpFolder = "npm-"+Date.now()
      return path.resolve(npm.config.get("tmp"), tmpFolder)
    }
  , enumerable : true
  })

// the better to repl you with
Object.getOwnPropertyNames(npm.commands).forEach(function (n) {
  if (npm.hasOwnProperty(n)) return

  Object.defineProperty(npm, n, { get: function () {
    return function () {
      var args = Array.prototype.slice.call(arguments, 0)
        , cb = defaultCb

      if (args.length === 1 && Array.isArray(args[0])) {
        args = args[0]
      }

      if (typeof args[args.length - 1] === "function") {
        cb = args.pop()
      }

      npm.commands[n](args, cb)
    }
  }, enumerable: false, configurable: true })
})

if (require.main === module) {
  require("./bin/npm.js")
}
<commit_msg>alias show->view for @guille<commit_after>
process.title = "npm"

// FIXME there really ought to be a path.split in node core
require("path").SPLIT_CHAR = process.platform === "win32" ? "\\" : "/"

var EventEmitter = require("events").EventEmitter
  , npm = module.exports = new EventEmitter
  , config = require("./lib/config.js")
  , set = require("./lib/utils/set.js")
  , get = require("./lib/utils/get.js")
  , ini = require("./lib/utils/ini.js")
  , log = require("./lib/utils/log.js")
  , fs = require("graceful-fs")
  , path = require("path")
  , abbrev = require("abbrev")
  , which = require("which")
  , semver = require("semver")
  , findPrefix = require("./lib/utils/find-prefix.js")
  , getUid = require("./lib/utils/uid-number.js")
  , mkdir = require("./lib/utils/mkdir-p.js")

npm.commands = {}
npm.ELIFECYCLE = {}
npm.E404 = {}
npm.EPUBLISHCONFLICT = {}
npm.EJSONPARSE = {}
npm.EISGIT = {}
npm.ECYCLE = {}
npm.ENOTSUP = {}

// HACK for windows
if (process.platform === "win32") {
  // stub in unavailable methods from process and fs binding
  if (!process.getuid) process.getuid = function() {}
  if (!process.getgid) process.getgid = function() {}
  var fsBinding = process.binding("fs")
  if (!fsBinding.chown) fsBinding.chown = function() {
    var cb = arguments[arguments.length - 1]
    if (typeof cb == "function") cb()
  }

  // patch rename/renameSync, but this should really be fixed in node
  var _fsRename = fs.rename
    , _fsPathPatch
  _fsPathPatch = function(p) {
    return p && p.replace(/\\/g, "/") || p;
  }
  fs.rename = function(p1, p2) {
    arguments[0] = _fsPathPatch(p1)
    arguments[1] = _fsPathPatch(p2)
    return _fsRename.apply(fs, arguments);
  }
}

try {
  // startup, ok to do this synchronously
  var j = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"))+"")
  npm.version = j.version
  npm.nodeVersionRequired = j.engines.node
  if (!semver.satisfies(process.version, j.engines.node)) {
    log.error([""
              ,"npm requires node version: "+j.engines.node
              ,"And you have: "+process.version
              ,"which is not satisfactory."
              ,""
              ,"Bad things will likely happen.  You have been warned."
              ,""].join("\n"), "unsupported version")
  }
} catch (ex) {
  try {
    log(ex, "error reading version")
  } catch (er) {}
  npm.version = ex
}

var commandCache = {}
  // short names for common things
  , aliases = { "rm" : "uninstall"
              , "r" : "uninstall"
              , "un" : "uninstall"
              , "unlink" : "uninstall"
              , "remove" : "uninstall"
              , "rb" : "rebuild"
              , "list" : "ls"
              , "la" : "ls"
              , "ll" : "ls"
              , "ln" : "link"
              , "i" : "install"
              , "up" : "update"
              , "c" : "config"
              , "info" : "view"
              , "show" : "view"
              , "find" : "search"
              , "s" : "search"
              , "se" : "search"
              , "author" : "owner"
              , "home" : "docs"
              , "unstar": "star" // same function
              , "apihelp" : "help"
              }

  , aliasNames = Object.keys(aliases)
  // these are filenames in ./lib
  , cmdList = [ "install"
              , "uninstall"
              , "cache"
              , "config"
              , "set"
              , "get"
              , "update"
              , "outdated"
              , "prune"
              , "submodule"
              , "pack"

              , "rebuild"
              , "link"

              , "publish"
              , "star"
              , "tag"
              , "adduser"
              , "unpublish"
              , "owner"
              , "deprecate"

              , "help"
              , "help-search"
              , "ls"
              , "search"
              , "view"
              , "init"
              , "version"
              , "edit"
              , "explore"
              , "docs"
              , "bugs"
              , "faq"
              , "root"
              , "prefix"
              , "bin"
              , "whoami"

              , "test"
              , "stop"
              , "start"
              , "restart"
              , "run-script"
              , "completion"
              ]
  , plumbing = [ "build"
               , "unbuild"
               , "xmas"
               ]
  , fullList = npm.fullList = cmdList.concat(aliasNames).filter(function (c) {
      return plumbing.indexOf(c) === -1
    })
  , abbrevs = abbrev(fullList)

Object.keys(abbrevs).concat(plumbing).forEach(function addCommand (c) {
  Object.defineProperty(npm.commands, c, { get : function () {
    if (!loaded) throw new Error(
      "Call npm.load(conf, cb) before using this command.\n"+
      "See the README.md or cli.js for example usage.")
    var a = npm.deref(c)
    if (c === "la" || c === "ll") {
      npm.config.set("long", true)
    }
    npm.command = c
    if (commandCache[a]) return commandCache[a]
    var cmd = require(__dirname+"/lib/"+a+".js")
    commandCache[a] = function () {
      var args = Array.prototype.slice.call(arguments, 0)
      if (typeof args[args.length - 1] !== "function") {
        args.push(defaultCb)
      }
      if (args.length === 1) args.unshift([])
      cmd.apply(npm, args)
    }
    Object.keys(cmd).forEach(function (k) {
      commandCache[a][k] = cmd[k]
    })
    return commandCache[a]
  }, enumerable: fullList.indexOf(c) !== -1 })

  // make css-case commands callable via camelCase as well
  if (c.match(/\-([a-z])/)) {
    addCommand(c.replace(/\-([a-z])/g, function (a, b) {
      return b.toUpperCase()
    }))
  }
})

function defaultCb (er, data) {
  if (er) console.error(er.stack || er.message)
  else console.log(data)
}

npm.deref = function (c) {
  if (!c) return ""
  if (c.match(/[A-Z]/)) c = c.replace(/([A-Z])/g, function (m) {
    return "-" + m.toLowerCase()
  })
  if (plumbing.indexOf(c) !== -1) return c
  var a = abbrevs[c]
  if (aliases[a]) a = aliases[a]
  return a
}

var loaded = false
  , loading = false
  , loadErr = null
  , loadListeners = []

function loadCb (er) {
  loadListeners.forEach(function (cb) {
    process.nextTick(cb.bind(npm, er, npm))
  })
  loadListeners.length = 0
}

npm.load = function (conf, cb_) {
  if (!cb_ && typeof conf === "function") cb_ = conf , conf = {}
  if (!cb_) cb_ = function () {}
  if (!conf) conf = {}
  loadListeners.push(cb_)
  if (loaded || loadErr) return cb(loadErr)
  if (loading) return
  loading = true
  var onload = true

  function cb (er) {
    if (loadErr) return
    loaded = true
    loadCb(loadErr = er)
    if (onload = onload && npm.config.get("onload-script")) {
      require(onload)
      onload = false
    }
  }

  log.waitForConfig()

  load(npm, conf, cb)
}

function load (npm, conf, cb) {
  which(process.argv[0], function (er, node) {
    //console.error("back from which")
    if (!er && node.toUpperCase() !== process.execPath.toUpperCase()) {
      log.verbose("node symlink", node)
      process.execPath = node
      process.installPrefix = path.resolve(node, "..", "..")
    }

    // look up configs
    //console.error("about to look up configs")

    ini.resolveConfigs(conf, function (er) {
      //console.error("back from config lookup", er && er.stack)
      if (er) return cb(er)

      var n = 2
        , errState

      loadPrefix(npm, conf, next)
      loadUid(npm, conf, next)

      function next (er) {
        //console.error("next", er && er.stack)
        if (errState) return
        if (er) return cb(errState = er)
        if (-- n <= 0) return cb()
      }
    })
  })
}

function loadPrefix (npm, conf, cb) {
  // try to guess at a good node_modules location.
  var p
  if (!npm.config.get("global")
      && !conf.hasOwnProperty("prefix")) {
    p = process.cwd()
  } else {
    p = npm.config.get("prefix")
  }

  findPrefix(p, function (er, p) {
    //console.log("Back from findPrefix", er && er.stack, p)
    Object.defineProperty(npm, "prefix",
      { get : function () { return p }
      , set : function (r) { return p = r }
      , enumerable : true
      })
    // the prefix MUST exist, or else nothing works.
    mkdir(p, cb)
  })
}

function loadUid (npm, conf, cb) {
  // if we're not in unsafe-perm mode, then figure out who
  // to run stuff as.  Do this first, to support `npm update npm -g`
  if (!npm.config.get("unsafe-perm")) {
    getUid(npm.config.get("user"), npm.config.get("group"), cb)
  } else {
    //console.error("skipping loadUid")
    process.nextTick(cb)
  }
}

npm.config =
  { get : function (key) { return ini.get(key) }
  , set : function (key, val) { return ini.set(key, val, "cli") }
  , del : function (key, val) { return ini.del(key, val, "cli") }
  }

Object.defineProperty(npm, "dir",
  { get : function () {
      if (npm.config.get("global")) {
        return path.resolve(npm.prefix, "lib", "node_modules")
      } else {
        return path.resolve(npm.prefix, "node_modules")
      }
    }
  , enumerable : true
  })

Object.defineProperty(npm, "root",
  { get : function () { return npm.dir } })

Object.defineProperty(npm, "cache",
  { get : function () { return npm.config.get("cache") }
  , set : function (r) { return npm.config.set("cache", r) }
  , enumerable : true
  })

var tmpFolder
Object.defineProperty(npm, "tmp",
  { get : function () {
      if (!tmpFolder) tmpFolder = "npm-"+Date.now()
      return path.resolve(npm.config.get("tmp"), tmpFolder)
    }
  , enumerable : true
  })

// the better to repl you with
Object.getOwnPropertyNames(npm.commands).forEach(function (n) {
  if (npm.hasOwnProperty(n)) return

  Object.defineProperty(npm, n, { get: function () {
    return function () {
      var args = Array.prototype.slice.call(arguments, 0)
        , cb = defaultCb

      if (args.length === 1 && Array.isArray(args[0])) {
        args = args[0]
      }

      if (typeof args[args.length - 1] === "function") {
        cb = args.pop()
      }

      npm.commands[n](args, cb)
    }
  }, enumerable: false, configurable: true })
})

if (require.main === module) {
  require("./bin/npm.js")
}
<|endoftext|>


<commit_before>/******************************************************************************/
/*                                                                            */
/*  Poetic Generator Reloaded is a multiplayer and collaborative art          */
/*  experience.                                                               */
/*                                                                            */
/*  Copyright (C) 2011 - Gnuside                                              */
/*                                                                            */
/*  This program is free software: you can redistribute it and/or modify it   */
/*  under the terms of the GNU Affero General Public License as published by  */
/*  the Free Software Foundation, either version 3 of the License, or (at     */
/*  your option) any later version.                                           */
/*                                                                            */
/*  This program is distributed in the hope that it will be useful, but       */
/*  WITHOUT ANY WARRANTY; without even the implied warranty of                */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero  */
/*  General Public License for more details.                                  */
/*                                                                            */
/*  You should have received a copy of the GNU Affero General Public License  */
/*  along with this program.  If not, see <http://www.gnu.org/licenses/>.     */
/*                                                                            */
/******************************************************************************/

// use phantomjs

<commit_msg>bot: Added base structure.<commit_after>/******************************************************************************/
/*                                                                            */
/*  Poetic Generator Reloaded is a multiplayer and collaborative art          */
/*  experience.                                                               */
/*                                                                            */
/*  Copyright (C) 2011 - Gnuside                                              */
/*                                                                            */
/*  This program is free software: you can redistribute it and/or modify it   */
/*  under the terms of the GNU Affero General Public License as published by  */
/*  the Free Software Foundation, either version 3 of the License, or (at     */
/*  your option) any later version.                                           */
/*                                                                            */
/*  This program is distributed in the hope that it will be useful, but       */
/*  WITHOUT ANY WARRANTY; without even the implied warranty of                */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero  */
/*  General Public License for more details.                                  */
/*                                                                            */
/*  You should have received a copy of the GNU Affero General Public License  */
/*  along with this program.  If not, see <http://www.gnu.org/licenses/>.     */
/*                                                                            */
/******************************************************************************/

// use phantomjs

var BASE_URL = "http://localhost:9393";

if (phantom.state.length === 0) {
	console.log("Setting up Phantom...");

	phantom.open( BASE_URL );

} else {

	if ( document.location.href == BASE_URL ) {
		// on base url
		// set username (bot + date)
		// set logout date
		// validate

		// on drawing page
		// pick a color
		// generate a random number of strokes in editor
		//   set a direction
		//   continue in that direction
	} else {
		console.log("  [oops] unknown page");
		phantom.exit();
	}

}
<|endoftext|>


<commit_before>var _ = require('lodash'),
    namer = require('./namer'),
    env = require('./env');

// Module wrapper expects a raw module objects as an argument for constructor.
module.exports = function(app, moduleConf, slot) {
    // register default partials and helpers for current module

    var templates = slot.templates,
        handlebars = env.get('handlebars'),
        templatePartials,
        templateHelpers,
        clientInited = false,
        moduleWrapper;

    templatePartials = _.omit(templates, moduleConf.type);

    // Кастомные хелперы
    templateHelpers = {
        // Подключение не основного файла шаблона через инструкцию {{partial "file" context=variable}}
        partial: function(partial, options) {
            var tmpl = (typeof partial == 'function') ? partial : templates[partial];

            if (!tmpl) throw new Error("Unable to render partial " + partial);

            return new handlebars.SafeString(tmpl(options.hash.context || this, getTmplOptions()));
        },

        // Подключение модуля. Модуль должен быть проинициализирован.
        module: function(module) {
            var moduleName = module;

            module = _.isString(module) ? slot.modules[module] : module;

            if (_.isArray(module)) throw new Error("Module helper: unable to render array of modules");

            // Из какого-то шаблона подключается несуществующий модуль
            if (!module) throw new Error("Module helper: unable to render module " + moduleName);

            return new handlebars.SafeString(module.render());
        },

        // Подключение первого имеющегося (из списка) партиала
        p: function() {
            var tmpl = null,
                options = arguments[arguments.length - 1],
                partial;

            for (var i = 0, len = arguments.length; i < len - 1; i++) {
                partial = arguments[i];
                tmpl = typeof partial == 'function' ? partial : templates[partial];
                if (tmpl) break;
            }

            if (!tmpl) {
                throw new Error(
                    "Unable to render first partial of this: " + _.toArray(arguments).slice(0, arguments.length - 1).join(', ')
                );
            }

            return templateHelpers.partial.call(this, tmpl, options);
        },

        /**
         * Модификаторы для элементов
         *
         * Пример использования:
         *

         * При этом модификаторы из последних элементов будут приоритетнее.
         *
         * На сервере проставляются через slot.element('name').mod({...})
         *
         * @returns {string}
         */
        mods: function() {
            var mods = {};

            for (var i = 0, len = arguments.length; i < len - 1; i++) {
                mods = _.extend(mods, slot.element(arguments[i]).mod());
            }

            return _.map(mods, function(value, name) {
                return namer.modificatorClass(name, value);
            }).join(' ');
        },

        /**
         * Хелпер для проверки наличия заиниченного модуля
         *
         * {{#ifmodule 'searchBar'}}
         *    {{module 'searchBar'}}
         * {{/ifmodule}}
         *
         * @param {String} name имя модуля
         * @param {Object} options
         * @returns {String}
         */
        ifmodule: function(name, options) {
            if (slot.modules[name]) {
                return options.fn(this);
            } else {
                return options.inverse(this);
            }
        }
    };

    slot.templateHelpers = templateHelpers;

    slot.extendTmplHelpers = function(helpersToAdd) {
        _.extend(templateHelpers, helpersToAdd);
    };

    function getTmplOptions() {
        var options = {},
            partials = moduleConf.getPartials ? moduleConf.getPartials() : {},
            helpers = moduleConf.getHelpers ? moduleConf.getHelpers() : {};

        options.partials = _.defaults(partials, templatePartials, handlebars.partials);
        options.helpers = _.defaults(helpers, templateHelpers, handlebars.helpers);

        return options;
    }

    slot.renderPartial = function(template, ctx) {
        var tmpl = slot.templates[template];

        if (!tmpl) {
            throw new Error('slot.renderPartial: partial "' + template + '" not found');
        }

        return tmpl(ctx, getTmplOptions());
    };

    slot.hasPartial = function(name) {
        return name in templatePartials;
    };

    function renderTag(tag, attrs, content) {
        // Собираем все HTML-аттрибуты в строку
        var attributesString = _.reduce(attrs, makeAttributes, '');

        function makeAttributes(memo, val, key) {
            return memo + ' ' + key + '="' + val + '"';
        }

        return '<' + tag + attributesString + '>' + content + '</' + tag + '>';
    }

    moduleWrapper = {
        // Initializes the module with the given params. Invokes callback when init process is ready.
        init: function(state, callback) {
            slot.stage = slot.STAGE_INITING;

            function done(err) {
                // При асинхронном ините модуля слот мог умереть раньше,
                // чем завершится инициализация, и не надо менять его стэйдж!
                if (!err && slot.stage == slot.STAGE_INITING) {
                    slot.stage = slot.STAGE_INITED;
                }

                if (callback) {
                    callback(err);
                }
            }

            if (moduleConf.init) {
                // Eсть второй аргумент (callback).
                if (moduleConf.init.length >= 2) {
                    moduleConf.init(state, done);
                } else {
                    moduleConf.init(state);
                    done();
                }
            } else {
                done();
            }
        },

        clientInit: function() {
            if (!clientInited) {
                if (moduleConf.clientInit) {
                    moduleConf.clientInit.apply(moduleConf, arguments);
                }

                clientInited = true;
            }
        },

        /**
         * Вызывается каждый раз после рендера представления.
         */
        bind: function() {
            if (moduleConf.bind) {
                moduleConf.bind();
            }
        },

        changeState: function() {
            if (moduleConf.changeState) {
                moduleConf.changeState.apply(moduleConf, arguments);
            }
        },

        /**
         * Renders the module and wraps it in a div with special metadata to identify this module in DOM.
         *
         * @param {Object} [customViewContext] Объект, который пошлется в шаблон вместо viewContext модуля.
         * @returns {string}
         */
        render: function(customViewContext) {
            var blockName = moduleConf.block || moduleConf.type;

            // Вызов метода viewContext должен быть всегда.
            var originalViewContext = moduleConf.viewContext ? moduleConf.viewContext(customViewContext) : slot.viewContext,
                viewContext = customViewContext || originalViewContext || {};

            if (!_.isObject(viewContext)) {
                throw new TypeError('viewContext must be an object, but а ' + viewContext);
            }

            var moduleId = moduleConf.uniqueId,
                moduleInstance = app.getModuleDescriptorById(moduleId),
                template = moduleConf.type || moduleConf.template,
                tag = moduleConf.tag || 'div',
                attrs = {};

            if (moduleConf.viewAttrs) {
                attrs = _.isFunction(moduleConf.viewAttrs) ? moduleConf.viewAttrs() : moduleConf.viewAttrs;
            }

            var mods = moduleInstance.mods,
                compiledTemplate = slot.templates[template],
                compiledTemplateHTML = typeof compiledTemplate == 'function' ?
                    compiledTemplate(viewContext, getTmplOptions()) :
                    '';

            attrs.id = 'module-' + moduleId;
            attrs['data-module'] = moduleConf.type;

            // Генерируем CSS класс модуля
            var moduleClass = namer.moduleClass(blockName),
                classString = attrs['class'] || '',
                classes = classString.split(/\s+/);

            classes.push(moduleClass);

            _.each(mods, function(val, key) {
                var modClass = namer.modificatorClass(key, val);
                classes.push(modClass);
            });

            attrs['class'] = _.uniq(classes).join(' ');

            return renderTag(tag, attrs, compiledTemplateHTML);
        },

        id: function() {
            return moduleConf.uniqueId;
        },

        interface: moduleConf.interface,

        dispatcher: moduleConf.dispatcher,

        elements: moduleConf.elements,

        slot: slot,

        isEventsBound: false,

        type: moduleConf.type
    };

    if (app.isClient) {
        moduleWrapper.block = _.partial(app.block, moduleConf.uniqueId);
        moduleWrapper.bindEvents = _.partial(app.bindEvents, moduleConf.uniqueId);
        moduleWrapper.unbindEvents = _.partial(app.unbindEvents, moduleConf.uniqueId);
    }

    return moduleWrapper;
};
<commit_msg>fix error with empty element #ONLINE-4779<commit_after>var _ = require('lodash'),
    namer = require('./namer'),
    env = require('./env');

// Module wrapper expects a raw module objects as an argument for constructor.
module.exports = function(app, moduleConf, slot) {
    // register default partials and helpers for current module

    var templates = slot.templates,
        handlebars = env.get('handlebars'),
        templatePartials,
        templateHelpers,
        clientInited = false,
        moduleWrapper;

    templatePartials = _.omit(templates, moduleConf.type);

    // Кастомные хелперы
    templateHelpers = {
        // Подключение не основного файла шаблона через инструкцию {{partial "file" context=variable}}
        partial: function(partial, options) {
            var tmpl = (typeof partial == 'function') ? partial : templates[partial];

            if (!tmpl) throw new Error("Unable to render partial " + partial);

            return new handlebars.SafeString(tmpl(options.hash.context || this, getTmplOptions()));
        },

        // Подключение модуля. Модуль должен быть проинициализирован.
        module: function(module) {
            var moduleName = module;

            module = _.isString(module) ? slot.modules[module] : module;

            if (_.isArray(module)) throw new Error("Module helper: unable to render array of modules");

            // Из какого-то шаблона подключается несуществующий модуль
            if (!module) throw new Error("Module helper: unable to render module " + moduleName);

            return new handlebars.SafeString(module.render());
        },

        // Подключение первого имеющегося (из списка) партиала
        p: function() {
            var tmpl = null,
                options = arguments[arguments.length - 1],
                partial;

            for (var i = 0, len = arguments.length; i < len - 1; i++) {
                partial = arguments[i];
                tmpl = typeof partial == 'function' ? partial : templates[partial];
                if (tmpl) break;
            }

            if (!tmpl) {
                throw new Error(
                    "Unable to render first partial of this: " + _.toArray(arguments).slice(0, arguments.length - 1).join(', ')
                );
            }

            return templateHelpers.partial.call(this, tmpl, options);
        },

        /**
         * Модификаторы для элементов
         *
         * Пример использования:
         *

         * При этом модификаторы из последних элементов будут приоритетнее.
         *
         * На сервере проставляются через slot.element('name').mod({...})
         *
         * @returns {string}
         */
        mods: function() {
            var mods = {};

            for (var i = 0, len = arguments.length; i < len - 1; i++) {
                if (slot.element(arguments[i])) {
                    mods = _.extend(mods, slot.element(arguments[i]).mod());
                }
            }

            return _.map(mods, function(value, name) {
                return namer.modificatorClass(name, value);
            }).join(' ');
        },

        /**
         * Хелпер для проверки наличия заиниченного модуля
         *
         * {{#ifmodule 'searchBar'}}
         *    {{module 'searchBar'}}
         * {{/ifmodule}}
         *
         * @param {String} name имя модуля
         * @param {Object} options
         * @returns {String}
         */
        ifmodule: function(name, options) {
            if (slot.modules[name]) {
                return options.fn(this);
            } else {
                return options.inverse(this);
            }
        }
    };

    slot.templateHelpers = templateHelpers;

    slot.extendTmplHelpers = function(helpersToAdd) {
        _.extend(templateHelpers, helpersToAdd);
    };

    function getTmplOptions() {
        var options = {},
            partials = moduleConf.getPartials ? moduleConf.getPartials() : {},
            helpers = moduleConf.getHelpers ? moduleConf.getHelpers() : {};

        options.partials = _.defaults(partials, templatePartials, handlebars.partials);
        options.helpers = _.defaults(helpers, templateHelpers, handlebars.helpers);

        return options;
    }

    slot.renderPartial = function(template, ctx) {
        var tmpl = slot.templates[template];

        if (!tmpl) {
            throw new Error('slot.renderPartial: partial "' + template + '" not found');
        }

        return tmpl(ctx, getTmplOptions());
    };

    slot.hasPartial = function(name) {
        return name in templatePartials;
    };

    function renderTag(tag, attrs, content) {
        // Собираем все HTML-аттрибуты в строку
        var attributesString = _.reduce(attrs, makeAttributes, '');

        function makeAttributes(memo, val, key) {
            return memo + ' ' + key + '="' + val + '"';
        }

        return '<' + tag + attributesString + '>' + content + '</' + tag + '>';
    }

    moduleWrapper = {
        // Initializes the module with the given params. Invokes callback when init process is ready.
        init: function(state, callback) {
            slot.stage = slot.STAGE_INITING;

            function done(err) {
                // При асинхронном ините модуля слот мог умереть раньше,
                // чем завершится инициализация, и не надо менять его стэйдж!
                if (!err && slot.stage == slot.STAGE_INITING) {
                    slot.stage = slot.STAGE_INITED;
                }

                if (callback) {
                    callback(err);
                }
            }

            if (moduleConf.init) {
                // Eсть второй аргумент (callback).
                if (moduleConf.init.length >= 2) {
                    moduleConf.init(state, done);
                } else {
                    moduleConf.init(state);
                    done();
                }
            } else {
                done();
            }
        },

        clientInit: function() {
            if (!clientInited) {
                if (moduleConf.clientInit) {
                    moduleConf.clientInit.apply(moduleConf, arguments);
                }

                clientInited = true;
            }
        },

        /**
         * Вызывается каждый раз после рендера представления.
         */
        bind: function() {
            if (moduleConf.bind) {
                moduleConf.bind();
            }
        },

        changeState: function() {
            if (moduleConf.changeState) {
                moduleConf.changeState.apply(moduleConf, arguments);
            }
        },

        /**
         * Renders the module and wraps it in a div with special metadata to identify this module in DOM.
         *
         * @param {Object} [customViewContext] Объект, который пошлется в шаблон вместо viewContext модуля.
         * @returns {string}
         */
        render: function(customViewContext) {
            var blockName = moduleConf.block || moduleConf.type;

            // Вызов метода viewContext должен быть всегда.
            var originalViewContext = moduleConf.viewContext ? moduleConf.viewContext(customViewContext) : slot.viewContext,
                viewContext = customViewContext || originalViewContext || {};

            if (!_.isObject(viewContext)) {
                throw new TypeError('viewContext must be an object, but а ' + viewContext);
            }

            var moduleId = moduleConf.uniqueId,
                moduleInstance = app.getModuleDescriptorById(moduleId),
                template = moduleConf.type || moduleConf.template,
                tag = moduleConf.tag || 'div',
                attrs = {};

            if (moduleConf.viewAttrs) {
                attrs = _.isFunction(moduleConf.viewAttrs) ? moduleConf.viewAttrs() : moduleConf.viewAttrs;
            }

            var mods = moduleInstance.mods,
                compiledTemplate = slot.templates[template],
                compiledTemplateHTML = typeof compiledTemplate == 'function' ?
                    compiledTemplate(viewContext, getTmplOptions()) :
                    '';

            attrs.id = 'module-' + moduleId;
            attrs['data-module'] = moduleConf.type;

            // Генерируем CSS класс модуля
            var moduleClass = namer.moduleClass(blockName),
                classString = attrs['class'] || '',
                classes = classString.split(/\s+/);

            classes.push(moduleClass);

            _.each(mods, function(val, key) {
                var modClass = namer.modificatorClass(key, val);
                classes.push(modClass);
            });

            attrs['class'] = _.uniq(classes).join(' ');

            return renderTag(tag, attrs, compiledTemplateHTML);
        },

        id: function() {
            return moduleConf.uniqueId;
        },

        interface: moduleConf.interface,

        dispatcher: moduleConf.dispatcher,

        elements: moduleConf.elements,

        slot: slot,

        isEventsBound: false,

        type: moduleConf.type
    };

    if (app.isClient) {
        moduleWrapper.block = _.partial(app.block, moduleConf.uniqueId);
        moduleWrapper.bindEvents = _.partial(app.bindEvents, moduleConf.uniqueId);
        moduleWrapper.unbindEvents = _.partial(app.unbindEvents, moduleConf.uniqueId);
    }

    return moduleWrapper;
};
<|endoftext|>


<commit_before>///*
//Copyright Jesus Perez <jesusprubio gmail com>
//
//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//*/
//
//'use strict';
//
//var async   = require('async'),
//    namiLib = require('nami'),
//    Nami    = namiLib.Nami,
//
//    printer = require('../utils/printer');
//
//
//module.exports = (function () {
//
//    return {
//
//        info : {
//            name        : 'amiGetVar',
//            description : 'Use the Asterisk Manager service (AMI) to get the value of a setup variable',
//            options     : {
//                target : {
//                    description  : 'IP address to brute-force',
//                    defaultValue : '127.0.0.1',
//                    type         : 'targetIp'
//                },
//                port : {
//                    description  : 'Port of the server',
//                    defaultValue : 5038,
//                    type         : 'port'
//                },
//                user : {
//                    description  : 'User to use in the request',
//                    defaultValue : 'admin',
//                    type         : 'anyValue'
//                },
//                password : {
//                    description  : 'Password to use in the request',
//                    defaultValue : 'amp111',
//                    type         : 'anyValue'
//                },
//                channel : {
//                    description  : 'Channel to use in the request',
//                    defaultValue : 'SIP/100@default',
//                    type         : 'anyValue'
//                },
//                variable : {
//                    description  : 'Name of the variable to get',
//                    defaultValue : 'extension',
//                    type         : 'anyValue'
//                },
//                timeout : {
//                    description  : 'Time to wait for a response (ms.)',
//                    defaultValue : 5000,
//                    type         : 'positiveInt'
//                }
//            }
//        },
//
//        run : function (options, callback) {
//            var connected = false,
//                ami       = new Nami({
//                    host     : options.target,
//                    port     : options.port,
//                    username : options.user,
//                    secret   : options.password
//                });
//
//            ami.logger.setLevel('OFF');
//
//            ami.on('namiConnected', function () {
//                var action = new namiLib.Actions.GetVar();
//
//                action.channel = options.channel;
//                action.variable = options.variable;
//                connected = true;
//                ami.send(action, function (res) {
//                    ami.close();
//                    callback(null, res);
//                });
//            });
//
//            ami.on('namiLoginIncorrect', function () {
//                callback({
//                    type : 'login'
//                });
//            });
//
//            // The module do not supports connection timeout, so
//            // we add it manually ("connected" var), really dirty trick
//            setTimeout(function () {
//                if (!connected) {
//                    callback({
//                        type : 'timeout'
//                    });
//                }
//            }, options.timeout);
//            ami.open();
//        }
//    };
//
//}());
<commit_msg>amiGetVar module modified<commit_after>/*
Copyright Jesus Perez <jesusprubio gmail com>
		  Sergio García <s3rgio.gr gmail com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

// Private stuff

var async   = require('async'),
    namiLib = require('nami'),
    Nami    = namiLib.Nami,

    printer = require('../utils/printer'),

	HELP = {
        description : 'Use the Asterisk Manager service (AMI) to get the value of a setup variable',
        options     : {
            target : {
            	type : 'ip',
                description  : 'IP address to brute-force',
                defaultValue : '127.0.0.1'
            },
            port : {
            	type         : 'port',
                description  : 'Port of the server',
                defaultValue : 5038
            },
            user : {
            	type         : 'allValid',
                description  : 'User to use in the request',
                defaultValue : 'admin'
            },
            password : {
            	type         : 'allValid',
                description  : 'Password to use in the request',
                defaultValue : 'amp111'
            },
            channel : {
            	type         : 'allValid',
                description  : 'Channel to use in the request',
                defaultValue : 'SIP/100@default'
               },
            variable : {
            	type         : 'allValid',
                description  : 'Name of the variable to get',
                defaultValue : 'extension'
            },
            timeout : {
            	type         : 'positiveInt',
                description  : 'Time to wait for a response (ms.)',
                defaultValue : 5000
            }
        }
    };

// Public stuff

module.exports.help = HELP;

module.exports.run = function (options, callback) {
   var connected = false,
       ami       = new Nami({
           host     : options.target,
           port     : options.port,
           username : options.user,
           secret   : options.password
       });

   ami.logger.setLevel('OFF');

   ami.on('namiConnected', function () {
       var action = new namiLib.Actions.GetVar();

       action.channel = options.channel;
       action.variable = options.variable;
       connected = true;
       ami.send(action, function (res) {
           ami.close();
           callback(null, res);
       });
   });

   ami.on('namiLoginIncorrect', function () {
       callback({
           type : 'login'
       });
   });

   // The module do not supports connection timeout, so
   // we add it manually ("connected" var), really dirty trick
   setTimeout(function () {
       if (!connected) {
           callback({
               type : 'timeout'
           });
       }
   }, options.timeout);
   ami.open();
};
<|endoftext|>


<commit_before><commit_msg>Halved basis time<commit_after><|endoftext|>


<commit_before>import extend from 'extend';
import Delta from 'quill-delta';
import Parchment from 'parchment';
import Quill from '../core/quill';
import logger from '../core/logger';
import Module from '../core/module';

import { AlignAttribute, AlignStyle } from '../formats/align';
import { BackgroundStyle } from '../formats/background';
import { ColorStyle } from '../formats/color';
import { DirectionAttribute, DirectionStyle } from '../formats/direction';
import { FontStyle } from '../formats/font';
import { SizeStyle } from '../formats/size';

let debug = logger('quill:clipboard');

const DOM_KEY = '__ql-matcher';

const CLIPBOARD_CONFIG = [
  [Node.TEXT_NODE, matchText],
  [Node.TEXT_NODE, matchNewline],
  ['br', matchBreak],
  [Node.ELEMENT_NODE, matchNewline],
  [Node.ELEMENT_NODE, matchBlot],
  [Node.ELEMENT_NODE, matchSpacing],
  [Node.ELEMENT_NODE, matchAttributor],
  [Node.ELEMENT_NODE, matchStyles],
  ['li', matchIndent],
  ['b', matchAlias.bind(matchAlias, 'bold')],
  ['i', matchAlias.bind(matchAlias, 'italic')],
  ['style', matchIgnore]
];

const ATTRIBUTE_ATTRIBUTORS = [
  AlignAttribute,
  DirectionAttribute
].reduce(function(memo, attr) {
  memo[attr.keyName] = attr;
  return memo;
}, {});

const STYLE_ATTRIBUTORS = [
  AlignStyle,
  BackgroundStyle,
  ColorStyle,
  DirectionStyle,
  FontStyle,
  SizeStyle
].reduce(function(memo, attr) {
  memo[attr.keyName] = attr;
  return memo;
}, {});

class Clipboard extends Module {
  constructor(quill, options) {
    super(quill, options);
    this.quill.root.addEventListener('paste', this.onPaste.bind(this));
    this.container = this.quill.addContainer('ql-clipboard');
    this.container.setAttribute('contenteditable', true);
    this.container.setAttribute('tabindex', -1);
    this.matchers = [];
    CLIPBOARD_CONFIG.concat(this.options.matchers).forEach(([selector, matcher]) => {
      if (!options.matchVisual && matcher === matchSpacing) return;
      this.addMatcher(selector, matcher);
    });
  }

  addMatcher(selector, matcher) {
    this.matchers.push([selector, matcher]);
  }

  convert(html) {
    if (typeof html === 'string') {
      this.container.innerHTML = html.replace(/\>\r?\n +\</g, '><'); // Remove spaces between tags
    }
    let [elementMatchers, textMatchers] = this.prepareMatching();
    let delta = traverse(this.container, elementMatchers, textMatchers);
    // Remove trailing newline
    if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) {
      delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1));
    }
    debug.log('convert', this.container.innerHTML, delta);
    this.container.innerHTML = '';
    return delta;
  }

  dangerouslyPasteHTML(index, html, source = Quill.sources.API) {
    if (typeof index === 'string') {
      return this.quill.setContents(this.convert(index), html);
    } else {
      let paste = this.convert(html);
      return this.quill.updateContents(new Delta().retain(index).concat(paste), source);
    }
  }

  onPaste(e) {
    if (e.defaultPrevented || !this.quill.isEnabled()) return;
    let range = this.quill.getSelection();
    let delta = new Delta().retain(range.index);
    let scrollTop = this.quill.scrollingContainer.scrollTop;
    this.container.focus();
    this.quill.selection.update(Quill.sources.SILENT);
    setTimeout(() => {
      delta = delta.concat(this.convert()).delete(range.length);
      this.quill.updateContents(delta, Quill.sources.USER);
      // range.length contributes to delta.length()
      this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT);
      this.quill.scrollingContainer.scrollTop = scrollTop;
      this.quill.focus();
    }, 1);
  }

  prepareMatching() {
    let elementMatchers = [], textMatchers = [];
    this.matchers.forEach((pair) => {
      let [selector, matcher] = pair;
      switch (selector) {
        case Node.TEXT_NODE:
          textMatchers.push(matcher);
          break;
        case Node.ELEMENT_NODE:
          elementMatchers.push(matcher);
          break;
        default:
          [].forEach.call(this.container.querySelectorAll(selector), (node) => {
            // TODO use weakmap
            node[DOM_KEY] = node[DOM_KEY] || [];
            node[DOM_KEY].push(matcher);
          });
          break;
      }
    });
    return [elementMatchers, textMatchers];
  }
}
Clipboard.DEFAULTS = {
  matchers: [],
  matchVisual: true
};

function applyFormat(delta, format, value) {
  if (typeof format === 'object') {
    return Object.keys(format).reduce(function(delta, key) {
      return applyFormat(delta, key, format[key]);
    }, delta);
  } else {
    return delta.reduce(function(delta, op) {
      if (op.attributes && op.attributes[format]) {
        return delta.push(op);
      } else {
        return delta.insert(op.insert, extend({}, {[format]: value}, op.attributes));
      }
    }, new Delta());
  }
}

function computeStyle(node) {
  if (node.nodeType !== Node.ELEMENT_NODE) return {};
  const DOM_KEY = '__ql-computed-style';
  return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));
}

function deltaEndsWith(delta, text) {
  let endText = "";
  for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {
    let op  = delta.ops[i];
    if (typeof op.insert !== 'string') break;
    endText = op.insert + endText;
  }
  return endText.slice(-1*text.length) === text;
}

function isLine(node) {
  if (node.childNodes.length === 0) return false;   // Exclude embed blocks
  let style = computeStyle(node);
  return ['block', 'list-item'].indexOf(style.display) > -1;
}

function traverse(node, elementMatchers, textMatchers) {  // Post-order
  if (node.nodeType === node.TEXT_NODE) {
    return textMatchers.reduce(function(delta, matcher) {
      return matcher(node, delta);
    }, new Delta());
  } else if (node.nodeType === node.ELEMENT_NODE) {
    return [].reduce.call(node.childNodes || [], (delta, childNode) => {
      let childrenDelta = traverse(childNode, elementMatchers, textMatchers);
      if (childNode.nodeType === node.ELEMENT_NODE) {
        childrenDelta = elementMatchers.reduce(function(childrenDelta, matcher) {
          return matcher(childNode, childrenDelta);
        }, childrenDelta);
        childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) {
          return matcher(childNode, childrenDelta);
        }, childrenDelta);
      }
      return delta.concat(childrenDelta);
    }, new Delta());
  } else {
    return new Delta();
  }
}

function matchAlias(format, node, delta) {
  return applyFormat(delta, format, true);
}

function matchAttributor(node, delta) {
  let attributes = Parchment.Attributor.Attribute.keys(node);
  let classes = Parchment.Attributor.Class.keys(node);
  let styles = Parchment.Attributor.Style.keys(node);
  let formats = {};
  attributes.concat(classes).concat(styles).forEach((name) => {
    let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE);
    if (attr != null) {
      formats[attr.attrName] = attr.value(node);
      if (formats[attr.attrName]) return;
    }
    attr = ATTRIBUTE_ATTRIBUTORS[name];
    if (attr != null && attr.attrName === name) {
      formats[attr.attrName] = attr.value(node) || undefined;
    }
    attr = STYLE_ATTRIBUTORS[name]
    if (attr != null && attr.attrName === name) {
      attr = STYLE_ATTRIBUTORS[name];
      formats[attr.attrName] = attr.value(node) || undefined;
    }
  });
  if (Object.keys(formats).length > 0) {
    delta = applyFormat(delta, formats);
  }
  return delta;
}

function matchBlot(node, delta) {
  let match = Parchment.query(node);
  if (match == null) return delta;
  if (match.prototype instanceof Parchment.Embed) {
    let embed = {};
    let value = match.value(node);
    if (value != null) {
      embed[match.blotName] = value;
      delta = new Delta().insert(embed, match.formats(node));
    }
  } else if (typeof match.formats === 'function') {
    delta = applyFormat(delta, match.blotName, match.formats(node));
  }
  return delta;
}

function matchBreak(node, delta) {
  if (!deltaEndsWith(delta, '\n')) {
    delta.insert('\n');
  }
  return delta;
}

function matchIgnore() {
  return new Delta();
}

function matchIndent(node, delta) {
  let match = Parchment.query(node);
  if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\n')) {
    return delta;
  }
  let indent = -1, parent = node.parentNode;
  while (!parent.classList.contains('ql-clipboard')) {
    if ((Parchment.query(parent) || {}).blotName === 'list') {
      indent += 1;
    }
    parent = parent.parentNode;
  }
  if (indent <= 0) return delta;
  return delta.compose(new Delta().retain(delta.length() - 1).retain(1, { indent: indent}));
}

function matchNewline(node, delta) {
  if (!deltaEndsWith(delta, '\n')) {
    if (isLine(node) || (delta.length() > 0 && node.nextSibling && isLine(node.nextSibling))) {
      delta.insert('\n');
    }
  }
  return delta;
}

function matchSpacing(node, delta) {
  if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) {
    let nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);
    if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight*1.5) {
      delta.insert('\n');
    }
  }
  return delta;
}

function matchStyles(node, delta) {
  let formats = {};
  let style = node.style || {};
  if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {
    formats.italic = true;
  }
  if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') ||
                           parseInt(computeStyle(node).fontWeight) >= 700)) {
    formats.bold = true;
  }
  if (Object.keys(formats).length > 0) {
    delta = applyFormat(delta, formats);
  }
  if (parseFloat(style.textIndent || 0) > 0) {  // Could be 0.5in
    delta = new Delta().insert('\t').concat(delta);
  }
  return delta;
}

function matchText(node, delta) {
  let text = node.data;
  // Word represents empty line with <o:p> </o:p>
  if (node.parentNode.tagName === 'O:P') {
    return delta.insert(text.trim());
  }
  if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) {
    return delta;
  }
  if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {
    // eslint-disable-next-line func-style
    let replacer = function(collapse, match) {
      match = match.replace(/[^\u00a0]/g, '');    // \u00a0 is nbsp;
      return match.length < 1 && collapse ? ' ' : match;
    };
    text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' ');
    text = text.replace(/\s\s+/g, replacer.bind(replacer, true));  // collapse whitespace
    if ((node.previousSibling == null && isLine(node.parentNode)) ||
        (node.previousSibling != null && isLine(node.previousSibling))) {
      text = text.replace(/^\s+/, replacer.bind(replacer, false));
    }
    if ((node.nextSibling == null && isLine(node.parentNode)) ||
        (node.nextSibling != null && isLine(node.nextSibling))) {
      text = text.replace(/\s+$/, replacer.bind(replacer, false));
    }
  }
  return delta.insert(text);
}

export { Clipboard as default, matchAttributor, matchBlot, matchNewline, matchSpacing, matchText };
<commit_msg>make it easier to extend convert<commit_after>import extend from 'extend';
import Delta from 'quill-delta';
import Parchment from 'parchment';
import Quill from '../core/quill';
import logger from '../core/logger';
import Module from '../core/module';

import { AlignAttribute, AlignStyle } from '../formats/align';
import { BackgroundStyle } from '../formats/background';
import { ColorStyle } from '../formats/color';
import { DirectionAttribute, DirectionStyle } from '../formats/direction';
import { FontStyle } from '../formats/font';
import { SizeStyle } from '../formats/size';

let debug = logger('quill:clipboard');

const DOM_KEY = '__ql-matcher';

const CLIPBOARD_CONFIG = [
  [Node.TEXT_NODE, matchText],
  [Node.TEXT_NODE, matchNewline],
  ['br', matchBreak],
  [Node.ELEMENT_NODE, matchNewline],
  [Node.ELEMENT_NODE, matchBlot],
  [Node.ELEMENT_NODE, matchSpacing],
  [Node.ELEMENT_NODE, matchAttributor],
  [Node.ELEMENT_NODE, matchStyles],
  ['li', matchIndent],
  ['b', matchAlias.bind(matchAlias, 'bold')],
  ['i', matchAlias.bind(matchAlias, 'italic')],
  ['style', matchIgnore]
];

const ATTRIBUTE_ATTRIBUTORS = [
  AlignAttribute,
  DirectionAttribute
].reduce(function(memo, attr) {
  memo[attr.keyName] = attr;
  return memo;
}, {});

const STYLE_ATTRIBUTORS = [
  AlignStyle,
  BackgroundStyle,
  ColorStyle,
  DirectionStyle,
  FontStyle,
  SizeStyle
].reduce(function(memo, attr) {
  memo[attr.keyName] = attr;
  return memo;
}, {});

class Clipboard extends Module {
  constructor(quill, options) {
    super(quill, options);
    this.quill.root.addEventListener('paste', this.onPaste.bind(this));
    this.container = this.quill.addContainer('ql-clipboard');
    this.container.setAttribute('contenteditable', true);
    this.container.setAttribute('tabindex', -1);
    this.matchers = [];
    CLIPBOARD_CONFIG.concat(this.options.matchers).forEach(([selector, matcher]) => {
      if (!options.matchVisual && matcher === matchSpacing) return;
      this.addMatcher(selector, matcher);
    });
  }

  addMatcher(selector, matcher) {
    this.matchers.push([selector, matcher]);
  }

  convert(html) {
    if (typeof html === 'string') {
      this.container.innerHTML = html.replace(/\>\r?\n +\</g, '><'); // Remove spaces between tags
      return this.convert();
    }
    let [elementMatchers, textMatchers] = this.prepareMatching();
    let delta = traverse(this.container, elementMatchers, textMatchers);
    // Remove trailing newline
    if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) {
      delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1));
    }
    debug.log('convert', this.container.innerHTML, delta);
    this.container.innerHTML = '';
    return delta;
  }

  dangerouslyPasteHTML(index, html, source = Quill.sources.API) {
    if (typeof index === 'string') {
      return this.quill.setContents(this.convert(index), html);
    } else {
      let paste = this.convert(html);
      return this.quill.updateContents(new Delta().retain(index).concat(paste), source);
    }
  }

  onPaste(e) {
    if (e.defaultPrevented || !this.quill.isEnabled()) return;
    let range = this.quill.getSelection();
    let delta = new Delta().retain(range.index);
    let scrollTop = this.quill.scrollingContainer.scrollTop;
    this.container.focus();
    this.quill.selection.update(Quill.sources.SILENT);
    setTimeout(() => {
      delta = delta.concat(this.convert()).delete(range.length);
      this.quill.updateContents(delta, Quill.sources.USER);
      // range.length contributes to delta.length()
      this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT);
      this.quill.scrollingContainer.scrollTop = scrollTop;
      this.quill.focus();
    }, 1);
  }

  prepareMatching() {
    let elementMatchers = [], textMatchers = [];
    this.matchers.forEach((pair) => {
      let [selector, matcher] = pair;
      switch (selector) {
        case Node.TEXT_NODE:
          textMatchers.push(matcher);
          break;
        case Node.ELEMENT_NODE:
          elementMatchers.push(matcher);
          break;
        default:
          [].forEach.call(this.container.querySelectorAll(selector), (node) => {
            // TODO use weakmap
            node[DOM_KEY] = node[DOM_KEY] || [];
            node[DOM_KEY].push(matcher);
          });
          break;
      }
    });
    return [elementMatchers, textMatchers];
  }
}
Clipboard.DEFAULTS = {
  matchers: [],
  matchVisual: true
};

function applyFormat(delta, format, value) {
  if (typeof format === 'object') {
    return Object.keys(format).reduce(function(delta, key) {
      return applyFormat(delta, key, format[key]);
    }, delta);
  } else {
    return delta.reduce(function(delta, op) {
      if (op.attributes && op.attributes[format]) {
        return delta.push(op);
      } else {
        return delta.insert(op.insert, extend({}, {[format]: value}, op.attributes));
      }
    }, new Delta());
  }
}

function computeStyle(node) {
  if (node.nodeType !== Node.ELEMENT_NODE) return {};
  const DOM_KEY = '__ql-computed-style';
  return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node));
}

function deltaEndsWith(delta, text) {
  let endText = "";
  for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) {
    let op  = delta.ops[i];
    if (typeof op.insert !== 'string') break;
    endText = op.insert + endText;
  }
  return endText.slice(-1*text.length) === text;
}

function isLine(node) {
  if (node.childNodes.length === 0) return false;   // Exclude embed blocks
  let style = computeStyle(node);
  return ['block', 'list-item'].indexOf(style.display) > -1;
}

function traverse(node, elementMatchers, textMatchers) {  // Post-order
  if (node.nodeType === node.TEXT_NODE) {
    return textMatchers.reduce(function(delta, matcher) {
      return matcher(node, delta);
    }, new Delta());
  } else if (node.nodeType === node.ELEMENT_NODE) {
    return [].reduce.call(node.childNodes || [], (delta, childNode) => {
      let childrenDelta = traverse(childNode, elementMatchers, textMatchers);
      if (childNode.nodeType === node.ELEMENT_NODE) {
        childrenDelta = elementMatchers.reduce(function(childrenDelta, matcher) {
          return matcher(childNode, childrenDelta);
        }, childrenDelta);
        childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) {
          return matcher(childNode, childrenDelta);
        }, childrenDelta);
      }
      return delta.concat(childrenDelta);
    }, new Delta());
  } else {
    return new Delta();
  }
}

function matchAlias(format, node, delta) {
  return applyFormat(delta, format, true);
}

function matchAttributor(node, delta) {
  let attributes = Parchment.Attributor.Attribute.keys(node);
  let classes = Parchment.Attributor.Class.keys(node);
  let styles = Parchment.Attributor.Style.keys(node);
  let formats = {};
  attributes.concat(classes).concat(styles).forEach((name) => {
    let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE);
    if (attr != null) {
      formats[attr.attrName] = attr.value(node);
      if (formats[attr.attrName]) return;
    }
    attr = ATTRIBUTE_ATTRIBUTORS[name];
    if (attr != null && attr.attrName === name) {
      formats[attr.attrName] = attr.value(node) || undefined;
    }
    attr = STYLE_ATTRIBUTORS[name]
    if (attr != null && attr.attrName === name) {
      attr = STYLE_ATTRIBUTORS[name];
      formats[attr.attrName] = attr.value(node) || undefined;
    }
  });
  if (Object.keys(formats).length > 0) {
    delta = applyFormat(delta, formats);
  }
  return delta;
}

function matchBlot(node, delta) {
  let match = Parchment.query(node);
  if (match == null) return delta;
  if (match.prototype instanceof Parchment.Embed) {
    let embed = {};
    let value = match.value(node);
    if (value != null) {
      embed[match.blotName] = value;
      delta = new Delta().insert(embed, match.formats(node));
    }
  } else if (typeof match.formats === 'function') {
    delta = applyFormat(delta, match.blotName, match.formats(node));
  }
  return delta;
}

function matchBreak(node, delta) {
  if (!deltaEndsWith(delta, '\n')) {
    delta.insert('\n');
  }
  return delta;
}

function matchIgnore() {
  return new Delta();
}

function matchIndent(node, delta) {
  let match = Parchment.query(node);
  if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\n')) {
    return delta;
  }
  let indent = -1, parent = node.parentNode;
  while (!parent.classList.contains('ql-clipboard')) {
    if ((Parchment.query(parent) || {}).blotName === 'list') {
      indent += 1;
    }
    parent = parent.parentNode;
  }
  if (indent <= 0) return delta;
  return delta.compose(new Delta().retain(delta.length() - 1).retain(1, { indent: indent}));
}

function matchNewline(node, delta) {
  if (!deltaEndsWith(delta, '\n')) {
    if (isLine(node) || (delta.length() > 0 && node.nextSibling && isLine(node.nextSibling))) {
      delta.insert('\n');
    }
  }
  return delta;
}

function matchSpacing(node, delta) {
  if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) {
    let nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom);
    if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight*1.5) {
      delta.insert('\n');
    }
  }
  return delta;
}

function matchStyles(node, delta) {
  let formats = {};
  let style = node.style || {};
  if (style.fontStyle && computeStyle(node).fontStyle === 'italic') {
    formats.italic = true;
  }
  if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') ||
                           parseInt(computeStyle(node).fontWeight) >= 700)) {
    formats.bold = true;
  }
  if (Object.keys(formats).length > 0) {
    delta = applyFormat(delta, formats);
  }
  if (parseFloat(style.textIndent || 0) > 0) {  // Could be 0.5in
    delta = new Delta().insert('\t').concat(delta);
  }
  return delta;
}

function matchText(node, delta) {
  let text = node.data;
  // Word represents empty line with <o:p> </o:p>
  if (node.parentNode.tagName === 'O:P') {
    return delta.insert(text.trim());
  }
  if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) {
    return delta;
  }
  if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) {
    // eslint-disable-next-line func-style
    let replacer = function(collapse, match) {
      match = match.replace(/[^\u00a0]/g, '');    // \u00a0 is nbsp;
      return match.length < 1 && collapse ? ' ' : match;
    };
    text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' ');
    text = text.replace(/\s\s+/g, replacer.bind(replacer, true));  // collapse whitespace
    if ((node.previousSibling == null && isLine(node.parentNode)) ||
        (node.previousSibling != null && isLine(node.previousSibling))) {
      text = text.replace(/^\s+/, replacer.bind(replacer, false));
    }
    if ((node.nextSibling == null && isLine(node.parentNode)) ||
        (node.nextSibling != null && isLine(node.nextSibling))) {
      text = text.replace(/\s+$/, replacer.bind(replacer, false));
    }
  }
  return delta.insert(text);
}

export { Clipboard as default, matchAttributor, matchBlot, matchNewline, matchSpacing, matchText };
<|endoftext|>


<commit_before><commit_msg>Add CreateInterviewer() func to the API Factiry<commit_after><|endoftext|>


<commit_before>(function() {
  var
  data,
  keys,
  students,
  chart = document.querySelector(".chart")
  ;

  function drawGraphic(containerWidth) {
    chart.innerHTML = "";

    var
    x,
    y,
    adm,
    color,
    dates,
    xAxis,
    yAxis,
    line,
    legend,
    revenue,
    enrollment,
    margin = { top: 16, right: 16, bottom: 32 , left: 64 },
    width  = calculateWidth(),
    mobile = (width <= 320),
    ratio  = (mobile) ? { width: 1, height: 2 } : { width: 3, height: 2 },
    height = calculateHeight()
    ;

    // Helper functions for chart dimensions
    // ---------------------------------------------------------------------------
    function marginWidth()  { return margin.left + margin.right; }
    function marginHeight() { return margin.top + margin.bottom; }

    function calculateWidth() {
      if (!containerWidth) { var containerWidth = +chart.offsetWidth; }
      return Math.ceil(containerWidth - marginWidth());
    }

    function calculateHeight() {
      return Math.ceil(width * ratio.height / ratio.width) - marginHeight();
    }

    // Data source and header container (Title, description, legend and adjustment button)
    // ---------------------------------------------------------------------------
    d3.select(".header").style({
      "margin-left": "7px",
      "margin-right": "7px"
    });
    d3.select(".data-source").style({
      "margin-left": "7px",
      "margin-right": "7px"
    });

    // Legend
    // ---------------------------------------------------------------------------
    legend = d3.select(".legend svg")
      .attr("width", width)
      .attr("height", (mobile) ? marginHeight() * 1.75 : marginHeight())
      .style("margin-top", "8px");

    d3.selectAll(".key-group")
      .attr("transform", function(d, i) {
        var x, y;

        if (mobile) {
          x = 0;
          y = i
        } else {
          x = (i <= 2) ? 0 : width / 2;
          y = (i <= 2) ? i : i - 3;
        }

        return "translate(" + x + "," + (y * 16 + 16) + ")";
      });

    // Scales, axes
    // ---------------------------------------------------------------------------
    dates = data[0].revenue.map(function(d) { return d.date; });

    // x scale by year
    x = d3.time.scale()
      .range([0, width])
      .domain(d3.extent(dates));

    // y scale by revenue
    y = d3.scale.linear()
      .range([height, 0])
      .domain([
        0,
        d3.max(data, function(d) {
          return d3.max(d.revenue, function(r) {
            return r.adjusted;
          })
        })
      ]);

    // y scale by enrollment
    adm = d3.scale.linear()
      .range([height / 2, 0])
      .domain(d3.extent(students, function(d) { return d.enrollment; }));

    // lines excluding enrollment
    color = d3.scale.ordinal()
      .domain(keys)
      .range(["#0060ae", "#0060ae", "#0060ae", "#8dbd45", "#df2027"]);

    xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom")
      .tickValues(dates.filter(function(d, i) {
        return (mobile) ? !(i % 4) : !(i % 2);
      }))
      .tickFormat(d3.time.format("%Y"));

    yAxis = d3.svg.axis()
      .scale(y)
      .tickFormat(function(d) { return "$" + d3.format(".2s")(d); })
      .tickSize(-width, 0, 0)
      .orient("left");

    aAxis = d3.svg.axis()
      .scale(adm)
      .orient("left")
      .tickSize(-width, 0, 0)
      .ticks(4);

    // svg setup
    // ---------------------------------------------------------------------------
    revenue = d3.select(chart).append("svg")
        .attr("width", width + marginWidth())
        .attr("height", height + marginHeight())
      .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    revenue.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    revenue.append("g")
      .attr("class", "y axis")
      .call(yAxis);

    d3.select(".y.axis .tick").remove(); // Removes first tick mark

    enrollment = d3.select(chart).append("svg")
        .attr("width", width + marginWidth())
        .attr("height", height / 2 + marginHeight())
      .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    enrollment.append("g")
      .attr("class", "a axis")
      .call(aAxis);

    // y axis labels
    d3.select(".y.axis")
      .append("text")
      .attr("class", "label")
      .attr("transform", "rotate(-90)")
      .attr("text-anchor", "middle")
      .attr("x", -height / 2)
      .attr("y", -margin.left * .8)
      .text("Revenue");

    d3.select(".a.axis")
      .append("text")
      .attr("class", "label")
      .attr("transform", "rotate(-90)")
      .attr("text-anchor", "middle")
      .attr("x", -height / 4)
      .attr("y", -margin.left * .8)
      .text("Enrollment");

    // Revenue line graphs
    // ---------------------------------------------------------------------------
    line = d3.svg.line()
      .x(function(d) { return x(d.date); })
      .y(function(d) { return y(d.unadjusted); });

    revenue.selectAll("g.revenue-source")
        .data(data)
      .enter().append("g")
        .attr("class", "revenue-source");

    d3.selectAll("g.revenue-source").append("path")
      .attr("class", "line")
      .attr("d", function(d) { return line(d.revenue); })
      .attr("stroke-dasharray", function(d) {
        switch (d.name) {
          case "Property tax":
            return "5,5";
          case "Sales tax":
            return "0.9";
          default:
            return false;
        }
      })
      .style("stroke", function(d) { return color(d.name); });

    document.querySelector("#adjustment").addEventListener("click", function() {
      var button = this;

      if (button.innerHTML == "Reset to nominal") {
        line.y(function(d) { return y(d.unadjusted); });
        button.innerHTML = "Adjust for inflation";
      } else {
        line.y(function(d) { return y(d.adjusted); });
        button.innerHTML = "Reset to nominal";
      }

      d3.selectAll(".revenue-source .line")
        .transition()
          .delay(100)
          .duration(500)
        .attr("d", function(d) { return line(d.revenue); });
    });

    // Enrollment line graph
    // ---------------------------------------------------------------------------
    enrollment.append("path")
      .datum(students)
      .attr("class", "line")
      .attr("d", d3.svg.line()
        .x(function(d) { return x(d.date); })
        .y(function(d) { return adm(d.enrollment); }));

  } //  End of drawGraphic()

  // Adjusts for inflation using CPI-U for South region
  // http://data.bls.gov/pdq/SurveyOutputServlet?series_id=CUUR0300SA0,CUUS0300SA0
  // ---------------------------------------------------------------------------
  function adjustForInflation(amount, current, previous) {
    var cpi = {
      1998: 158.9,   1999: 162.0,   2000: 167.2,   2001: 171.1,   2002: 173.3,
      2003: 177.3,   2004: 181.8,   2005: 188.3,   2006: 194.7,   2007: 200.361,
      2008: 208.681, 2009: 207.845, 2010: 211.338, 2011: 218.618, 2012: 223.242,
      2013: 226.721, 2014: 230.552, 2015: 230.147
    };

    return amount * cpi[previous] / cpi[current];
  };

  // Load and map data
  // ---------------------------------------------------------------------------
  d3.csv("data.csv", function(error, csv) {
    if (error) throw error;

    keys = d3.keys(csv[0]).filter(function(key) {
      return key != "year" && key != "adm";
    });

    data = keys.map(function(key) {
      return {
        name: key,
        revenue: csv.map(function(row) {
          return {
            date: d3.time.format("%Y%m%d").parse(row.year + "0630"),
            unadjusted: +row[key],
            adjusted: Math.round(adjustForInflation(row[key], row.year, 2015))
          };
        })
      };
    });

    students = csv.map(function(row) {
      return {
        date: d3.time.format("%Y%m%d").parse(row.year + "0630"),
        enrollment: +row.adm
      };
    });

    console.log(students);

    new pym.Child({ renderCallback: drawGraphic });
  });
})()<commit_msg>Adds touch event for inflation adjustment<commit_after>(function() {
  var
  data,
  keys,
  students,
  chart = document.querySelector(".chart")
  ;

  function drawGraphic(containerWidth) {
    chart.innerHTML = "";

    var
    x,
    y,
    adm,
    color,
    dates,
    xAxis,
    yAxis,
    line,
    legend,
    revenue,
    enrollment,
    margin = { top: 16, right: 16, bottom: 32 , left: 64 },
    width  = calculateWidth(),
    mobile = (width <= 320),
    ratio  = (mobile) ? { width: 1, height: 2 } : { width: 3, height: 2 },
    height = calculateHeight()
    ;

    // Helper functions for chart dimensions
    // ---------------------------------------------------------------------------
    function marginWidth()  { return margin.left + margin.right; }
    function marginHeight() { return margin.top + margin.bottom; }

    function calculateWidth() {
      if (!containerWidth) { var containerWidth = +chart.offsetWidth; }
      return Math.ceil(containerWidth - marginWidth());
    }

    function calculateHeight() {
      return Math.ceil(width * ratio.height / ratio.width) - marginHeight();
    }

    // Data source and header container (Title, description, legend and adjustment button)
    // ---------------------------------------------------------------------------
    d3.select(".header").style({
      "margin-left": "7px",
      "margin-right": "7px"
    });
    d3.select(".data-source").style({
      "margin-left": "7px",
      "margin-right": "7px"
    });

    // Legend
    // ---------------------------------------------------------------------------
    legend = d3.select(".legend svg")
      .attr("width", width)
      .attr("height", (mobile) ? marginHeight() * 1.75 : marginHeight())
      .style("margin-top", "8px");

    d3.selectAll(".key-group")
      .attr("transform", function(d, i) {
        var x, y;

        if (mobile) {
          x = 0;
          y = i
        } else {
          x = (i <= 2) ? 0 : width / 2;
          y = (i <= 2) ? i : i - 3;
        }

        return "translate(" + x + "," + (y * 16 + 16) + ")";
      });

    // Scales, axes
    // ---------------------------------------------------------------------------
    dates = data[0].revenue.map(function(d) { return d.date; });

    // x scale by year
    x = d3.time.scale()
      .range([0, width])
      .domain(d3.extent(dates));

    // y scale by revenue
    y = d3.scale.linear()
      .range([height, 0])
      .domain([
        0,
        d3.max(data, function(d) {
          return d3.max(d.revenue, function(r) {
            return r.adjusted;
          })
        })
      ]);

    // y scale by enrollment
    adm = d3.scale.linear()
      .range([height / 2, 0])
      .domain(d3.extent(students, function(d) { return d.enrollment; }));

    // lines excluding enrollment
    color = d3.scale.ordinal()
      .domain(keys)
      .range(["#0060ae", "#0060ae", "#0060ae", "#8dbd45", "#df2027"]);

    xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom")
      .tickValues(dates.filter(function(d, i) {
        return (mobile) ? !(i % 4) : !(i % 2);
      }))
      .tickFormat(d3.time.format("%Y"));

    yAxis = d3.svg.axis()
      .scale(y)
      .tickFormat(function(d) { return "$" + d3.format(".2s")(d); })
      .tickSize(-width, 0, 0)
      .orient("left");

    aAxis = d3.svg.axis()
      .scale(adm)
      .orient("left")
      .tickSize(-width, 0, 0)
      .ticks(4);

    // svg setup
    // ---------------------------------------------------------------------------
    revenue = d3.select(chart).append("svg")
        .attr("width", width + marginWidth())
        .attr("height", height + marginHeight())
      .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    revenue.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    revenue.append("g")
      .attr("class", "y axis")
      .call(yAxis);

    d3.select(".y.axis .tick").remove(); // Removes first tick mark

    enrollment = d3.select(chart).append("svg")
        .attr("width", width + marginWidth())
        .attr("height", height / 2 + marginHeight())
      .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    enrollment.append("g")
      .attr("class", "a axis")
      .call(aAxis);

    // y axis labels
    d3.select(".y.axis")
      .append("text")
      .attr("class", "label")
      .attr("transform", "rotate(-90)")
      .attr("text-anchor", "middle")
      .attr("x", -height / 2)
      .attr("y", -margin.left * .8)
      .text("Revenue");

    d3.select(".a.axis")
      .append("text")
      .attr("class", "label")
      .attr("transform", "rotate(-90)")
      .attr("text-anchor", "middle")
      .attr("x", -height / 4)
      .attr("y", -margin.left * .8)
      .text("Enrollment");

    // Revenue line graphs
    // ---------------------------------------------------------------------------
    line = d3.svg.line()
      .x(function(d) { return x(d.date); })
      .y(function(d) { return y(d.unadjusted); });

    revenue.selectAll("g.revenue-source")
        .data(data)
      .enter().append("g")
        .attr("class", "revenue-source");

    d3.selectAll("g.revenue-source").append("path")
      .attr("class", "line")
      .attr("d", function(d) { return line(d.revenue); })
      .attr("stroke-dasharray", function(d) {
        switch (d.name) {
          case "Property tax":
            return "5,5";
          case "Sales tax":
            return "0.9";
          default:
            return false;
        }
      })
      .style("stroke", function(d) { return color(d.name); });

    function cpiAdjust() {
      var button = this;

      if (button.innerHTML == "Reset to nominal") {
        line.y(function(d) { return y(d.unadjusted); });
        button.innerHTML = "Adjust for inflation";
      } else {
        line.y(function(d) { return y(d.adjusted); });
        button.innerHTML = "Reset to nominal";
      }

      d3.selectAll(".revenue-source .line")
        .transition()
          .delay(100)
          .duration(500)
        .attr("d", function(d) { return line(d.revenue); });
    }

    document.querySelector("#adjustment").addEventListener("click", cpiAdjust);
    document.querySelector("#adjustment").addEventListener("touchend", cpiAdjust);

    // Enrollment line graph
    // ---------------------------------------------------------------------------
    enrollment.append("path")
      .datum(students)
      .attr("class", "line")
      .attr("d", d3.svg.line()
        .x(function(d) { return x(d.date); })
        .y(function(d) { return adm(d.enrollment); }));

  } //  End of drawGraphic()

  // Adjusts for inflation using CPI-U for South region
  // http://data.bls.gov/pdq/SurveyOutputServlet?series_id=CUUR0300SA0,CUUS0300SA0
  // ---------------------------------------------------------------------------
  function adjustForInflation(amount, current, previous) {
    var cpi = {
      1998: 158.9,   1999: 162.0,   2000: 167.2,   2001: 171.1,   2002: 173.3,
      2003: 177.3,   2004: 181.8,   2005: 188.3,   2006: 194.7,   2007: 200.361,
      2008: 208.681, 2009: 207.845, 2010: 211.338, 2011: 218.618, 2012: 223.242,
      2013: 226.721, 2014: 230.552, 2015: 230.147
    };

    return amount * cpi[previous] / cpi[current];
  };

  // Load and map data
  // ---------------------------------------------------------------------------
  d3.csv("data.csv", function(error, csv) {
    if (error) throw error;

    keys = d3.keys(csv[0]).filter(function(key) {
      return key != "year" && key != "adm";
    });

    data = keys.map(function(key) {
      return {
        name: key,
        revenue: csv.map(function(row) {
          return {
            date: d3.time.format("%Y%m%d").parse(row.year + "0630"),
            unadjusted: +row[key],
            adjusted: Math.round(adjustForInflation(row[key], row.year, 2015))
          };
        })
      };
    });

    students = csv.map(function(row) {
      return {
        date: d3.time.format("%Y%m%d").parse(row.year + "0630"),
        enrollment: +row.adm
      };
    });

    console.log(students);

    new pym.Child({ renderCallback: drawGraphic });
  });
})()<|endoftext|>