<commit_before>"use strict";
(function(window) {
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _hooks = opts.hooks || {created: function() {}, mounted: function() {}, updated: function() {}, destroyed: function() {}};
        var _destroyed = false;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$methods = opts.methods || {};
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        /**
        * Logs a Message
        * @param {String} msg
        */
        this.log = function(msg) {
          if(!config.silent) console.log(msg);
        }

        /**
        * Throws an Error
        * @param {String} msg
        */
        this.error = function(msg) {
          console.log("Moon ERR: " + msg);
        }

        /**
        * Creates an object to be used in a Virtual DOM
        * @param {String} type
        * @param {Array} children
        * @param {String} val
        * @param {Object} props
        * @param {Node} node
        * @return {Object} Object usable in Virtual DOM
        */
        this.createElement = function(type, children, val, props, node) {
          return {type: type, children: children, val: val, props: props, node: node};
        }

        /**
        * Create Elements Recursively For all Children
        * @param {Array} children
        * @return {Array} Array of elements usable in Virtual DOM
        */
        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
          }
          return recursiveChildrenArr;
        }

        /**
        * Creates Virtual DOM
        * @param {Node} node
        */
        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
          this.$dom = vdom;
        }

        /**
        * Sets Value in Data
        * @param {String} key
        * @param {String} val
        */
        this.set = function(key, val) {
          this.$data[key] = val;
          if(!_destroyed) this.build(this.$dom.children);
          if(_hooks.updated) {
            _hooks.updated();
          }
        }

        /**
        * Gets Value in Data
        * @param {String} key
        * @return {String} Value of key in data
        */
        this.get = function(key) {
          return this.$data[key];
        }

        /**
        * Calls a method
        * @param {String} method
        */
        this.method = function(method) {
          this.$methods[method]();
        }

        this.destroy = function() {
          Object.defineProperty(this, '$data', {
            set: function(value) {
              _data = value;
            }
          });
          _destroyed = true;
          if(_hooks.destroyed) _hooks.destroyed();
        }

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          el.removeAttribute("m-model");
          delete vdom.props["m-model"];
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        // directives["m-for"] = function(el, val, vdom) {
        //   var splitVal = val.split(" in ");
        //   var alias = splitVal[0];
        //   var arr = self.get(splitVal[1]);
        //   var clone = el.cloneNode(true);
        //   var oldVal = vdom.val;
        //   var compilable = vdom.val.replace(new RegExp(alias, "gi"), splitVal[1] + '[0]');
        //   el.innerHTML = compileTemplate(compilable, self.$data);
        //   for(var i = 1; i < arr.length; i++) {
        //     var newClone = clone.cloneNode(true);
        //     var compilable = oldVal.replace(new RegExp(alias, "gi"), splitVal[1] + '[' + i + ']');
        //     newClone.innerHTML = compileTemplate(compilable, self.$data);
        //     var parent = el.parentNode;
        //     parent.appendChild(newClone);
        //   }
        //   vdom.val = el.textContent;
        //   delete vdom.props["m-for"];
        // }

        /**
        * Builds the DOM With Data
        * @param {Array} children
        */
        this.build = function(children) {
          for(var i = 0; i < children.length; i++) {
            var el = children[i];

            if(el.type === "#text") {
              el.node.textContent = compileTemplate(el.val, this.$data);
            } else if(el.props) {
              for(var prop in el.props) {
                var propVal = el.props[prop];
                var compiledProperty = compileTemplate(propVal, this.$data);
                var directive = directives[prop];
                if(directive) {
                  el.node.removeAttribute(prop);
                  directive(el.node, compiledProperty, el);
                }

                if(!directive) el.node.setAttribute(prop, compiledProperty);
              }
            }

            this.build(el.children);
          }
        }

        /**
        * Initializes Moon
        */
        this.init = function() {
          this.log("======= Moon =======");
          if(_hooks.created) {
            _hooks.created();
          }
          this.createVirtualDOM(this.$el);
          this.build(this.$dom.children);
          if(_hooks.mounted) {
            _hooks.mounted();
          }
        }

        // Initialize 🎉
        this.init();
    }

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a subclass of Moon
    * @param {Object} opts
    */
    Moon.extend = function(opts) {
      var Super = this;
      function Sub(opts) {
        Super.call(this, opts);
      }
      Sub.prototype = Object.create(Super.prototype);
      Sub.prototype.constructor = Sub;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, opts) {
      components[name] = opts;
    }

    /**
    * Makes an AJAX Request
    * @param {String} method
    * @param {String} url
    * @param {Object} params
    * @param {Function} cb
    */
    Moon.ajax = function(method, url, params, cb) {
      var xmlHttp = new XMLHttpRequest();
      method = method.toUpperCase();
      if(typeof params === "function") {
        cb = params;
      }
      var urlParams = "?";
      if(method === "POST") {
        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        for(var param in params) {
          urlParams += param + "=" + params[param] + "&";
        }
      }
      xmlHttp.onreadystatechange = function() {
      if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
        cb(JSON.parse(xmlHttp.responseText));
      }
      xmlHttp.open(method, url, true);
      xmlHttp.send(method === "POST" ? urlParams : null);
    }

    window.Moon = Moon;
})(window);
<commit_msg>return the new subclass<commit_after>"use strict";
(function(window) {
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _hooks = opts.hooks || {created: function() {}, mounted: function() {}, updated: function() {}, destroyed: function() {}};
        var _destroyed = false;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$methods = opts.methods || {};
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        /**
        * Logs a Message
        * @param {String} msg
        */
        this.log = function(msg) {
          if(!config.silent) console.log(msg);
        }

        /**
        * Throws an Error
        * @param {String} msg
        */
        this.error = function(msg) {
          console.log("Moon ERR: " + msg);
        }

        /**
        * Creates an object to be used in a Virtual DOM
        * @param {String} type
        * @param {Array} children
        * @param {String} val
        * @param {Object} props
        * @param {Node} node
        * @return {Object} Object usable in Virtual DOM
        */
        this.createElement = function(type, children, val, props, node) {
          return {type: type, children: children, val: val, props: props, node: node};
        }

        /**
        * Create Elements Recursively For all Children
        * @param {Array} children
        * @return {Array} Array of elements usable in Virtual DOM
        */
        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
          }
          return recursiveChildrenArr;
        }

        /**
        * Creates Virtual DOM
        * @param {Node} node
        */
        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
          this.$dom = vdom;
        }

        /**
        * Sets Value in Data
        * @param {String} key
        * @param {String} val
        */
        this.set = function(key, val) {
          this.$data[key] = val;
          if(!_destroyed) this.build(this.$dom.children);
          if(_hooks.updated) {
            _hooks.updated();
          }
        }

        /**
        * Gets Value in Data
        * @param {String} key
        * @return {String} Value of key in data
        */
        this.get = function(key) {
          return this.$data[key];
        }

        /**
        * Calls a method
        * @param {String} method
        */
        this.method = function(method) {
          this.$methods[method]();
        }

        this.destroy = function() {
          Object.defineProperty(this, '$data', {
            set: function(value) {
              _data = value;
            }
          });
          _destroyed = true;
          if(_hooks.destroyed) _hooks.destroyed();
        }

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          el.removeAttribute("m-model");
          delete vdom.props["m-model"];
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        // directives["m-for"] = function(el, val, vdom) {
        //   var splitVal = val.split(" in ");
        //   var alias = splitVal[0];
        //   var arr = self.get(splitVal[1]);
        //   var clone = el.cloneNode(true);
        //   var oldVal = vdom.val;
        //   var compilable = vdom.val.replace(new RegExp(alias, "gi"), splitVal[1] + '[0]');
        //   el.innerHTML = compileTemplate(compilable, self.$data);
        //   for(var i = 1; i < arr.length; i++) {
        //     var newClone = clone.cloneNode(true);
        //     var compilable = oldVal.replace(new RegExp(alias, "gi"), splitVal[1] + '[' + i + ']');
        //     newClone.innerHTML = compileTemplate(compilable, self.$data);
        //     var parent = el.parentNode;
        //     parent.appendChild(newClone);
        //   }
        //   vdom.val = el.textContent;
        //   delete vdom.props["m-for"];
        // }

        /**
        * Builds the DOM With Data
        * @param {Array} children
        */
        this.build = function(children) {
          for(var i = 0; i < children.length; i++) {
            var el = children[i];

            if(el.type === "#text") {
              el.node.textContent = compileTemplate(el.val, this.$data);
            } else if(el.props) {
              for(var prop in el.props) {
                var propVal = el.props[prop];
                var compiledProperty = compileTemplate(propVal, this.$data);
                var directive = directives[prop];
                if(directive) {
                  el.node.removeAttribute(prop);
                  directive(el.node, compiledProperty, el);
                }

                if(!directive) el.node.setAttribute(prop, compiledProperty);
              }
            }

            this.build(el.children);
          }
        }

        /**
        * Initializes Moon
        */
        this.init = function() {
          this.log("======= Moon =======");
          if(_hooks.created) {
            _hooks.created();
          }
          this.createVirtualDOM(this.$el);
          this.build(this.$dom.children);
          if(_hooks.mounted) {
            _hooks.mounted();
          }
        }

        // Initialize 🎉
        this.init();
    }

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a subclass of Moon
    * @param {Object} opts
    */
    Moon.extend = function(opts) {
      var Super = this;
      function Sub(opts) {
        Super.call(this, opts);
      }
      Sub.prototype = Object.create(Super.prototype);
      Sub.prototype.constructor = Sub;
      return Sub;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, opts) {
      components[name] = opts;
    }

    /**
    * Makes an AJAX Request
    * @param {String} method
    * @param {String} url
    * @param {Object} params
    * @param {Function} cb
    */
    Moon.ajax = function(method, url, params, cb) {
      var xmlHttp = new XMLHttpRequest();
      method = method.toUpperCase();
      if(typeof params === "function") {
        cb = params;
      }
      var urlParams = "?";
      if(method === "POST") {
        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        for(var param in params) {
          urlParams += param + "=" + params[param] + "&";
        }
      }
      xmlHttp.onreadystatechange = function() {
      if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
        cb(JSON.parse(xmlHttp.responseText));
      }
      xmlHttp.open(method, url, true);
      xmlHttp.send(method === "POST" ? urlParams : null);
    }

    window.Moon = Moon;
})(window);
<|endoftext|>


<commit_before>var http = require('http');
var net = require('net');
var url = require('url');
var config = require('../lib/config');
var util = require('../lib/util');

function parseHeaders(headers, rawHeaderNames) {
  if (!headers || typeof headers != 'string') {
    return {};
  }

  try {
    return util.lowerCaseify(JSON.parse(headers), rawHeaderNames);
  } catch(e) {}

  return util.parseHeaders(headers, rawHeaderNames);
}

function getMethod(method) {
  if (typeof method !== 'string') {
    return 'GET';
  }
  return method.toUpperCase();
}

function isWebSocket(options) {
  var p = options.protocol;
  return p === 'ws:' || p === 'wss:';
}

var crypto = require('crypto');
function isConnect(options) {
  if (options.method === 'CONNECT') {
    return true;
  }
  var p = options.protocol;
  return p === 'connect:' || p === 'socket:' || p === 'tunnel:';
}

function drain(socket) {
  socket.on('error', util.noop);
  socket.on('data', util.noop);
}

function handleConnect(options) {
  options.headers['x-whistle-policy'] = 'tunnel';
  config.connect({
    host: options.hostname,
    port: options.port,
    proxyHost: '127.0.0.1',
    proxyPort: config.port,
    headers: options.headers
  }, drain).on('error', util.noop);
}

function getReqRaw(options) {
  var headers = options.headers;
  var statusLine = options.method +' ' + (options.path || '/') +' ' + 'HTTP/1.1';
  var raw = [statusLine, util.getRawHeaders(headers)];
  return raw.join('\r\n') + '\r\n\r\n';
}

function handleWebSocket(options) {
  if (options.protocol === 'https:' || options.protocol === 'wss:') {
    options.headers[config.HTTPS_FIELD] = 1;
  }
  var socket = net.connect(config.port, '127.0.0.1', function() {
    socket.write(getReqRaw(options));
  });
  drain(socket);
}

function handleHttp(options) {
  if (options.protocol === 'https:') {
    options.headers[config.HTTPS_FIELD] = 1;
  }
  options.protocol = null;
  options.hostname = null;
  options.host = '127.0.0.1';
  options.port = config.port;
  http.request(options, function(res) {
    res.on('error', util.noop);
    util.drain(res);
  }).on('error', util.noop).end(options.body);
}

module.exports = function(req, res) {
  var fullUrl = req.body.url;
  if (!fullUrl || typeof fullUrl !== 'string') {
    return res.json({ec: 0});
  }

  fullUrl = util.encodeNonAsciiChar(fullUrl.replace(/#.*$/, ''));
  var options = url.parse(util.setProtocol(fullUrl));
  if (!options.host) {
    return res.json({ec: 0});
  }

  var rawHeaderNames = {};
  var headers = parseHeaders(req.body.headers, rawHeaderNames);
  if (!headers['user-agent']) {
    headers['user-agent'] = 'whistle/' + config.version;
  }
  headers.host = options.host;
  headers[config.CLIENT_IP_HEAD] = util.getClientIp(req);
  options.method = getMethod(req.body.method);

  if (headers['content-length'] != null) {
    req.body.body = util.toBuffer(req.body.body || '');
    headers['content-length'] = req.body.body.length;
  }

  var isWs = isWebSocket(options)
    || (/upgrade/i.test(headers.connection) && /websocket/i.test(headers.upgrade));
  if (isWs) {
    headers.connection = 'Upgrade';
    headers.upgrade = 'websocket';
    headers['sec-websocket-version'] = 13;
    headers['sec-websocket-key'] = crypto.randomBytes(16).toString('base64');
  } else {
    headers.connection = 'close';
    delete headers.upgrade;
  }
  options.headers = util.formatHeaders(headers, rawHeaderNames);
  options.body = req.body.body;
  if (isWs) {
    handleWebSocket(options);
  } else if (isConnect(options)) {
    handleConnect(options);
  } else  {
    handleHttp(options);
  }

  res.json({ec: 0, em: 'success'});
};
<commit_msg>refactor: Composer<commit_after>var http = require('http');
var net = require('net');
var url = require('url');
var config = require('../lib/config');
var util = require('../lib/util');

function parseHeaders(headers, rawHeaderNames) {
  if (!headers || typeof headers != 'string') {
    return {};
  }

  try {
    return util.lowerCaseify(JSON.parse(headers), rawHeaderNames);
  } catch(e) {}

  return util.parseHeaders(headers, rawHeaderNames);
}

function getMethod(method) {
  if (typeof method !== 'string') {
    return 'GET';
  }
  return method.toUpperCase();
}

function isWebSocket(options) {
  var p = options.protocol;
  return p === 'ws:' || p === 'wss:';
}

var crypto = require('crypto');
function isConnect(options) {
  if (options.method === 'CONNECT') {
    return true;
  }
  var p = options.protocol;
  return p === 'connect:' || p === 'socket:' || p === 'tunnel:';
}

function drain(socket) {
  socket.on('error', util.noop);
  socket.on('data', util.noop);
}

function handleConnect(options) {
  options.headers['x-whistle-policy'] = 'tunnel';
  config.connect({
    host: options.hostname,
    port: options.port,
    proxyHost: '127.0.0.1',
    proxyPort: config.port,
    headers: options.headers
  }, function(socket) {
    drain(socket);
    if (options.body) {
      socket.write(options.body);
    }
  }).on('error', util.noop);
}

function getReqRaw(options) {
  var headers = options.headers;
  var statusLine = options.method +' ' + (options.path || '/') +' ' + 'HTTP/1.1';
  var raw = [statusLine, util.getRawHeaders(headers)];
  return raw.join('\r\n') + '\r\n\r\n';
}

function handleWebSocket(options) {
  if (options.protocol === 'https:' || options.protocol === 'wss:') {
    options.headers[config.HTTPS_FIELD] = 1;
  }
  var socket = net.connect(config.port, '127.0.0.1', function() {
    socket.write(getReqRaw(options));
    var handleResponse = function(data) {
      socket.removeListener('data', handleResponse);
      socket.write(options.body);
    };
    if (options.body) {
      socket.on('data', handleResponse);
    }
  });
  drain(socket);
}

function handleHttp(options) {
  if (options.protocol === 'https:') {
    options.headers[config.HTTPS_FIELD] = 1;
  }
  options.protocol = null;
  options.hostname = null;
  options.host = '127.0.0.1';
  options.port = config.port;
  http.request(options, function(res) {
    res.on('error', util.noop);
    util.drain(res);
  }).on('error', util.noop).end(options.body);
}

module.exports = function(req, res) {
  var fullUrl = req.body.url;
  if (!fullUrl || typeof fullUrl !== 'string') {
    return res.json({ec: 0});
  }

  fullUrl = util.encodeNonAsciiChar(fullUrl.replace(/#.*$/, ''));
  var options = url.parse(util.setProtocol(fullUrl));
  if (!options.host) {
    return res.json({ec: 0});
  }

  var rawHeaderNames = {};
  var headers = parseHeaders(req.body.headers, rawHeaderNames);
  if (!headers['user-agent']) {
    headers['user-agent'] = 'whistle/' + config.version;
  }
  headers.host = options.host;
  headers[config.CLIENT_IP_HEAD] = util.getClientIp(req);
  options.method = getMethod(req.body.method);

  var isWs = isWebSocket(options)
    || (/upgrade/i.test(headers.connection) && /websocket/i.test(headers.upgrade));
  if (isWs) {
    headers.connection = 'Upgrade';
    headers.upgrade = 'websocket';
    headers['sec-websocket-version'] = 13;
    headers['sec-websocket-key'] = crypto.randomBytes(16).toString('base64');
  } else {
    headers.connection = 'close';
    delete headers.upgrade;
  }
  var isConn = !isWs && isConnect(options);
  var body = req.body.body;
  if (body && (isWs || isConn || util.hasRequestBody(options))) {
    body = options.body = util.toBuffer(body);
  }
  if ('content-length' in headers) {
    headers['content-length'] = body ? body.length : 0;
  }
  options.headers = util.formatHeaders(headers, rawHeaderNames);

  if (isWs) {
    handleWebSocket(options);
  } else if (isConn) {
    handleConnect(options);
  } else  {
    handleHttp(options);
  }

  res.json({ec: 0, em: 'success'});
};
<|endoftext|>


<commit_before>'use strict';

var moment = require('moment');

var Part = require('./part');
var units = require('./units');

/**
 * Creates an instance of Cron.
 * Cron objects each represent a cron schedule.
 *
 * @constructor
 * @this {Cron}
 */
function Cron() {
  this.parts = null;
}

/**
 * Parses a cron string.
 *
 * @this {Cron}
 * @param {string} str The string to parse.
 */
Cron.prototype.parse = function(str) {
  if (typeof str !== 'string') {
    throw new Error('Invalid cron string');
  }
  var parts = str.replace(/\s+/g, ' ').trim().split(' ');
  if (parts.length === 5) {
    this.parts = parts.map(function(str, idx) {
      return new Part(str, units[idx]);
    });
  } else {
    throw new Error('Invalid cron string format');
  }
  return this;
};

/**
 * Returns the cron schedule as
 * a 2-dimentional array of integers.
 *
 * @this {Cron}
 * @return {array} The cron schedule as an array.
 */
Cron.prototype.toArray = function() {
  if (this.parts === null) {
    throw new Error('No schedule found');
  }
  return this.parts.map(function(part) {
    return part.toArray();
  });
};

/**
 * Returns the cron schedule as a string.
 *
 * @this {Cron}
 * @return {string} The cron schedule as a string.
 */
Cron.prototype.toString = function() {
  if (this.parts === null) {
    throw new Error('No schedule found');
  }
  return this.parts.join(' ');
};

/**
 * Returns the time the schedule would run next.
 *
 * @this {Cron}
 * @return {Date} The time the schedule would run next.
 */
Cron.prototype.next = function() {
  var date = moment();
  // Check month
  while (!this.parts[3].has(date.month() + 1)) {
    date.add(1, 'months').startOf('month');
  }
  // Check day of month and weekday
  while (!this.parts[2].has(date.date()) && !this.parts[4].has(date.day())) {
    date.add(1, 'days').startOf('day');
  }
  // Check hour
  while (!this.parts[1].has(date.hour())) {
    date.add(1, 'hours').startOf('hour');
  }
  // Check minute
  while (!this.parts[0].has(date.minute())) {
    date.add(1, 'minutes').startOf('minute');
  }
  date.seconds(0).milliseconds(0);
  // Return JS Date object
  return date.toDate();
};

/**
 * Returns the time the schedule would have last run at.
 *
 * @this {Cron}
 * @return {Date} The time the schedule would have last run at.
 */
Cron.prototype.prev = function() {
  var date = moment();
  // Check month
  while (!this.parts[3].has(date.month() + 1)) {
    date.subtract(1, 'months').endOf('month');
  }
  // Check day of month and weekday
  while (!this.parts[2].has(date.date()) && !this.parts[4].has(date.day())) {
    date.subtract(1, 'days').endOf('day');
  }
  // Check hour
  while (!this.parts[1].has(date.hour())) {
    date.subtract(1, 'hours').endOf('hour');
  }
  // Check minute
  while (!this.parts[0].has(date.minute())) {
    date.subtract(1, 'minutes').endOf('minute');
  }
  date.seconds(0).milliseconds(0);
  // Return JS Date object
  return date.toDate();
};

module.exports = Cron;
<commit_msg>Refactored Cron.next<commit_after>'use strict';

var moment = require('moment');

var Part = require('./part');
var units = require('./units');

/**
 * Creates an instance of Cron.
 * Cron objects each represent a cron schedule.
 *
 * @constructor
 * @this {Cron}
 */
function Cron() {
  this.parts = null;
}

/**
 * Parses a cron string.
 *
 * @this {Cron}
 * @param {string} str The string to parse.
 */
Cron.prototype.parse = function(str) {
  if (typeof str !== 'string') {
    throw new Error('Invalid cron string');
  }
  var parts = str.replace(/\s+/g, ' ').trim().split(' ');
  if (parts.length === 5) {
    this.parts = parts.map(function(str, idx) {
      return new Part(str, units[idx]);
    });
  } else {
    throw new Error('Invalid cron string format');
  }
  return this;
};

/**
 * Returns the cron schedule as
 * a 2-dimentional array of integers.
 *
 * @this {Cron}
 * @return {array} The cron schedule as an array.
 */
Cron.prototype.toArray = function() {
  if (this.parts === null) {
    throw new Error('No schedule found');
  }
  return this.parts.map(function(part) {
    return part.toArray();
  });
};

/**
 * Returns the cron schedule as a string.
 *
 * @this {Cron}
 * @return {string} The cron schedule as a string.
 */
Cron.prototype.toString = function() {
  if (this.parts === null) {
    throw new Error('No schedule found');
  }
  return this.parts.join(' ');
};

/**
 * Returns the time the schedule would run next.
 *
 * @this {Cron}
 * @param {mixed} now Any date object that moment.js will accept.
 *                    undefined works the same as new Date()
 * @return {Date} The time the schedule would run next.
 */
Cron.prototype.next = function(now, reverse) {
  var date = moment(now);
  if (!date.isValid()) {
    throw new Error('Invalid date provided');
  }
  var operation = 'add';
  var reset = 'startOf';
  if (reverse) {
    operation = 'subtract';
    reset = 'endOf';
  }
  // Check month
  while (!this.parts[3].has(date.month() + 1)) {
    // date.add(1, 'months').startOf('month')
    // date.subtract(1, 'months').endOf('month')
    date[operation](1, 'months')[reset]('month');
  }
  // Check day of month and weekday
  while (!this.parts[2].has(date.date()) && !this.parts[4].has(date.day())) {
    date[operation](1, 'days')[reset]('day');
  }
  // Check hour
  while (!this.parts[1].has(date.hour())) {
    date[operation](1, 'hours')[reset]('hour');
  }
  // Check minute
  while (!this.parts[0].has(date.minute())) {
    date[operation](1, 'minutes')[reset]('minute');
  }
  date.seconds(0).milliseconds(0);
  // Return JS Date object
  return date.toDate();
};

/**
 * Returns the time the schedule would have last run at.
 *
 * @this {Cron}
 * @param {mixed} now Any date object that moment.js will accept.
 *                    undefined works the same as new Date()
 * @return {Date} The time the schedule would have last run at.
 */
Cron.prototype.prev = function(now) {
  return this.next(now, true);
};

module.exports = Cron;
<|endoftext|>


<commit_before>'use strict'

const assert = require('assert')

const Bitmask = require('./bitmask')
const ExtDateTime = require('./interface')
const mixin = require('./mixin')

const { abs } = Math
const { isArray } = Array

const P = new WeakMap()
const U = new WeakMap()
const A = new WeakMap()
const X = new WeakMap()

const PM = [Bitmask.YMD, Bitmask.Y, Bitmask.YM, Bitmask.YMD]

class Date extends global.Date {
  constructor(...args) { // eslint-disable-line complexity
    let precision = 0
    let uncertain, approximate, unspecified

    switch (args.length) {
    case 0:
      break

    case 1:
      switch (typeof args[0]) {
      case 'number':
        break

      case 'string':
        args = [Date.parse(args[0])]
        // eslint-disable-line no-fallthrough

      case 'object':
        if (isArray(args[0]))
          args[0] = { values: args[0] }

        {
          let obj = args[0]

          assert(obj != null)
          if (obj.type) assert.equal('Date', obj.type)

          if (obj.values && obj.values.length) {
            precision = obj.values.length
            args = obj.values.slice()

            // ECMA Date constructor needs at least two date parts!
            if (args.length < 2) args.push(0)

            if (obj.offset) {
              if (args.length < 3) args.push(1)
              while (args.length < 5) args.push(0)

              // ECMA Date constructor handles overflows so we
              // simply add the offset!
              args[4] = args[4] + obj.offset
            }

            args = [ExtDateTime.UTC(...args)]
          }

          ({ uncertain, approximate, unspecified } = obj)
        }
        break

      default:
        throw new RangeError('Invalid time value')
      }

      break

    default:
      precision = args.length
    }

    super(...args)

    this.precision = precision

    this.uncertain = uncertain
    this.approximate = approximate
    this.unspecified = unspecified
  }

  set precision(value) {
    P.set(this, (value > 3) ? 0 : Number(value));
  }

  get precision() {
    return P.get(this)
  }

  set uncertain(value) {
    U.set(this, this.bits(value))
  }

  get uncertain() {
    return U.get(this)
  }

  set approximate(value) {
    A.set(this, this.bits(value))
  }

  get approximate() {
    return A.get(this)
  }

  set unspecified(value) {
    X.set(this, new Bitmask(value))
  }

  get unspecified() {
    return X.get(this)
  }

  get min() {
    // TODO uncertain and approximate

    return this.getTime()
  }

  get max() {
    // TODO unspecified
    // TODO uncertain and approximate

    if (this.precision) return this.next() - 1

    return this.getTime()
  }

  get year() {
    return this.getUTCFullYear()
  }

  get month() {
    return this.getUTCMonth()
  }

  get date() {
    return this.getUTCDate()
  }

  get hours() {
    return this.getUTCHours()
  }

  get minutes() {
    return this.getUTCMinutes()
  }

  get seconds() {
    return this.getUTCSeconds()
  }

  get values() {
    switch (this.precision) {
    case 1:
      return [this.year]
    case 2:
      return [this.year, this.month]
    case 3:
      return [this.year, this.month, this.date]
    default:
      return [
        this.year, this.month, this.date, this.hours, this.minutes, this.seconds
      ]
    }
  }

  /**
   * Returns the next second, day, month, or year, depending on
   * the current date's precision. Uncertain, approximate and
   * unspecified masks are copied.
   */
  next(k = 1) {
    let { values, unspecified, uncertain, approximate } = this

    // values = values.slice(0, 3)
    values.push(values.pop() + k)

    return new Date({ values, unspecified, uncertain, approximate })
  }

  prev(k = 1) {
    return this.next(-k)
  }

  *until(then) {
    yield this

    if (!this.compare(then)) return

    yield* this.between(then)
    yield then
  }

  *between(then) {
    then = Date.from(then)

    let cur = this
    let dir = this.compare(then)

    if (!dir) return

    for (;;) {
      cur = cur.next(-dir)
      if (cur.compare(then) !== dir) break
      yield cur
    }
  }

  *[Symbol.iterator]() {
    let cur = this

    while (cur <= this.max) {
      yield cur
      cur = cur.next()
    }
  }

  toEDTF() {
    if (!this.precision) return this.toISOString()

    let values = this.values.map(Date.pad)

    if (this.unspecified.value)
      return this.unspecified.masks(values).join('-')

    if (this.uncertain.value)
      values = this.uncertain.marks(values, '?')

    if (this.approximate.value) {
      values = this.approximate.marks(values, '~')
        .map(value => value.replace(/(~\?)|(\?~)/, '%'))
    }

    return values.join('-')
  }

  static pad(number, idx = 0) { // idx 0 = year, 1 = month, ...
    if (!idx) {
      let k = abs(number)
      let sign = (k === number) ? '' : '-'

      if (k < 10)   return `${sign}000${k}`
      if (k < 100)  return `${sign}00${k}`
      if (k < 1000) return `${sign}0${k}`

      return `${number}`
    }

    if (idx === 1) number = number + 1

    return (number < 10) ? `0${number}` : `${number}`
  }

  bits(value) {
    if (value === true)
      value = PM[this.precision]

    return new Bitmask(value)
  }
}

mixin(Date, ExtDateTime)

module.exports = Date
<commit_msg>Linting<commit_after>'use strict'

const assert = require('assert')

const Bitmask = require('./bitmask')
const ExtDateTime = require('./interface')
const mixin = require('./mixin')

const { abs } = Math
const { isArray } = Array

const P = new WeakMap()
const U = new WeakMap()
const A = new WeakMap()
const X = new WeakMap()

const PM = [Bitmask.YMD, Bitmask.Y, Bitmask.YM, Bitmask.YMD]

class Date extends global.Date {
  constructor(...args) { // eslint-disable-line complexity
    let precision = 0
    let uncertain, approximate, unspecified

    switch (args.length) {
    case 0:
      break

    case 1:
      switch (typeof args[0]) {
      case 'number':
        break

      case 'string':
        args = [Date.parse(args[0])]
        // eslint-disable-line no-fallthrough

      case 'object':
        if (isArray(args[0]))
          args[0] = { values: args[0] }

        {
          let obj = args[0]

          assert(obj != null)
          if (obj.type) assert.equal('Date', obj.type)

          if (obj.values && obj.values.length) {
            precision = obj.values.length
            args = obj.values.slice()

            // ECMA Date constructor needs at least two date parts!
            if (args.length < 2) args.push(0)

            if (obj.offset) {
              if (args.length < 3) args.push(1)
              while (args.length < 5) args.push(0)

              // ECMA Date constructor handles overflows so we
              // simply add the offset!
              args[4] = args[4] + obj.offset
            }

            args = [ExtDateTime.UTC(...args)]
          }

          ({ uncertain, approximate, unspecified } = obj)
        }
        break

      default:
        throw new RangeError('Invalid time value')
      }

      break

    default:
      precision = args.length
    }

    super(...args)

    this.precision = precision

    this.uncertain = uncertain
    this.approximate = approximate
    this.unspecified = unspecified
  }

  set precision(value) {
    P.set(this, (value > 3) ? 0 : Number(value))
  }

  get precision() {
    return P.get(this)
  }

  set uncertain(value) {
    U.set(this, this.bits(value))
  }

  get uncertain() {
    return U.get(this)
  }

  set approximate(value) {
    A.set(this, this.bits(value))
  }

  get approximate() {
    return A.get(this)
  }

  set unspecified(value) {
    X.set(this, new Bitmask(value))
  }

  get unspecified() {
    return X.get(this)
  }

  get min() {
    // TODO uncertain and approximate

    return this.getTime()
  }

  get max() {
    // TODO unspecified
    // TODO uncertain and approximate

    if (this.precision) return this.next() - 1

    return this.getTime()
  }

  get year() {
    return this.getUTCFullYear()
  }

  get month() {
    return this.getUTCMonth()
  }

  get date() {
    return this.getUTCDate()
  }

  get hours() {
    return this.getUTCHours()
  }

  get minutes() {
    return this.getUTCMinutes()
  }

  get seconds() {
    return this.getUTCSeconds()
  }

  get values() {
    switch (this.precision) {
    case 1:
      return [this.year]
    case 2:
      return [this.year, this.month]
    case 3:
      return [this.year, this.month, this.date]
    default:
      return [
        this.year, this.month, this.date, this.hours, this.minutes, this.seconds
      ]
    }
  }

  /**
   * Returns the next second, day, month, or year, depending on
   * the current date's precision. Uncertain, approximate and
   * unspecified masks are copied.
   */
  next(k = 1) {
    let { values, unspecified, uncertain, approximate } = this

    // values = values.slice(0, 3)
    values.push(values.pop() + k)

    return new Date({ values, unspecified, uncertain, approximate })
  }

  prev(k = 1) {
    return this.next(-k)
  }

  *until(then) {
    yield this

    if (!this.compare(then)) return

    yield* this.between(then)
    yield then
  }

  *between(then) {
    then = Date.from(then)

    let cur = this
    let dir = this.compare(then)

    if (!dir) return

    for (;;) {
      cur = cur.next(-dir)
      if (cur.compare(then) !== dir) break
      yield cur
    }
  }

  *[Symbol.iterator]() {
    let cur = this

    while (cur <= this.max) {
      yield cur
      cur = cur.next()
    }
  }

  toEDTF() {
    if (!this.precision) return this.toISOString()

    let values = this.values.map(Date.pad)

    if (this.unspecified.value)
      return this.unspecified.masks(values).join('-')

    if (this.uncertain.value)
      values = this.uncertain.marks(values, '?')

    if (this.approximate.value) {
      values = this.approximate.marks(values, '~')
        .map(value => value.replace(/(~\?)|(\?~)/, '%'))
    }

    return values.join('-')
  }

  static pad(number, idx = 0) { // idx 0 = year, 1 = month, ...
    if (!idx) {
      let k = abs(number)
      let sign = (k === number) ? '' : '-'

      if (k < 10)   return `${sign}000${k}`
      if (k < 100)  return `${sign}00${k}`
      if (k < 1000) return `${sign}0${k}`

      return `${number}`
    }

    if (idx === 1) number = number + 1

    return (number < 10) ? `0${number}` : `${number}`
  }

  bits(value) {
    if (value === true)
      value = PM[this.precision]

    return new Bitmask(value)
  }
}

mixin(Date, ExtDateTime)

module.exports = Date
<|endoftext|>


<commit_before>const Discord = require('discord.js');
module.exports = async function (message, hotline){
    let me = message.embeds.shift();
    if(me && me!=null)
    {
        return me;
    }
    const embed = new Discord.RichEmbed();
    embed.setAuthor(message.author.username, message.author.avatarURL)
    if (!hotline)
        embed.setTitle((message.guild)?(message.channel.name + " in " + message.guild.name):(" in a DM with " + message.channel.recipient.tag));
    if (!message.content.trim() == "")
        embed.setDescription(message.cleanContent);
    else
        embed.setDescription("**" + (( me.author && me.author!=null)?(me.author.name+ " : "):"")  + ((me.title && me.title!=null)?me.title:"") + "**\n	" + (me.description || "") + ((me.footer) ? ("\n*At " + me.footer.text + "*") : ""));
    if (!hotline)
        embed.setTimestamp(message.createdAt)
    let color = message.member;
    if (color) {
        color = color.roles.filter((r) => {
            if (r.color !== 0)
                return r;
        }).array().sort((r1, r2) => {
            if (r1.position < r2.position)
                return 1;
        }).shift();
        if (color)
            color = color.color;
    }
    else
        color = 0;
    embed.setColor(color);
    //embed.setTitle("Quote");
    return embed;
}
<commit_msg>remove unnecessary fields from received embeds<commit_after>const Discord = require('discord.js');
module.exports = async function (message, hotline){
    let me = message.embeds.shift();
    if(me && me!=null)
    {
        delete me.client;
        delete me.createdAt;
        delete me.createdTimestamp;
        delete me.hexColor;
        delete me.message;
        delete me.provider;
        delete me.type;
        delete me.video;
        return me;
    }
    const embed = new Discord.RichEmbed();
    embed.setAuthor(message.author.username, message.author.avatarURL)
    if (!hotline)
        embed.setTitle((message.guild)?(message.channel.name + " in " + message.guild.name):(" in a DM with " + message.channel.recipient.tag));
    if (!message.content.trim() == "")
        embed.setDescription(message.cleanContent);
    else
        embed.setDescription("**" + (( me.author && me.author!=null)?(me.author.name+ " : "):"")  + ((me.title && me.title!=null)?me.title:"") + "**\n	" + (me.description || "") + ((me.footer) ? ("\n*At " + me.footer.text + "*") : ""));
    if (!hotline)
        embed.setTimestamp(message.createdAt)
    let color = message.member;
    if (color) {
        color = color.roles.filter((r) => {
            if (r.color !== 0)
                return r;
        }).array().sort((r1, r2) => {
            if (r1.position < r2.position)
                return 1;
        }).shift();
        if (color)
            color = color.color;
    }
    else
        color = 0;
    embed.setColor(color);
    //embed.setTitle("Quote");
    return embed;
}
<|endoftext|>


<commit_before>/**
 * @fileOverview shape 的辅助方法
 * @author dxq613@gmail.com
 */
const Util = require('../../util');

const ShapeUtil = {
  splitPoints(obj) {
    const points = [];
    const x = obj.x;
    let y = obj.y;
    y = Util.isArray(y) ? y : [ y ];
    Util.each(y, function(yItem, index) {
      const point = {
        x: Util.isArray(x) ? x[index] : x,
        y: yItem
      };
      points.push(point);
    });
    return points;
  },
  addFillAttrs(attrs, cfg) {
    if (cfg.color) {
      attrs.fill = cfg.color;
    }
    if (cfg.opacity) {
      attrs.opacity = attrs.fillOpacity = cfg.opacity;
    }
  },
  addStrokeAttrs(attrs, cfg) {
    if (cfg.color) {
      attrs.stroke = cfg.color;
    }
    if (!Util.isNil(cfg.opacity)) {
      attrs.opacity = attrs.strokeOpacity = cfg.opacity;
    }
  }
};

module.exports = ShapeUtil;
<commit_msg>fix(shape): add fillOpacity correctly, Closes #1065<commit_after>/**
 * @fileOverview shape 的辅助方法
 * @author dxq613@gmail.com
 */
const Util = require('../../util');

const ShapeUtil = {
  splitPoints(obj) {
    const points = [];
    const x = obj.x;
    let y = obj.y;
    y = Util.isArray(y) ? y : [ y ];
    Util.each(y, function(yItem, index) {
      const point = {
        x: Util.isArray(x) ? x[index] : x,
        y: yItem
      };
      points.push(point);
    });
    return points;
  },
  addFillAttrs(attrs, cfg) {
    if (cfg.color) {
      attrs.fill = cfg.color;
    }
    if (Util.isNumber(cfg.opacity)) {
      attrs.opacity = attrs.fillOpacity = cfg.opacity;
    }
  },
  addStrokeAttrs(attrs, cfg) {
    if (cfg.color) {
      attrs.stroke = cfg.color;
    }
    if (Util.isNumber(cfg.opacity)) {
      attrs.opacity = attrs.strokeOpacity = cfg.opacity;
    }
  }
};

module.exports = ShapeUtil;
<|endoftext|>


<commit_before>import utils from './utils';

/**
 * 得到会导致元素显示不全的祖先元素
 */
const { getParent } = utils;

function getOffsetParent(element) {
  if (utils.isWindow(element) || element.nodeType === 9) {
    return null;
  }
  // ie 这个也不是完全可行
  /*

   元素 6 高 100px 宽 50px

   */
  // element.offsetParent does the right thing in ie7 and below. Return parent with layout!
  //  In other browsers it only includes elements with position absolute, relative or
  // fixed, not elements with overflow set to auto or scroll.
  //        if (UA.ie && ieMode < 8) {
  //            return element.offsetParent;
  //        }
  // 统一的 offsetParent 方法
  const doc = utils.getDocument(element);
  const body = doc.body;
  let parent;
  let positionStyle = utils.css(element, 'position');
  const skipStatic = positionStyle === 'fixed' || positionStyle === 'absolute';

  if (!skipStatic) {
    return element.nodeName.toLowerCase() === 'html'
      ? null
      : getParent(element);
  }

  for (
    parent = getParent(element);
    parent && parent !== body;
    parent = getParent(parent)
  ) {
    positionStyle = utils.css(parent, 'position');
    if (positionStyle !== 'static') {
      return parent;
    }
  }
  return null;
}

export default getOffsetParent;
<commit_msg>Fix align in content scripts of chrome extension<commit_after>import utils from './utils';

/**
 * 得到会导致元素显示不全的祖先元素
 */
const { getParent } = utils;

function getOffsetParent(element) {
  if (utils.isWindow(element) || element.nodeType === 9) {
    return null;
  }
  // ie 这个也不是完全可行
  /*

   元素 6 高 100px 宽 50px

   */
  // element.offsetParent does the right thing in ie7 and below. Return parent with layout!
  //  In other browsers it only includes elements with position absolute, relative or
  // fixed, not elements with overflow set to auto or scroll.
  //        if (UA.ie && ieMode < 8) {
  //            return element.offsetParent;
  //        }
  // 统一的 offsetParent 方法
  const doc = utils.getDocument(element);
  const body = doc.body;
  let parent;
  let positionStyle = utils.css(element, 'position');
  const skipStatic = positionStyle === 'fixed' || positionStyle === 'absolute';

  if (!skipStatic) {
    return element.nodeName.toLowerCase() === 'html'
      ? null
      : getParent(element);
  }

  for (
    parent = getParent(element);
    parent && parent !== body && parent.nodeType !== 9;
    parent = getParent(parent)
  ) {
    positionStyle = utils.css(parent, 'position');
    if (positionStyle !== 'static') {
      return parent;
    }
  }
  return null;
}

export default getOffsetParent;
<|endoftext|>


<commit_before>define(['goo/renderer/MeshData'], function(MeshData) {
	"use strict";

	Quad.prototype = Object.create(MeshData.prototype);

	/**
	 * @name Box
	 * @class A four sided, two dimensional shape. The local height of the Quad defines it's size about the y-axis, while the width defines the x-axis. The z-axis will always be 0.
	 * @property {Float} xExtent Extent along the local x axis.
	 * @property {Float} yExtent Extent along the local y axis.
	 * @property {Integer} tileX Number of texture repetitions in the texture's x direction.
	 * @property {Integer} tileY Number of texture repetitions in the texture's y direction.
	 * @constructor
	 * @description Creates a new quad.
	 * @param {Float} width Total width of quad.
	 * @param {Float} height Total height of quad.
	 * @param {Integer} tileX Number of texture repetitions in the texture's x direction.
	 * @param {Integer} tileY Number of texture repetitions in the texture's y direction.
	 */

	function Quad(width, height, tileX, tileY) {
		this.xExtent = width !== undefined ? width * 0.5 : 0.5;
		this.yExtent = height !== undefined ? height * 0.5 : 0.5;
		this.tileX = tileX || 1;
		this.tileY = tileY || 1;

		var attributeMap = MeshData.defaultMap([MeshData.POSITION, MeshData.NORMAL, MeshData.TEXCOORD0]);
		MeshData.call(this, attributeMap, 4, 6);

		this.rebuild();
	}

	/**
	 * @description Builds or rebuilds the mesh data.
	 * @returns {Quad} Self for chaining.
	 */

	Quad.prototype.rebuild = function() {
		var xExtent = this.xExtent;
		var yExtent = this.yExtent;
		var tileX = this.tileX;
		var tileY = this.tileY;

		this.getAttributeBuffer(MeshData.POSITION).set([-xExtent, -yExtent, 0, -xExtent, yExtent, 0, xExtent, yExtent, 0, xExtent, -yExtent, 0]);
		this.getAttributeBuffer(MeshData.NORMAL).set([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
		this.getAttributeBuffer(MeshData.TEXCOORD0).set([0, 0, 0, tileY, tileX, tileY, tileX, 0]);

		this.getIndexBuffer().set([0, 3, 1, 1, 3, 2]);

		return this;
	};

	return Quad;
});<commit_msg>Fixed incorrect @name tag in Quad<commit_after>define(['goo/renderer/MeshData'], function(MeshData) {
	"use strict";

	Quad.prototype = Object.create(MeshData.prototype);

	/**
	 * @name Quad
	 * @class A four sided, two dimensional shape. The local height of the Quad defines it's size about the y-axis, while the width defines the x-axis. The z-axis will always be 0.
	 * @property {Float} xExtent Extent along the local x axis.
	 * @property {Float} yExtent Extent along the local y axis.
	 * @property {Integer} tileX Number of texture repetitions in the texture's x direction.
	 * @property {Integer} tileY Number of texture repetitions in the texture's y direction.
	 * @constructor
	 * @description Creates a new quad.
	 * @param {Float} width Total width of quad.
	 * @param {Float} height Total height of quad.
	 * @param {Integer} tileX Number of texture repetitions in the texture's x direction.
	 * @param {Integer} tileY Number of texture repetitions in the texture's y direction.
	 */
	function Quad(width, height, tileX, tileY) {
		this.xExtent = width !== undefined ? width * 0.5 : 0.5;
		this.yExtent = height !== undefined ? height * 0.5 : 0.5;
		this.tileX = tileX || 1;
		this.tileY = tileY || 1;

		var attributeMap = MeshData.defaultMap([MeshData.POSITION, MeshData.NORMAL, MeshData.TEXCOORD0]);
		MeshData.call(this, attributeMap, 4, 6);

		this.rebuild();
	}

	/**
	 * @description Builds or rebuilds the mesh data.
	 * @returns {Quad} Self for chaining.
	 */
	Quad.prototype.rebuild = function() {
		var xExtent = this.xExtent;
		var yExtent = this.yExtent;
		var tileX = this.tileX;
		var tileY = this.tileY;

		this.getAttributeBuffer(MeshData.POSITION).set([-xExtent, -yExtent, 0, -xExtent, yExtent, 0, xExtent, yExtent, 0, xExtent, -yExtent, 0]);
		this.getAttributeBuffer(MeshData.NORMAL).set([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
		this.getAttributeBuffer(MeshData.TEXCOORD0).set([0, 0, 0, tileY, tileX, tileY, tileX, 0]);

		this.getIndexBuffer().set([0, 3, 1, 1, 3, 2]);

		return this;
	};

	return Quad;
});<|endoftext|>


<commit_before>window.ga = window.ga || function initGa(...args) {
  (window.ga.q = window.ga.q || []).push(args);
};

window.ga('create', 'UA-89861822-3', 'auto');
window.ga('send', 'pageview');
<commit_msg>set GA page to /<commit_after>window.ga = window.ga || function initGa(...args) {
  (window.ga.q = window.ga.q || []).push(args);
};

window.ga('create', 'UA-89861822-3', 'auto');
window.ga('set', 'page', '/');
window.ga('send', 'pageview');
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const stringUtil = require('ember-cli-string-utils');
const pathUtil = require('ember-cli-path-utils');
const getPathOption = require('ember-cli-get-component-path-option');
const normalizeEntityName = require('ember-cli-normalize-entity-name');
const isModuleUnificationProject = require('../module-unification').isModuleUnificationProject;

module.exports = {
  description: 'Generates a component.',

  availableOptions: [
    {
      name: 'path',
      type: String,
      default: 'components',
      aliases: [{ 'no-path': '' }],
    },
  ],

  filesPath: function() {
    let filesDirectory = 'files';

    if (isModuleUnificationProject(this.project)) {
      filesDirectory = 'module-unification-files';
    }

    return path.join(this.path, filesDirectory);
  },

  fileMapTokens: function() {
    if (isModuleUnificationProject(this.project)) {
      return {
        __root__(options) {
          if (options.inRepoAddon) {
            return path.join('packages', options.inRepoAddon, 'src');
          }
          if (options.inDummy) {
            return path.join('tests', 'dummy', 'src');
          }
          return 'src';
        },
        __path__(options) {
          return path.join('ui', 'components', options.dasherizedModuleName);
        },
      };
    } else {
      return {
        __path__: function(options) {
          if (options.pod) {
            return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
          } else {
            return 'components';
          }
        },
        __templatepath__: function(options) {
          if (options.pod) {
            return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
          }
          return 'templates/components';
        },
        __templatename__: function(options) {
          if (options.pod) {
            return 'template';
          }
          return options.dasherizedModuleName;
        },
      };
    }
  },

  normalizeEntityName: function(entityName) {
    return normalizeEntityName(entityName);
  },

  locals: function(options) {
    let templatePath = '';
    let importTemplate = '';
    let contents = '';

    // if we're in an addon, build import statement
    if (options.project.isEmberCLIAddon() || (options.inRepoAddon && !options.inDummy)) {
      if (options.pod) {
        templatePath = './template';
      } else {
        templatePath =
          pathUtil.getRelativeParentPath(options.entity.name) +
          'templates/components/' +
          stringUtil.dasherize(options.entity.name);
      }
      importTemplate = "import layout from '" + templatePath + "';\n";
      contents = '\n  layout';
    }

    return {
      importTemplate: importTemplate,
      contents: contents,
      path: getPathOption(options),
    };
  },
};
<commit_msg>[BUGFIX beta] fix line endings of component blueprint on Windows<commit_after>'use strict';

const path = require('path');
const stringUtil = require('ember-cli-string-utils');
const pathUtil = require('ember-cli-path-utils');
const getPathOption = require('ember-cli-get-component-path-option');
const normalizeEntityName = require('ember-cli-normalize-entity-name');
const isModuleUnificationProject = require('../module-unification').isModuleUnificationProject;
const EOL = require('os').EOL;

module.exports = {
  description: 'Generates a component.',

  availableOptions: [
    {
      name: 'path',
      type: String,
      default: 'components',
      aliases: [{ 'no-path': '' }],
    },
  ],

  filesPath: function() {
    let filesDirectory = 'files';

    if (isModuleUnificationProject(this.project)) {
      filesDirectory = 'module-unification-files';
    }

    return path.join(this.path, filesDirectory);
  },

  fileMapTokens: function() {
    if (isModuleUnificationProject(this.project)) {
      return {
        __root__(options) {
          if (options.inRepoAddon) {
            return path.join('packages', options.inRepoAddon, 'src');
          }
          if (options.inDummy) {
            return path.join('tests', 'dummy', 'src');
          }
          return 'src';
        },
        __path__(options) {
          return path.join('ui', 'components', options.dasherizedModuleName);
        },
      };
    } else {
      return {
        __path__: function(options) {
          if (options.pod) {
            return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
          } else {
            return 'components';
          }
        },
        __templatepath__: function(options) {
          if (options.pod) {
            return path.join(options.podPath, options.locals.path, options.dasherizedModuleName);
          }
          return 'templates/components';
        },
        __templatename__: function(options) {
          if (options.pod) {
            return 'template';
          }
          return options.dasherizedModuleName;
        },
      };
    }
  },

  normalizeEntityName: function(entityName) {
    return normalizeEntityName(entityName);
  },

  locals: function(options) {
    let templatePath = '';
    let importTemplate = '';
    let contents = '';

    // if we're in an addon, build import statement
    if (options.project.isEmberCLIAddon() || (options.inRepoAddon && !options.inDummy)) {
      if (options.pod) {
        templatePath = './template';
      } else {
        templatePath =
          pathUtil.getRelativeParentPath(options.entity.name) +
          'templates/components/' +
          stringUtil.dasherize(options.entity.name);
      }
      importTemplate = "import layout from '" + templatePath + "';" + EOL;
      contents = EOL + '  layout';
    }

    return {
      importTemplate: importTemplate,
      contents: contents,
      path: getPathOption(options),
    };
  },
};
<|endoftext|>


<commit_before>import constant from "./constant";
import noop from "./noop";
import {event, customEvent, select, mouse, touch} from "d3-selection";

function nodefault() {
  event.preventDefault();
}

// Ignore right-click, since that should open the context menu.
// Ignore multiple starting touches on the same element.
function defaultFilter() {
  return event.identifier == null
      ? !event.sourceEvent.button
      : event.sourceEvent.changedTouches[0].identifier === event.identifier;
}

function defaultContainer() {
  return this.parentNode;
}

// Listen to the window for mousemove and mouseup such that we continue to
// receive events if the mouse goes outside the window.
function mouseContext() {
  return event.view;
}

// Listen to the event target for touchmove, touchend and touchcancel.
function touchContext() {
  return event.target;
}

export default function(dragstarted) {
  var x = null,
      y = null,
      filter = defaultFilter,
      container = defaultContainer,
      mousestart = start("mousemove", "mouseup", mouse, mouseContext),
      touchstart = start("touchmove", "touchend touchcancel", touch, touchContext);

  function drag(selection) {
    selection
        .on("mousedown.drag", mousedowned)
        .on("touchstart.drag", touchstarted);
  }

  function mousedowned() {
    mousestart(this, arguments);
  }

  function touchstarted() {
    for (var touches = event.changedTouches, i = 0, n = touches.length; i < n; ++i) {
      touchstart(this, arguments, touches[i].identifier);
    }
  }

  function start(move, end, position, contextify) {
    return function(that, args, id) {
      var parent,
          p0,
          dx,
          dy;

      if (!customEvent({type: "beforedragstart", identifier: id}, function() {
        if (filter.apply(that, args)) {
          parent = container.apply(that, args);
          p0 = position(parent, id);
          dx = x == null ? 0 : x.apply(that, args) - p0[0];
          dy = y == null ? 0 : y.apply(that, args) - p0[1];
          return true;
        }
      })) return;

      var listen = customEvent({type: "dragstart", identifier: id, x: p0[0], y: p0[1]}, dragstarted, that, args) || noop,
          dragged = typeof listen === "function" ? listen : listen.drag || noop,
          dragended = listen.dragend || noop,
          noclick = false,
          view = select(event.view).on(name("dragstart selectstart"), nodefault, true),
          context = select(contextify.apply(that, args)).on(name(move), moved).on(name(end), ended);

      // I’d like to call preventDefault on mousedown to disable native dragging
      // of links or images and native text selection. However, in Chrome this
      // causes mousemove and mouseup events outside an iframe to be dropped:
      // https://bugs.chromium.org/p/chromium/issues/detail?id=269917
      // And if you preventDefault on touchstart on iOS, it prevents the click
      // event on touchend, even if there was no touchmove! So instead, we
      // cancel the specific undesirable behaviors.

      function name(types) {
        var name = id == null ? ".drag" : ".drag-" + id;
        return types == null ? name : types.trim()
            .split(/^|\s+/)
            .map(function(type) { return type + name; })
            .join(" ");
      }

      function moved() {
        var p = position(parent, id);
        if (p == null) return; // This touch didn’t change.
        nodefault(), noclick = true;
        dragged.call(that, p[0] + dx, p[1] + dy, id);
      }

      function ended() {
        var p = position(parent, id);
        if (p == null) return; // This touch didn’t end.
        view.on(name(), null);
        context.on(name(), null);
        if (noclick) view.on(name("click"), nodefault, true), setTimeout(afterended, 0);
        dragended.call(that, p[0] + dx, p[1] + dy, id);
      }

      function afterended() {
        view.on(name("click"), null);
      }
    };
  }

  drag.filter = function(_) {
    return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), drag) : filter;
  };

  drag.container = function(_) {
    return arguments.length ? (container = typeof _ === "function" ? _ : constant(_), drag) : container;
  };

  drag.x = function(_) {
    return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), drag) : x;
  };

  drag.y = function(_) {
    return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), drag) : y;
  };

  return drag;
}
<commit_msg>Only pass {x, y} to drag and dragend.<commit_after>import constant from "./constant";
import noop from "./noop";
import {event, customEvent, select, mouse, touch} from "d3-selection";

function nodefault() {
  event.preventDefault();
}

// Ignore right-click, since that should open the context menu.
// Ignore multiple starting touches on the same element.
function defaultFilter() {
  return event.identifier == null
      ? !event.sourceEvent.button
      : event.sourceEvent.changedTouches[0].identifier === event.identifier;
}

function defaultContainer() {
  return this.parentNode;
}

// Listen to the window for mousemove and mouseup such that we continue to
// receive events if the mouse goes outside the window.
function mouseContext() {
  return event.view;
}

// Listen to the event target for touchmove, touchend and touchcancel.
function touchContext() {
  return event.target;
}

export default function(dragstarted) {
  var x = null,
      y = null,
      filter = defaultFilter,
      container = defaultContainer,
      mousestart = start("mousemove", "mouseup", mouse, mouseContext),
      touchstart = start("touchmove", "touchend touchcancel", touch, touchContext);

  function drag(selection) {
    selection
        .on("mousedown.drag", mousedowned)
        .on("touchstart.drag", touchstarted);
  }

  function mousedowned() {
    mousestart(this, arguments);
  }

  function touchstarted() {
    for (var touches = event.changedTouches, i = 0, n = touches.length; i < n; ++i) {
      touchstart(this, arguments, touches[i].identifier);
    }
  }

  function start(move, end, position, contextify) {
    return function(that, args, id) {
      var parent,
          p0,
          dx,
          dy;

      if (!customEvent({type: "beforedragstart", identifier: id}, function() {
        if (filter.apply(that, args)) {
          parent = container.apply(that, args);
          p0 = position(parent, id);
          dx = x == null ? 0 : x.apply(that, args) - p0[0];
          dy = y == null ? 0 : y.apply(that, args) - p0[1];
          return true;
        }
      })) return;

      var listen = customEvent({type: "dragstart", identifier: id, x: p0[0], y: p0[1]}, dragstarted, that, args) || noop,
          dragged = typeof listen === "function" ? listen : listen.drag || noop,
          dragended = listen.dragend || noop,
          noclick = false,
          view = select(event.view).on(name("dragstart selectstart"), nodefault, true),
          context = select(contextify.apply(that, args)).on(name(move), moved).on(name(end), ended);

      // I’d like to call preventDefault on mousedown to disable native dragging
      // of links or images and native text selection. However, in Chrome this
      // causes mousemove and mouseup events outside an iframe to be dropped:
      // https://bugs.chromium.org/p/chromium/issues/detail?id=269917
      // And if you preventDefault on touchstart on iOS, it prevents the click
      // event on touchend, even if there was no touchmove! So instead, we
      // cancel the specific undesirable behaviors.

      function name(types) {
        var name = id == null ? ".drag" : ".drag-" + id;
        return types == null ? name : types.trim()
            .split(/^|\s+/)
            .map(function(type) { return type + name; })
            .join(" ");
      }

      function moved() {
        var p = position(parent, id);
        if (p == null) return; // This touch didn’t change.
        nodefault(), noclick = true;
        dragged.call(that, p[0] + dx, p[1] + dy);
      }

      function ended() {
        var p = position(parent, id);
        if (p == null) return; // This touch didn’t end.
        view.on(name(), null);
        context.on(name(), null);
        if (noclick) view.on(name("click"), nodefault, true), setTimeout(afterended, 0);
        dragended.call(that, p[0] + dx, p[1] + dy);
      }

      function afterended() {
        view.on(name("click"), null);
      }
    };
  }

  drag.filter = function(_) {
    return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), drag) : filter;
  };

  drag.container = function(_) {
    return arguments.length ? (container = typeof _ === "function" ? _ : constant(_), drag) : container;
  };

  drag.x = function(_) {
    return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), drag) : x;
  };

  drag.y = function(_) {
    return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), drag) : y;
  };

  return drag;
}
<|endoftext|>


<commit_before>'use strict';

var extend = require('xtend');
var Control = require('./control');
var themeStyle = require('./theme/style');
var themeEdit = require('./theme/edit');
var util = require('./util');
var DOM = util.DOM;

// Data store
var Store = require('./store');

// Control handlers
var Polygon = require('./handlers/polygon');
var Line = require('./handlers/line');
var Square = require('./handlers/square');
var Point = require('./handlers/point');

function Draw(options) {
  if (!(this instanceof Draw)) return new Draw(options);
  util.setOptions(this, options);
}

Draw.prototype = extend(Control, {
  options: {
    position: 'top-left',
    keybindings: true,
    geoJSON: [],
    controls: {
      marker: true,
      line: true,
      shape: true,
      square: true
    }
  },

  onAdd(map) {
    var controlClass = this._controlClass = 'mapboxgl-ctrl-draw-btn';
    var container = this._container = DOM.create('div', 'mapboxgl-ctrl-group', map.getContainer());
    var controls = this.options.controls;
    this.options.geoJSON = new Store(this.options.geoJSON);

    // Build out draw controls
    if (controls.line) {
      this.lineStringCtrl = this._createButton({
        className: controlClass + ' line',
        title: 'LineString tool' + (this.options.keybindings ? ' (l)' : ''),
        fn: this._drawLine.bind(this, map, this.options)
      });
    }

    if (controls.shape) {
      this.polygonCtrl = this._createButton({
        className: controlClass + ' shape',
        title: 'Polygon tool' + (this.options.keybindings ? ' (p)' : ''),
        fn: this._drawPolygon.bind(this, map, this.options)
      });
    }

    if (controls.square) {
      this.squareCtrl = this._createButton({
        className: controlClass + ' square',
        title: 'Square tool' + (this.options.keybindings ? ' (s)' : ''),
        fn: this._drawSquare.bind(this, map, this.options)
      });
    }

    if (controls.marker) {
      this.markerCtrl = this._createButton({
        className: controlClass + ' marker',
        title: 'Marker tool' + (this.options.keybindings ? ' (m)' : ''),
        fn: this._drawPoint.bind(this, map, this.options)
      });
    }

    map.getContainer().addEventListener('mousedown', this._onMouseDown, true);

    if (this.options.keybindings) {
      map.getContainer().addEventListener('keyup', this._onKeyUp.bind(this));
    }

    this._mapState(map);
    return container;
  },

  _onKeyUp(e) {
    var event = document.createEvent('HTMLEvents');
    event.initEvent('click', true, false);
    switch (e.keyCode) {
      case 76: // (l) linestring
        if (!this.lineStringCtrl.classList.contains('active')) {
          this.lineStringCtrl.dispatchEvent(event);
        }
      break;
      case 77: // (m) marker
        if (!this.markerCtrl.classList.contains('active')) {
          this.markerCtrl.dispatchEvent(event);
        }
      break;
      case 80: // (p) polygon
        if (!this.polygonCtrl.classList.contains('active')) {
          this.polygonCtrl.dispatchEvent(event);
        }
      break;
      case 83: // (s) square
        if (!this.squareCtrl.classList.contains('active')) {
          this.squareCtrl.dispatchEvent(event);
        }
      break;
    }
  },

  _onMouseDown(e) {
    if (e.altKey) {
      e.stopPropagation();
      // TODO https://github.com/mapbox/mapbox-gl-js/issues/1264
      // this._captureFeatures();
    }
  },

  _drawPolygon(map, options) {
    this._control = new Polygon(map, options);
  },

  _drawLine(map, options) {
    this._control = new Line(map, options);
  },

  _drawSquare(map, options) {
    this._control = new Square(map, options);
  },

  _drawPoint(map, options) {
    this._control = new Point(map, options);
  },

  _createButton(opts) {
    var a = DOM.create('button', opts.className, this._container, {
      title: opts.title
    });

    var controlClass = this._controlClass;

    a.addEventListener('click', (e) => {
      e.preventDefault();
      e.stopPropagation();

      var el = e.target;

      if (el.classList.contains('active')) {
        if (this._control) this._control.disable();
        el.classList.remove('active');
      } else {
        DOM.removeClass(document.querySelectorAll('.' + controlClass), 'active');
        if (this._control) this._control.disable();
        el.classList.add('active');
        opts.fn();
      }
    });

    return a;
  },

  _mapState(map) {
    var controlClass = this._controlClass;

    map.on('load', () => {

      // Initialize the draw layer with any possible
      // features passed via `options.geoJSON`
      var drawLayer = new mapboxgl.GeoJSONSource({
        data: this.options.geoJSON.getAll()
      });

      map.addSource('draw', drawLayer);
      themeStyle.forEach((style) => {
        map.addLayer(style);
      });

      // Initialize an editLayer that provides
      // marker anchors and guides during the
      // draw process.
      var editLayer = new mapboxgl.GeoJSONSource({
        data: []
      });

      map.addSource('edit', editLayer);
      themeEdit.forEach((style) => {
        map.addLayer(style);
      });

      map.on('draw.stop', () => {
        DOM.removeClass(document.querySelectorAll('.' + controlClass), 'active');
      });

      map.on('edit.feature.update', (e) => {
        editLayer.setData(e.geojson);
      });

      map.on('draw.feature.update', (e) => {
        drawLayer.setData(e.geojson);
      });
    });
  }
});

module.exports = Draw;
<commit_msg>start feature drag<commit_after>'use strict';

var extend = require('xtend');
var Control = require('./control');
var themeStyle = require('./theme/style');
var themeEdit = require('./theme/edit');
var util = require('./util');
var DOM = util.DOM;

// Data store
var Store = require('./store');

// Control handlers
var Polygon = require('./handlers/polygon');
var Line = require('./handlers/line');
var Square = require('./handlers/square');
var Point = require('./handlers/point');

function Draw(options) {
  if (!(this instanceof Draw)) return new Draw(options);
  util.setOptions(this, options);
}

Draw.prototype = extend(Control, {
  options: {
    position: 'top-left',
    keybindings: true,
    geoJSON: [],
    controls: {
      marker: true,
      line: true,
      shape: true,
      square: true
    }
  },

  onAdd(map) {
    var controlClass = this._controlClass = 'mapboxgl-ctrl-draw-btn';
    var container = this._container = DOM.create('div', 'mapboxgl-ctrl-group', map.getContainer());
    var controls = this.options.controls;
    this.options.geoJSON = new Store(this.options.geoJSON);

    // Build out draw controls
    if (controls.line) {
      this.lineStringCtrl = this._createButton({
        className: controlClass + ' line',
        title: 'LineString tool' + (this.options.keybindings ? ' (l)' : ''),
        fn: this._drawLine.bind(this, map, this.options)
      });
    }

    if (controls.shape) {
      this.polygonCtrl = this._createButton({
        className: controlClass + ' shape',
        title: 'Polygon tool' + (this.options.keybindings ? ' (p)' : ''),
        fn: this._drawPolygon.bind(this, map, this.options)
      });
    }

    if (controls.square) {
      this.squareCtrl = this._createButton({
        className: controlClass + ' square',
        title: 'Square tool' + (this.options.keybindings ? ' (s)' : ''),
        fn: this._drawSquare.bind(this, map, this.options)
      });
    }

    if (controls.marker) {
      this.markerCtrl = this._createButton({
        className: controlClass + ' marker',
        title: 'Marker tool' + (this.options.keybindings ? ' (m)' : ''),
        fn: this._drawPoint.bind(this, map, this.options)
      });
    }

    map.getContainer().addEventListener('mousedown', this._onMouseDown.bind(this, map), true);

    if (this.options.keybindings) {
      map.getContainer().addEventListener('keyup', this._onKeyUp.bind(this));
    }

    this._mapState(map);

    return container;
  },

  _onKeyUp(e) {
    var event = document.createEvent('HTMLEvents');
    event.initEvent('click', true, false);
    switch (e.keyCode) {
      case 76: // (l) linestring
        if (!this.lineStringCtrl.classList.contains('active')) {
          this.lineStringCtrl.dispatchEvent(event);
        }
      break;
      case 77: // (m) marker
        if (!this.markerCtrl.classList.contains('active')) {
          this.markerCtrl.dispatchEvent(event);
        }
      break;
      case 80: // (p) polygon
        if (!this.polygonCtrl.classList.contains('active')) {
          this.polygonCtrl.dispatchEvent(event);
        }
      break;
      case 83: // (s) square
        if (!this.squareCtrl.classList.contains('active')) {
          this.squareCtrl.dispatchEvent(event);
        }
      break;
    }
  },

  _onMouseDown(map, e) {
    var coords = DOM.mousePos(e, map._container);
    map.featuresAt([coords.x, coords.y], { radius: 10 }, (err, features) => {
      if (!err && features.length) {
        console.log(features);
      }
    });

    if (e.altKey) {
      e.stopPropagation();
      // TODO https://github.com/mapbox/mapbox-gl-js/issues/1264
      // this._captureFeatures();
    }
  },

  _drawPolygon(map, options) {
    this._control = new Polygon(map, options);
  },

  _drawLine(map, options) {
    this._control = new Line(map, options);
  },

  _drawSquare(map, options) {
    this._control = new Square(map, options);
  },

  _drawPoint(map, options) {
    this._control = new Point(map, options);
  },

  _createButton(opts) {
    var a = DOM.create('button', opts.className, this._container, {
      title: opts.title
    });

    var controlClass = this._controlClass;

    a.addEventListener('click', (e) => {
      e.preventDefault();
      e.stopPropagation();

      var el = e.target;

      if (el.classList.contains('active')) {
        if (this._control) this._control.disable();
        el.classList.remove('active');
      } else {
        DOM.removeClass(document.querySelectorAll('.' + controlClass), 'active');
        if (this._control) this._control.disable();
        el.classList.add('active');
        opts.fn();
      }
    });

    return a;
  },

  _mapState(map) {
    var controlClass = this._controlClass;

    map.on('load', () => {

      // Initialize the draw layer with any possible
      // features passed via `options.geoJSON`
      var drawLayer = new mapboxgl.GeoJSONSource({
        data: this.options.geoJSON.getAll()
      });

      map.addSource('draw', drawLayer);
      themeStyle.forEach((style) => {
        map.addLayer(style);
      });

      // Initialize an editLayer that provides
      // marker anchors and guides during the
      // draw process.
      var editLayer = new mapboxgl.GeoJSONSource({
        data: []
      });

      map.addSource('edit', editLayer);
      themeEdit.forEach((style) => {
        map.addLayer(style);
      });

      map.on('draw.stop', () => {
        DOM.removeClass(document.querySelectorAll('.' + controlClass), 'active');
      });

      map.on('edit.feature.update', (e) => {
        editLayer.setData(e.geojson);
      });

      map.on('draw.feature.update', (e) => {
        drawLayer.setData(e.geojson);
      });
    });
  }
});

module.exports = Draw;
<|endoftext|>


<commit_before>(function() {

    var trimLeft = /^\s+/;
    var trimRight = /\s+$/;

    // group 1 must be the id
    var idRegExp = /^#(\S+)$/;

    // group 1 must be the tagName and group 2 must be the className
    var tagClassRegExp = /^([\w-]+)?(?:\.([\w-]+))?$/;

    function trim(str) {
        return str.replace(trimLeft, '').replace(trimRight, '');
    }

    function isHostMethod(obj, prop) {
        return (typeof obj[prop] === 'function') ||
                ((typeof obj[prop] === 'object') && (obj[prop] !== null)); // Internet Explorer
    }

    function filterDOM(node, func) {
       var results = [];
       function walk(node) {
           if (func(node)) {
               results.push(node);
           }
           node = node.firstChild;
           while (node) {
               walk(node);
               node = node.nextSibling;
           }
       }
       walk(node);
       return results;
    }

    function firstInDOM(node, func) {
        function walk(node) {
            if (func(node)) {
                return node;
            }
            node = node.firstChild;
            while (node) {
                var result = walk(node);
                if (result) {
                    return result;
                }
                node = node.nextSibling;
            }
        }
        return walk(node);
    }

    function findById(id, root) {
        return (root.id === id) ?
                   root :
                   (isHostMethod(root, 'getElementById')) ?
                       root.getElementById(id) :
                       (isHostMethod(root, 'querySelector')) ?
                           root.querySelector('#' + id) :
                           firstInDOM(root, function(node) {return node.id === id;});
    }

    function getTagNameClassNameMatcher(tagName, className) {
        tagName = tagName ? tagName.toUpperCase() : '*';
        if (className) {
            var regExp = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
        }
        return function(element) {
            return (((tagName === '*') ||
                     (element.tagName && (element.tagName.toUpperCase() === tagName))) &&
                    ((!className) ||
                     regExp.test(element.className)));
        }
    }

/**

Search for all elements matching the CSS selector. Returns an array of the elements.

Acceptable simple selectors are of the following forms only.

    div
    #alpha
    .beta
    div.gamma

In the case of a #myId selector, the returned array will always have
zero or one elements. It is more likely that you want to call grail.find when
using an id selector.

If the root element is supplied then it is used as the starting point for the search.
The root element will be in the results if it matches the selector.
If the root element is not supplied then the current document is used
as the search starting point.

    grail.findAll('#alpha');
    grail.findAll('div.gamma', document.body);

@param {string} selector The CSS selector for the search.

@param {Document|Element} [root] The element to use as the search start point.

@return {Array} An array of matching `Element` objects.

*/
    grail.findAll = function(selector, root) {
        selector = trim(selector);
        root = root || document;
        var matches;
        if (matches = selector.match(idRegExp)) {
            var el = findById(matches[1], root);
            return el ? [el] : [];
        }
        else if (matches = selector.match(tagClassRegExp)) {
            var tagNameClassNameMatcher = getTagNameClassNameMatcher(matches[1], matches[2]);
            if (isHostMethod(root, 'querySelectorAll')) {
                var elements;
                var results = [];
                if (tagNameClassNameMatcher(root)) {
                    results.push(root);
                }
                elements = root.querySelectorAll(selector);
                for (var i = 0, ilen = elements.length; i < ilen; i++) {
                    results.push(elements[i]);
                }
                return results;
            }
            else {
                return filterDOM(root, tagNameClassNameMatcher);
            }
        }
        else {
            throw new Error('grail.findAll: Unsupported selector "'+selector+'".');
        }
    };

/**

Search for the first element matching the CSS selector. If the element is
found then it is returned. If no matching element is found then
null or undefined is returned.

The rest of the details are the same as for grail.findAll.

@param {string} [selector] The CSS selector for the search.

@param {Document|Element} [root] The element to use as the search start point.

@return {Element} The found `Element`.

*/
    grail.find = function(selector, root) {
        selector = trim(selector);
        root = root || document;
        var matches;
        if (matches = selector.match(idRegExp)) {
            return findById(matches[1], root);
        }
        else if (matches = selector.match(tagClassRegExp)) {
            var tagNameClassNameMatcher = getTagNameClassNameMatcher(matches[1], matches[2]);
            if (isHostMethod(root, 'querySelector')) {
                return tagNameClassNameMatcher(root) ? root : root.querySelector(selector);
            }
            else {
                return firstInDOM(root, tagNameClassNameMatcher);
            }
        }
        else {
            throw new Error('grail.find: Unsupported selector "'+selector+'".');
        }
    };

}());
<commit_msg>use names for fallback helpers that are similar to the names of the grail API functions<commit_after>(function() {

    var trimLeft = /^\s+/;
    var trimRight = /\s+$/;

    // group 1 must be the id
    var idRegExp = /^#(\S+)$/;

    // group 1 must be the tagName and group 2 must be the className
    var tagClassRegExp = /^([\w-]+)?(?:\.([\w-]+))?$/;

    function trim(str) {
        return str.replace(trimLeft, '').replace(trimRight, '');
    }

    function isHostMethod(obj, prop) {
        return (typeof obj[prop] === 'function') ||
                ((typeof obj[prop] === 'object') && (obj[prop] !== null)); // Internet Explorer
    }

    function findAllInDOM(node, func) {
       var results = [];
       function walk(node) {
           if (func(node)) {
               results.push(node);
           }
           node = node.firstChild;
           while (node) {
               walk(node);
               node = node.nextSibling;
           }
       }
       walk(node);
       return results;
    }

    function findInDOM(node, func) {
        function walk(node) {
            if (func(node)) {
                return node;
            }
            node = node.firstChild;
            while (node) {
                var result = walk(node);
                if (result) {
                    return result;
                }
                node = node.nextSibling;
            }
        }
        return walk(node);
    }

    function findById(id, root) {
        return (root.id === id) ?
                   root :
                   (isHostMethod(root, 'getElementById')) ?
                       root.getElementById(id) :
                       (isHostMethod(root, 'querySelector')) ?
                           root.querySelector('#' + id) :
                           findInDOM(root, function(node) {return node.id === id;});
    }

    function getTagNameClassNameMatcher(tagName, className) {
        tagName = tagName ? tagName.toUpperCase() : '*';
        if (className) {
            var regExp = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
        }
        return function(element) {
            return (((tagName === '*') ||
                     (element.tagName && (element.tagName.toUpperCase() === tagName))) &&
                    ((!className) ||
                     regExp.test(element.className)));
        }
    }

/**

Search for all elements matching the CSS selector. Returns an array of the elements.

Acceptable simple selectors are of the following forms only.

    div
    #alpha
    .beta
    div.gamma

In the case of a #myId selector, the returned array will always have
zero or one elements. It is more likely that you want to call grail.find when
using an id selector.

If the root element is supplied then it is used as the starting point for the search.
The root element will be in the results if it matches the selector.
If the root element is not supplied then the current document is used
as the search starting point.

    grail.findAll('#alpha');
    grail.findAll('div.gamma', document.body);

@param {string} selector The CSS selector for the search.

@param {Document|Element} [root] The element to use as the search start point.

@return {Array} An array of matching `Element` objects.

*/
    grail.findAll = function(selector, root) {
        selector = trim(selector);
        root = root || document;
        var matches;
        if (matches = selector.match(idRegExp)) {
            var el = findById(matches[1], root);
            return el ? [el] : [];
        }
        else if (matches = selector.match(tagClassRegExp)) {
            var tagNameClassNameMatcher = getTagNameClassNameMatcher(matches[1], matches[2]);
            if (isHostMethod(root, 'querySelectorAll')) {
                var elements;
                var results = [];
                if (tagNameClassNameMatcher(root)) {
                    results.push(root);
                }
                elements = root.querySelectorAll(selector);
                for (var i = 0, ilen = elements.length; i < ilen; i++) {
                    results.push(elements[i]);
                }
                return results;
            }
            else {
                return findAllInDOM(root, tagNameClassNameMatcher);
            }
        }
        else {
            throw new Error('grail.findAll: Unsupported selector "'+selector+'".');
        }
    };

/**

Search for the first element matching the CSS selector. If the element is
found then it is returned. If no matching element is found then
null or undefined is returned.

The rest of the details are the same as for grail.findAll.

@param {string} [selector] The CSS selector for the search.

@param {Document|Element} [root] The element to use as the search start point.

@return {Element} The found `Element`.

*/
    grail.find = function(selector, root) {
        selector = trim(selector);
        root = root || document;
        var matches;
        if (matches = selector.match(idRegExp)) {
            return findById(matches[1], root);
        }
        else if (matches = selector.match(tagClassRegExp)) {
            var tagNameClassNameMatcher = getTagNameClassNameMatcher(matches[1], matches[2]);
            if (isHostMethod(root, 'querySelector')) {
                return tagNameClassNameMatcher(root) ? root : root.querySelector(selector);
            }
            else {
                return findInDOM(root, tagNameClassNameMatcher);
            }
        }
        else {
            throw new Error('grail.find: Unsupported selector "'+selector+'".');
        }
    };

}());
<|endoftext|>