<commit_before>"use strict";
(function(window) {
    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        var directives = {};
        var self = this;
        this.$el = document.querySelector(_el);
        this.components = opts.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);
            }
        });

        /**
        * 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");
          return compile(data);
        }

        /**
        * 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;
        }

        /**
        * Turns Custom Components into their Corresponding Templates
        */
        this.componentsToHTML = function() {
          for(var component in this.components) {
            var componentsFound = document.getElementsByTagName(component);
            for(var i = 0; i < componentsFound.length; i++) {
              var componentFound = componentsFound[i];
              var componentProps = extractAttrs(componentFound);
              var componentDummy = document.createElement('div');
              componentDummy.innerHTML = this.components[component].template;
              componentDummy = componentDummy.firstChild;

              for(var attr in componentProps) {
                componentDummy.setAttribute(attr, componentProps[attr]);
              }

              componentFound.outerHTML = componentDummy.outerHTML;
            }
          }
        }

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

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

        /**
        * Makes an AJAX Request
        * @param {String} method
        * @param {String} url
        * @param {Object} params
        * @param {Function} cb
        */
        this.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);
        }

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

        // Directive Initialization
        this.directive = function(name, action) {
          directives["m-" + name] = action;
        }

        // 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-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.addEventListener("input", function() {
            self.set(val, el.value);
          });
          el.removeAttribute("m-model");
          delete vdom.props["m-model"];
        }

        /**
        * 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) {
                  directive(el.node, compiledProperty, el);
                }

                el.node.setAttribute(prop, compiledProperty);
              }
            }

            this.build(el.children);
          }
        }

        // Initialize 🎉
        this.componentsToHTML();
        this.createVirtualDOM(this.$el);
        this.build(this.dom.children);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<commit_msg>initialize with data<commit_after>"use strict";
(function(window) {
    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        var directives = {};
        var self = this;
        this.$el = document.querySelector(_el);
        this.components = opts.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);
            }
        });

        /**
        * 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");
          return compile(data);
        }

        /**
        * 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;
        }

        /**
        * Turns Custom Components into their Corresponding Templates
        */
        this.componentsToHTML = function() {
          for(var component in this.components) {
            var componentsFound = document.getElementsByTagName(component);
            for(var i = 0; i < componentsFound.length; i++) {
              var componentFound = componentsFound[i];
              var componentProps = extractAttrs(componentFound);
              var componentDummy = document.createElement('div');
              componentDummy.innerHTML = this.components[component].template;
              componentDummy = componentDummy.firstChild;

              for(var attr in componentProps) {
                componentDummy.setAttribute(attr, componentProps[attr]);
              }

              componentFound.outerHTML = componentDummy.outerHTML;
            }
          }
        }

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

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

        /**
        * Makes an AJAX Request
        * @param {String} method
        * @param {String} url
        * @param {Object} params
        * @param {Function} cb
        */
        this.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);
        }

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

        // Directive Initialization
        this.directive = function(name, action) {
          directives["m-" + name] = action;
        }

        // 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-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"];
        }

        /**
        * 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) {
                  directive(el.node, compiledProperty, el);
                }

                el.node.setAttribute(prop, compiledProperty);
              }
            }

            this.build(el.children);
          }
        }

        // Initialize 🎉
        this.componentsToHTML();
        this.createVirtualDOM(this.$el);
        this.build(this.dom.children);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<|endoftext|>


<commit_before>'use strict';

const calculator = require('./calculator');
const coloriser = require('./coloriser');
const deferAndDropExcess = require('./deferAndDropExcess');
const displayBlockStore = require('./displayBlockStore');
const scheduler = require('./scheduler');

const getDiscreteScroll = function (el, threshold, cb) {
  let excess = 0;

  el.addEventListener('wheel', (evt) => {
    excess += evt.deltaY;

    while (excess >= threshold) {
      cb(evt, threshold);
      console.log(threshold);
      excess -= threshold;
    }

    while (excess <= -threshold) {
      cb(evt, -threshold);
      console.log(-threshold);
      excess += threshold;
    }
  });
};

module.exports = function Renderer(canvas) {
  const self = this;

  self.depth = 500;
  self.canvas = canvas;
  self.ctx = canvas.getContext('2d');
  self.center = { x: -0.75, y: 0 };
  self.width = 8;

  self.coloriser = new coloriser();

  self.calculator = calculator();
  self.displayBlockStore = null;

  self.pixelRatio = window.devicePixelRatio || 1;

  self.scheduler = scheduler(20);

  self.drawBegin = null;
  self.drawEnd = null;

  self.updateSize = function () {
    canvas.style.width = `${canvas.parentNode.clientWidth}px`;
    canvas.style.height = `${canvas.parentNode.clientHeight}px`;

    canvas.width = self.pixelRatio * canvas.parentNode.clientWidth;
    canvas.height = self.pixelRatio * canvas.parentNode.clientHeight;
  }

  ;(function () {
    self.updateSize();

    canvas.parentNode.addEventListener('keydown', (evt) => {
      if (evt.keyCode === 67) {
        self.coloriser.randomise(!evt.shiftKey ? 1 : -1);
        self.drawBlocksCached(); // TODO: rename to redrawCurrentBlocks?
      }

      if (evt.keyCode === 187 || evt.keyCode === 189) {
        if (evt.shiftKey) {
          self.coloriser.shift(0.05 * (188 - evt.keyCode));
        } else {
          self.coloriser.multiplySpeed(Math.exp(0.05 * (188 - evt.keyCode)));
        }

        self.drawBlocksCached();
      }
    });

    let touchSession = null;

    canvas.addEventListener('touchstart', (e) => {
      e.preventDefault();

      if (!touchSession) {
        const touch = e.changedTouches[0];

        touchSession = {
          primary: {
            id: touch.identifier,
            start: {
              x: touch.clientX,
              y: touch.clientY,
            },
            curr: {
              x: touch.clientX,
              y: touch.clientY,
            },
          },
          dragData: (() => {
            const cvs = document.createElement('canvas');
            cvs.setAttribute('width', canvas.width);
            cvs.setAttribute('height', canvas.height);

            const ctx = cvs.getContext('2d');
            const imgData = self.ctx.getImageData(0, 0, canvas.width, canvas.height);
            ctx.putImageData(imgData, 0, 0);

            return cvs;
          })(),
          dragDataIncomplete: self.drawBegin && !self.drawEnd,
        };
      } else if (!touchSession.secondary) {
        const touch = Array.from(e.changedTouches).filter(t => t.identifier !== touchSession.primaryId)[0];

        touchSession.secondary = {
          id: touch.identifier,
          start: {
            x: touch.clientX,
            y: touch.clientY,
          },
          curr: {
            x: touch.clientX,
            y: touch.clientY,
          },
        };
      }
    });

    canvas.addEventListener('touchmove', (e) => {
      if (!touchSession) {
        return;
      }

      let primaryTouchChanged = false;
      let secondaryTouchChanged = false;

      const primaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.primary.id)[0];

      if (primaryTouch) {
        e.preventDefault();

        touchSession.primary.curr.x = primaryTouch.clientX;
        touchSession.primary.curr.y = primaryTouch.clientY;

        primaryTouchChanged = true;
      }

      if (touchSession.secondary) {
        const secondaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.secondary.id)[0];

        if (secondaryTouch) {
          touchSession.secondary.curr.x = secondaryTouch.clientX;
          touchSession.secondary.curr.y = secondaryTouch.clientY;

          const sq = x => x * x;
          const dist = (p1, p2) => Math.sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));

          touchSession.zoom = (
            dist(touchSession.primary.curr, touchSession.secondary.curr) /
            dist(touchSession.primary.start, touchSession.secondary.start)
          );

          secondaryTouchChanged = true;
        }
      }

      if (primaryTouchChanged || secondaryTouchChanged) {
        const diff = { x: touchSession.primary.curr.x - touchSession.primary.start.x, y: touchSession.primary.curr.y - touchSession.primary.start.y };
        self.ctx.clearRect(0, 0, canvas.width, canvas.height);

        const zoom = touchSession.zoom || 1;

        let dx = 0;
        dx += self.pixelRatio * diff.x;
        dx += self.pixelRatio * touchSession.primary.start.x * (1 - zoom);

        let dy = 0;
        dy += self.pixelRatio * diff.y;
        dy += self.pixelRatio * touchSession.primary.start.y * (1 - zoom);

        self.ctx.drawImage(
          touchSession.dragData,
          dx,
          dy,
          touchSession.dragData.width * zoom,
          touchSession.dragData.height * zoom,
        );
      }
    });

    canvas.addEventListener('touchcancel', (e) => {
      if (!touchSession) {
        return;
      }

      const primaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.primary.id)[0];

      if (!primaryTouch) {
        return;
      }

      touchSession = null;
      self.draw(self.center);
    });

    canvas.addEventListener('touchend', (e) => {
      if (!touchSession) {
        return;
      }

      const primaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.primary.id)[0];

      if (!primaryTouch) {
        return;
      }

      const diff = { x: e.changedTouches[0].clientX - touchSession.primary.start.x, y: e.changedTouches[0].clientY - touchSession.primary.start.y };

      if (diff.x === 0 && diff.y === 0) {
        return;
      }

      const pixelSize = self.width / canvas.width;
      const aspectRatio = canvas.width / canvas.height;

      const alreadyDrawnRect = touchSession.dragDataIncomplete || touchSession.zoom ? null : {
        topLeft: {
          x: self.center.x - 0.5 * self.width,
          y: self.center.y - 0.5 / aspectRatio * self.width,
        },
        bottomRight: {
          x: self.center.x + 0.5 * self.width,
          y: self.center.y + 0.5 / aspectRatio * self.width,
        },
      };

      const pos = {
        x: (
          self.center.x -
          0.5 * self.width +
          touchSession.primary.start.x * self.pixelRatio * pixelSize
        ),
        y: (
          self.center.y -
          0.5 / aspectRatio *
          self.width +
          touchSession.primary.start.y * self.pixelRatio * pixelSize
        ),
      };

      self.moveCenter({
        x: -diff.x * pixelSize * self.pixelRatio,
        y: -diff.y * pixelSize * self.pixelRatio,
      });

      if (touchSession.zoom) {
        self.scale(1 / touchSession.zoom, pos);
      } else {
        self.draw(pos, alreadyDrawnRect);
      }

      touchSession = null;
    });

    const zoom = function (dz, pos) {
      const pixelSize = self.width / canvas.width;
      const aspectRatio = canvas.width / canvas.height;

      self.scale(
        dz < 0 ? 2 / 3 : 3 / 2,
        {
          x: (
            self.center.x -
            0.5 * self.width +
            pos.x * self.pixelRatio * pixelSize
          ),
          y: (
            self.center.y -
            0.5 / aspectRatio * self.width +
            self.pixelRatio * pixelSize * pos.y
          ),
        },
      );
    };

    const mousePos = {
      x: 0,
      y: 0,
    };

    canvas.addEventListener('mousemove', (e) => {
      mousePos.x = e.clientX;
      mousePos.y = e.clientY;
    });

    getDiscreteScroll(canvas, 30, (e, dy) => {
      zoom(dy > 0 ? 1 : -1, mousePos);
    });

    canvas.parentNode.addEventListener('keydown', (e) => {
      if (e.keyCode === 68) {
        self.depth = parseInt(window.prompt('Enter new depth', '500'));
        self.draw();
      } else if (e.keyCode === 65) { // a
        zoom(-1, mousePos);
      } else if (e.keyCode === 90) { // z
        zoom(1, mousePos);
      }
    });
  }());

  self.draw = deferAndDropExcess((pos, alreadyDrawnRect) => { // pos === referencePoint?
    pos = pos || self.center;
    self.scheduler.clear();

    // self.coloriser.clearCache() // TODO: was this a good idea when it used to work?

    const pixelWidth = self.width / canvas.width;
    const aspectRatio = canvas.width / canvas.height;

    const topLeft = {
      x: self.center.x - 0.5 * self.width,
      y: self.center.y - 0.5 / aspectRatio * self.width,
    };

    const bottomRight = {
      x: topLeft.x + self.width,
      y: topLeft.y + self.width / aspectRatio,
    };

    const rect = { topLeft, bottomRight };

    self.drawBegin = Date.now();
    self.drawEnd = null;

    self.displayBlockStore = new displayBlockStore(
      self.center,
      self.canvas.width,
      self.canvas.height,
      self.width,
      self.width / self.canvas.width * self.canvas.height,
    );

    let incompleteDraw = false;

    Promise.all(
      self.calculator.getBlocksForScreen(
        pos,
        rect,
        alreadyDrawnRect,
        pixelWidth,
        self.depth,
      ).map(blockPromise => blockPromise.then((block) => {
        if (!block) {
          incompleteDraw = true;
          return null;
        }

        const scaledBlock = self.displayBlockStore.scaleBlock(block);
        self.displayBlockStore.add(scaledBlock);

        return self.drawBlock(scaledBlock).then((drawn) => {
          if (!drawn) {
            incompleteDraw = true;
          }

          return scaledBlock;
        });
      })),
    ).then((scaledBlocks) => {
      if (!incompleteDraw) {
        self.drawEnd = Date.now();
        console.log(self.drawEnd - self.drawBegin);
      }

      self.coloriser.updateReferenceColor(scaledBlocks);
    });
  });

  self.drawBlock = self.scheduler((block) => {
    const pix = self.ctx.createImageData(block.size, block.size);

    // TODO: this belongs in the coloriser
    self.coloriser.blockDataToPixelData(block.data, pix);

    self.ctx.putImageData(
      pix,
      block.pixelPos.x,
      block.pixelPos.y,
    );

    return true;
  });

  self.drawBlocksCached = deferAndDropExcess(() => {
    if (!self.displayBlockStore) {
      self.draw();
      return;
    }

    self.scheduler.clear();
    // self.displayBlockStore.blocks.forEach(self.scheduler(self.drawBlock))
    self.displayBlockStore.blocks.forEach(self.drawBlock);
  });

  self.scale = function (factor, pos) {
    pos = (pos || self.center);

    self.center = {
      x: factor * self.center.x + (1 - factor) * pos.x,
      y: factor * self.center.y + (1 - factor) * pos.y,
    };

    self.width *= factor;

    self.draw(pos);
  };

  self.moveCenter = function (p) {
    self.center.x += p.x;
    self.center.y += p.y;
  };
};
<commit_msg>Handle multiple touches beginning simultaneously<commit_after>'use strict';

const calculator = require('./calculator');
const coloriser = require('./coloriser');
const deferAndDropExcess = require('./deferAndDropExcess');
const displayBlockStore = require('./displayBlockStore');
const scheduler = require('./scheduler');

const getDiscreteScroll = function (el, threshold, cb) {
  let excess = 0;

  el.addEventListener('wheel', (evt) => {
    excess += evt.deltaY;

    while (excess >= threshold) {
      cb(evt, threshold);
      console.log(threshold);
      excess -= threshold;
    }

    while (excess <= -threshold) {
      cb(evt, -threshold);
      console.log(-threshold);
      excess += threshold;
    }
  });
};

module.exports = function Renderer(canvas) {
  const self = this;

  self.depth = 500;
  self.canvas = canvas;
  self.ctx = canvas.getContext('2d');
  self.center = { x: -0.75, y: 0 };
  self.width = 8;

  self.coloriser = new coloriser();

  self.calculator = calculator();
  self.displayBlockStore = null;

  self.pixelRatio = window.devicePixelRatio || 1;

  self.scheduler = scheduler(20);

  self.drawBegin = null;
  self.drawEnd = null;

  self.updateSize = function () {
    canvas.style.width = `${canvas.parentNode.clientWidth}px`;
    canvas.style.height = `${canvas.parentNode.clientHeight}px`;

    canvas.width = self.pixelRatio * canvas.parentNode.clientWidth;
    canvas.height = self.pixelRatio * canvas.parentNode.clientHeight;
  }

  ;(function () {
    self.updateSize();

    canvas.parentNode.addEventListener('keydown', (evt) => {
      if (evt.keyCode === 67) {
        self.coloriser.randomise(!evt.shiftKey ? 1 : -1);
        self.drawBlocksCached(); // TODO: rename to redrawCurrentBlocks?
      }

      if (evt.keyCode === 187 || evt.keyCode === 189) {
        if (evt.shiftKey) {
          self.coloriser.shift(0.05 * (188 - evt.keyCode));
        } else {
          self.coloriser.multiplySpeed(Math.exp(0.05 * (188 - evt.keyCode)));
        }

        self.drawBlocksCached();
      }
    });

    let touchSession = null;

    canvas.addEventListener('touchstart', (e) => {
      e.preventDefault();

      if (!touchSession) {
        touchSession = {
          dragData: (() => {
            const cvs = document.createElement('canvas');
            cvs.setAttribute('width', canvas.width);
            cvs.setAttribute('height', canvas.height);

            const ctx = cvs.getContext('2d');
            const imgData = self.ctx.getImageData(0, 0, canvas.width, canvas.height);
            ctx.putImageData(imgData, 0, 0);

            return cvs;
          })(),
          dragDataIncomplete: self.drawBegin && !self.drawEnd,
        };
      }

      Array.from(e.changedTouches).forEach((touch) => {
        const touchDetails = {
          id: touch.identifier,
          start: {
            x: touch.clientX,
            y: touch.clientY,
          },
          curr: {
            x: touch.clientX,
            y: touch.clientY,
          },
        };

        if (!touchSession.primary) {
          touchSession.primary = touchDetails;
        } else if (!touchSession.secondary) {
          touchSession.secondary = touchDetails;
        }
      });
    });

    canvas.addEventListener('touchmove', (e) => {
      if (!touchSession) {
        return;
      }

      let primaryTouchChanged = false;
      let secondaryTouchChanged = false;

      const primaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.primary.id)[0];

      if (primaryTouch) {
        e.preventDefault();

        touchSession.primary.curr.x = primaryTouch.clientX;
        touchSession.primary.curr.y = primaryTouch.clientY;

        primaryTouchChanged = true;
      }

      if (touchSession.secondary) {
        const secondaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.secondary.id)[0];

        if (secondaryTouch) {
          touchSession.secondary.curr.x = secondaryTouch.clientX;
          touchSession.secondary.curr.y = secondaryTouch.clientY;

          const sq = x => x * x;
          const dist = (p1, p2) => Math.sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));

          touchSession.zoom = (
            dist(touchSession.primary.curr, touchSession.secondary.curr) /
            dist(touchSession.primary.start, touchSession.secondary.start)
          );

          secondaryTouchChanged = true;
        }
      }

      if (primaryTouchChanged || secondaryTouchChanged) {
        const diff = { x: touchSession.primary.curr.x - touchSession.primary.start.x, y: touchSession.primary.curr.y - touchSession.primary.start.y };
        self.ctx.clearRect(0, 0, canvas.width, canvas.height);

        const zoom = touchSession.zoom || 1;

        let dx = 0;
        dx += self.pixelRatio * diff.x;
        dx += self.pixelRatio * touchSession.primary.start.x * (1 - zoom);

        let dy = 0;
        dy += self.pixelRatio * diff.y;
        dy += self.pixelRatio * touchSession.primary.start.y * (1 - zoom);

        self.ctx.drawImage(
          touchSession.dragData,
          dx,
          dy,
          touchSession.dragData.width * zoom,
          touchSession.dragData.height * zoom,
        );
      }
    });

    canvas.addEventListener('touchcancel', (e) => {
      if (!touchSession) {
        return;
      }

      const primaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.primary.id)[0];

      if (!primaryTouch) {
        return;
      }

      touchSession = null;
      self.draw(self.center);
    });

    canvas.addEventListener('touchend', (e) => {
      if (!touchSession) {
        return;
      }

      const primaryTouch = Array.from(e.changedTouches).filter(t => t.identifier === touchSession.primary.id)[0];

      if (!primaryTouch) {
        return;
      }

      const diff = { x: e.changedTouches[0].clientX - touchSession.primary.start.x, y: e.changedTouches[0].clientY - touchSession.primary.start.y };

      if (diff.x === 0 && diff.y === 0) {
        return;
      }

      const pixelSize = self.width / canvas.width;
      const aspectRatio = canvas.width / canvas.height;

      const alreadyDrawnRect = touchSession.dragDataIncomplete || touchSession.zoom ? null : {
        topLeft: {
          x: self.center.x - 0.5 * self.width,
          y: self.center.y - 0.5 / aspectRatio * self.width,
        },
        bottomRight: {
          x: self.center.x + 0.5 * self.width,
          y: self.center.y + 0.5 / aspectRatio * self.width,
        },
      };

      const pos = {
        x: (
          self.center.x -
          0.5 * self.width +
          touchSession.primary.start.x * self.pixelRatio * pixelSize
        ),
        y: (
          self.center.y -
          0.5 / aspectRatio *
          self.width +
          touchSession.primary.start.y * self.pixelRatio * pixelSize
        ),
      };

      self.moveCenter({
        x: -diff.x * pixelSize * self.pixelRatio,
        y: -diff.y * pixelSize * self.pixelRatio,
      });

      if (touchSession.zoom) {
        self.scale(1 / touchSession.zoom, pos);
      } else {
        self.draw(pos, alreadyDrawnRect);
      }

      touchSession = null;
    });

    const zoom = function (dz, pos) {
      const pixelSize = self.width / canvas.width;
      const aspectRatio = canvas.width / canvas.height;

      self.scale(
        dz < 0 ? 2 / 3 : 3 / 2,
        {
          x: (
            self.center.x -
            0.5 * self.width +
            pos.x * self.pixelRatio * pixelSize
          ),
          y: (
            self.center.y -
            0.5 / aspectRatio * self.width +
            self.pixelRatio * pixelSize * pos.y
          ),
        },
      );
    };

    const mousePos = {
      x: 0,
      y: 0,
    };

    canvas.addEventListener('mousemove', (e) => {
      mousePos.x = e.clientX;
      mousePos.y = e.clientY;
    });

    getDiscreteScroll(canvas, 30, (e, dy) => {
      zoom(dy > 0 ? 1 : -1, mousePos);
    });

    canvas.parentNode.addEventListener('keydown', (e) => {
      if (e.keyCode === 68) {
        self.depth = parseInt(window.prompt('Enter new depth', '500'));
        self.draw();
      } else if (e.keyCode === 65) { // a
        zoom(-1, mousePos);
      } else if (e.keyCode === 90) { // z
        zoom(1, mousePos);
      }
    });
  }());

  self.draw = deferAndDropExcess((pos, alreadyDrawnRect) => { // pos === referencePoint?
    pos = pos || self.center;
    self.scheduler.clear();

    // self.coloriser.clearCache() // TODO: was this a good idea when it used to work?

    const pixelWidth = self.width / canvas.width;
    const aspectRatio = canvas.width / canvas.height;

    const topLeft = {
      x: self.center.x - 0.5 * self.width,
      y: self.center.y - 0.5 / aspectRatio * self.width,
    };

    const bottomRight = {
      x: topLeft.x + self.width,
      y: topLeft.y + self.width / aspectRatio,
    };

    const rect = { topLeft, bottomRight };

    self.drawBegin = Date.now();
    self.drawEnd = null;

    self.displayBlockStore = new displayBlockStore(
      self.center,
      self.canvas.width,
      self.canvas.height,
      self.width,
      self.width / self.canvas.width * self.canvas.height,
    );

    let incompleteDraw = false;

    Promise.all(
      self.calculator.getBlocksForScreen(
        pos,
        rect,
        alreadyDrawnRect,
        pixelWidth,
        self.depth,
      ).map(blockPromise => blockPromise.then((block) => {
        if (!block) {
          incompleteDraw = true;
          return null;
        }

        const scaledBlock = self.displayBlockStore.scaleBlock(block);
        self.displayBlockStore.add(scaledBlock);

        return self.drawBlock(scaledBlock).then((drawn) => {
          if (!drawn) {
            incompleteDraw = true;
          }

          return scaledBlock;
        });
      })),
    ).then((scaledBlocks) => {
      if (!incompleteDraw) {
        self.drawEnd = Date.now();
        console.log(self.drawEnd - self.drawBegin);
      }

      self.coloriser.updateReferenceColor(scaledBlocks);
    });
  });

  self.drawBlock = self.scheduler((block) => {
    const pix = self.ctx.createImageData(block.size, block.size);

    // TODO: this belongs in the coloriser
    self.coloriser.blockDataToPixelData(block.data, pix);

    self.ctx.putImageData(
      pix,
      block.pixelPos.x,
      block.pixelPos.y,
    );

    return true;
  });

  self.drawBlocksCached = deferAndDropExcess(() => {
    if (!self.displayBlockStore) {
      self.draw();
      return;
    }

    self.scheduler.clear();
    // self.displayBlockStore.blocks.forEach(self.scheduler(self.drawBlock))
    self.displayBlockStore.blocks.forEach(self.drawBlock);
  });

  self.scale = function (factor, pos) {
    pos = (pos || self.center);

    self.center = {
      x: factor * self.center.x + (1 - factor) * pos.x,
      y: factor * self.center.y + (1 - factor) * pos.y,
    };

    self.width *= factor;

    self.draw(pos);
  };

  self.moveCenter = function (p) {
    self.center.x += p.x;
    self.center.y += p.y;
  };
};
<|endoftext|>


<commit_before>/**
 *
 * Get property descriptor
 * @method _getPropertyDescriptor
 * @param {object} obj
 * @param {string} name
 * @return {Object}
 *
 */
function _getPropertyDescriptor(obj, name) {
    if (obj) {
        var pd = Object.getOwnPropertyDescriptor(obj, name);
        return pd || _getPropertyDescriptor(Object.getPrototypeOf(obj), name);
    }
}

function _copyprop(name, source, target) {
    var pd = _getPropertyDescriptor(source, name);
    Object.defineProperty(target, name, pd);
}

Fire.JS = {
    clear: function (obj) {
        var keys = Object.keys(obj);
        for (var i = 0; i < keys.length; i++) {
            delete obj[keys[i]];
        }
    }
};

/**
 *
 * copy all properties not defined in obj from arguments[1...n]
 * @method addon
 * @param {object} obj object to extend its properties
 * @param {object} sourceObj source object to copy properties from
 * @return {object} the result obj
 */
Fire.addon = function (obj) {
    'use strict';
    obj = obj || {};
    for (var i = 1, length = arguments.length; i < length; i++) {
        var source = arguments[i];
        for ( var name in source) {
            if ( !(name in obj) ) {
                _copyprop( name, source, obj);
            }
        }
    }
    return obj;
};

/**
 *
 * copy all properties from arguments[1...n] to obj
 * @method mixin
 * @param {object} obj
 * @param {object} source
 * @return {object} the result obj
 */
Fire.mixin = function (obj) {
    'use strict';
    obj = obj || {};
    for (var i = 1, length = arguments.length; i < length; i++) {
        var source = arguments[i];
        if (source) {
            if (typeof source !== 'object') {
                Fire.error('Fire.mixin called on non-object:', source);
                continue;
            }
            for ( var name in source) {
                _copyprop( name, source, obj);
            }
        }
    }
    return obj;
};

/**
 * Derive the class from the supplied base class.
 * Both classes are just native javascript constructors, not created by Fire.define, so
 * usually you will want to inherit using {% crosslink Fire.define define %} instead.
 *
 * @method extend
 * @param {function} cls
 * @param {function} base - the baseclass to inherit
 * @return {function} the result class
 *
 */
Fire.extend = function (cls, base) {
// @ifdef DEV
    if ( !base ) {
        Fire.error('The base class to extend from must be non-nil');
        return;
    }
    if ( !cls ) {
        Fire.error('The class to extend must be non-nil');
        return;
    }
// @endif
    for (var p in base) if (base.hasOwnProperty(p)) cls[p] = base[p];
    function __() { this.constructor = cls; }
    __.prototype = base.prototype;
    cls.prototype = new __();
    return cls;
};

/**
 * Get class name of the object, if object is just a {} (and which class named 'Object'), it will return null.
 * (modified from the code from this stackoverflow post)
 * @method getClassName
 * @param {object|function} obj - instance or constructor
 * @return {string}
 */
Fire.getClassName = function (obj) {
    if (typeof obj === 'function' && obj.prototype.__classname__) {
        return obj.prototype.__classname__;
    }
    if (obj && obj.constructor) {
        if (obj.constructor.prototype && obj.constructor.prototype.hasOwnProperty('__classname__')) {
            return obj.__classname__;
        }
        var retval;
        //  for browsers which have name property in the constructor of the object, such as chrome
        if (obj.constructor.name) {
            retval = obj.constructor.name;
        }
        if (obj.constructor.toString) {
            var arr, str = obj.constructor.toString();
            if (str.charAt(0) === '[') {
                // str is "[object objectClass]"
                arr = str.match(/\[\w+\s*(\w+)\]/);
            }
            else {
                // str is function objectClass () {} for IE Firefox
                arr = str.match(/function\s*(\w+)/);
            }
            if (arr && arr.length === 2) {
                retval = arr[1];
            }
        }
        return retval !== 'Object' ? retval : null;
    }
    return null;
};

// id 注册
(function () {
    var _idToClass = {};
    var _nameToClass = {};

    function getRegister (key, table) {
        return function (id, constructor) {
            // deregister old
            if (constructor.prototype.hasOwnProperty(key)) {
                delete table[constructor.prototype[key]];
            }
            constructor.prototype[key] = id;
            // register class
            if (id) {
                var registered = table[id];
                if (registered && registered !== constructor) {
                    var error = 'A Class already exists with the same ' + key + ' : "' + id + '".';
                    // @ifdef EDITOR
                    if (!Fire.isEditor) {
                        error += ' (This may be caused by error of unit test.) \
If you dont need serialization, you can set class id to "". You can also call \
Fire.unregisterClass to remove the id of unused class';
                    }
                    // @endif
                    Fire.error(error);
                }
                else {
                    table[id] = constructor;
                }
                //if (id === "") {
                //    console.trace("", table === _nameToClass);
                //}
            }
        };
    }

    /**
     * Register the class by specified id, if its classname is not defined, the class name will also be set.
     * @method _setClassId
     * @param {string} classId
     * @param {function} constructor
     */
    Fire._setClassId = getRegister('__cid__', _idToClass);

    var doSetClassName = getRegister('__classname__', _nameToClass);

    /**
     * Register the class by specified name
     * @method setClassName
     * @param {string} className
     * @param {function} constructor
     */
    Fire.setClassName = function (className, constructor) {
        doSetClassName(className, constructor);
        // auto set class id
        if (className && !constructor.prototype.hasOwnProperty('__cid__')) {
            Fire._setClassId(className, constructor);
        }
    };

    /**
     * If you dont need a class (which defined by Fire.define or Fire.setClassName) anymore,
     * You should unregister the class so that Fireball-x will not keep its reference anymore.
     * Please note that its still your responsibility to free other references to the class.
     *
     * @method unregisterClass
     * @param {function} [constructor] - the class you will want to unregister, any number of classes can be added
     */
    Fire.unregisterClass = function (constructor) {
        'use strict';
        for (var i = 0; i < arguments.length; i++) {
            var p = arguments[i].prototype;
            var classId = p.__cid__;
            if (classId) {
                delete _idToClass[classId];
            }
            var classname = p.__classname__;
            if (classname) {
                delete _nameToClass[classname];
            }
        }
    };

    /**
     * Get the registered class by id
     * @method _getClassById
     * @param {string} classId
     * @return {function} constructor
     */
    Fire._getClassById = function (classId) {
        return _idToClass[classId];
    };

    /**
     * Get the registered class by name
     * @method getClassByName
     * @param {string} classname
     * @return {function} constructor
     */
    Fire.getClassByName = function (classname) {
        return _nameToClass[classname];
    };

    /**
     * Get class id of the object
     * @method _getClassId
     * @param {object|function} obj - instance or constructor
     * @return {string}
     */
    Fire._getClassId = function (obj) {
        if (typeof obj === 'function' && obj.prototype.__cid__) {
            return obj.prototype.__cid__;
        }
        if (obj && obj.constructor) {
            if (obj.constructor.prototype && obj.constructor.prototype.hasOwnProperty('__cid__')) {
                return obj.__cid__;
            }
        }
        return '';
    };

    // @ifdef EDITOR
    Object.defineProperty(Fire, '_registeredClassIds', {
        get: function () {
            var dump = {};
            for (var id in _idToClass) {
                dump[id] = _idToClass[id];
            }
            return dump;
        },
        set: function (value) {
            Fire.JS.clear(_idToClass);
            for (var id in value) {
                _idToClass[id] = value[id];
            }
        }
    });
    Object.defineProperty(Fire, '_registeredClassNames', {
        get: function () {
            var dump = {};
            for (var id in _nameToClass) {
                dump[id] = _nameToClass[id];
            }
            return dump;
        },
        set: function (value) {
            Fire.JS.clear(_nameToClass);
            for (var id in value) {
                _nameToClass[id] = value[id];
            }
        }
    });
    // @endif
})();

// logs

Fire.log = function () {
    console.log.apply(console, arguments);
};
Fire.info = function () {
    (console.info || console.log).apply(console, arguments);
};
Fire.warn = function () {
    console.warn.apply(console, arguments);
};
if (console.error.bind) {
    // error会dump call stack，用bind可以避免dump Fire.error自己。
    Fire.error = console.error.bind(console);
}
else {
    Fire.error = function () {
        console.error.apply(console, arguments);
    };
}
<commit_msg>fireball-x/dev#319 添加双语言注释范例<commit_after>/**
 *
 * !#en Get property descriptor !#zh 获取 property 的描述物体
 * @method _getPropertyDescriptor
 * @param {object} obj - !#en do how many times !#zh 表示做几次的参数
 * @param {string} name
 * @return {Object}
 *
 */
function _getPropertyDescriptor(obj, name) {
    if (obj) {
        var pd = Object.getOwnPropertyDescriptor(obj, name);
        return pd || _getPropertyDescriptor(Object.getPrototypeOf(obj), name);
    }
}

function _copyprop(name, source, target) {
    var pd = _getPropertyDescriptor(source, name);
    Object.defineProperty(target, name, pd);
}

Fire.JS = {
    clear: function (obj) {
        var keys = Object.keys(obj);
        for (var i = 0; i < keys.length; i++) {
            delete obj[keys[i]];
        }
    }
};

/**
 *
 * copy all properties not defined in obj from arguments[1...n]
 * @method addon
 * @param {object} obj object to extend its properties
 * @param {object} sourceObj source object to copy properties from
 * @return {object} the result obj
 */
Fire.addon = function (obj) {
    'use strict';
    obj = obj || {};
    for (var i = 1, length = arguments.length; i < length; i++) {
        var source = arguments[i];
        for ( var name in source) {
            if ( !(name in obj) ) {
                _copyprop( name, source, obj);
            }
        }
    }
    return obj;
};

/**
 *
 * copy all properties from arguments[1...n] to obj
 * @method mixin
 * @param {object} obj
 * @param {object} source
 * @return {object} the result obj
 */
Fire.mixin = function (obj) {
    'use strict';
    obj = obj || {};
    for (var i = 1, length = arguments.length; i < length; i++) {
        var source = arguments[i];
        if (source) {
            if (typeof source !== 'object') {
                Fire.error('Fire.mixin called on non-object:', source);
                continue;
            }
            for ( var name in source) {
                _copyprop( name, source, obj);
            }
        }
    }
    return obj;
};

/**
 * Derive the class from the supplied base class.
 * Both classes are just native javascript constructors, not created by Fire.define, so
 * usually you will want to inherit using {% crosslink Fire.define define %} instead.
 *
 * @method extend
 * @param {function} cls
 * @param {function} base - the baseclass to inherit
 * @return {function} the result class
 *
 */
Fire.extend = function (cls, base) {
// @ifdef DEV
    if ( !base ) {
        Fire.error('The base class to extend from must be non-nil');
        return;
    }
    if ( !cls ) {
        Fire.error('The class to extend must be non-nil');
        return;
    }
// @endif
    for (var p in base) if (base.hasOwnProperty(p)) cls[p] = base[p];
    function __() { this.constructor = cls; }
    __.prototype = base.prototype;
    cls.prototype = new __();
    return cls;
};

/**
 * Get class name of the object, if object is just a {} (and which class named 'Object'), it will return null.
 * (modified from the code from this stackoverflow post)
 * @method getClassName
 * @param {object|function} obj - instance or constructor
 * @return {string}
 */
Fire.getClassName = function (obj) {
    if (typeof obj === 'function' && obj.prototype.__classname__) {
        return obj.prototype.__classname__;
    }
    if (obj && obj.constructor) {
        if (obj.constructor.prototype && obj.constructor.prototype.hasOwnProperty('__classname__')) {
            return obj.__classname__;
        }
        var retval;
        //  for browsers which have name property in the constructor of the object, such as chrome
        if (obj.constructor.name) {
            retval = obj.constructor.name;
        }
        if (obj.constructor.toString) {
            var arr, str = obj.constructor.toString();
            if (str.charAt(0) === '[') {
                // str is "[object objectClass]"
                arr = str.match(/\[\w+\s*(\w+)\]/);
            }
            else {
                // str is function objectClass () {} for IE Firefox
                arr = str.match(/function\s*(\w+)/);
            }
            if (arr && arr.length === 2) {
                retval = arr[1];
            }
        }
        return retval !== 'Object' ? retval : null;
    }
    return null;
};

// id 注册
(function () {
    var _idToClass = {};
    var _nameToClass = {};

    function getRegister (key, table) {
        return function (id, constructor) {
            // deregister old
            if (constructor.prototype.hasOwnProperty(key)) {
                delete table[constructor.prototype[key]];
            }
            constructor.prototype[key] = id;
            // register class
            if (id) {
                var registered = table[id];
                if (registered && registered !== constructor) {
                    var error = 'A Class already exists with the same ' + key + ' : "' + id + '".';
                    // @ifdef EDITOR
                    if (!Fire.isEditor) {
                        error += ' (This may be caused by error of unit test.) \
If you dont need serialization, you can set class id to "". You can also call \
Fire.unregisterClass to remove the id of unused class';
                    }
                    // @endif
                    Fire.error(error);
                }
                else {
                    table[id] = constructor;
                }
                //if (id === "") {
                //    console.trace("", table === _nameToClass);
                //}
            }
        };
    }

    /**
     * Register the class by specified id, if its classname is not defined, the class name will also be set.
     * @method _setClassId
     * @param {string} classId
     * @param {function} constructor
     */
    Fire._setClassId = getRegister('__cid__', _idToClass);

    var doSetClassName = getRegister('__classname__', _nameToClass);

    /**
     * Register the class by specified name
     * @method setClassName
     * @param {string} className
     * @param {function} constructor
     */
    Fire.setClassName = function (className, constructor) {
        doSetClassName(className, constructor);
        // auto set class id
        if (className && !constructor.prototype.hasOwnProperty('__cid__')) {
            Fire._setClassId(className, constructor);
        }
    };

    /**
     * If you dont need a class (which defined by Fire.define or Fire.setClassName) anymore,
     * You should unregister the class so that Fireball-x will not keep its reference anymore.
     * Please note that its still your responsibility to free other references to the class.
     *
     * @method unregisterClass
     * @param {function} [constructor] - the class you will want to unregister, any number of classes can be added
     */
    Fire.unregisterClass = function (constructor) {
        'use strict';
        for (var i = 0; i < arguments.length; i++) {
            var p = arguments[i].prototype;
            var classId = p.__cid__;
            if (classId) {
                delete _idToClass[classId];
            }
            var classname = p.__classname__;
            if (classname) {
                delete _nameToClass[classname];
            }
        }
    };

    /**
     * Get the registered class by id
     * @method _getClassById
     * @param {string} classId
     * @return {function} constructor
     */
    Fire._getClassById = function (classId) {
        return _idToClass[classId];
    };

    /**
     * Get the registered class by name
     * @method getClassByName
     * @param {string} classname
     * @return {function} constructor
     */
    Fire.getClassByName = function (classname) {
        return _nameToClass[classname];
    };

    /**
     * Get class id of the object
     * @method _getClassId
     * @param {object|function} obj - instance or constructor
     * @return {string}
     */
    Fire._getClassId = function (obj) {
        if (typeof obj === 'function' && obj.prototype.__cid__) {
            return obj.prototype.__cid__;
        }
        if (obj && obj.constructor) {
            if (obj.constructor.prototype && obj.constructor.prototype.hasOwnProperty('__cid__')) {
                return obj.__cid__;
            }
        }
        return '';
    };

    // @ifdef EDITOR
    Object.defineProperty(Fire, '_registeredClassIds', {
        get: function () {
            var dump = {};
            for (var id in _idToClass) {
                dump[id] = _idToClass[id];
            }
            return dump;
        },
        set: function (value) {
            Fire.JS.clear(_idToClass);
            for (var id in value) {
                _idToClass[id] = value[id];
            }
        }
    });
    Object.defineProperty(Fire, '_registeredClassNames', {
        get: function () {
            var dump = {};
            for (var id in _nameToClass) {
                dump[id] = _nameToClass[id];
            }
            return dump;
        },
        set: function (value) {
            Fire.JS.clear(_nameToClass);
            for (var id in value) {
                _nameToClass[id] = value[id];
            }
        }
    });
    // @endif
})();

// logs

Fire.log = function () {
    console.log.apply(console, arguments);
};
Fire.info = function () {
    (console.info || console.log).apply(console, arguments);
};
Fire.warn = function () {
    console.warn.apply(console, arguments);
};
if (console.error.bind) {
    // error会dump call stack，用bind可以避免dump Fire.error自己。
    Fire.error = console.error.bind(console);
}
else {
    Fire.error = function () {
        console.error.apply(console, arguments);
    };
}
<|endoftext|>


<commit_before>'use strict';

var asap = require('asap/raw');

function noop() {}

// States:
//
// 0 - pending
// 1 - fulfilled with _value
// 2 - rejected with _value
// 3 - adopted the state of another promise, _value
//
// once the state is no longer pending (0) it is immutable

// All `_` prefixed properties will be reduced to `_{random number}`
// at build time to obfuscate them and discourage their use.
// We don't use symbols or Object.defineProperty to fully hide them
// because the performance isn't good enough.

// to avoid using try/catch inside critical functions, we
// extract them to here.
var LAST_ERROR = null;
var IS_ERROR = {};
function getThen(obj) {
  try {
    return obj.then;
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

function tryCallOne(fn, a) {
  try {
    return fn(a);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

module.exports = Promise;
function Promise(fn) {
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('not a function');
  }
  this._state = 0;
  this._value = null;
  this._deferreds = [];
  if (fn === noop) return;
  doResolve(fn, this);
}
Promise.prototype._safeThen = function (onFulfilled, onRejected) {
  var self = this;
  return new this.constructor(function (resolve, reject) {
    var res = new Promise(noop);
    res.then(resolve, reject);
    self._handle(new Handler(onFulfilled, onRejected, res));
  });
};
Promise.prototype.then = function(onFulfilled, onRejected) {
  if (this.constructor !== Promise) {
    return this._safeThen(onFulfilled, onRejected);
  }
  var res = new Promise(noop);
  this._handle(new Handler(onFulfilled, onRejected, res));
  return res;
};
Promise.prototype._handle = function(deferred) {
  if (this._state === 3) {
    this._value._handle(deferred);
    return;
  }
  if (this._state === 0) {
    this._deferreds.push(deferred);
    return;
  }
  var state = this._state;
  var value = this._value;
  asap(function() {
    var cb = state === 1 ? deferred.onFulfilled : deferred.onRejected;
    if (cb === null) {
      if (state === 1) {
        deferred.promise._resolve(value);
      } else {
        deferred.promise._reject(value);
      }
      return;
    }
    var ret = tryCallOne(cb, value);
    if (ret === IS_ERROR) {
      deferred.promise._reject(LAST_ERROR);
    } else {
      deferred.promise._resolve(ret);
    }
  });
};
Promise.prototype._resolve = function(newValue) {
  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  if (newValue === this) {
    return this._reject(
      new TypeError('A promise cannot be resolved with itself.')
    );
  }
  if (
    newValue &&
    (typeof newValue === 'object' || typeof newValue === 'function')
  ) {
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      return this._reject(LAST_ERROR);
    }
    if (
      then === this.then &&
      newValue instanceof Promise &&
      newValue._handle === this._handle
    ) {
      this._state = 3;
      this._value = newValue;
      for (var i = 0; i < this._deferreds.length; i++) {
        newValue._handle(this._deferreds[i]);
      }
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), this);
      return;
    }
  }
  this._state = 1;
  this._value = newValue;
  this._finale();
};

Promise.prototype._reject = function (newValue) {
  this._state = 2;
  this._value = newValue;
  this._finale();
};
Promise.prototype._finale = function () {
  for (var i = 0; i < this._deferreds.length; i++)
    this._handle(this._deferreds[i]);
  this._deferreds = null;
};

function Handler(onFulfilled, onRejected, promise){
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
}

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 */
function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function (value) {
    if (done) return;
    done = true;
    promise._resolve(value);
  }, function (reason) {
    if (done) return;
    done = true;
    promise._reject(reason);
  });
  if (!done && res === IS_ERROR) {
    done = true;
    promise._reject(LAST_ERROR);
  }
}
<commit_msg>Avoid attaching unnecessary methods to the prototype<commit_after>'use strict';

var asap = require('asap/raw');

function noop() {}

// States:
//
// 0 - pending
// 1 - fulfilled with _value
// 2 - rejected with _value
// 3 - adopted the state of another promise, _value
//
// once the state is no longer pending (0) it is immutable

// All `_` prefixed properties will be reduced to `_{random number}`
// at build time to obfuscate them and discourage their use.
// We don't use symbols or Object.defineProperty to fully hide them
// because the performance isn't good enough.

// to avoid using try/catch inside critical functions, we
// extract them to here.
var LAST_ERROR = null;
var IS_ERROR = {};
function getThen(obj) {
  try {
    return obj.then;
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

function tryCallOne(fn, a) {
  try {
    return fn(a);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}
function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

module.exports = Promise;

function Promise(fn) {
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('not a function');
  }
  this._state = 0;
  this._value = null;
  this._deferreds = [];
  if (fn === noop) return;
  doResolve(fn, this);
}

Promise.prototype.then = function(onFulfilled, onRejected) {
  if (this.constructor !== Promise) {
    return safeThen(this, onFulfilled, onRejected);
  }
  var res = new Promise(noop);
  handle(this, new Handler(onFulfilled, onRejected, res));
  return res;
};

function safeThen(self, onFulfilled, onRejected) {
  return new self.constructor(function (resolve, reject) {
    var res = new Promise(noop);
    res.then(resolve, reject);
    handle(self, new Handler(onFulfilled, onRejected, res));
  });
};
function handle(self, deferred) {
  while (self._state === 3) {
    self = self._value;
  }
  if (self._state === 0) {
    self._deferreds.push(deferred);
    return;
  }
  asap(function() {
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
    if (cb === null) {
      if (self._state === 1) {
        resolve(deferred.promise, self._value);
      } else {
        reject(deferred.promise, self._value);
      }
      return;
    }
    var ret = tryCallOne(cb, self._value);
    if (ret === IS_ERROR) {
      reject(deferred.promise, LAST_ERROR);
    } else {
      resolve(deferred.promise, ret);
    }
  });
}
function resolve(self, newValue) {
  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  if (newValue === self) {
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.')
    );
  }
  if (
    newValue &&
    (typeof newValue === 'object' || typeof newValue === 'function')
  ) {
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      return reject(self, LAST_ERROR);
    }
    if (
      then === self.then &&
      newValue instanceof Promise
    ) {
      self._state = 3;
      self._value = newValue;
      finale(self);
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  self._state = 1;
  self._value = newValue;
  finale(self);
}

function reject(self, newValue) {
  self._state = 2;
  self._value = newValue;
  finale(self);
}
function finale(self) {
  for (var i = 0; i < self._deferreds.length; i++) {
    handle(self, self._deferreds[i]);
  }
  self._deferreds = null;
}

function Handler(onFulfilled, onRejected, promise){
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  this.promise = promise;
}

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 */
function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function (value) {
    if (done) return;
    done = true;
    resolve(promise, value);
  }, function (reason) {
    if (done) return;
    done = true;
    reject(promise, reason);
  })
  if (!done && res === IS_ERROR) {
    done = true;
    reject(promise, LAST_ERROR);
  }
}
<|endoftext|>


<commit_before>window.cls = window.cls || (window.cls = {});

/**
 * @constructor
 * @extends ViewBase
 */

cls.ReplView = function(id, name, container_class, html, default_handler) {
  this._resolver = new cls.PropertyFinder();
  this._data = new cls.ReplData(this);
  this._service = new cls.ReplService(this, this._data);
  this._linelist = null;
  this._textarea = null;
  this._lastupdate = null;
  this._current_input = "";
  this._current_scroll = 0;
  this._container = null;
  this._backlog_index = -1;

  this.ondestroy = function()
  {
    this._lastupdate = 0;
    this._backlog_index = -1;
    this._current_scroll = this._container.scrollTop;
    this._current_input = this._textarea.value;
  };

  this.createView = function(container)
  {
    var switched_to_view = false;
    if (!this._lastupdate)
    {
      container.innerHTML = "";
      container.render(templates.repl_main());
      this._linelist = container.querySelector("ol");
      this._textarea = container.querySelector("textarea");
      this._textarea.value = this._current_input;
      this._container = container;
      switched_to_view = true;
      // note: events are bound to handlers at the bottom of this class
    }

    var scroll_at_bottom = this._container.scrollTop + this._container.offsetHeight >= this._container.scrollHeight;
    this._update();

    if (this._current_scroll)
    {
      this._container.scrollTop = this._current_scroll;
      this._current_scroll = null;
    }
    else if (scroll_at_bottom)
    {
      this._container.scrollTop = 9999999; this._container.scrollHeight;
      if (switched_to_view) {
        window.setTimeout(function() {this._textarea.focus();}.bind(this), 100);
      }
    }

    return;
  };

  this.clear = function()
  {
    this.ondestroy();
  };

  /**
   * Pulls all the available, non-rendered, events from the data
   * object and renders them
   */
  this._update = function()
  {
    var now = new Date().getTime();
    var entries = this._data.get_log(this._lastupdate);
    this._lastupdate = now;

    for (var n=0, e; e=entries[n]; n++)
    {
      switch(e.type) {
        case "input":
          this._render_input(e.data);
          break;
        case "string":
          this._render_string(e.data);
          break;
        case "exception":
          this._render_error(e.data);
          break;
        case "iobj":
          this._render_inspectable_object(e.data);
          break;
        case "iele":
          this._render_inspectable_element(e.data);
          break;
        case "pobj":
          this._render_pointer_to_object(e.data);
          break;
        case "valuelist":
          this._render_value_list(e.data);
          break;
        case "trace":
          this._render_trace(e.data);
          break;
        case "groupstart":
          this._render_groupstart(e.data);
          break;
        case "groupend":
          this._render_groupend();
          break;
      default:
          this._render_string("unknown");
      }
    }
  };

  this._render_groupstart = function(data)
  {
    this._add_line([["button", "", "class", "folder-key"+(data.collapsed ? "" : " open" ),
                                   "handler", "repl-toggle-group"
                    ],
                    data.name]);
    var ol = document.createElement("ol");
    ol.className="repl-lines";
    this._add_line(ol);
    if (data.collapsed) {
      ol.parentNode.style.display = "none";
    }

    this._linelist = ol;
  };

  this._render_groupend = function()
  {
    if (this._linelist.parentNode.parentNode.nodeName.toLowerCase() == "ol")
    {
      this._linelist = this._linelist.parentNode.parentNode;
    }
  };

  this._render_pointer_to_object = function(data)
  {
    this._add_line(templates.repl_output_pobj(data));
  };

  this._render_inspectable_element = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableDomNodeView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_element.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_inspectable_object = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableObjectView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_object.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_error = function(data)
  {
    this._render_string(data.message, data.stacktrace);
  };

  this._render_trace = function(data)
  {
    this._add_line(templates.repl_output_trace(data));
  };

  this._render_value_list = function(values) {
    var tpl = values.map(templates.repl_output_native_or_pobj);
    var separated = [];
    separated.push(tpl.shift());
    while (tpl.length)
    {
      separated.push(["span", ", "]);
      separated.push(tpl.shift());
    }
    this._add_line(separated);
  };

  /**
   * Render an arbitrary numver of string arguments
   */
  this._render_string = function()
  {
    for (var n=0; n<arguments.length; n++)
    {
      this._add_line(templates.repl_output_native(arguments[n]));
    }
  };

  this._render_input = function(str)
  {
    this._render_string(">>> " + str);
  };

  this.set_current_input = function(str)
  {
    this._textarea.textContent = str;
  };

  this._add_line = function(elem_or_template)
  {
    var line = document.createElement("li");

    if (elem_or_template.nodeType === undefined)
    {
      line.render(elem_or_template);
    }
    else
    {
      line.appendChild(elem_or_template);
    }
    this._linelist.appendChild(line);
  };

  this._handle_keypress_bound = function(evt)
  {
    //opera.postError("" + evt.keyCode + " " + evt.which );
    switch (evt.keyCode) {
      case 9: // tab
      {
        evt.preventDefault();
        this._resolver.find_props(this._handle_completer.bind(this),
                                  this._textarea.value,
                                  window.stop_at.getSelectedFrame());
        break;
      }
      case 13: // enter
      {
        // stop it from adding a newline just before processing. Looks strange
        evt.preventDefault();
        var input = this._textarea.value;
        input = input.trim();
        this._textarea.value = "";
        this._backlog_index = -1;
        this._current_input = "";

        if (input == "") {
          this._render_input("");
          return;
        }

        this._service.handle_input(input);
        break;
      }
      case 38: // up and down. maybe. See DSK-246193
      case 40:
      {
        // workaround as long as we don't have support for keyIdentifier
        // event.which is 0 in a keypress event for function keys
        if( !evt.which )
        {
          evt.preventDefault();
          this._handle_backlog(evt.keyCode == 38 ? 1 : -1);
        }
        break;
      }
      case 107: // k key. ctrl-k == kill to en of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          var pos = this._textarea.selectionStart;
          this._textarea.value = this._textarea.value.slice(0, pos);
        }
        break;
      }
      case 108: // l key
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this.clear();
          this._data.clear();
        }
        break;
      }
    }
  }.bind(this);

  this._handle_backlog = function(delta)
  {
    this._set_input_from_backlog(this._backlog_index + delta);
  };

  this._set_input_from_backlog = function(index)
  {
    if (index <= -1)
    {
      this._backlog_index = -1;
      this._textarea.value = this._current_input;
      return;
    }

    if (this._backlog_index == -1)
    {
      this._current_input = this._textarea.value;
    }

    var log = this._data.get_typed_history();
    this._backlog_index = Math.min(index, log.length-1);
    var entry = log[this._backlog_index];

    if (entry != undefined)
    {
      this._textarea.value = entry;
    }
  };

  this._handle_completer = function(props)
  {
    var localpart = props.identifier;

    var matches = props.props.filter(function(e) {
      return e.indexOf(localpart) == 0;
    });

    if (! matches.length) {
      return;
    }

    var match = this._longest_common_prefix(matches.slice(0));
    if (match.length > localpart.length)
    {
      var pos = this._textarea.value.lastIndexOf(localpart);
      this._textarea.value = this._textarea.value.slice(0, pos) + match;
    }
    else
    {
      this._render_input(this._textarea.value);
      this._render_string(matches.sort().join(", "));
    }

  };

  /**
   * Return the longest common prefix of all the strings in the array
   * of strings. For example ["foobar", "foobaz", "foomatic"] -> "foo"
   */
  this._longest_common_prefix = function(strings)
  {
    if (strings.length == 0)
    {
      return "";
    }
    else if (strings.length == 1)
    {
      return strings[0];
    }
    else
    {
      var sorted = strings.slice(0).sort();
      var first = sorted.shift();
      var last = sorted.pop();

      for (var n=last.length; n; n--)
      {
        if (first.indexOf(last.slice(0,n)) == 0) { return last.slice(0, n); }
      }
    }
    return "";
  };

  this._handle_repl_toggle_group = function(event, target)
  {
    var li = target.parentNode;
    if (target.hasClass("open"))
    {
      target.removeClass("open");
      li.nextSibling.style.display = "none";
    }
    else
    {
      target.addClass("open");
      li.nextSibling.style.display = "";
    }
  };

  this._handle_option_change_bound = function(event, target)
  {
    settings.repl.set('max-typed-history-length', target.value);
    messages.post("setting-changed", {id: "repl", key: "max-typed-history-length"});
  }.bind(this);

  var eh = window.eventHandlers;
  eh.click["repl-toggle-group"] = this._handle_repl_toggle_group;
  eh.keypress['repl-textarea'] = this._handle_keypress_bound;
  eh.change['set-typed-history-length'] = this._handle_option_change_bound;

  this.init(id, name, container_class, html, default_handler);
};
cls.ReplView.prototype = ViewBase;

cls.ReplView.create_ui_widgets = function()
{

  new Settings(
    'repl',
    { // key/value
      'max-typed-history-length': 8,
      'typed-history': []
    },
    { // key/label
      'max-typed-history-length': "Max items in typed history to remember"
    },
    { // settings map
      customSettings:
      [
        'max-typed-history-length'
      ]
    },
    {  // custom templates
      'max-typed-history-length':
      function(setting)
      {
        return (
        [
          'setting-composite',
          ['label',
           setting.label_map['max-typed-history-length'] + ': ',
           ['input',
            'type', 'number',
            'handler', 'set-typed-history-length',
            'max', '1000',
            'min', '0',
            'value', setting.get('max-typed-history-length')
           ]
          ]
        ]);
      }
    }
  );
};
<commit_msg>Added ctrl-a shortcut (Move to end of line)<commit_after>window.cls = window.cls || (window.cls = {});

/**
 * @constructor
 * @extends ViewBase
 */

cls.ReplView = function(id, name, container_class, html, default_handler) {
  this._resolver = new cls.PropertyFinder();
  this._data = new cls.ReplData(this);
  this._service = new cls.ReplService(this, this._data);
  this._linelist = null;
  this._textarea = null;
  this._lastupdate = null;
  this._current_input = "";
  this._current_scroll = 0;
  this._container = null;
  this._backlog_index = -1;

  this.ondestroy = function()
  {
    this._lastupdate = 0;
    this._backlog_index = -1;
    this._current_scroll = this._container.scrollTop;
    this._current_input = this._textarea.value;
  };

  this.createView = function(container)
  {
    var switched_to_view = false;
    if (!this._lastupdate)
    {
      container.innerHTML = "";
      container.render(templates.repl_main());
      this._linelist = container.querySelector("ol");
      this._textarea = container.querySelector("textarea");
      this._textarea.value = this._current_input;
      this._container = container;
      switched_to_view = true;
      // note: events are bound to handlers at the bottom of this class
    }

    var scroll_at_bottom = this._container.scrollTop + this._container.offsetHeight >= this._container.scrollHeight;
    this._update();

    if (this._current_scroll)
    {
      this._container.scrollTop = this._current_scroll;
      this._current_scroll = null;
    }
    else if (scroll_at_bottom)
    {
      this._container.scrollTop = 9999999; this._container.scrollHeight;
      if (switched_to_view) {
        window.setTimeout(function() {this._textarea.focus();}.bind(this), 100);
      }
    }

    return;
  };

  this.clear = function()
  {
    this.ondestroy();
  };

  /**
   * Pulls all the available, non-rendered, events from the data
   * object and renders them
   */
  this._update = function()
  {
    var now = new Date().getTime();
    var entries = this._data.get_log(this._lastupdate);
    this._lastupdate = now;

    for (var n=0, e; e=entries[n]; n++)
    {
      switch(e.type) {
        case "input":
          this._render_input(e.data);
          break;
        case "string":
          this._render_string(e.data);
          break;
        case "exception":
          this._render_error(e.data);
          break;
        case "iobj":
          this._render_inspectable_object(e.data);
          break;
        case "iele":
          this._render_inspectable_element(e.data);
          break;
        case "pobj":
          this._render_pointer_to_object(e.data);
          break;
        case "valuelist":
          this._render_value_list(e.data);
          break;
        case "trace":
          this._render_trace(e.data);
          break;
        case "groupstart":
          this._render_groupstart(e.data);
          break;
        case "groupend":
          this._render_groupend();
          break;
      default:
          this._render_string("unknown");
      }
    }
  };

  this._render_groupstart = function(data)
  {
    this._add_line([["button", "", "class", "folder-key"+(data.collapsed ? "" : " open" ),
                                   "handler", "repl-toggle-group"
                    ],
                    data.name]);
    var ol = document.createElement("ol");
    ol.className="repl-lines";
    this._add_line(ol);
    if (data.collapsed) {
      ol.parentNode.style.display = "none";
    }

    this._linelist = ol;
  };

  this._render_groupend = function()
  {
    if (this._linelist.parentNode.parentNode.nodeName.toLowerCase() == "ol")
    {
      this._linelist = this._linelist.parentNode.parentNode;
    }
  };

  this._render_pointer_to_object = function(data)
  {
    this._add_line(templates.repl_output_pobj(data));
  };

  this._render_inspectable_element = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableDomNodeView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_element.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_inspectable_object = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableObjectView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_object.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_error = function(data)
  {
    this._render_string(data.message, data.stacktrace);
  };

  this._render_trace = function(data)
  {
    this._add_line(templates.repl_output_trace(data));
  };

  this._render_value_list = function(values) {
    var tpl = values.map(templates.repl_output_native_or_pobj);
    var separated = [];
    separated.push(tpl.shift());
    while (tpl.length)
    {
      separated.push(["span", ", "]);
      separated.push(tpl.shift());
    }
    this._add_line(separated);
  };

  /**
   * Render an arbitrary numver of string arguments
   */
  this._render_string = function()
  {
    for (var n=0; n<arguments.length; n++)
    {
      this._add_line(templates.repl_output_native(arguments[n]));
    }
  };

  this._render_input = function(str)
  {
    this._render_string(">>> " + str);
  };

  this.set_current_input = function(str)
  {
    this._textarea.textContent = str;
  };

  this._add_line = function(elem_or_template)
  {
    var line = document.createElement("li");

    if (elem_or_template.nodeType === undefined)
    {
      line.render(elem_or_template);
    }
    else
    {
      line.appendChild(elem_or_template);
    }
    this._linelist.appendChild(line);
  };

  this._handle_keypress_bound = function(evt)
  {
    // opera.postError("" + evt.keyCode + " " + evt.which );
    switch (evt.keyCode) {
      case 9: // tab
      {
        evt.preventDefault();
        this._resolver.find_props(this._handle_completer.bind(this),
                                  this._textarea.value,
                                  window.stop_at.getSelectedFrame());
        break;
      }
      case 13: // enter
      {
        // stop it from adding a newline just before processing. Looks strange
        evt.preventDefault();
        var input = this._textarea.value;
        input = input.trim();
        this._textarea.value = "";
        this._backlog_index = -1;
        this._current_input = "";

        if (input == "") {
          this._render_input("");
          return;
        }

        this._service.handle_input(input);
        break;
      }
      case 38: // up and down. maybe. See DSK-246193
      case 40:
      {
        // workaround as long as we don't have support for keyIdentifier
        // event.which is 0 in a keypress event for function keys
        if( !evt.which )
        {
          evt.preventDefault();
          this._handle_backlog(evt.keyCode == 38 ? 1 : -1);
        }
        break;
      }
      case 97: // a key. ctrl-a == move to start of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this._textarea.selectionStart = 0;
          this._textarea.selectionEnd = 0;
        }
        break;
      }
      case 107: // k key. ctrl-k == kill to en of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          var pos = this._textarea.selectionStart;
          this._textarea.value = this._textarea.value.slice(0, pos);
        }
        break;
      }
      case 108: // l key
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this.clear();
          this._data.clear();
        }
        break;
      }
    }
  }.bind(this);

  this._handle_backlog = function(delta)
  {
    this._set_input_from_backlog(this._backlog_index + delta);
  };

  this._set_input_from_backlog = function(index)
  {
    if (index <= -1)
    {
      this._backlog_index = -1;
      this._textarea.value = this._current_input;
      return;
    }

    if (this._backlog_index == -1)
    {
      this._current_input = this._textarea.value;
    }

    var log = this._data.get_typed_history();
    this._backlog_index = Math.min(index, log.length-1);
    var entry = log[this._backlog_index];

    if (entry != undefined)
    {
      this._textarea.value = entry;
    }
  };

  this._handle_completer = function(props)
  {
    var localpart = props.identifier;

    var matches = props.props.filter(function(e) {
      return e.indexOf(localpart) == 0;
    });

    if (! matches.length) {
      return;
    }

    var match = this._longest_common_prefix(matches.slice(0));
    if (match.length > localpart.length)
    {
      var pos = this._textarea.value.lastIndexOf(localpart);
      this._textarea.value = this._textarea.value.slice(0, pos) + match;
    }
    else
    {
      this._render_input(this._textarea.value);
      this._render_string(matches.sort().join(", "));
    }

  };

  /**
   * Return the longest common prefix of all the strings in the array
   * of strings. For example ["foobar", "foobaz", "foomatic"] -> "foo"
   */
  this._longest_common_prefix = function(strings)
  {
    if (strings.length == 0)
    {
      return "";
    }
    else if (strings.length == 1)
    {
      return strings[0];
    }
    else
    {
      var sorted = strings.slice(0).sort();
      var first = sorted.shift();
      var last = sorted.pop();

      for (var n=last.length; n; n--)
      {
        if (first.indexOf(last.slice(0,n)) == 0) { return last.slice(0, n); }
      }
    }
    return "";
  };

  this._handle_repl_toggle_group = function(event, target)
  {
    var li = target.parentNode;
    if (target.hasClass("open"))
    {
      target.removeClass("open");
      li.nextSibling.style.display = "none";
    }
    else
    {
      target.addClass("open");
      li.nextSibling.style.display = "";
    }
  };

  this._handle_option_change_bound = function(event, target)
  {
    settings.repl.set('max-typed-history-length', target.value);
    messages.post("setting-changed", {id: "repl", key: "max-typed-history-length"});
  }.bind(this);

  var eh = window.eventHandlers;
  eh.click["repl-toggle-group"] = this._handle_repl_toggle_group;
  eh.keypress['repl-textarea'] = this._handle_keypress_bound;
  eh.change['set-typed-history-length'] = this._handle_option_change_bound;

  this.init(id, name, container_class, html, default_handler);
};
cls.ReplView.prototype = ViewBase;

cls.ReplView.create_ui_widgets = function()
{

  new Settings(
    'repl',
    { // key/value
      'max-typed-history-length': 8,
      'typed-history': []
    },
    { // key/label
      'max-typed-history-length': "Max items in typed history to remember"
    },
    { // settings map
      customSettings:
      [
        'max-typed-history-length'
      ]
    },
    {  // custom templates
      'max-typed-history-length':
      function(setting)
      {
        return (
        [
          'setting-composite',
          ['label',
           setting.label_map['max-typed-history-length'] + ': ',
           ['input',
            'type', 'number',
            'handler', 'set-typed-history-length',
            'max', '1000',
            'min', '0',
            'value', setting.get('max-typed-history-length')
           ]
          ]
        ]);
      }
    }
  );
};
<|endoftext|>


<commit_before>import { LNil } from './types';

export var add = (...exprs) => exprs.reduce((acc, val) => acc + val);

export var divide = (...exprs) => exprs.reduce((acc, val) => acc / val);

export var multiply = (...exprs) => exprs.reduce((acc, val) => acc * val);

export var subtract = (...exprs) => exprs.reduce((acc, val) => acc - val);

export var lt = (a, b) => a < b;

export var lte = (a, b) => a <= b;

export var gt = (a, b) => a > b;

export var gte = (a, b) => a >= b;

export var first = (list) => list.length !== 0 ? list[0] : LNil();

export var rest = (list) => list.slice(1);

export var eq = (x, y) => x.valueOf() === y.valueOf();

export var list = (...elems) => elems;

export var cons = (a, b) => {
  if (!Array.isArray(b)) {
    throw new TypeError('Expected a list');
  }

  return [a, ...b];
};

export var type = (value) => value._type;
<commit_msg>Prevent JS primitive from leaking into env via `type` function<commit_after>import { LNil, LString } from './types';

export var add = (...exprs) => exprs.reduce((acc, val) => acc + val);

export var divide = (...exprs) => exprs.reduce((acc, val) => acc / val);

export var multiply = (...exprs) => exprs.reduce((acc, val) => acc * val);

export var subtract = (...exprs) => exprs.reduce((acc, val) => acc - val);

export var lt = (a, b) => a < b;

export var lte = (a, b) => a <= b;

export var gt = (a, b) => a > b;

export var gte = (a, b) => a >= b;

export var first = (list) => list.length !== 0 ? list[0] : LNil();

export var rest = (list) => list.slice(1);

export var eq = (x, y) => x.valueOf() === y.valueOf();

export var list = (...elems) => elems;

export var cons = (a, b) => {
  if (!Array.isArray(b)) {
    throw new TypeError('Expected a list');
  }

  return [a, ...b];
};

export var type = (value) => LString(value._type);
<|endoftext|>


<commit_before>import fmt from "../fmt"
import timeInfo from "./timeInfo"
import { log } from "fly-util"

export default function () {
  return this
  .on("fly_run", ({ path }) =>
    log(`Flying with ${fmt.path}...`, path))

  .on("flyfile_not_found", ({ error }) =>
    log(`No Flyfile Error: ${fmt.error}`, error))

  .on("fly_watch", () =>
    log(`${fmt.warn}`, "Watching files..."))

  .on("plugin_load", ({ plugin }) =>
    log(`Loading plugin ${fmt.title}`, plugin))

  .on("plugin_error", ({ plugin, error }) =>
    log(`${fmt.error} failed due to ${fmt.error}`, plugin, error))

  .on("task_error", ({ task, error }) =>
    log(`${fmt.error} failed due to ${fmt.error}`, task, error))

  .on("task_start", ({ task }) =>
    log(`Starting ${fmt.start}`, task))

  .on("task_complete", ({ task, duration }) => {
    const time = timeInfo(duration)
    log(`Finished ${fmt.complete} in ${fmt.secs}`,
      task, time.duration, time.scale)
  })

  .on("task_not_found", ({ task }) =>
    log(`${fmt.error} not found in Flyfile.`, task))
}
<commit_msg>trace errors on task_error<commit_after>import fmt from "../fmt"
import timeInfo from "./timeInfo"
import { log, trace } from "fly-util"

export default function () {
  return this
  .on("fly_run", ({ path }) =>
    log(`Flying with ${fmt.path}...`, path))

  .on("flyfile_not_found", ({ error }) =>
    log(`No Flyfile Error: ${fmt.error}`, error))

  .on("fly_watch", () =>
    log(`${fmt.warn}`, "Watching files..."))

  .on("plugin_load", ({ plugin }) =>
    log(`Loading plugin ${fmt.title}`, plugin))

  .on("plugin_error", ({ plugin, error }) =>
    log(`${fmt.error} failed due to ${fmt.error}`, plugin, error))

  .on("task_error", ({ task, error }) => {
    trace(error)
    log(`${fmt.error} failed due to ${fmt.error}`, task, error)
  })

  .on("task_start", ({ task }) =>
    log(`Starting ${fmt.start}`, task))

  .on("task_complete", ({ task, duration }) => {
    const time = timeInfo(duration)
    log(`Finished ${fmt.complete} in ${fmt.secs}`,
      task, time.duration, time.scale)
  })

  .on("task_not_found", ({ task }) =>
    log(`${fmt.error} not found in Flyfile.`, task))
}
<|endoftext|>