<commit_before>"use strict";

var _ = require("underscore");
var Article = require("substance-article");
var ImporterError = require("./converter_errors").ImporterError;

var State = function() {
  // an id generator for different types
  var ids = {};
  this.nextId = function(type) {
    ids[type] = ids[type] || 0;
    ids[type]++;
    return type +"_"+ids[type];
  };

  var stack = [];

  this.current = function() {
    return stack[stack.length-1];
  };

  this.push = function(node) {
    stack.push(node);
  };

  this.pop = function() {
    return stack.pop();
  };

  this.annotations = [];
};

var _annotationTypes = {
  "Emph": "emphasis",
  "Strong": "strong",
  "Link": "link",
  "Code": "code"
};

var _isTextish = function(node) {
  var type = node.tag;
  return (type === "Str" || type === "Space" || _annotationTypes[type] !== undefined);
};

var _isInline = function(node) {
  var type = node.tag;
  if (type === "Math") {
    return node.contents[0] === "InlineMath";
  }
  return false;
};

var _isAnnotation = function(type) {
  return (_annotationTypes[type] !== undefined);
};

var _isParagraphElem = function(item) {
  return (_isTextish(item) || _isInline(item));
};

var PandocImporter = function() {
};

PandocImporter.Prototype = function() {

  var _segmentParagraphElements = function(input) {
    var blocks = [];
    var last = {tag: "", contents: null};
    for (var i = 0; i < input.length; i++) {
      var item = input[i];
      if (_isParagraphElem(item)) {
        if (last.tag !== "Para") {
          last = {tag: "Para", contents: []};
          blocks.push(last);
        }
        last.contents.push(item);
      } else {
        blocks.push(item);
        last = item;
      }
    }

    return blocks;
  };

  this.import = function(input) {
    var state = new State();
    return this.document(state, input);
  };

  this.document = function(state, input) {
    var meta = input[0];
    var body = input[1];

    var doc = new Article({"id": meta.doc_id});
    state.doc = doc;

    if (meta.unMeta) {
      this.meta(state, meta.unMeta);
    }

    // this flattens the input so that some elements e.g., Image,
    // are top-level nodes.
    var idx;
    var nodes = [];
    for (var idx = 0; idx < body.length; idx++) {
      var item = body[idx];
      var type = item.tag;
      if (type === "Para") {
        nodes = nodes.concat(_segmentParagraphElements(item.contents));
      } else {
        nodes.push(item);
      }
    }

    // all nodes on this level are inserted and shown
    for (var idx = 0; idx < nodes.length; idx++) {
      var node = this.topLevelNode(state, nodes[idx]);
      if (!node) continue;

      if (_.isArray(node)) {
        for (var i = 0; i < node.length; i++) {
          doc.show("content", node[i].id, -1);
        }
      } else {
        doc.show("content", node.id, -1);
      }
    }

    // we are creating the annotations afterwards
    // to be sure that the annotated nodes are registered already
    for (idx = 0; idx < state.annotations.length; idx++) {
      doc.create(state.annotations[idx]);
    }

    return doc;
  };

  this.topLevelNode = function(state, input) {
    var type = input.tag;
    var content = input.contents;

    switch(type) {
      case "HorizontalRule":
        return false;
      case "Header":
        return this.header(state, content);
      case "Para":
        return this.paragraph(state, content);
      case "CodeBlock":
        return this.codeblock(state, content);
      case 'RawBlock':
        return this.rawblock(state, content);
      case 'BlockQuote':
        return this.blockquote(state, content);
      case "BulletList":
        return this.list(state, content, false);
      case "OrderedList":
        return this.list(state, content, true);
      case "Image":
        return this.figure(state, content);
      case "Math":
        return this.math(state, content);
      case "Table":
        return this.table(state, content);
      default:
        throw new ImporterError("Node not supported: " + type);
    }

  };

  this.text = function(state, iterator) {
    if (_.isArray(iterator)) {
      iterator = new PandocImporter.Iterator(iterator);
    }
    var doc = state.doc;
    var id = state.nextId("text");
    var node = {
      id: id,
      type: "text"
    };
    state.push(node);
    node.content = this.annotatedText(state, iterator);
    state.pop();
    return doc.create(node);
  };

  this.header = function(state, input) {
    var doc = state.doc;

    var level = input[0];
    var id = state.nextId("header");
    var node = {
      id: id,
      type: "heading",
      level: level,
      content: null
    };

    state.push(node);
    node.content = this.annotatedText(state, input[2]);
    state.pop();

    return doc.create(node);
  };

  this.paragraph = function(state, children) {
    var doc = state.doc;
    var nodes = [];
    var node;

    var iterator = new PandocImporter.Iterator(children);
    while (iterator.hasNext()) {
      var next = iterator.peek();
      var type = next.tag;

      if (_isTextish(next)) {
        node = this.text(state, iterator);
      } else if (_isInline(next)) {
        if (type === "Math") {
          node = this.math(state, iterator.next().contents);
        } else {
          throw new ImporterError("Paragraph Inline element not yet supported: " + type);
        }
      } else {
        throw new ImporterError("Node supported as element of a rich paragraph: " + type);
      }
      if (node) nodes.push(node);
    }

    if (nodes.length === 0) {
      return null;
    }
    // do not wrap single nodes into an extra paragraph
    else if (nodes.length == 1) {
      return nodes[0];
    }

    else {
      var id = state.nextId("paragraph");
      var paragraph = {
        id: id,
        type: "paragraph",
        children: _.map(nodes, function(n) {
          return n.id;
        })
      };
      return doc.create(paragraph);
    }
  };

  this.rawblock = function(state, input) {
    var doc = state.doc;
    var id = state.nextId("text");
    var node = {
      id: id,
      type: "text",
      content: input[1]
    };
    return doc.create(node);
  };

  this.codeblock = function(state, input) {
    var doc = state.doc;
    var id = state.nextId("codeblock");
    var node = {
      id: id,
      "type": "codeblock",
      content: input[1]
    };
    return doc.create(node);
  };

  this.blockquote = function(state, input) {
    var doc = state.doc;
    for (var idx = 0; idx < input.length; idx++) {
      var item = input[idx];
      var type = item.tag;
      var content = item.contents;

      var quote;
      if (type === "Para") {
        quote = this.paragraph(state, content);
        doc.show("content", quote.id, -1);
      }
      else if (type === "BlockQuote") {
        this.blockquote(state, content);
      }
      else {
        throw new ImporterError("Node not supported as blockquote: " + JSON.stringify(quote));
      }
    }

    return false;
  };

  this.figure = function(state, input) {
    var doc = state.doc;

    var caption = this.text(state, input[0]);
    var url = input[1][0];

    var id = state.nextId("figure");
    var node = {
      id: id,
      type: "figure",
      url: url,
      caption: caption.id
    };

    return doc.create(node);
  };

  this.list = function(state, input, ordered) {
    var doc = state.doc;

    var id = state.nextId("list");
    var node = {
      id: id,
      type: "list",
      items: [],
      ordered: ordered
    };

    state.push(node);
    if (ordered) {
      input = input[1];
    }
    for (var idx = 0; idx < input.length; idx++) {
      // Note: an item's content comes as an array
      // we do not support this, howevere, keep it in mind...
      var item = input[idx][0];
      var type = item.tag;
      var content = item.contents;

      var listItem;

      if (type === "Plain") {
        listItem = this.paragraph(state, content);
      }
      else {
        throw new ImporterError("Node not supported as list item: " + JSON.stringify(item));
      }
      node.items.push(listItem.id);
    }
    state.pop();

    return doc.create(node);
  };

  this.math = function(state, input) {
    var doc = state.doc;
    var mathType = input[0];
    var data = input[1].trim();

    var isInline = (mathType === "InlineMath");

    var id = state.nextId("formula");
    var formula = {
      id: id,
      type: "formula",
      data: data,
      format: "latex",
      inline: isInline
    };

    return doc.create(formula);
  };

  this.table = function(state, input) {
    var doc = state.doc;

    var id = state.nextId("table");
    var table = {
      id: id,
      type: "table",
      headers: [],
      cells: [],
      caption: null
    }

    var caption = this.text(state, input[0]);
    table.caption = caption.id;

    // TODO: what to do with that?
    // var alignments = input[1];
    // var somethingElse = input[2];

    var headerRow = input[3];
    var col, item, type, node;
    for (col = 0; col < headerRow.length; col++) {
      item = headerRow[col];
      if (item.length !== 1) {
        throw new ImporterError("Until now I have seen 1-element arrays only.");
      }
      item = item[0];
      type = item.tag;
      if (type === "Plain") {
        node = this.text(state, item.contents);
        table.headers.push(node.id);
      } else {
        throw new ImporterError("Table cell type not supported: " + type);
      }
    };

    var body = input[4];
    for (var row = 0; row < body.length; row++) {
      var rowIds = [];
      var rowInput = body[row];
      for (col = 0; col < rowInput.length; col++) {
        item = rowInput[col];
        if (item.length !== 1) {
          throw new ImporterError("Until now I have seen 1-element arrays only.");
        }
        item = item[0];
        type = item.tag;
        if (type === "Plain") {
          node = this.text(state, item.contents);
          rowIds.push(node.id);
        } else {
          throw new ImporterError("Table cell type not supported: " + type);
        }
      };
      table.cells.push(rowIds);
    };

    return doc.create(table);
  };

  // Retrieves a text block from an array of textish fragments
  // and creates annotations on the fly.
  // --------
  //

  this.annotatedText = function(state, iterator, startPos) {
    var result = [];
    var pos = startPos || 0;

    if (_.isArray(iterator)) {
      iterator = new PandocImporter.Iterator(iterator);
    }

    var str;
    while(iterator.hasNext()) {
      var item = iterator.next();
      var type = item.tag;

      if (type === "Space") {
        result.push(" ");
        pos++;
      } else if (type === "Str") {
        str = item.contents;
        result.push(str);
        pos += str.length;
      } else if (_isAnnotation(type)) {
        str = this.annotation(state, item, pos);
        result.push(str);
        pos += str.length;
      } else {
        iterator.back();
        break;
      }
    }

    return result.join("");
  };

  // Create an annotation that begins at the given startPos
  // --------
  //

  this.annotation = function(state, input, startPos) {
    var targetNode = state.current();
    if (targetNode === undefined) {
      throw new ImporterError("No target for annotation available");
    }
    var options = {};

    var type = input.tag;
    var children = input.contents;
    var iterator, content;

    if(type === 'Link') {
      options.url = children[1][0];
      iterator = new PandocImporter.Iterator(children[0]);
      content = this.annotatedText(state, iterator, startPos);
    }
    else if(type === 'Code') {
      content = children[1];
    }
    else {
      iterator = new PandocImporter.Iterator(children);
      content = this.annotatedText(state, iterator, startPos);
    }

    var endPos = startPos + content.length;

    var annotationType = _annotationTypes[type];
    var id = state.nextId(annotationType);
    var annotation = _.extend({
      id: id,
      type: annotationType,
      path: [targetNode.id, "content"],
      range: [startPos, endPos]
    },options);

    state.annotations.push(annotation);

    return content;
  };

  this.meta = function(state, meta) {
    var doc = state.doc;

    if (Object.keys(meta).length === 0) {
      return;
    }

    var metaData;
    if (_.isObject(meta)) {
      metaData = this.metaMap(state, meta);
    }

    if (metaData) {
      console.log("setting meta data", metaData);
      doc.set(["document", "meta"], metaData);
    }
  };

  this.getMetaValue = function(state, item) {
    var type = item.tag;
    var content = item.contents;
    var val;
    switch (type) {
    case "MetaMap":
      val = this.metaMap(state, content);
      break;
    case "MetaList":
      val = this.metaList(state, content);
      break;
    case "MetaInlines":
      val = this.metaInlines(state, content);
      break;
    default:
      val = content;
    }
    return val;
  };

  this.metaMap = function(state, metaMap) {
    var result = {};
    _.each(metaMap, function(item, name) {
      result[name] = this.getMetaValue(state, item);
    }, this);
    return result;
  };

  this.metaList = function(state, metaList) {
    var result = [];
    _.each(metaList, function(item) {
      result.push(this.getMetaValue(state, item));
    }, this);
    return result;
  };

  this.metaInlines = function(state, metaInlines) {
    var result = [];
    for (var i = 0; i < metaInlines.length; i++) {
      var item = metaInlines[i];
      var type = item.tag;
      switch (type) {
      case "Space":
        result.push(" ");
        break;
      case "Str":
        result.push(item.contents);
        break;
      default:
        console.error("Unknown type for MetaInlines ", type);
      }
    }
    return result.join("");
  };

};

PandocImporter.Iterator = function(elements) {
  this.elements = elements;
  this.length = this.elements.length;
  this.pos = -1;
};

PandocImporter.Iterator.prototype = {
  hasNext: function() {
    return this.pos < this.length - 1;
  },

  next: function() {
    this.pos += 1;
    return this.elements[this.pos];
  },

  peek: function() {
    return this.elements[this.pos+1];
  },

  back: function() {
    this.pos -= 1;
    return this;
  }
};

PandocImporter.prototype = new PandocImporter.Prototype();

module.exports = PandocImporter;
<commit_msg>Store labels of headings as source_id for cross references.<commit_after>"use strict";

var _ = require("underscore");
var Article = require("substance-article");
var ImporterError = require("./converter_errors").ImporterError;

var State = function() {
  // an id generator for different types
  var ids = {};
  this.nextId = function(type) {
    ids[type] = ids[type] || 0;
    ids[type]++;
    return type +"_"+ids[type];
  };

  var stack = [];

  this.current = function() {
    return stack[stack.length-1];
  };

  this.push = function(node) {
    stack.push(node);
  };

  this.pop = function() {
    return stack.pop();
  };

  this.annotations = [];
};

var _annotationTypes = {
  "Emph": "emphasis",
  "Strong": "strong",
  "Link": "link",
  "Code": "code"
};

var _isTextish = function(node) {
  var type = node.tag;
  return (type === "Str" || type === "Space" || _annotationTypes[type] !== undefined);
};

var _isInline = function(node) {
  var type = node.tag;
  if (type === "Math") {
    return node.contents[0] === "InlineMath";
  }
  return false;
};

var _isAnnotation = function(type) {
  return (_annotationTypes[type] !== undefined);
};

var _isParagraphElem = function(item) {
  return (_isTextish(item) || _isInline(item));
};

var PandocImporter = function() {
};

PandocImporter.Prototype = function() {

  var _segmentParagraphElements = function(input) {
    var blocks = [];
    var last = {tag: "", contents: null};
    for (var i = 0; i < input.length; i++) {
      var item = input[i];
      if (_isParagraphElem(item)) {
        if (last.tag !== "Para") {
          last = {tag: "Para", contents: []};
          blocks.push(last);
        }
        last.contents.push(item);
      } else {
        blocks.push(item);
        last = item;
      }
    }

    return blocks;
  };

  this.import = function(input) {
    var state = new State();
    return this.document(state, input);
  };

  this.document = function(state, input) {
    var meta = input[0];
    var body = input[1];

    var doc = new Article({"id": meta.doc_id});
    state.doc = doc;

    if (meta.unMeta) {
      this.meta(state, meta.unMeta);
    }

    // this flattens the input so that some elements e.g., Image,
    // are top-level nodes.
    var idx;
    var nodes = [];
    for (var idx = 0; idx < body.length; idx++) {
      var item = body[idx];
      var type = item.tag;
      if (type === "Para") {
        nodes = nodes.concat(_segmentParagraphElements(item.contents));
      } else {
        nodes.push(item);
      }
    }

    // all nodes on this level are inserted and shown
    for (var idx = 0; idx < nodes.length; idx++) {
      var node = this.topLevelNode(state, nodes[idx]);
      if (!node) continue;

      if (_.isArray(node)) {
        for (var i = 0; i < node.length; i++) {
          doc.show("content", node[i].id, -1);
        }
      } else {
        doc.show("content", node.id, -1);
      }
    }

    // we are creating the annotations afterwards
    // to be sure that the annotated nodes are registered already
    for (idx = 0; idx < state.annotations.length; idx++) {
      doc.create(state.annotations[idx]);
    }

    return doc;
  };

  this.topLevelNode = function(state, input) {
    var type = input.tag;
    var content = input.contents;

    switch(type) {
      case "HorizontalRule":
        return false;
      case "Header":
        return this.header(state, content);
      case "Para":
        return this.paragraph(state, content);
      case "CodeBlock":
        return this.codeblock(state, content);
      case 'RawBlock':
        return this.rawblock(state, content);
      case 'BlockQuote':
        return this.blockquote(state, content);
      case "BulletList":
        return this.list(state, content, false);
      case "OrderedList":
        return this.list(state, content, true);
      case "Image":
        return this.figure(state, content);
      case "Math":
        return this.math(state, content);
      case "Table":
        return this.table(state, content);
      default:
        throw new ImporterError("Node not supported: " + type);
    }

  };

  this.text = function(state, iterator) {
    if (_.isArray(iterator)) {
      iterator = new PandocImporter.Iterator(iterator);
    }
    var doc = state.doc;
    var id = state.nextId("text");
    var node = {
      id: id,
      type: "text"
    };
    state.push(node);
    node.content = this.annotatedText(state, iterator);
    state.pop();
    return doc.create(node);
  };

  this.header = function(state, input) {
    var doc = state.doc;

    var level = input[0];
    var source_id = input[1][0];
    var id = state.nextId("header");
    var node = {
      id: id,
      source_id: source_id,
      type: "heading",
      level: level,
      content: null
    };

    state.push(node);
    node.content = this.annotatedText(state, input[2]);
    state.pop();

    return doc.create(node);
  };

  this.paragraph = function(state, children) {
    var doc = state.doc;
    var nodes = [];
    var node;

    var iterator = new PandocImporter.Iterator(children);
    while (iterator.hasNext()) {
      var next = iterator.peek();
      var type = next.tag;

      if (_isTextish(next)) {
        node = this.text(state, iterator);
      } else if (_isInline(next)) {
        if (type === "Math") {
          node = this.math(state, iterator.next().contents);
        } else {
          throw new ImporterError("Paragraph Inline element not yet supported: " + type);
        }
      } else {
        throw new ImporterError("Node supported as element of a rich paragraph: " + type);
      }
      if (node) nodes.push(node);
    }

    if (nodes.length === 0) {
      return null;
    }
    // do not wrap single nodes into an extra paragraph
    else if (nodes.length == 1) {
      return nodes[0];
    }

    else {
      var id = state.nextId("paragraph");
      var paragraph = {
        id: id,
        type: "paragraph",
        children: _.map(nodes, function(n) {
          return n.id;
        })
      };
      return doc.create(paragraph);
    }
  };

  this.rawblock = function(state, input) {
    var doc = state.doc;
    var id = state.nextId("text");
    var node = {
      id: id,
      type: "text",
      content: input[1]
    };
    return doc.create(node);
  };

  this.codeblock = function(state, input) {
    var doc = state.doc;
    var id = state.nextId("codeblock");
    var node = {
      id: id,
      "type": "codeblock",
      content: input[1]
    };
    return doc.create(node);
  };

  this.blockquote = function(state, input) {
    var doc = state.doc;
    for (var idx = 0; idx < input.length; idx++) {
      var item = input[idx];
      var type = item.tag;
      var content = item.contents;

      var quote;
      if (type === "Para") {
        quote = this.paragraph(state, content);
        doc.show("content", quote.id, -1);
      }
      else if (type === "BlockQuote") {
        this.blockquote(state, content);
      }
      else {
        throw new ImporterError("Node not supported as blockquote: " + JSON.stringify(quote));
      }
    }

    return false;
  };

  this.figure = function(state, input) {
    var doc = state.doc;

    var caption = this.text(state, input[0]);
    var url = input[1][0];

    var id = state.nextId("figure");
    var node = {
      id: id,
      type: "figure",
      url: url,
      caption: caption.id
    };

    return doc.create(node);
  };

  this.list = function(state, input, ordered) {
    var doc = state.doc;

    var id = state.nextId("list");
    var node = {
      id: id,
      type: "list",
      items: [],
      ordered: ordered
    };

    state.push(node);
    if (ordered) {
      input = input[1];
    }
    for (var idx = 0; idx < input.length; idx++) {
      // Note: an item's content comes as an array
      // we do not support this, howevere, keep it in mind...
      var item = input[idx][0];
      var type = item.tag;
      var content = item.contents;

      var listItem;

      if (type === "Plain") {
        listItem = this.paragraph(state, content);
      }
      else {
        throw new ImporterError("Node not supported as list item: " + JSON.stringify(item));
      }
      node.items.push(listItem.id);
    }
    state.pop();

    return doc.create(node);
  };

  this.math = function(state, input) {
    var doc = state.doc;
    var mathType = input[0];
    var data = input[1].trim();

    var isInline = (mathType === "InlineMath");

    var id = state.nextId("formula");
    var formula = {
      id: id,
      type: "formula",
      data: data,
      format: "latex",
      inline: isInline
    };

    return doc.create(formula);
  };

  this.table = function(state, input) {
    var doc = state.doc;

    var id = state.nextId("table");
    var table = {
      id: id,
      type: "table",
      headers: [],
      cells: [],
      caption: null
    }

    var caption = this.text(state, input[0]);
    table.caption = caption.id;

    // TODO: what to do with that?
    // var alignments = input[1];
    // var somethingElse = input[2];

    var headerRow = input[3];
    var col, item, type, node;
    for (col = 0; col < headerRow.length; col++) {
      item = headerRow[col];
      if (item.length !== 1) {
        throw new ImporterError("Until now I have seen 1-element arrays only.");
      }
      item = item[0];
      type = item.tag;
      if (type === "Plain") {
        node = this.text(state, item.contents);
        table.headers.push(node.id);
      } else {
        throw new ImporterError("Table cell type not supported: " + type);
      }
    };

    var body = input[4];
    for (var row = 0; row < body.length; row++) {
      var rowIds = [];
      var rowInput = body[row];
      for (col = 0; col < rowInput.length; col++) {
        item = rowInput[col];
        if (item.length !== 1) {
          throw new ImporterError("Until now I have seen 1-element arrays only.");
        }
        item = item[0];
        type = item.tag;
        if (type === "Plain") {
          node = this.text(state, item.contents);
          rowIds.push(node.id);
        } else {
          throw new ImporterError("Table cell type not supported: " + type);
        }
      };
      table.cells.push(rowIds);
    };

    return doc.create(table);
  };

  // Retrieves a text block from an array of textish fragments
  // and creates annotations on the fly.
  // --------
  //

  this.annotatedText = function(state, iterator, startPos) {
    var result = [];
    var pos = startPos || 0;

    if (_.isArray(iterator)) {
      iterator = new PandocImporter.Iterator(iterator);
    }

    var str;
    while(iterator.hasNext()) {
      var item = iterator.next();
      var type = item.tag;

      if (type === "Space") {
        result.push(" ");
        pos++;
      } else if (type === "Str") {
        str = item.contents;
        result.push(str);
        pos += str.length;
      } else if (_isAnnotation(type)) {
        str = this.annotation(state, item, pos);
        result.push(str);
        pos += str.length;
      } else {
        iterator.back();
        break;
      }
    }

    return result.join("");
  };

  // Create an annotation that begins at the given startPos
  // --------
  //

  this.annotation = function(state, input, startPos) {
    var targetNode = state.current();
    if (targetNode === undefined) {
      throw new ImporterError("No target for annotation available");
    }
    var options = {};

    var type = input.tag;
    var children = input.contents;
    var iterator, content;

    if(type === 'Link') {
      options.url = children[1][0];
      iterator = new PandocImporter.Iterator(children[0]);
      content = this.annotatedText(state, iterator, startPos);
    }
    else if(type === 'Code') {
      content = children[1];
    }
    else {
      iterator = new PandocImporter.Iterator(children);
      content = this.annotatedText(state, iterator, startPos);
    }

    var endPos = startPos + content.length;

    var annotationType = _annotationTypes[type];
    var id = state.nextId(annotationType);
    var annotation = _.extend({
      id: id,
      type: annotationType,
      path: [targetNode.id, "content"],
      range: [startPos, endPos]
    },options);

    state.annotations.push(annotation);

    return content;
  };

  this.meta = function(state, meta) {
    var doc = state.doc;

    if (Object.keys(meta).length === 0) {
      return;
    }

    var metaData;
    if (_.isObject(meta)) {
      metaData = this.metaMap(state, meta);
    }

    if (metaData) {
      console.log("setting meta data", metaData);
      doc.set(["document", "meta"], metaData);
    }
  };

  this.getMetaValue = function(state, item) {
    var type = item.tag;
    var content = item.contents;
    var val;
    switch (type) {
    case "MetaMap":
      val = this.metaMap(state, content);
      break;
    case "MetaList":
      val = this.metaList(state, content);
      break;
    case "MetaInlines":
      val = this.metaInlines(state, content);
      break;
    default:
      val = content;
    }
    return val;
  };

  this.metaMap = function(state, metaMap) {
    var result = {};
    _.each(metaMap, function(item, name) {
      result[name] = this.getMetaValue(state, item);
    }, this);
    return result;
  };

  this.metaList = function(state, metaList) {
    var result = [];
    _.each(metaList, function(item) {
      result.push(this.getMetaValue(state, item));
    }, this);
    return result;
  };

  this.metaInlines = function(state, metaInlines) {
    var result = [];
    for (var i = 0; i < metaInlines.length; i++) {
      var item = metaInlines[i];
      var type = item.tag;
      switch (type) {
      case "Space":
        result.push(" ");
        break;
      case "Str":
        result.push(item.contents);
        break;
      default:
        console.error("Unknown type for MetaInlines ", type);
      }
    }
    return result.join("");
  };

};

PandocImporter.Iterator = function(elements) {
  this.elements = elements;
  this.length = this.elements.length;
  this.pos = -1;
};

PandocImporter.Iterator.prototype = {
  hasNext: function() {
    return this.pos < this.length - 1;
  },

  next: function() {
    this.pos += 1;
    return this.elements[this.pos];
  },

  peek: function() {
    return this.elements[this.pos+1];
  },

  back: function() {
    this.pos -= 1;
    return this;
  }
};

PandocImporter.prototype = new PandocImporter.Prototype();

module.exports = PandocImporter;
<|endoftext|>


<commit_before>function createService(container) {
  var arr = container.Config.Hostname.split('.');
  return {
    Port: 80,
    Environment: arr[1],
    TTL: defaultTTL,
    Service: arr[0],
    Instance: removeSlash(container.Name),
    Host: container.NetworkSettings.IpAddress
  };
}<commit_msg>updated to filter out the kb_ prefix on container names<commit_after>function createService(container) {
  var arr = container.Config.Hostname.split('.');
  return {
    Port: 80,
    Environment: arr[1],
    TTL: defaultTTL,
    Service: arr[0],
    Instance: removeSlash(container.Name).substring(3),
    Host: container.NetworkSettings.IpAddress
  };
}<|endoftext|>


<commit_before>/**
 * Patterns autosuggest - suggestion/completion support
 *
 * Copyright 2012-2013 Florian Friesdorf
 * Copyright 2012 JC Brand
 * Copyright 2013 Marko Durkovic
 */
define([
    "jquery",
    "pat-parser",
    "pat-registry",
    "select2"
], function($, Parser, registry) {
    var parser = new Parser("autosuggest");
    parser.add_argument("words", "");
    parser.add_argument("pre-fill", function($el) { return $el.val(); });
    parser.add_argument("placeholder", function($el) {
        return $el.attr("placeholder") || "Enter text";
    });

    var _ = {
        name: "autosuggest",
        trigger: "input.pat-autosuggest",
        init: function($el, opts) {
            if ($el.length > 1)
                return $el.each(function() { _.init($(this), opts); });

            var cfg = parser.parse($el, opts);
            $el.val(cfg.preFill.split(','));
            $el.select2({
                placeholder: $el.attr("readonly") ? "" : cfg.placeholder,
                tags: cfg.words.split(/\s*,\s*/),
                tokenSeparators: [","]
            });

            // suppress propagation for second input field
            $el.prev().on("input-change input-defocus input-change-delayed",
                function(e) { e.stopPropagation(); }
            );

            $(document).on('pat-update', $.proxy(function (ev, data) {
                // Clear the tags if the select2 input is hidden via pat-switch
                if (!data) {
                    return;
                }
                if ((data.pattern == 'switch') && (!$el.select2('container').is(':visible')) && $el.select2('val')) {
                    $el.select2('val', '');
                }
            }, this));
            return $el;
        },
        destroy: function($el) {
            $el.off(".pat-autosuggest");
            $el.select2("destroy");
        },
        transform: function($content) {
            $content.findInclusive('input[type=text].pat-autosuggest').each(function() {
                var $src = $(this),
                    $dest = $('<input type="hidden"/>').insertAfter($src);

                $src.detach();
                $.each($src.prop('attributes'), function() {
                    if (this.name !== 'type') {
                        $dest.attr(this.name, this.value);
                    }
                });
                $src.remove();
            });
        }
    };
    registry.register(_);
    return _;
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<commit_msg>Clear autosuggest values when a button of type 'reset' is clicked.<commit_after>/**
 * Patterns autosuggest - suggestion/completion support
 *
 * Copyright 2012-2013 Florian Friesdorf
 * Copyright 2012 JC Brand
 * Copyright 2013 Marko Durkovic
 */
define([
    "jquery",
    "pat-parser",
    "pat-registry",
    "select2"
], function($, Parser, registry) {
    var parser = new Parser("autosuggest");
    parser.add_argument("words", "");
    parser.add_argument("pre-fill", function($el) { return $el.val(); });
    parser.add_argument("placeholder", function($el) {
        return $el.attr("placeholder") || "Enter text";
    });

    var _ = {
        name: "autosuggest",
        trigger: "input.pat-autosuggest",
        init: function($el, opts) {
            if ($el.length > 1)
                return $el.each(function() { _.init($(this), opts); });

            var cfg = parser.parse($el, opts);
            $el.val(cfg.preFill.split(','));
            $el.select2({
                placeholder: $el.attr("readonly") ? "" : cfg.placeholder,
                tags: cfg.words.split(/\s*,\s*/),
                tokenSeparators: [","]
            });

            // suppress propagation for second input field
            $el.prev().on("input-change input-defocus input-change-delayed",
                function(e) { e.stopPropagation(); }
            );

            // Clear the values when a reset button is pressed
            $el.closest('form').find('button[type=reset]').on('click', function () {
                $el.select2('val', '');
            });
            return $el;
        },
        destroy: function($el) {
            $el.off(".pat-autosuggest");
            $el.select2("destroy");
        },
        transform: function($content) {
            $content.findInclusive('input[type=text].pat-autosuggest').each(function() {
                var $src = $(this),
                    $dest = $('<input type="hidden"/>').insertAfter($src);

                $src.detach();
                $.each($src.prop('attributes'), function() {
                    if (this.name !== 'type') {
                        $dest.attr(this.name, this.value);
                    }
                });
                $src.remove();
            });
        }
    };
    registry.register(_);
    return _;
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<|endoftext|>


<commit_before>/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

'use strict';

import * as utils from './utils'
import {getInnerHTML} from './innerHTML'
import {getProperty, hasProperty} from './logical-properties'
import * as nativeTree from './native-tree'

function generateSimpleDescriptor(prop) {
  return {
    get() {
      let l = getProperty(this, prop);
      return l !== undefined ? l : nativeTree[prop](this);
    },
    configurable: true
  }
}

function clearNode(node) {
  while (node.firstChild) {
    node.removeChild(node.firstChild);
  }
}

const nativeInnerHTMLDesc =
  Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML') ||
  Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'innerHTML');

const inertDoc = document.implementation.createHTMLDocument('inert');
const htmlContainer = inertDoc.createElement('div');

const nativeActiveElementDescriptor = Object.getOwnPropertyDescriptor(
  Document.prototype, 'activeElement');
function getDocumentActiveElement() {
  if (nativeActiveElementDescriptor && nativeActiveElementDescriptor.get) {
    return nativeActiveElementDescriptor.get.call(document);
  } else if (!utils.settings.hasDescriptors) {
    return document.activeElement;
  }
}

function activeElementForNode(node) {
  let active = getDocumentActiveElement();
  // In IE11, activeElement might be an empty object if the document is
  // contained in an iframe.
  // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10998788/
  if (!active || !active.nodeType) {
    return null;
  }
  let isShadyRoot = !!(utils.isShadyRoot(node));
  if (node !== document) {
    // If this node isn't a document or shady root, then it doesn't have
    // an active element.
    if (!isShadyRoot) {
      return null;
    }
    // If this shady root's host is the active element or the active
    // element is not a descendant of the host (in the composed tree),
    // then it doesn't have an active element.
    if (node.host === active ||
        !node.host.contains(active)) {
      return null;
    }
  }
  // This node is either the document or a shady root of which the active
  // element is a (composed) descendant of its host; iterate upwards to
  // find the active element's most shallow host within it.
  let activeRoot = utils.ownerShadyRootForNode(active);
  while (activeRoot && activeRoot !== node) {
    active = activeRoot.host;
    activeRoot = utils.ownerShadyRootForNode(active);
  }
  if (node === document) {
    // This node is the document, so activeRoot should be null.
    return activeRoot ? null : active;
  } else {
    // This node is a non-document shady root, and it should be
    // activeRoot.
    return activeRoot === node ? active : null;
  }
}

let OutsideAccessors = {
  // node...
  parentElement: generateSimpleDescriptor('parentElement'),

  parentNode: generateSimpleDescriptor('parentNode'),

  nextSibling: generateSimpleDescriptor('nextSibling'),

  previousSibling: generateSimpleDescriptor('previousSibling'),

  className: {
    /**
     * @this {HTMLElement}
     */
    get() {
      let className =  this.getAttribute('class');
      return this.getAttribute('class') ? className : '';
    },
    /**
     * @this {HTMLElement}
     */
    set(value) {
      this.setAttribute('class', value);
    },
    configurable: true
  },

  // fragment, element, document
  nextElementSibling: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'nextSibling')) {
        let n = this.nextSibling;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.nextSibling;
        }
        return n;
      } else {
        return nativeTree.nextElementSibling(this);
      }
    },
    configurable: true
  },

  previousElementSibling: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'previousSibling')) {
        let n = this.previousSibling;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.previousSibling;
        }
        return n;
      } else {
        return nativeTree.previousElementSibling(this);
      }
    },
    configurable: true
  }

};

let InsideAccessors = {

  childNodes: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        if (!this.__shady.childNodes) {
          this.__shady.childNodes = [];
          for (let n=this.firstChild; n; n=n.nextSibling) {
            this.__shady.childNodes.push(n);
          }
        }
        return this.__shady.childNodes;
      } else {
        return nativeTree.childNodes(this);
      }
    },
    configurable: true
  },

  firstChild: generateSimpleDescriptor('firstChild'),

  lastChild: generateSimpleDescriptor('lastChild'),

  textContent: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        let tc = [];
        for (let i = 0, cn = this.childNodes, c; (c = cn[i]); i++) {
          if (c.nodeType !== Node.COMMENT_NODE) {
            tc.push(c.textContent);
          }
        }
        return tc.join('');
      } else {
        return nativeTree.textContent(this);
      }
    },
    /**
     * @this {HTMLElement}
     */
    set(text) {
      if (this.nodeType !== Node.ELEMENT_NODE) {
        // TODO(sorvell): can't do this if patch nodeValue.
        this.nodeValue = text;
      } else {
        clearNode(this);
        if (text) {
          this.appendChild(document.createTextNode(text));
        }
      }
    },
    configurable: true
  },

  // fragment, element, document
  firstElementChild: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        let n = this.firstChild;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.nextSibling;
        }
        return n;
      } else {
        return nativeTree.firstElementChild(this);
      }
    },
    configurable: true
  },

  lastElementChild: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'lastChild')) {
        let n = this.lastChild;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.previousSibling;
        }
        return n;
      } else {
        return nativeTree.lastElementChild(this);
      }
    },
    configurable: true
  },

  children: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        return Array.prototype.filter.call(this.childNodes, function(n) {
          return (n.nodeType === Node.ELEMENT_NODE);
        });
      } else {
        return nativeTree.children(this);
      }
    },
    configurable: true
  },

  // element (HTMLElement on IE11)
  innerHTML: {
    /**
     * @this {HTMLElement}
     */
    get() {
      let content = this.localName === 'template' ?
        /** @type {HTMLTemplateElement} */(this).content : this;
      if (hasProperty(this, 'firstChild')) {
        return getInnerHTML(content);
      } else {
        return nativeTree.innerHTML(content);
      }
    },
    /**
     * @this {HTMLElement}
     */
    set(text) {
      let content = this.localName === 'template' ?
        /** @type {HTMLTemplateElement} */(this).content : this;
      clearNode(content);
      if (nativeInnerHTMLDesc && nativeInnerHTMLDesc.set) {
        nativeInnerHTMLDesc.set.call(htmlContainer, text);
      } else {
        htmlContainer.innerHTML = text;
      }
      while (htmlContainer.firstChild) {
        content.appendChild(htmlContainer.firstChild);
      }
    },
    configurable: true
  }

};

// Note: Can be patched on element prototype on all browsers.
// Must be patched on instance on browsers that support native Shadow DOM
// but do not have builtin accessors (old Chrome).
export let ShadowRootAccessor = {

  shadowRoot: {
    /**
     * @this {HTMLElement}
     */
    get() {
      return this.shadyRoot;
    },
    /**
     * @this {HTMLElement}
     */
    set(value) {
      this.shadyRoot = value;
    },
    configurable: true
  }
};

// Note: Can be patched on document prototype on browsers with builtin accessors.
// Must be patched separately on simulated ShadowRoot.
// Must be patched as `_activeElement` on browsers without builtin accessors.
export let ActiveElementAccessor = {

  activeElement: {
    /**
     * @this {HTMLElement}
     */
    get() {
      return activeElementForNode(this);
    },
    /**
     * @this {HTMLElement}
     */
    set() {},
    configurable: true
  }

};

// patch a group of descriptors on an object only if it exists or if the `force`
// argument is true.
/**
 * @param {!Object} obj
 * @param {!Object} descriptors
 * @param {boolean=} force
 */
function patchAccessorGroup(obj, descriptors, force) {
  for (let p in descriptors) {
    let objDesc = Object.getOwnPropertyDescriptor(obj, p);
    if ((objDesc && objDesc.configurable) ||
      (!objDesc && force)) {
      Object.defineProperty(obj, p, descriptors[p]);
    } else if (force) {
      console.warn('Could not define', p, 'on', obj);
    }
  }
}

// patch dom accessors on proto where they exist
export function patchAccessors(proto) {
  patchAccessorGroup(proto, OutsideAccessors);
  patchAccessorGroup(proto, InsideAccessors);
  patchAccessorGroup(proto, ActiveElementAccessor);
}

// ensure element descriptors (IE/Edge don't have em)
export function patchShadowRootAccessors(proto) {
  patchAccessorGroup(proto, InsideAccessors, true);
  patchAccessorGroup(proto, ActiveElementAccessor, true);
}

// ensure an element has patched "outside" accessors; no-op when not needed
export let patchOutsideElementAccessors = utils.settings.hasDescriptors ?
  function() {} : function(element) {
    if (!(element.__shady && element.__shady.__outsideAccessors)) {
      element.__shady = element.__shady || {};
      element.__shady.__outsideAccessors = true;
      patchAccessorGroup(element, OutsideAccessors, true);
    }
  }

// ensure an element has patched "inside" accessors; no-op when not needed
export let patchInsideElementAccessors = utils.settings.hasDescriptors ?
  function() {} : function(element) {
    if (!(element.__shady && element.__shady.__insideAccessors)) {
      element.__shady = element.__shady || {};
      element.__shady.__insideAccessors = true;
      patchAccessorGroup(element, InsideAccessors, true);
      patchAccessorGroup(element, ShadowRootAccessor, true);
    }
  }
<commit_msg>Update patch-accessors.js<commit_after>/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

'use strict';

import * as utils from './utils'
import {getInnerHTML} from './innerHTML'
import {getProperty, hasProperty} from './logical-properties'
import * as nativeTree from './native-tree'

function generateSimpleDescriptor(prop) {
  return {
    get() {
      let l = getProperty(this, prop);
      return l !== undefined ? l : nativeTree[prop](this);
    },
    configurable: true
  }
}

function clearNode(node) {
  while (node.firstChild) {
    node.removeChild(node.firstChild);
  }
}

const nativeInnerHTMLDesc =
  Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML') ||
  Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'innerHTML');

const inertDoc = document.implementation.createHTMLDocument('inert');
const htmlContainer = inertDoc.createElement('div');

const nativeActiveElementDescriptor = Object.getOwnPropertyDescriptor(
  Document.prototype, 'activeElement');
function getDocumentActiveElement() {
  if (nativeActiveElementDescriptor && nativeActiveElementDescriptor.get) {
    return nativeActiveElementDescriptor.get.call(document);
  } else if (!utils.settings.hasDescriptors) {
    return document.activeElement;
  }
}

function activeElementForNode(node) {
  let active = getDocumentActiveElement();
  // In IE11, activeElement might be an empty object if the document is
  // contained in an iframe.
  // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10998788/
  if (!active || !active.nodeType) {
    return null;
  }
  let isShadyRoot = !!(utils.isShadyRoot(node));
  if (node !== document) {
    // If this node isn't a document or shady root, then it doesn't have
    // an active element.
    if (!isShadyRoot) {
      return null;
    }
    // If this shady root's host is the active element or the active
    // element is not a descendant of the host (in the composed tree),
    // then it doesn't have an active element.
    if (node.host === active ||
        !node.host.contains(active)) {
      return null;
    }
  }
  // This node is either the document or a shady root of which the active
  // element is a (composed) descendant of its host; iterate upwards to
  // find the active element's most shallow host within it.
  let activeRoot = utils.ownerShadyRootForNode(active);
  while (activeRoot && activeRoot !== node) {
    active = activeRoot.host;
    activeRoot = utils.ownerShadyRootForNode(active);
  }
  if (node === document) {
    // This node is the document, so activeRoot should be null.
    return activeRoot ? null : active;
  } else {
    // This node is a non-document shady root, and it should be
    // activeRoot.
    return activeRoot === node ? active : null;
  }
}

let OutsideAccessors = {
  // node...
  parentElement: generateSimpleDescriptor('parentElement'),

  parentNode: generateSimpleDescriptor('parentNode'),

  nextSibling: generateSimpleDescriptor('nextSibling'),

  previousSibling: generateSimpleDescriptor('previousSibling'),

  className: {
    /**
     * @this {HTMLElement}
     */
    get() {
      let className =  this.getAttribute('class');
      return className ? className : '';
    },
    /**
     * @this {HTMLElement}
     */
    set(value) {
      this.setAttribute('class', value);
    },
    configurable: true
  },

  // fragment, element, document
  nextElementSibling: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'nextSibling')) {
        let n = this.nextSibling;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.nextSibling;
        }
        return n;
      } else {
        return nativeTree.nextElementSibling(this);
      }
    },
    configurable: true
  },

  previousElementSibling: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'previousSibling')) {
        let n = this.previousSibling;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.previousSibling;
        }
        return n;
      } else {
        return nativeTree.previousElementSibling(this);
      }
    },
    configurable: true
  }

};

let InsideAccessors = {

  childNodes: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        if (!this.__shady.childNodes) {
          this.__shady.childNodes = [];
          for (let n=this.firstChild; n; n=n.nextSibling) {
            this.__shady.childNodes.push(n);
          }
        }
        return this.__shady.childNodes;
      } else {
        return nativeTree.childNodes(this);
      }
    },
    configurable: true
  },

  firstChild: generateSimpleDescriptor('firstChild'),

  lastChild: generateSimpleDescriptor('lastChild'),

  textContent: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        let tc = [];
        for (let i = 0, cn = this.childNodes, c; (c = cn[i]); i++) {
          if (c.nodeType !== Node.COMMENT_NODE) {
            tc.push(c.textContent);
          }
        }
        return tc.join('');
      } else {
        return nativeTree.textContent(this);
      }
    },
    /**
     * @this {HTMLElement}
     */
    set(text) {
      if (this.nodeType !== Node.ELEMENT_NODE) {
        // TODO(sorvell): can't do this if patch nodeValue.
        this.nodeValue = text;
      } else {
        clearNode(this);
        if (text) {
          this.appendChild(document.createTextNode(text));
        }
      }
    },
    configurable: true
  },

  // fragment, element, document
  firstElementChild: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        let n = this.firstChild;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.nextSibling;
        }
        return n;
      } else {
        return nativeTree.firstElementChild(this);
      }
    },
    configurable: true
  },

  lastElementChild: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'lastChild')) {
        let n = this.lastChild;
        while (n && n.nodeType !== Node.ELEMENT_NODE) {
          n = n.previousSibling;
        }
        return n;
      } else {
        return nativeTree.lastElementChild(this);
      }
    },
    configurable: true
  },

  children: {
    /**
     * @this {HTMLElement}
     */
    get() {
      if (hasProperty(this, 'firstChild')) {
        return Array.prototype.filter.call(this.childNodes, function(n) {
          return (n.nodeType === Node.ELEMENT_NODE);
        });
      } else {
        return nativeTree.children(this);
      }
    },
    configurable: true
  },

  // element (HTMLElement on IE11)
  innerHTML: {
    /**
     * @this {HTMLElement}
     */
    get() {
      let content = this.localName === 'template' ?
        /** @type {HTMLTemplateElement} */(this).content : this;
      if (hasProperty(this, 'firstChild')) {
        return getInnerHTML(content);
      } else {
        return nativeTree.innerHTML(content);
      }
    },
    /**
     * @this {HTMLElement}
     */
    set(text) {
      let content = this.localName === 'template' ?
        /** @type {HTMLTemplateElement} */(this).content : this;
      clearNode(content);
      if (nativeInnerHTMLDesc && nativeInnerHTMLDesc.set) {
        nativeInnerHTMLDesc.set.call(htmlContainer, text);
      } else {
        htmlContainer.innerHTML = text;
      }
      while (htmlContainer.firstChild) {
        content.appendChild(htmlContainer.firstChild);
      }
    },
    configurable: true
  }

};

// Note: Can be patched on element prototype on all browsers.
// Must be patched on instance on browsers that support native Shadow DOM
// but do not have builtin accessors (old Chrome).
export let ShadowRootAccessor = {

  shadowRoot: {
    /**
     * @this {HTMLElement}
     */
    get() {
      return this.shadyRoot;
    },
    /**
     * @this {HTMLElement}
     */
    set(value) {
      this.shadyRoot = value;
    },
    configurable: true
  }
};

// Note: Can be patched on document prototype on browsers with builtin accessors.
// Must be patched separately on simulated ShadowRoot.
// Must be patched as `_activeElement` on browsers without builtin accessors.
export let ActiveElementAccessor = {

  activeElement: {
    /**
     * @this {HTMLElement}
     */
    get() {
      return activeElementForNode(this);
    },
    /**
     * @this {HTMLElement}
     */
    set() {},
    configurable: true
  }

};

// patch a group of descriptors on an object only if it exists or if the `force`
// argument is true.
/**
 * @param {!Object} obj
 * @param {!Object} descriptors
 * @param {boolean=} force
 */
function patchAccessorGroup(obj, descriptors, force) {
  for (let p in descriptors) {
    let objDesc = Object.getOwnPropertyDescriptor(obj, p);
    if ((objDesc && objDesc.configurable) ||
      (!objDesc && force)) {
      Object.defineProperty(obj, p, descriptors[p]);
    } else if (force) {
      console.warn('Could not define', p, 'on', obj);
    }
  }
}

// patch dom accessors on proto where they exist
export function patchAccessors(proto) {
  patchAccessorGroup(proto, OutsideAccessors);
  patchAccessorGroup(proto, InsideAccessors);
  patchAccessorGroup(proto, ActiveElementAccessor);
}

// ensure element descriptors (IE/Edge don't have em)
export function patchShadowRootAccessors(proto) {
  patchAccessorGroup(proto, InsideAccessors, true);
  patchAccessorGroup(proto, ActiveElementAccessor, true);
}

// ensure an element has patched "outside" accessors; no-op when not needed
export let patchOutsideElementAccessors = utils.settings.hasDescriptors ?
  function() {} : function(element) {
    if (!(element.__shady && element.__shady.__outsideAccessors)) {
      element.__shady = element.__shady || {};
      element.__shady.__outsideAccessors = true;
      patchAccessorGroup(element, OutsideAccessors, true);
    }
  }

// ensure an element has patched "inside" accessors; no-op when not needed
export let patchInsideElementAccessors = utils.settings.hasDescriptors ?
  function() {} : function(element) {
    if (!(element.__shady && element.__shady.__insideAccessors)) {
      element.__shady = element.__shady || {};
      element.__shady.__insideAccessors = true;
      patchAccessorGroup(element, InsideAccessors, true);
      patchAccessorGroup(element, ShadowRootAccessor, true);
    }
  }
<|endoftext|>


<commit_before>define([
    'require',
    '../core/parser',
    '../lib/jquery',
    '../lib/jquery.form'
], function(require) {
    var Parser = require('../core/parser'),
        parser = new Parser("source; target; replace");

    var init = function($el, opts) {
        // XXX: if opts, set them on $el as if defined there

        if ($el.is('a')) $el.click(function(ev) {
            ev.preventDefault();
            trigger($el);
        });

        if ($el.is('form')) $el.submit(function(ev) {
            ev.preventDefault();
            trigger($el);
        });

        if ($el.hasClass('autoLoading-visible')) {
            trigger($el);
        }
    };

    var _injectmethod = function(name, method) {
        var injectwrapper = function($sources, $targets, suppress) {
            // no $targets -> called as a jquery method
            // XXX: is it good to have that here?
            if ($targets === undefined) $targets = this;
            $targets = method($sources, $targets);
            if (!suppress) {
                $targets.trigger('inject', {
                    method: name,
                    $sources: $sources
                });
            }
            return $targets;
        };
        return injectwrapper;
    };

    var content = _injectmethod("content", function($sources, $targets) {
        $targets.each(function() {
            $(this).html($sources.html());
        });
        return $targets;
    });

    var replace = _injectmethod("replace", function($sources, $targets) {
        if ($targets.length === 1) {
            $targets.replaceWith($sources);
            return $sources;
        }
        $targets.each(function() {
            $(this).replaceWith($sources.clone().addClass(replace.marker));
        });
        return $("." + replace.marker).removeClass(replace.marker);
    });
    replace.marker = 'tmp-injection-marker';

    // create an injector to be run on ajax success
    var injector = function($el, method_name, opts, callback) {
        if (!opts.target) {
            opts.target = opts.source;
        }

        var method = pattern[method_name],
            $targets = $(opts.target);

        if ($targets.length === 0) {
            if (opts.target.slice(0,1) !== '#') {
                console.error('only id supported for non-existing target');
            }
            $targets = $('
').attr({id: opts.target.slice(1)});
            $('body').append($targets);
        }

        var inject = function(data, textStatus, jqXHR) {
            // just copied from old inject code
            data = data
                .replace(/


<commit_before>/*
 * changes to previous injection implementations
 * - no support for data-injection anymore, switch to new data-inject
 * - no support for data-href-next anymore, switch to data-inject: next-href
 * - XXX: add support for forms, see remnants in inject1 and ajaxify
 */
define([
    "jquery",
    "../core/parser",
    "../core/logger",
    "../lib/ajax",
    "../registry",
    "URIjs/URI",
    "jquery_ext", // for :scrollable for autoLoading-visible
    "URIjs/jquery.URI"
], function($, Parser, logger, ajax, registry, URI) {
    var log = logger.getLogger('pat.inject'),
        parser = new Parser("inject");

    //parser.add_argument('selector', 'body');
    parser.add_argument('selector');
    //XXX: (yet) unsupported: parser.add_argument('target', '$selector');
    parser.add_argument('target');
    parser.add_argument('type');
    parser.add_argument('next-href');
    //XXX: (yet) unsupported: parser.add_argument('source', '$selector');
    parser.add_argument('source');
    // XXX: this should not be here but the parser would bail on
    // unknown parameters and expand/collapsible need to pass the url
    // to us
    parser.add_argument('url');
    parser.add_argument('class');

    var _ = {
        name: "inject",
        trigger: "a.pat-inject, form.pat-inject",
        init: function($el, opts) {
            if ($el.length > 1)
                return $el.each(function() { _.init($(this), opts); });

            var cfgs = _.extractConfig($el, opts);
            $el.data('patterns.inject', cfgs);

            // In case next-href is specified the anchor's href will
            // be set to it after the injection is triggered. In case
            // the next href already exists, we do not activate the
            // injection but instead just change the anchors href.
            //
            // XXX: This is used in only one project for linked
            // fullcalendars, it's sanity is wonky and we should
            // probably solve it differently. -- Maybe it's cool
            // after all.
            var $nexthref = $(cfgs[0].nextHref);
            if ($el.is('a') && $nexthref.length > 0) {
                log.debug('Skipping as next href already exists', $nexthref);
                // XXX: reconsider how the injection enters exhausted state
                return $el.attr({href: (window.location.href.split('#')[0] || '') +
                                 cfgs[0].nextHref});
            }

            // setup event handlers
            if ($el.is('a'))
                $el.on("click.pat-inject", _.onClick);
            else if ($el.is('form'))
                $el.on("submit.pat-inject", _.onSubmit);

            // XXX: hack to support the old autoLoading-visible class
            if ($el.hasClass("autoLoading-visible"))
                _._initAutoloadVisible($el);

            log.debug('initialised:', $el);

            return $el;
        },
        destroy: function($el) {
            $el.off('.pat-inject');
            $el.data('patterns.inject', null);
            return $el;
        },
        onClick: function(ev) {
            var cfgs = $(this).data('patterns.inject'),
                $el = $(this);
            if (ev)
                ev.preventDefault();
            $el.trigger('patterns-inject-triggered');
            _.execute(cfgs, $el);
        },
        onSubmit: function(ev) {
            var cfgs = $(this).data('patterns.inject'),
                $el = $(this);
            if (ev)
                ev.preventDefault();
            $el.trigger('patterns-inject-triggered');
            _.execute(cfgs, $el);
        },
        extractConfig: function($el, opts) {
            opts = $.extend({}, opts);

            var urlparts, cfgs, defaultSelector;
            // opts has priority, fallback to href/action
            opts.url = opts.url || $el.attr('href') || $el.attr('action') || "";

            // separate selector from url
            urlparts = opts.url.split('#');
            opts.url = urlparts[0];

            // if no selector, check for selector as part of original url
            defaultSelector = urlparts[1] && '#' + urlparts[1] || 'body';

            if (urlparts.length > 2) {
                log.warn('Ignoring additional source ids:', urlparts.slice(2));
            }

            cfgs = parser.parse($el, opts, true);
            cfgs.forEach(function(cfg) {
                cfg.selector = cfg.selector || defaultSelector;
            });
            return cfgs;
        },
        // verify and post-process config
        // XXX: this should return a command instead of messing around on the config
        verifyConfig: function(cfgs) {
            var url = cfgs[0].url;

            // verification for each cfg in the array needs to succeed
            return cfgs.every(function(cfg) {
                // in case of multi-injection, all injections need to use
                // the same url
                if (cfg.url !== url) {
                    log.error('Unsupported different urls for multi-inject');
                    return false;
                }

                // defaults
                cfg.source = cfg.source || cfg.selector;
                cfg.target = cfg.target || cfg.selector;

                if (!_._extractModifiers(cfg))
                    return false;

                // make sure target exist
                cfg.$target = cfg.$target || $(cfg.target);
                if (cfg.$target.length === 0) {
                    if (!cfg.target) {
                        log.error('Need target selector', cfg);
                        return false;
                    }
                    cfg.$target = _._createTarget(cfg.target);
                    cfg.$injected = cfg.$target;
                }
                return true;
            });
        },
        _extractModifiers: function(cfg) {
            var source_re = /^(.+?)(::element)?$/,
                target_re = /^(.+?)(::element)?(::after|::before)?$/,
                source_match = source_re.exec(cfg.source),
                target_match = target_re.exec(cfg.target),
                targetMod, targetPosition;

            // source content or element?
            cfg.source = source_match[1];
            // XXX: turn into source processor
            cfg.sourceMod = source_match[2] ? "element" : "content";

            // will be added while the ajax request is in progress
            cfg.targetLoadClasses = "injecting";

            // target content or element?
            targetMod = target_match[2] ? "element" : "content";
            cfg.target = target_match[1];
            cfg.targetLoadClasses += " injecting-" + targetMod;

            // position relative to target
            targetPosition = (target_match[3] || "::").slice(2);
            if (targetPosition)
                cfg.targetLoadClasses += " injecting-" + targetPosition;

            cfg.action = targetMod + targetPosition;

            // Once we start detecting illegal combinations, we'll
            // return false in case of error
            return true;
        },

        // create a target that matches the selector
        //
        // XXX: so far we only support #target and create a div with
        // that id appended to the body.
        _createTarget: function(selector) {
            var $target;
            if (selector.slice(0,1) !== '#') {
                log.error('only id supported for non-existing target');
                return null;
            }
            $target = $('
').attr({id: selector.slice(1)});
            $('body').append($target);
            return $target;
        },
        execute: function(cfgs, $el) {
            // get a kinda deep copy, we scribble on it
            cfgs = cfgs.map(function(cfg) {
                return $.extend({}, cfg);
            });

            // XXX: this need to get functional, returning a command
            // for internal use, instead of passing on cfgs
            if (!_.verifyConfig(cfgs))
                return;

            // possibility for spinners on targets
            cfgs.forEach(function(cfg) {
                cfg.$target.addClass(cfg.targetLoadClasses);
            });

            var onSuccess = function(ev) {
                if (ev.target !== $el[0]) return;

                var data = ev && ev.jqxhr && ev.jqxhr.responseText;
                if (!data) {
                    log.warn('No response content, aborting', ev);
                    return;
                }
                // list of $source objects, one for each cfg
                var sources = cfgs.map(function(cfg) { return cfg.source; }),
                    sources$ = _._sourcesFromHtml(data, cfgs[0].url, sources);

                cfgs.forEach(function(cfg, idx) {
                    var $source = sources$[idx];

                    if (cfg.sourceMod === "content")
                        $source = $source.contents();

                    // perform injection
                    cfg.$target.each(function() {
                        var $target = $(this),
                            $src = $source.clone(),
                            $injected = cfg.$injected || $src;
                        if (_._inject($src, $target, cfg.action, $el)) {
                            $injected.filter(function() {
                                // setting data on textnode fails in IE8
                                return this.nodeType !== 3; //Node.TEXT_NODE
                            }).data('pat-injected', {origin: cfg.url});
                            $injected.addClass(cfg["class"])
                                .trigger('patterns-injected', cfg);
                        }
                    });
                });

                if (cfgs[0].nextHref) {
                    $el.attr({href: (window.location.href.split('#')[0] || '') +
                              cfgs[0].nextHref});
                    _.destroy($el);

                    // XXX: this used to be the case, but I don't see
                    // why that would be a good idea.
                    //
                    // jump to new href target
                    //if (!$el.hasClass("autoLoading-visible"))
                    //    window.location.href = $el.attr('href');
                }
                $el.off('pat-ajax-success.pat-inject');
            };

            $el.on('pat-ajax-success.pat-inject', onSuccess);

            ajax($el, {
                url: cfgs[0].url
            });
        },
        _inject: function($source, $target, action, $trigger) {
            // action to jquery method mapping, except for "content"
            // and "element"
            var method = {
                contentbefore: "prepend",
                contentafter:  "append",
                elementbefore: "before",
                elementafter:  "after"
            }[action];

            if ($source.length === 0) {
                log.warn('Aborting injection, source not found:', $source);
                return false;
            }
            if ($target.length === 0) {
                log.warn('Aborting injection, target not found:', $target);
                return false;
            }

            if (action === "content") {
                $target.empty().append($source);
            } else if (action === "element") {
                if ($target[0] === $trigger[0] ||
                    $target.find($trigger).length > 0) {
                    // delay removing the trigger element until all event
                    // handlers have fired
                    $target.hide().after($source);

                    // use a named function to support concurrent injections
                    var remove = function(ev) {
                        if (ev.target === $trigger[0]) {
                            $target.remove();
                            $(document)
                                .off("pat-ajax-success.pat-inject", remove);
                        }
                    };
                    $(document).on("pat-ajax-success.pat-inject", remove);
                } else {
                    $target.replaceWith($source);
                }
            } else {
                $target[method]($source);
            }

            return true;
        },
        _sourcesFromHtml: function(html, url, sources) {
            var $html = _._parseRawHtml(html, url);
            return sources.map(function(source) {
                if (source === "body")
                    source = "#__original_body";

                var $source = $html.find(source);

                if ($source.length === 0)
                    log.warn('No source elements for selector:', source, $html);

                return $source;
            });
        },

        _link_attributes: {
            A: "href",
            FORM: "action",
            IMG: "src"
        },

        _parseRawHtml: function(html, url) {
            url = url || "";

            // remove script tags and head and replace body by a div
            var clean_html = html
                    .replace(/


<commit_before>/**
 * @license Copyright 2017 The Lighthouse Authors. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

const Audit = require('../audit.js');
const URL = require('../../lib/url-shim.js');
const BLOCKLIST = new Set([
  // English
  'click here',
  'click this',
  'go',
  'here',
  'information',
  'learn more',
  'more',
  'more info',
  'more information',
  'right here',
  'read more',
  'see more',
  'start',
  'this',
  // Japanese
  'ここをクリック',
  'こちらをクリック',
  'リンク',
  '続きを読む',
  '続く',
  '全文表示',
  // Spanish
  'click aquí',
  'click aqui',
  'clicka aquí',
  'clicka aqui',
  'pincha aquí',
  'pincha aqui',
  'aquí',
  'aqui',
  'más',
  'mas',
  'más información',
  'más informacion',
  'mas información',
  'mas informacion',
  'este',
  'enlace',
  'este enlace',
  'empezar',
  // Portuguese
  'clique aqui',
  'inicio',
  'início',
  'ir',
  'mais informação',
  'mais informações',
  'mais',
  'veja mais',
  // Korean
  '여기',
  '여기를 클릭',
  '클릭',
  '링크',
  '자세히',
  '자세히 보기',
  '계속',
  '이동',
  '전체 보기',
  // Swedish
  'här',
  'klicka här',
  'läs mer',
  'mer',
  'mer info',
  'mer information',
]);
const i18n = require('../../lib/i18n/i18n.js');

const UIStrings = {
  /** Title of a Lighthouse audit that tests if each link on a page contains a sufficient description of what a user will find when they click it. Generic, non-descriptive text like "click here" doesn't give an indication of what the link leads to. This descriptive title is shown when all links on the page have sufficient textual descriptions. */
  title: 'Links have descriptive text',
  /** Title of a Lighthouse audit that tests if each link on a page contains a sufficient description of what a user will find when they click it. Generic, non-descriptive text like "click here" doesn't give an indication of what the link leads to. This descriptive title is shown when one or more links on the page contain generic, non-descriptive text. */
  failureTitle: 'Links do not have descriptive text',
  /** Description of a Lighthouse audit that tells the user *why* they need to have descriptive text on the links in their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
  description: 'Descriptive link text helps search engines understand your content. ' +
  '[Learn more](https://web.dev/link-text/).',
  /** [ICU Syntax] Label for the audit identifying the number of links found. "link" here refers to the links in a web page to other web pages. */
  displayValue: `{itemCount, plural,
    =1 {1 link found}
    other {# links found}
    }`,
};

const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);

class LinkText extends Audit {
  /**
   * @return {LH.Audit.Meta}
   */
  static get meta() {
    return {
      id: 'link-text',
      title: str_(UIStrings.title),
      failureTitle: str_(UIStrings.failureTitle),
      description: str_(UIStrings.description),
      requiredArtifacts: ['URL', 'AnchorElements'],
    };
  }

  /**
   * @param {LH.Artifacts} artifacts
   * @return {LH.Audit.Product}
   */
  static audit(artifacts) {
    const failingLinks = artifacts.AnchorElements
      .filter(link => link.href && !link.rel.includes('nofollow'))
      .filter(link => {
        const href = link.href.toLowerCase();
        if (
          href.startsWith('javascript:') ||
          href.startsWith('mailto:') ||
          URL.equalWithExcludedFragments(link.href, artifacts.URL.finalUrl)
        ) {
          return false;
        }

        return BLOCKLIST.has(link.text.trim().toLowerCase());
      })
      .map(link => {
        return {
          href: link.href,
          text: link.text.trim(),
        };
      });

    /** @type {LH.Audit.Details.Table['headings']} */
    const headings = [
      {key: 'href', itemType: 'url', text: 'Link destination'},
      {key: 'text', itemType: 'text', text: 'Link Text'},
    ];

    const details = Audit.makeTableDetails(headings, failingLinks, {});
    let displayValue;

    if (failingLinks.length) {
      displayValue = str_(UIStrings.displayValue, {itemCount: failingLinks.length});
    }

    return {
      score: Number(failingLinks.length === 0),
      details,
      displayValue,
    };
  }
}

module.exports = LinkText;
module.exports.UIStrings = UIStrings;
<commit_msg>core(link-text): removing inicio from blocklist resolves #11026 (#11073)<commit_after>/**
 * @license Copyright 2017 The Lighthouse Authors. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

const Audit = require('../audit.js');
const URL = require('../../lib/url-shim.js');
const BLOCKLIST = new Set([
  // English
  'click here',
  'click this',
  'go',
  'here',
  'information',
  'learn more',
  'more',
  'more info',
  'more information',
  'right here',
  'read more',
  'see more',
  'start',
  'this',
  // Japanese
  'ここをクリック',
  'こちらをクリック',
  'リンク',
  '続きを読む',
  '続く',
  '全文表示',
  // Spanish
  'click aquí',
  'click aqui',
  'clicka aquí',
  'clicka aqui',
  'pincha aquí',
  'pincha aqui',
  'aquí',
  'aqui',
  'más',
  'mas',
  'más información',
  'más informacion',
  'mas información',
  'mas informacion',
  'este',
  'enlace',
  'este enlace',
  'empezar',
  // Portuguese
  'clique aqui',
  'ir',
  'mais informação',
  'mais informações',
  'mais',
  'veja mais',
  // Korean
  '여기',
  '여기를 클릭',
  '클릭',
  '링크',
  '자세히',
  '자세히 보기',
  '계속',
  '이동',
  '전체 보기',
  // Swedish
  'här',
  'klicka här',
  'läs mer',
  'mer',
  'mer info',
  'mer information',
]);
const i18n = require('../../lib/i18n/i18n.js');

const UIStrings = {
  /** Title of a Lighthouse audit that tests if each link on a page contains a sufficient description of what a user will find when they click it. Generic, non-descriptive text like "click here" doesn't give an indication of what the link leads to. This descriptive title is shown when all links on the page have sufficient textual descriptions. */
  title: 'Links have descriptive text',
  /** Title of a Lighthouse audit that tests if each link on a page contains a sufficient description of what a user will find when they click it. Generic, non-descriptive text like "click here" doesn't give an indication of what the link leads to. This descriptive title is shown when one or more links on the page contain generic, non-descriptive text. */
  failureTitle: 'Links do not have descriptive text',
  /** Description of a Lighthouse audit that tells the user *why* they need to have descriptive text on the links in their page. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
  description: 'Descriptive link text helps search engines understand your content. ' +
  '[Learn more](https://web.dev/link-text/).',
  /** [ICU Syntax] Label for the audit identifying the number of links found. "link" here refers to the links in a web page to other web pages. */
  displayValue: `{itemCount, plural,
    =1 {1 link found}
    other {# links found}
    }`,
};

const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);

class LinkText extends Audit {
  /**
   * @return {LH.Audit.Meta}
   */
  static get meta() {
    return {
      id: 'link-text',
      title: str_(UIStrings.title),
      failureTitle: str_(UIStrings.failureTitle),
      description: str_(UIStrings.description),
      requiredArtifacts: ['URL', 'AnchorElements'],
    };
  }

  /**
   * @param {LH.Artifacts} artifacts
   * @return {LH.Audit.Product}
   */
  static audit(artifacts) {
    const failingLinks = artifacts.AnchorElements
      .filter(link => link.href && !link.rel.includes('nofollow'))
      .filter(link => {
        const href = link.href.toLowerCase();
        if (
          href.startsWith('javascript:') ||
          href.startsWith('mailto:') ||
          URL.equalWithExcludedFragments(link.href, artifacts.URL.finalUrl)
        ) {
          return false;
        }

        return BLOCKLIST.has(link.text.trim().toLowerCase());
      })
      .map(link => {
        return {
          href: link.href,
          text: link.text.trim(),
        };
      });

    /** @type {LH.Audit.Details.Table['headings']} */
    const headings = [
      {key: 'href', itemType: 'url', text: 'Link destination'},
      {key: 'text', itemType: 'text', text: 'Link Text'},
    ];

    const details = Audit.makeTableDetails(headings, failingLinks, {});
    let displayValue;

    if (failingLinks.length) {
      displayValue = str_(UIStrings.displayValue, {itemCount: failingLinks.length});
    }

    return {
      score: Number(failingLinks.length === 0),
      details,
      displayValue,
    };
  }
}

module.exports = LinkText;
module.exports.UIStrings = UIStrings;
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
/* global __fbBatchedBridge, self, importScripts, postMessage, onmessage: true */
/* eslint no-unused-vars: 0 */
'use strict';

var messageHandlers = {
  'executeApplicationScript': function(message, sendReply) {
    for (var key in message.inject) {
      self[key] = JSON.parse(message.inject[key]);
    }
    importScripts(message.url);
    sendReply();
  }
};

onmessage = function(message) {
  var object = message.data;

  var sendReply = function(result) {
    postMessage({replyID: object.id, result: result});
  };

  var handler = messageHandlers[object.method];
  if (handler) {
    // Special cased handlers
    handler(object, sendReply);
  } else {
    // Other methods get called on the bridge
    var returnValue = [[], [], [], [], []];
    try {
      if (typeof __fbBatchedBridge === 'object') {
        returnValue = __fbBatchedBridge[object.method].apply(null, object.arguments);
      }
    } finally {
      sendReply(JSON.stringify(returnValue));
    }
  }
};
<commit_msg>Don't send bogus values to the app<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
/* global __fbBatchedBridge, self, importScripts, postMessage, onmessage: true */
/* eslint no-unused-vars: 0 */
'use strict';

var messageHandlers = {
  'executeApplicationScript': function(message, sendReply) {
    for (var key in message.inject) {
      self[key] = JSON.parse(message.inject[key]);
    }
    importScripts(message.url);
    sendReply();
  }
};

onmessage = function(message) {
  var object = message.data;

  var sendReply = function(result) {
    postMessage({replyID: object.id, result: result});
  };

  var handler = messageHandlers[object.method];
  if (handler) {
    // Special cased handlers
    handler(object, sendReply);
  } else {
    // Other methods get called on the bridge
    var returnValue = [[], [], [], 0];
    try {
      if (typeof __fbBatchedBridge === 'object') {
        returnValue = __fbBatchedBridge[object.method].apply(null, object.arguments);
      }
    } finally {
      sendReply(JSON.stringify(returnValue));
    }
  }
};
<|endoftext|>