<commit_before>"use strict";

var NodeView = require('../node/node_view');
var $$ = require("substance-application").$$;
var Fragmenter = require("substance-util").Fragmenter;

// Substance.Text.View
// -----------------
//
// Manipulation interface shared by all textish types (paragraphs, headings)
// This behavior can overriden by the concrete node types

var LAST_CHAR_HACK = false;

function _getAnnotationBehavior(doc) {
  if (doc.constructor && doc.constructor.annotationBehavior) {
    return doc.constructor.annotationBehavior;
  } else {
    throw new Error("Missing AnnotationBehavior.");
  }
}

var TextView = function(node, renderer, options) {
  NodeView.call(this, node);
  options = options || {};

  this.property = options.property || "content";

  this.$el.addClass('content-node text');

  if (node.type === "text") {
    this.$el.addClass("text-node");
  }

  // If TextView is used to display a custom property,
  // we don't have an id. Only full-fledged text nodes
  // have id's.
  if (options.property) {
    // Note: currently NodeView sets the id. In this mode the must not be set
    // as we are displaying a textish property of a node, not a text node.
    // IMO it is ok to have the id set by default, as it is the 99% case.
    this.$el.removeAttr('id');
    this.$el.removeClass('content-node');
    // this.$el.addClass('node-property');
    this.$el.addClass(options.property);
    this.$el.attr('data-path', options.property);
  }

  this._annotations = {};
  this.annotationBehavior = _getAnnotationBehavior(node.document);
};

var _findTextEl;

TextView.Prototype = function() {

  // Rendering
  // =============================
  //

  this.render = function(enhancer) {
    NodeView.prototype.render.call(this, enhancer);

    this.renderContent();
    return this;
  };

  this.renderContent = function() {
    this.content.innerHTML = "";
    this._annotations = this.node.document.getIndex("annotations").get([this.node.id, this.property]);
    this.renderWithAnnotations(this._annotations);
  };

  this.insert = function(pos, str) {
    var wrange = _findTextEl(this.content, pos);
    var textNode = wrange[0];
    var offset = wrange[1];
    var text = textNode.textContent;
    text = text.substring(0, offset) + str + text.substring(offset);
    textNode.textContent = text;
  };

  this.delete = function(pos, length) {
    var wrange = _findTextEl(this.content, pos);
    var textNode = wrange[0];
    var offset = wrange[1];
    var text = textNode.textContent;
    text = text.substring(0, offset) + text.substring(offset+length);
    textNode.textContent = text;
  };

  this.onNodeUpdate = function(op) {
    if (op.path[1] === "content") {
      // console.log("Updating text view: ", op);
      if (op.type === "update") {
        var update = op.diff;
        if (update.isInsert()) {
          this.insert(update.pos, update.str);
        } else if (update.isDelete()) {
          this.delete(update.pos, update.str.length);
        }
      } else if (op.type === "set") {
        this.renderContent();
      }
    }
  };

  this.onGraphUpdate = function(op) {
    NodeView.prototype.onGraphUpdate.call(this, op);

    var doc = this.node.document;
    var schema = doc.getSchema();

    var node;
    if (op.type !== "delete") {
      node = doc.get(op.path[0]);
    } else {
      node = op.val;
    }
    if (schema.isInstanceOf(node.type, "annotation")) {
      var rerender = false;
      if (node.path[0] === this.node.id) {
        rerender = true;
      } else if (op.type === "set" && op.path[1] === "path" && op.original[0] === this.node.id) {
        rerender = true;
      }

      if (rerender) {
        console.log("Rerendering TextView due to annotation update", op);
        this.renderContent();
      }
    }
  };

  this.createAnnotationElement = function(entry) {
    var el;
    if (entry.type === "link") {
      el = $$('a.annotation.'+entry.type, {
        id: entry.id,
        href: this.node.document.get(entry.id).url // "http://zive.at"
      });
    } else {
      el = $$('span.annotation.'+entry.type, {
        id: entry.id
      });
    }

    return el;
  };

  this.renderWithAnnotations = function(annotations) {
    var that = this;
    var text = this.node[this.property];
    var fragment = document.createDocumentFragment();

    // this splits the text and annotations into smaller pieces
    // which is necessary to generate proper HTML.
    var fragmenter = new Fragmenter(this.annotationBehavior.levels);

    fragmenter.onText = function(context, text) {
      context.appendChild(document.createTextNode(text));
    };

    fragmenter.onEnter = function(entry, parentContext) {
      var el = that.createAnnotationElement(entry);
      parentContext.appendChild(el);
      return el;
    };

    // this calls onText and onEnter in turns...
    fragmenter.start(fragment, text, annotations);

    // EXPERIMENTAL HACK:
    // append a trailing white-space to improve the browser's behaviour with softbreaks at the end
    // of a node.
    if (LAST_CHAR_HACK) {
      fragment.appendChild(document.createTextNode(" "));
    }

    // set the content
    this.content.innerHTML = "";
    this.content.appendChild(fragment);
  };

  // Free the memory
  // --------

  this.dispose = function() {
    this.stopListening();
  };
};

TextView.Prototype.prototype = NodeView.prototype;
TextView.prototype = new TextView.Prototype();

var _unshiftAll = function(arr, other) {
  for (var i = 0; i < other.length; i++) {
    arr.unshift(other[i]);
  };
};

_findTextEl = function(el, pos) {
  var childNodes = [];
  _unshiftAll(childNodes, el.childNodes);

  while(childNodes.length) {
    var next = childNodes.shift();
    if (next.nodeType === Node.TEXT_NODE) {
      var t = next.textContent;
      if (t.length >= pos) {
        return [next, pos];
      } else {
        pos -= t.length;
      }
    } else {
      _unshiftAll(childNodes, next.childNodes);
    }
  }
};

module.exports = TextView;
<commit_msg>Use the TextSurface within TextView for incremental DOM manipulations.<commit_after>"use strict";

var NodeView = require('../node/node_view');
var $$ = require("substance-application").$$;
var Fragmenter = require("substance-util").Fragmenter;
var TextSurface = require("./text_surface");

// Substance.Text.View
// -----------------
//
// Manipulation interface shared by all textish types (paragraphs, headings)
// This behavior can overriden by the concrete node types

var LAST_CHAR_HACK = false;

function _getAnnotationBehavior(doc) {
  if (doc.constructor && doc.constructor.annotationBehavior) {
    return doc.constructor.annotationBehavior;
  } else {
    throw new Error("Missing AnnotationBehavior.");
  }
}

var TextView = function(node, renderer, options) {
  NodeView.call(this, node);
  options = options || {};

  this.property = options.property || "content";

  this.$el.addClass('content-node text');

  if (node.type === "text") {
    this.$el.addClass("text-node");
  }

  // If TextView is used to display a custom property,
  // we don't have an id. Only full-fledged text nodes
  // have id's.
  if (options.property) {
    // Note: currently NodeView sets the id. In this mode the must not be set
    // as we are displaying a textish property of a node, not a text node.
    // IMO it is ok to have the id set by default, as it is the 99% case.
    this.$el.removeAttr('id');
    this.$el.removeClass('content-node');
    // this.$el.addClass('node-property');
    this.$el.addClass(options.property);
    this.$el.attr('data-path', options.property);
  }

  this._annotations = {};
  this.annotationBehavior = _getAnnotationBehavior(node.document);

  // Note: usually the surface is not needed within the view.
  // However, here I wan't to use the getDOMPosition function to an easy implementation of the
  // incremental text insertion/deletion.
  this.__surface = new TextSurface(this.node);
  this.__surface.attachView(this);
};

var _findTextEl;

TextView.Prototype = function() {

  // Rendering
  // =============================
  //

  this.render = function(enhancer) {
    NodeView.prototype.render.call(this, enhancer);

    this.renderContent();
    return this;
  };

  this.renderContent = function() {
    this.content.innerHTML = "";
    this._annotations = this.node.document.getIndex("annotations").get([this.node.id, this.property]);
    this.renderWithAnnotations(this._annotations);
  };

  this.insert = function(pos, str) {
    var range = this.__surface.getDOMPosition(pos);
    var textNode = range.startContainer;
    var offset = range.startOffset;

    var text = textNode.textContent;
    text = text.substring(0, offset) + str + text.substring(offset);
    textNode.textContent = text;
  };

  this.delete = function(pos, length) {
    var range = this.__surface.getDOMPosition(pos);
    var textNode = range.startContainer;
    var offset = range.startOffset;
    var text = textNode.textContent;
    text = text.substring(0, offset) + text.substring(offset+length);
    textNode.textContent = text;
  };

  this.onNodeUpdate = function(op) {
    if (op.path[1] === "content") {
      // console.log("Updating text view: ", op);
      if (op.type === "update") {
        var update = op.diff;
        if (update.isInsert()) {
          this.insert(update.pos, update.str);
        } else if (update.isDelete()) {
          this.delete(update.pos, update.str.length);
        }
      } else if (op.type === "set") {
        this.renderContent();
      }
    }
  };

  this.onGraphUpdate = function(op) {
    NodeView.prototype.onGraphUpdate.call(this, op);

    var doc = this.node.document;
    var schema = doc.getSchema();

    var node;
    if (op.type !== "delete") {
      node = doc.get(op.path[0]);
    } else {
      node = op.val;
    }
    if (schema.isInstanceOf(node.type, "annotation")) {
      var rerender = false;
      if (node.path[0] === this.node.id) {
        rerender = true;
      } else if (op.type === "set" && op.path[1] === "path" && op.original[0] === this.node.id) {
        rerender = true;
      }

      if (rerender) {
        console.log("Rerendering TextView due to annotation update", op);
        this.renderContent();
      }
    }
  };

  this.createAnnotationElement = function(entry) {
    var el;
    if (entry.type === "link") {
      el = $$('a.annotation.'+entry.type, {
        id: entry.id,
        href: this.node.document.get(entry.id).url // "http://zive.at"
      });
    } else {
      el = $$('span.annotation.'+entry.type, {
        id: entry.id
      });
    }

    return el;
  };

  this.renderWithAnnotations = function(annotations) {
    var that = this;
    var text = this.node[this.property];
    var fragment = document.createDocumentFragment();

    // this splits the text and annotations into smaller pieces
    // which is necessary to generate proper HTML.
    var fragmenter = new Fragmenter(this.annotationBehavior.levels);

    fragmenter.onText = function(context, text) {
      context.appendChild(document.createTextNode(text));
    };

    fragmenter.onEnter = function(entry, parentContext) {
      var el = that.createAnnotationElement(entry);
      parentContext.appendChild(el);
      return el;
    };

    // this calls onText and onEnter in turns...
    fragmenter.start(fragment, text, annotations);

    // EXPERIMENTAL HACK:
    // append a trailing white-space to improve the browser's behaviour with softbreaks at the end
    // of a node.
    if (LAST_CHAR_HACK) {
      fragment.appendChild(document.createTextNode(" "));
    }

    // set the content
    this.content.innerHTML = "";
    this.content.appendChild(fragment);
  };

  // Free the memory
  // --------

  this.dispose = function() {
    this.stopListening();
  };
};

TextView.Prototype.prototype = NodeView.prototype;
TextView.prototype = new TextView.Prototype();

var _unshiftAll = function(arr, other) {
  for (var i = 0; i < other.length; i++) {
    arr.unshift(other[i]);
  };
};

_findTextEl = function(el, pos) {
  var childNodes = [];
  _unshiftAll(childNodes, el.childNodes);

  while(childNodes.length) {
    var next = childNodes.shift();
    if (next.nodeType === Node.TEXT_NODE) {
      var t = next.textContent;
      if (t.length >= pos) {
        return [next, pos];
      } else {
        pos -= t.length;
      }
    } else {
      _unshiftAll(childNodes, next.childNodes);
    }
  }
};

module.exports = TextView;
<|endoftext|>


<commit_before>import { bindable, bindingMode, inject, customAttribute, LogManager } from 'aurelia-framework';

@inject(Element, LogManager)
@customAttribute('md-select')
export class MdSelect {
  @bindable({
    defaultBindingMode: bindingMode.twoWay
  }) selected;
  constructor(element, logManager) {
    this.element = element;
    this.changeHandler = this.handleChangeFromNativeSelect.bind(this);
    this.log = LogManager.getLogger('md-select');
  }
  attached() {
    $(this.element).material_select();
    $(this.element).on('change', this.changeHandler);
  }

  detached() {
    $(this.element).off('change', this.changeHandler);
    $(this.element).material_select('destroy');
  }

  /*
   * This handler is called when the native <select> changes.
   */
  handleChangeFromNativeSelect() {
    this.selected = this.element.value;
    this.log.debug('handleChangeFromNativeSelect', $(this.element).val());
  }

  selectedChanged() {
    this.element.value = this.selected;
    this.log.debug('selectedChanged', this.selected);
    $(this.element).material_select();
  }
}
<commit_msg>fix(md-select): handle multiple selection, fixes #54<commit_after>import { bindable, bindingMode, inject, customAttribute, LogManager } from 'aurelia-framework';

@inject(Element, LogManager)
@customAttribute('md-select')
export class MdSelect {
  @bindable({
    defaultBindingMode: bindingMode.twoWay
  }) selected;
  constructor(element, logManager) {
    this.element = element;
    this.changeHandler = this.handleChangeFromNativeSelect.bind(this);
    this.log = LogManager.getLogger('md-select');
  }
  attached() {
    $(this.element).material_select();
    $(this.element).on('change', this.changeHandler);
  }

  detached() {
    $(this.element).off('change', this.changeHandler);
    $(this.element).material_select('destroy');
  }

  /*
   * This handler is called when the native <select> changes.
   */
  handleChangeFromNativeSelect() {
    // this.selected = this.element.value;
    this.selected = $(this.element).val();
  }

  selectedChanged() {
    // this.element.value = this.selected;
    $(this.element).val(this.selected);
    $(this.element).material_select();
  }
}
<|endoftext|>


<commit_before>export const noop = (payload) => payload;

const transformPayload = (message, transformer = noop) => ({
  ...message,
  // If the message has a payload, transform it. Otherwise,
  // just return a copy of the message.
  // We return a copy rather than the original message so that we're not
  // mutating the original action object.
  ...(message.payload ? {payload: transformer(message.payload)} : {})
});

const deserializeListener = (listener, deserializer = noop, shouldDeserialize) => {
  // If a shouldDeserialize function is passed, return a function that uses it
  // to check if any given message payload should be deserialized
  if (shouldDeserialize) {
    return (message, ...args) => {
      if (shouldDeserialize(message, ...args)) {
        return listener(transformPayload(message, deserializer), ...args);
      } else {
        return listener(message, ...args);
      }
    };
  }
  // Otherwise, return a function that tries to deserialize on every message
  return (message, ...args) => listener(transformPayload(message, deserializer), ...args);
};

/**
 * A function returned from withDeserializer that, when called, wraps addListenerFn with the
 * deserializer passed to withDeserializer.
 * @name AddListenerDeserializer
 * @function
 * @param {Function} addListenerFn The add listener function to wrap.
 * @returns {DeserializedAddListener}
 */

/**
 * A wrapped add listener function that registers the given listener.
 * @name DeserializedAddListener
 * @function
 * @param {Function} listener The listener function to register. It should expect the (optionally)
 * deserialized message as its first argument.
 * @param {Function} [shouldDeserialize] A function that takes the arguments passed to the listener
 * and returns whether the message payload should be deserialized. Not all messages (notably, messages
 * this listener doesn't care about) should be attempted to be deserialized.
 */

/**
 * Given a deserializer, returns an AddListenerDeserializer function that that takes an add listener
 * function and returns a DeserializedAddListener that automatically deserializes message payloads.
 * Each message listener is expected to take the message as its first argument.
 * @param {Function} deserializer A function that deserializes a message payload.
 * @returns {AddListenerDeserializer}
 * Example Usage:
 *   const withJsonDeserializer = withDeserializer(payload => JSON.parse(payload));
 *   const deserializedChromeListener = withJsonDeserializer(chrome.runtime.onMessage.addListener);
 *   const shouldDeserialize = (message) => message.type === 'DESERIALIZE_ME';
 *   deserializedChromeListener(message => console.log("Payload:", message.payload), shouldDeserialize);
 *   chrome.runtime.sendMessage("{'type:'DESERIALIZE_ME','payload':{'prop':4}}");
 *   //Payload: { prop: 4 };
 *   chrome.runtime.sendMessage("{'payload':{'prop':4}}");
 *   //Payload: "{'prop':4}";
 */
export const withDeserializer = (deserializer = noop) =>
  (addListenerFn) =>
    (listener, shouldDeserialize) =>
      addListenerFn(deserializeListener(listener, deserializer, shouldDeserialize));

/**
 * Given a serializer, returns a function that takes a message sending
 * function as its sole argument and returns a wrapped message sender that
 * automaticaly serializes message payloads. The message sender
 * is expected to take the message as its first argument, unless messageArgIndex
 * is nonzero, in which case it is expected in the position specified by messageArgIndex.
 * @param {Function} serializer A function that serializes a message payload
 * Example Usage:
 *   const withJsonSerializer = withSerializer(payload => JSON.stringify(payload))
 *   const serializedChromeSender = withJsonSerializer(chrome.runtime.sendMessage)
 *   chrome.runtime.addListener(message => console.log("Payload:", message.payload))
 *   serializedChromeSender({ payload: { prop: 4 }})
 *   //Payload: "{'prop':4}"
 */
export const withSerializer = (serializer = noop) =>
  (sendMessageFn, messageArgIndex = 0) => {
    return (...args) => {
      if (args.length <= messageArgIndex) {
        throw new Error(`Message in request could not be serialized. ` +
                        `Expected message in position ${messageArgIndex} but only received ${args.length} args.`);
      }
      args[messageArgIndex] = transformPayload(args[messageArgIndex], serializer);
      return sendMessageFn(...args);
    };
  };
<commit_msg>Fix lint error<commit_after>export const noop = (payload) => payload;

const transformPayload = (message, transformer = noop) => ({
  ...message,
  // If the message has a payload, transform it. Otherwise,
  // just return a copy of the message.
  // We return a copy rather than the original message so that we're not
  // mutating the original action object.
  ...(message.payload ? {payload: transformer(message.payload)} : {})
});

const deserializeListener = (listener, deserializer = noop, shouldDeserialize) => {
  // If a shouldDeserialize function is passed, return a function that uses it
  // to check if any given message payload should be deserialized
  if (shouldDeserialize) {
    return (message, ...args) => {
      if (shouldDeserialize(message, ...args)) {
        return listener(transformPayload(message, deserializer), ...args);
      }
      return listener(message, ...args);
    };
  }
  // Otherwise, return a function that tries to deserialize on every message
  return (message, ...args) => listener(transformPayload(message, deserializer), ...args);
};

/**
 * A function returned from withDeserializer that, when called, wraps addListenerFn with the
 * deserializer passed to withDeserializer.
 * @name AddListenerDeserializer
 * @function
 * @param {Function} addListenerFn The add listener function to wrap.
 * @returns {DeserializedAddListener}
 */

/**
 * A wrapped add listener function that registers the given listener.
 * @name DeserializedAddListener
 * @function
 * @param {Function} listener The listener function to register. It should expect the (optionally)
 * deserialized message as its first argument.
 * @param {Function} [shouldDeserialize] A function that takes the arguments passed to the listener
 * and returns whether the message payload should be deserialized. Not all messages (notably, messages
 * this listener doesn't care about) should be attempted to be deserialized.
 */

/**
 * Given a deserializer, returns an AddListenerDeserializer function that that takes an add listener
 * function and returns a DeserializedAddListener that automatically deserializes message payloads.
 * Each message listener is expected to take the message as its first argument.
 * @param {Function} deserializer A function that deserializes a message payload.
 * @returns {AddListenerDeserializer}
 * Example Usage:
 *   const withJsonDeserializer = withDeserializer(payload => JSON.parse(payload));
 *   const deserializedChromeListener = withJsonDeserializer(chrome.runtime.onMessage.addListener);
 *   const shouldDeserialize = (message) => message.type === 'DESERIALIZE_ME';
 *   deserializedChromeListener(message => console.log("Payload:", message.payload), shouldDeserialize);
 *   chrome.runtime.sendMessage("{'type:'DESERIALIZE_ME','payload':{'prop':4}}");
 *   //Payload: { prop: 4 };
 *   chrome.runtime.sendMessage("{'payload':{'prop':4}}");
 *   //Payload: "{'prop':4}";
 */
export const withDeserializer = (deserializer = noop) =>
  (addListenerFn) =>
    (listener, shouldDeserialize) =>
      addListenerFn(deserializeListener(listener, deserializer, shouldDeserialize));

/**
 * Given a serializer, returns a function that takes a message sending
 * function as its sole argument and returns a wrapped message sender that
 * automaticaly serializes message payloads. The message sender
 * is expected to take the message as its first argument, unless messageArgIndex
 * is nonzero, in which case it is expected in the position specified by messageArgIndex.
 * @param {Function} serializer A function that serializes a message payload
 * Example Usage:
 *   const withJsonSerializer = withSerializer(payload => JSON.stringify(payload))
 *   const serializedChromeSender = withJsonSerializer(chrome.runtime.sendMessage)
 *   chrome.runtime.addListener(message => console.log("Payload:", message.payload))
 *   serializedChromeSender({ payload: { prop: 4 }})
 *   //Payload: "{'prop':4}"
 */
export const withSerializer = (serializer = noop) =>
  (sendMessageFn, messageArgIndex = 0) => {
    return (...args) => {
      if (args.length <= messageArgIndex) {
        throw new Error(`Message in request could not be serialized. ` +
                        `Expected message in position ${messageArgIndex} but only received ${args.length} args.`);
      }
      args[messageArgIndex] = transformPayload(args[messageArgIndex], serializer);
      return sendMessageFn(...args);
    };
  };
<|endoftext|>


<commit_before>let readFileSync = require('fs').readFileSync
let { readFromNodeBuffer } = require('../node-build')

for (let index = 2; index < process.argv.length; index++) {
  let filename = process.argv[index]
  console.log('file', filename)
  processFile(filename)
  console.log('--------\n\n')
}

function processFile(filename) {
  let buffer = readFileSync(filename)
  let cfb = readFromNodeBuffer(buffer)
  let header = cfb.header
  let sectors = cfb.sectors
  console.log('signature', header.headerSignature)
  console.log('version', header.version)
  console.log('bytesOrder', header.bytesOrder)
  console.log('start of directoryChain', header.startOfDirectoryChain)
  console.log('start of minifat', header.startOfMiniFat)
  console.log('start of difat', header.startOfDifat)
  console.log('sector size', header.sectorSize)
  console.log('number of fat sectors', header.numberOfFatSectors)
  console.log('number of sectors', sectors.length)
  console.log('number of chains', cfb.fatChain.chains.size)
  cfb.fatChain.chains.forEach((buffer, startIndex) => {
    console.log(`startIndex ${startIndex}, byteLength ${buffer.byteLength}`)
  })

  function leftpad(str, len) {
    str = String(str);
    let i = -1;
    let ch = ' ';
    len = len - str.length;
    while (++i < len) {
      str = ch + str;
    }
    return str;
  }

  function rightpad(str, len) {
    str = String(str);
    let i = -1;
    let ch = ' ';
    len = len - str.length
    while (++i < len) {
      str = str + ch;
    }
    return str;
  }

  function ignoreSpecialValues(value) {
    return value <= 0xFFFFFFFA ? value.toString() : '-'
  }

  console.log('Directory entries')
  console.log(['index', rightpad('name', 32), 'sector', 'size',
    rightpad('leftId', 8), rightpad('rightId', 8), rightpad('childId', 8)
  ].join('\t'))
  cfb.directoryEntries.forEach((entry, index) => {
    console.log([index,
      rightpad(entry.name, 32),
      ignoreSpecialValues(entry.startingSectorLocation),
      entry.streamSize,
      leftpad(ignoreSpecialValues(entry.leftSiblingId), 8),
      leftpad(ignoreSpecialValues(entry.rightSiblingId), 8),
      leftpad(ignoreSpecialValues(entry.childId), 8)
    ].join('\t'))
  })

  console.log('directories', cfb.root)
}
<commit_msg>fix build<commit_after>let readFileSync = require('fs').readFileSync
let { readFromNodeBuffer } = require('../node-build')

for (let index = 2; index < process.argv.length; index++) {
  let filename = process.argv[index]
  console.log('file', filename)
  processFile(filename)
  console.log('--------\n\n')
}

function processFile(filename) {
  let buffer = readFileSync(filename)
  let cfb = readFromNodeBuffer(buffer)
  let header = cfb.header
  let sectors = cfb.sectors
  console.log('signature', header.signature())
  console.log('version', header.version())
  console.log('bytesOrder', header.bytesOrder())
  console.log('start of directoryChain', header.getStartOfDirectoryChain)
  console.log('start of minifat', header.getStartOfMiniFat)
  console.log('start of difat', header.getStartOfDifat)
  console.log('sector size', header.sectorSize())
  console.log('number of fat sectors', header.getNumberOfFatSectors())
  console.log('number of sectors', sectors.length)
  console.log('number of chains', cfb.fatChain.size)
  cfb.fatChain.forEach((buffer, startIndex) => {
    console.log(`startIndex ${startIndex}, byteLength ${buffer.byteLength}`)
  })

  function leftpad(str, len) {
    str = String(str);
    let i = -1;
    let ch = ' ';
    len = len - str.length;
    while (++i < len) {
      str = ch + str;
    }
    return str;
  }

  function rightpad(str, len) {
    str = String(str);
    let i = -1;
    let ch = ' ';
    len = len - str.length
    while (++i < len) {
      str = str + ch;
    }
    return str;
  }

  function ignoreSpecialValues(value) {
    return value <= 0xFFFFFFFA ? value.toString() : '-'
  }

  console.log('Directory entries')
  console.log(['index', rightpad('name', 32), 'sector', 'size',
    rightpad('leftId', 8), rightpad('rightId', 8), rightpad('childId', 8)
  ].join('\t'))
  cfb.directoryEntries.forEach((entry, index) => {
    console.log([index,
      rightpad(entry.name, 32),
      ignoreSpecialValues(entry.startingSectorLocation),
      entry.streamSize,
      leftpad(ignoreSpecialValues(entry.leftSiblingId), 8),
      leftpad(ignoreSpecialValues(entry.rightSiblingId), 8),
      leftpad(ignoreSpecialValues(entry.childId), 8)
    ].join('\t'))
  })

  console.log('directories', cfb.root)
}
<|endoftext|>


<commit_before>'use strict';

const { httpStartServer } = require('./http');
const { attachRequire, console } = require('../utilities');

/**
 * Start server for each protocol, for the moment only HTTP
 *
 * @param {object} options
 * @param {object} options.idl - IDL definitions
 */
const startServer = async function (options) {
  try {
    attachRequire();
    const server = await Promise.all([
      httpStartServer(options),
    ]);
    return server;
  } catch (exception) {
    console.error(exception);
    throw exception;
  }
};

module.exports = {
  startServer,
};<commit_msg>Do not log startup exceptions, otherwise they appear twice<commit_after>'use strict';

const { httpStartServer } = require('./http');
const { attachRequire } = require('../utilities');

/**
 * Start server for each protocol, for the moment only HTTP
 *
 * @param {object} options
 * @param {object} options.idl - IDL definitions
 */
const startServer = async function (options) {
  attachRequire();
  const server = await Promise.all([
    httpStartServer(options),
  ]);
  return server;
};

module.exports = {
  startServer,
};
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var cluster = require('../');

var proc = cluster()
  .set('workers', 4)
  .use(cluster.debug())
  .start();

if (proc.isWorker) {
  var id = process.env.CLUSTER_WORKER;
  console.log('  worker #%d started', id);
  setInterval(function(){
    console.log('  processing job from worker #%d', id);
  }, 1000);
}<commit_msg>close example<commit_after>
/**
 * Module dependencies.
 */

var cluster = require('../');

var proc = cluster()
  .set('workers', 4)
  .use(cluster.debug())
  .start();

if (proc.isWorker) {
  var id = process.env.CLUSTER_WORKER;
  console.log('  worker #%d started', id);
  setInterval(function(){
    console.log('  processing job from worker #%d', id);
  }, 1000);
} else {
  setTimeout(function(){
    proc.close();
  }, 10000);
}<|endoftext|>


<commit_before>import socketio from 'socket.io'
import socketioJwt from 'socketio-jwt'

module.exports = bp => {

  async function install(server) {
    const io = socketio(server)

    if (bp.botfile.login.enabled) {
      io.use(socketioJwt.authorize({
        secret: await bp.security.getSecret(),
        handshake: true
      }))
    }

    io.on('connection', function(socket) {
      bp.stats.track('socket', 'connected')

      socket.on('event', function(event) {
        bp.events.emit(event.name, event.data, 'client', { socketId: socket.id })
      })
    })

    bp.events.onAny(function(event, data, from) {
      if (from === 'client') {
        return // we sent this ourselves
      }

      if (data && (data.__socketId || data.__room)) {
        // Send only to this socketId or room
        console.log(io.rooms, io, data.__socketId)
        return io.to(data.__socketId || data.__room).emit('event', {
          name: event,
          data: data
        })
      }

      // broadcast event to the front-end clients
      io.emit('event', {
        name: event,
        data: data
      })
    })
  }

  return { install }
}
<commit_msg>Removed debug log<commit_after>import socketio from 'socket.io'
import socketioJwt from 'socketio-jwt'

module.exports = bp => {

  async function install(server) {
    const io = socketio(server)

    if (bp.botfile.login.enabled) {
      io.use(socketioJwt.authorize({
        secret: await bp.security.getSecret(),
        handshake: true
      }))
    }

    io.on('connection', function(socket) {
      bp.stats.track('socket', 'connected')

      socket.on('event', function(event) {
        bp.events.emit(event.name, event.data, 'client', { socketId: socket.id })
      })
    })

    bp.events.onAny(function(event, data, from) {
      if (from === 'client') {
        return // we sent this ourselves
      }

      if (data && (data.__socketId || data.__room)) {
        // Send only to this socketId or room
        return io.to(data.__socketId || data.__room).emit('event', {
          name: event,
          data: data
        })
      }

      // broadcast event to the front-end clients
      io.emit('event', {
        name: event,
        data: data
      })
    })
  }

  return { install }
}
<|endoftext|>


<commit_before>/* global process */
var WebDriver = require("selenium-webdriver");
var Https = require("https");

var driver;
if (process.env.SAUCE_USERNAME !== undefined) {
    var browserArray = process.env.BROWSER.split(':');
    driver = new WebDriver.Builder().usingServer(
        'http://'+ process.env.SAUCE_USERNAME+':'+process.env.SAUCE_ACCESS_KEY+'@ondemand.saucelabs.com:80/wd/hub'
    ).withCapabilities({
        "tunnel-identifier": process.env.TRAVIS_JOB_NUMBER,
        "build": process.env.TRAVIS_JOB_NUMBER,
        "username": process.env.SAUCE_USERNAME,
        "accessKey": process.env.SAUCE_ACCESS_KEY,
    }).forBrowser(browserArray[0], browserArray[1], browserArray[2]).build();
} else {
    driver = new WebDriver.Builder().withCapabilities({ browserName: "chrome" }).build();
}

var logSauceLink = function (cb) {
    Https.request("https://saucelabs.com/rest/v1.1/" + process.env.SAUCE_USERNAME +
            "/jobs?auto_only=true&full=true&limit=50&subaccounts=true", function (resp) {
        var str = '';
        resp.on('data', function (chunk) { str += chunk; });
        resp.on('end', function () {
            JSON.parse(str).jobs.forEach(function (j) {
                var banner = new Array(80).join('=');
                if (j.build === process.env.TRAVIS_JOB_NUMBER) {
                    console.log("\n\n\n" + banner);
                    console.log("SauceLabs Link: " + j.video_url.replace(/\/video\.flv$/), '');
                    console.log(banner + "\n\n\n");
                }
            });
            cb();
        });
    });
};

driver.get('http://localhost:3000/assert/');
var report = driver.wait(WebDriver.until.elementLocated(WebDriver.By.className("report")), 5000);
report.getAttribute("class").then(function (cls) {
    driver.quit();
    logSauceLink(function () {
        if (!cls) {
            throw new Error("cls is null");
        } else if (cls.indexOf("failure") !== -1) {
            throw new Error("cls contains the word failure");
        } else if (cls.indexOf("success") === -1) {
            throw new Error("cls does not contain the word success");
        }
    });
});
<commit_msg>debugging on the CI<commit_after>/* global process */
var WebDriver = require("selenium-webdriver");
var Https = require("https");

var driver;
if (process.env.SAUCE_USERNAME !== undefined) {
    var browserArray = process.env.BROWSER.split(':');
    driver = new WebDriver.Builder().usingServer(
        'http://'+ process.env.SAUCE_USERNAME+':'+process.env.SAUCE_ACCESS_KEY+'@ondemand.saucelabs.com:80/wd/hub'
    ).withCapabilities({
        "tunnel-identifier": process.env.TRAVIS_JOB_NUMBER,
        "build": process.env.TRAVIS_JOB_NUMBER,
        "username": process.env.SAUCE_USERNAME,
        "accessKey": process.env.SAUCE_ACCESS_KEY,
    }).forBrowser(browserArray[0], browserArray[1], browserArray[2]).build();
} else {
    driver = new WebDriver.Builder().withCapabilities({ browserName: "chrome" }).build();
}

var logSauceLink = function (cb) {
    Https.request("https://saucelabs.com/rest/v1.1/" + process.env.SAUCE_USERNAME +
            "/jobs?auto_only=true&full=true&limit=50&subaccounts=true", function (resp) {
        var str = '';
        resp.on('data', function (chunk) { str += chunk; });
        resp.on('end', function () {
            JSON.parse(str).jobs.forEach(function (j) {
                var banner = new Array(80).join('=');
                if (j.build === process.env.TRAVIS_JOB_NUMBER) {
                    console.log("\n\n\n" + banner);
                    console.log("SauceLabs Link: " + j.video_url.replace(/\/video\.flv$/), '');
                    console.log(banner + "\n\n\n");
                }
            });
            console.log(str);
            cb();
        });
    });
};

driver.get('http://localhost:3000/assert/');
var report = driver.wait(WebDriver.until.elementLocated(WebDriver.By.className("report")), 5000);
report.getAttribute("class").then(function (cls) {
    driver.quit();
    logSauceLink(function () {
        if (!cls) {
            throw new Error("cls is null");
        } else if (cls.indexOf("failure") !== -1) {
            throw new Error("cls contains the word failure");
        } else if (cls.indexOf("success") === -1) {
            throw new Error("cls does not contain the word success");
        }
    });
});
<|endoftext|>


<commit_before>/**
 * prompt-promise <https://github.com/tunnckoCore/prompt-promise>
 *
 * Copyright (c) 2014 Charlike Mike Reagent, contributors.
 * Released under the MIT license.
 */

'use strict';

var prompt = require('../index');
var result = [];

// node < 0.11
prompt('username: ')
.then(function usernameResponse(val) {
  result.push(val);
  return prompt.password('password: ');
})
.then(function passwordResponse(val) {
  result.push(val);
  return prompt.multiline('description: ');
})
.then(function multilineResponse(val) {
  result.push(val);
  return prompt.confirm('Is this ok? (yes) ');
})
.then(function confirmResponse(val) {
  console.log('response:', result);
  console.log('Done! :)');
});
<commit_msg>update examples<commit_after>/**
 * prompt-promise <https://github.com/tunnckoCore/prompt-promise>
 *
 * Copyright (c) 2014 Charlike Mike Reagent, contributors.
 * Released under the MIT license.
 */

'use strict';

var prompt = require('../index');
var result = [];

// node < 0.11
prompt('username: ')
.then(function usernameResponse(val) {
  result.push(val);
  return prompt.password('password: ');
})
.then(function passwordResponse(val) {
  result.push(val);
  return prompt.multiline('description: ');
})
.then(function multilineResponse(val) {
  result.push(val);
  return prompt.confirm('Is this ok? (yes) ');
})
.then(function confirmResponse(val) {
  result.push(val);
  console.log('response:', result);
  console.log('Done! :)');
});
<|endoftext|>


<commit_before>var g_patterns = []
function load() {
	var str = localStorage['url_patterns']
	if (!str) {
		str = [
			"https://issues[.]apache[.]org/jira/secure/attachment/[^/]*/.*[.]patch",
			"https://issues[.]apache[.]org/jira/secure/attachment/[^/]*/.*[.]txt",
			"https://github.com/[^/]*/[^/]*/commit/[^/]*[.]patch",
			"https://github.com/[^/]*/[^/]*/commit/[^/]*[.]diff",
			"https://gitlab.com/[^/]*/[^/]*/commit/[^/]*[.]patch",
			"https://gitlab.com/[^/]*/[^/]*/commit/[^/]*[.]diff"
		].join("\n")
	}
	g_patterns = str.split("\n")
}
load()

function save() {
	localStorage['url_patterns'] = g_patterns.join("\n")
}

var shouldInject = function(url) {
	for (var i = 0; i < g_patterns.length; i++) {
		if (url.match(g_patterns[i])) {
			return true
		}
	}
	return false
}

chrome.runtime.onMessage.addListener(
	function(msg, sender, sendResponse) {
		if (msg.set_options) {
			g_patterns = msg.options.patterns
			save()
			return
		}

		if (msg.get_options) {
			sendResponse({
				patterns: g_patterns
			})
			return
		}
	}
)

var doRender = function(tabId) {
	chrome.tabs.executeScript(tabId, {
		file: "cs.js"
	})
	chrome.tabs.insertCSS(tabId, {
		file: "cs.css"
	})
}

chrome.tabs.onUpdated.addListener(
	function(tabId, changeInfo, tab) {
		if (changeInfo.status == "complete") {
			if (!shouldInject(tab.url)) {
				return
			}

			doRender(tabId)
		}
	}
)

chrome.browserAction.onClicked.addListener(function(tab) {
	chrome.tabs.executeScript(tab.id, {
		code: "typeof(g_gpv_rendered) == 'undefined'"
	}, function(res) {
		if (res[0]) {
			doRender(tab.id)
		} else {
			chrome.tabs.executeScript(tab.id, {
				code: "document.body.classList.toggle('gvcrendered')"
			})
		}
	})
})
<commit_msg>Two additional github urls<commit_after>var g_patterns = []
function load() {
	var str = localStorage['url_patterns']
	if (!str) {
		str = [
			"https://issues[.]apache[.]org/jira/secure/attachment/[^/]*/.*[.]patch",
			"https://issues[.]apache[.]org/jira/secure/attachment/[^/]*/.*[.]txt",
			"https://github.com/[^/]*/[^/]*/commit/[^/]*[.]patch",
			"https://github.com/[^/]*/[^/]*/commit/[^/]*[.]diff",
			"https://gitlab.com/[^/]*/[^/]*/commit/[^/]*[.]patch",
			"https://gitlab.com/[^/]*/[^/]*/commit/[^/]*[.]diff",
			"https://patch-diff.githubusercontent.com/raw/.*[.]patch",
			"https://patch-diff.githubusercontent.com/raw/.*[.]diff"
		].join("\n")
	}
	g_patterns = str.split("\n")
}
load()

function save() {
	localStorage['url_patterns'] = g_patterns.join("\n")
}

var shouldInject = function(url) {
	for (var i = 0; i < g_patterns.length; i++) {
		if (url.match(g_patterns[i])) {
			return true
		}
	}
	return false
}

chrome.runtime.onMessage.addListener(
	function(msg, sender, sendResponse) {
		if (msg.set_options) {
			g_patterns = msg.options.patterns
			save()
			return
		}

		if (msg.get_options) {
			sendResponse({
				patterns: g_patterns
			})
			return
		}
	}
)

var doRender = function(tabId) {
	chrome.tabs.executeScript(tabId, {
		file: "cs.js"
	})
	chrome.tabs.insertCSS(tabId, {
		file: "cs.css"
	})
}

chrome.tabs.onUpdated.addListener(
	function(tabId, changeInfo, tab) {
		if (changeInfo.status == "complete") {
			if (!shouldInject(tab.url)) {
				return
			}

			doRender(tabId)
		}
	}
)

chrome.browserAction.onClicked.addListener(function(tab) {
	chrome.tabs.executeScript(tab.id, {
		code: "typeof(g_gpv_rendered) == 'undefined'"
	}, function(res) {
		if (res[0]) {
			doRender(tab.id)
		} else {
			chrome.tabs.executeScript(tab.id, {
				code: "document.body.classList.toggle('gvcrendered')"
			})
		}
	})
})
<|endoftext|>


<commit_before>const Nightmare = require('nightmare');
const RecipesPage = require('../pages/recipes');
const CreateRecipePage = require('../pages/createRecipe');
const EditRecipePage = require('../pages/editRecipe');
const apiCall = require('../utils/apiCall');
const pageConfig = require('../config');

describe('Create Recipe Page', () => {
  // Set case running timeout
  const timeout = 15000;

  // Check BDCS API and Web service first
  beforeAll((done) => {
    apiCall.serviceCheck(done);
  });

  const recipesPage = new RecipesPage();
  const createRecipePage = new CreateRecipePage(pageConfig.recipe.simple.name
    , pageConfig.recipe.simple.description);

  describe('Input Data Validation Test', () => {
    describe('Required Field Missing #acceptance', () => {
      test('should show alert message by clicking Save button when create recipe without name @create-recipe-page', (done) => {
        // Highlight the expected result
        const expectedAlertInfo = createRecipePage.varAlertInfo;
        const expectedHelpBlockMsg = createRecipePage.varHelpBlockMsg;

        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.activeElement.id === page.inputNameEleId
            , createRecipePage)
          .insert(createRecipePage.inputName, createRecipePage.varEmptyName)
          .wait(createRecipePage.btnSave)
          .click(createRecipePage.btnSave)
          .wait(createRecipePage.labelAlertInfo)
          .evaluate(page => document.querySelector(page.labelAlertInfo).innerText
            , createRecipePage)
          .then((element) => {
            expect(element).toBe(expectedAlertInfo);
          })
          .then(() => nightmare
            .wait(createRecipePage.spanHelpBlockMsg)
            .evaluate(page => document.querySelector(page.spanHelpBlockMsg).innerText
              , createRecipePage)
            .end())
          .then((element) => {
            expect(element).toBe(expectedHelpBlockMsg);
            done();
          });
      }, timeout);
      test('should show alert message by clicking Enter key when create recipe without name @create-recipe-page', (done) => {
        // Highlight the expected result
        const expectedAlertInfo = createRecipePage.varAlertInfo;
        const expectedHelpBlockMsg = createRecipePage.varHelpBlockMsg;

        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.activeElement.id === page.inputNameEleId
            , createRecipePage)
          .wait(createRecipePage.btnSave)
          .type('body', '\u000d')
          .wait(createRecipePage.labelAlertInfo)
          .evaluate(page => document.querySelector(page.labelAlertInfo).innerText
            , createRecipePage)
          .then((element) => {
            expect(element).toBe(expectedAlertInfo);
          })
          .then(() => nightmare
            .wait(createRecipePage.spanHelpBlockMsg)
            .evaluate(page => document.querySelector(page.spanHelpBlockMsg).innerText
              , createRecipePage)
            .end())
          .then((element) => {
            expect(element).toBe(expectedHelpBlockMsg);
            done();
          });
      }, timeout);
      test('should show alert message by changing focus to description input @create-recipe-page', (done) => {
        // Highlight the expected result
        const expected = createRecipePage.varHelpBlockMsg;

        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.activeElement.id === page.inputNameEleId
            , createRecipePage)
          .evaluate(page => document.querySelector(page.inputDescription).focus()
            , createRecipePage)
          .wait(createRecipePage.spanHelpBlockMsg)
          .evaluate(page => document.querySelector(page.spanHelpBlockMsg).innerText
            , createRecipePage)
          .end()
          .then((element) => {
            expect(element).toBe(expected);
            done();
          });
      }, timeout);
    });
    describe('Simple Valid Input Test #acceptance', () => {
      const editRecipePage = new EditRecipePage(pageConfig.recipe.simple.name);

      // Delete created recipe after each creation case
      afterEach((done) => {
        apiCall.deleteRecipe(editRecipePage.recipeName, done);
      });

      test('should switch to Edit Recipe page - recipe creation success @create-recipe-page', (done) => {
        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.querySelector(page.dialogRootElement).style.display === 'block'
            , createRecipePage)
          .insert(createRecipePage.inputName, createRecipePage.varRecName)
          .insert(createRecipePage.inputDescription, createRecipePage.varRecDesc)
          .click(createRecipePage.btnSave)
          .wait(editRecipePage.componentListItemRootElement)
          .exists(editRecipePage.componentListItemRootElement)
          .end()
          .then((element) => {
            expect(element).toBe(true);
            done();
          });
      }, timeout);
    });
  });
});
<commit_msg>removed useless insert.<commit_after>const Nightmare = require('nightmare');
const RecipesPage = require('../pages/recipes');
const CreateRecipePage = require('../pages/createRecipe');
const EditRecipePage = require('../pages/editRecipe');
const apiCall = require('../utils/apiCall');
const pageConfig = require('../config');

describe('Create Recipe Page', () => {
  // Set case running timeout
  const timeout = 15000;

  // Check BDCS API and Web service first
  beforeAll((done) => {
    apiCall.serviceCheck(done);
  });

  const recipesPage = new RecipesPage();
  const createRecipePage = new CreateRecipePage(pageConfig.recipe.simple.name
    , pageConfig.recipe.simple.description);

  describe('Input Data Validation Test', () => {
    describe('Required Field Missing #acceptance', () => {
      test('should show alert message by clicking Save button when create recipe without name @create-recipe-page', (done) => {
        // Highlight the expected result
        const expectedAlertInfo = createRecipePage.varAlertInfo;
        const expectedHelpBlockMsg = createRecipePage.varHelpBlockMsg;

        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.activeElement.id === page.inputNameEleId
            , createRecipePage)
          .wait(createRecipePage.btnSave)
          .click(createRecipePage.btnSave)
          .wait(createRecipePage.labelAlertInfo)
          .evaluate(page => document.querySelector(page.labelAlertInfo).innerText
            , createRecipePage)
          .then((element) => {
            expect(element).toBe(expectedAlertInfo);
          })
          .then(() => nightmare
            .wait(createRecipePage.spanHelpBlockMsg)
            .evaluate(page => document.querySelector(page.spanHelpBlockMsg).innerText
              , createRecipePage)
            .end())
          .then((element) => {
            expect(element).toBe(expectedHelpBlockMsg);
            done();
          });
      }, timeout);
      test('should show alert message by clicking Enter key when create recipe without name @create-recipe-page', (done) => {
        // Highlight the expected result
        const expectedAlertInfo = createRecipePage.varAlertInfo;
        const expectedHelpBlockMsg = createRecipePage.varHelpBlockMsg;

        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.activeElement.id === page.inputNameEleId
            , createRecipePage)
          .wait(createRecipePage.btnSave)
          .type('body', '\u000d')
          .wait(createRecipePage.labelAlertInfo)
          .evaluate(page => document.querySelector(page.labelAlertInfo).innerText
            , createRecipePage)
          .then((element) => {
            expect(element).toBe(expectedAlertInfo);
          })
          .then(() => nightmare
            .wait(createRecipePage.spanHelpBlockMsg)
            .evaluate(page => document.querySelector(page.spanHelpBlockMsg).innerText
              , createRecipePage)
            .end())
          .then((element) => {
            expect(element).toBe(expectedHelpBlockMsg);
            done();
          });
      }, timeout);
      test('should show alert message by changing focus to description input @create-recipe-page', (done) => {
        // Highlight the expected result
        const expected = createRecipePage.varHelpBlockMsg;

        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.activeElement.id === page.inputNameEleId
            , createRecipePage)
          .evaluate(page => document.querySelector(page.inputDescription).focus()
            , createRecipePage)
          .wait(createRecipePage.spanHelpBlockMsg)
          .evaluate(page => document.querySelector(page.spanHelpBlockMsg).innerText
            , createRecipePage)
          .end()
          .then((element) => {
            expect(element).toBe(expected);
            done();
          });
      }, timeout);
    });
    describe('Simple Valid Input Test #acceptance', () => {
      const editRecipePage = new EditRecipePage(pageConfig.recipe.simple.name);

      // Delete created recipe after each creation case
      afterEach((done) => {
        apiCall.deleteRecipe(editRecipePage.recipeName, done);
      });

      test('should switch to Edit Recipe page - recipe creation success @create-recipe-page', (done) => {
        const nightmare = new Nightmare();
        nightmare
          .goto(recipesPage.url)
          .wait(recipesPage.btnCreateRecipe)
          .click(recipesPage.btnCreateRecipe)
          .wait(page => document.querySelector(page.dialogRootElement).style.display === 'block'
            , createRecipePage)
          .insert(createRecipePage.inputName, createRecipePage.varRecName)
          .insert(createRecipePage.inputDescription, createRecipePage.varRecDesc)
          .click(createRecipePage.btnSave)
          .wait(editRecipePage.componentListItemRootElement)
          .exists(editRecipePage.componentListItemRootElement)
          .end()
          .then((element) => {
            expect(element).toBe(true);
            done();
          });
      }, timeout);
    });
  });
});
<|endoftext|>


<commit_before>const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyModuleGetter(this, "blocklists",
  "resource://tracking-protection-study/BlockLists.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
  "resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebRequest",
  "resource://gre/modules/WebRequest.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
  "resource://gre/modules/MatchPattern.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
  "@mozilla.org/content/style-sheet-service;1", "nsIStyleSheetService");

Cu.importGlobalProperties(["URL"]);

// TODO disable built-in tracking protection
// const TRACKING_PROTECTION_PREF = "privacy.trackingprotection.enabled";
// const TRACKING_PROTECTION_UI_PREF = "privacy.trackingprotection.ui.enabled";
const DOORHANGER_ID = "onboarding-trackingprotection-notification";
const DOORHANGER_ICON = "chrome://browser/skin/tracking-protection-16.svg#enabled";
const STYLESHEET_URL = "resource://tracking-protection-study/tracking-protection-study.css";

this.TrackingProtectionStudy = {
  /**
   * Open doorhanger-style notification on desired chrome window.
   *
   * @param {ChromeWindow} win
   * @param {String} message
   * @param {String} url
   */
  openDoorhanger(win, message, url) {
    const options = {
      popupIconURL: DOORHANGER_ICON,
      learnMoreURL: url,
      persistent: true,
      persistWhileVisible: true,
    };

    const action = {
      label: "Got it!",
      accessKey: "G",
      callback: () => {},
    };

    win.PopupNotifications.show(win.gBrowser.selectedBrowser, DOORHANGER_ID, message,
      null, action, [], options);
  },

  onOpenWindow(xulWindow) {
    var win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
              .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
    win.addEventListener("load", () => this.addEventListeners(win.gBrowser), {once: true});
  },

  onLocationChange(browser, progress, request, uri, flags) {
    if (this.state.blockedResources.has(browser)) {
      this.showPageAction(browser.getRootNode());
      this.setPageActionCounter(browser.getRootNode(), 0);
      this.state.blockedResources.set(browser, 0);
    }
  },

  onBeforeRequest(details) {
    if (details && details.url && details.browser) {
      let browser = details.browser;
      let currentURI = browser.currentURI;

      if (!currentURI) {
        return;
      }

      if (!details.originUrl) {
        return;
      }

      if (currentURI.scheme != "http" && currentURI.scheme != "https") {
        return;
      }

      let currentHost = currentURI.host;
      let host = new URL(details.originUrl).host;

      // Block third-party requests only.
      if (currentHost != host && blocklists.hostInBlocklist(this.state.blocklist, host)) {
        let counter;
        if (this.state.blockedResources.has(details.browser)) {
          counter = this.state.blockedResources.get(details.browser);
          counter++;
        } else {
          counter = 1;
        }

        // TODO enable allowed hosts.
        if (this.state.allowedHosts.has(currentHost)) {
          this.state.totalAllowedResources += 1;
        } else {
          this.state.totalBlockedResources += 1;
        }

        this.state.blockedResources.set(details.browser, counter);

        let enumerator = Services.wm.getEnumerator("navigator:browser");
        while (enumerator.hasMoreElements()) {
          let win = enumerator.getNext();
          if (win === Services.appShell.hiddenDOMWindow) {
            continue;
          }

          if (details.browser == win.gBrowser.selectedBrowser) {
            this.showPageAction(browser.getRootNode());
            this.setPageActionCounter(browser.getRootNode(), counter);
          }
        }
        return {cancel: true};
      }
    }
  },

  /**
   * Shows the page action button for the current window.
   */
  showPageAction(doc) {
    let win = Services.wm.getMostRecentWindow("navigator:browser");
    let currentHost = win.gBrowser.currentURI.host;

    let button = doc.getElementById("tracking-protection-study-button");
    if (button) {
      button.parentElement.removeChild(button);
    }
    doc.getElementById("tracking")
    let urlbar = doc.getElementById("urlbar-icons");

    let panel = doc.createElement("panel");
    panel.setAttribute("id", "tracking-protection-study-panel");
    panel.setAttribute("type", "arrow");
    panel.setAttribute("level", "parent");
    let panelHbox = doc.createElement("hbox");

    let controls = doc.createElement("vbox");

    let group = doc.createElement("radiogroup");
    let enabled = doc.createElement("radio");
    enabled.setAttribute("label", "Enable on this site");
    enabled.addEventListener("click", () => {
      if (this.state.allowedHosts.has(currentHost)) {
        this.state.allowedHosts.delete(currentHost);
      }
      win.gBrowser.reload();
    });
    let disabled = doc.createElement("radio");
    disabled.setAttribute("label", "Disable on this site");
    disabled.addEventListener("click", () => {
      this.state.allowedHosts.add(currentHost);
      win.gBrowser.reload();
    });
    if (this.state.allowedHosts.has(currentHost)) {
      disabled.setAttribute("selected", true);
    } else {
      enabled.setAttribute("selected", true);
    }
    group.append(enabled);
    group.append(disabled);
    controls.append(group);

    let footer = doc.createElement("vbox");
    footer.setAttribute("value", "If the website appears broken, consider disabling" +
                                  "tracking protection and refreshing the page.");

    panelHbox.append(controls);
    panelHbox.append(footer);
    panel.append(panelHbox);

    button = doc.createElement("toolbarbutton");
    if (this.state.allowedHosts.has(currentHost)) {
      button.style.backgroundColor = "yellow";
    } else {
      button.style.backgroundColor = "green";
    }
    button.setAttribute("id", "tracking-protection-study-button");
    button.setAttribute("image", "chrome://browser/skin/controlcenter/tracking-protection.svg#enabled");
    button.append(panel);
    button.addEventListener("command", event => {
      doc.getElementById("panel");
      panel.openPopup(button);
    });

    urlbar.append(button);
  },

  setPageActionCounter(doc, counter) {
    let toolbarButton = doc.getElementById("tracking-protection-study-button");
    if (toolbarButton) {
      toolbarButton.setAttribute("label", counter);
    }
  },

  hidePageAction(doc) {
    let button = doc.getElementById("tracking-protection-study-button");
    if (button) {
      button.parentElement.removeChild(button);
    }
  },

  onTabChange(evt) {
    let win = evt.target.ownerGlobal;
    let currentURI = win.gBrowser.currentURI;
    if (currentURI.scheme != "http" && currentURI.scheme != "https") {
      this.hidePageAction(win.document);
      return;
    }

    let currentWin = Services.wm.getMostRecentWindow("navigator:browser");

    if (win == currentWin) {
      this.hidePageAction(win.document);
      let counter = this.state.blockedResources.get(win.gBrowser.selectedBrowser);

      if (counter) {
        this.showPageAction(win.document);
        this.setPageActionCounter(win.document, counter);
      }
    }
  },

  onTabOpen(evt) {
    let win = evt.target.ownerGlobal;
    let currentURI = win.gBrowser.currentURI;
    if (currentURI.spec == "about:newtab") {
      console.log("rhelmer debug1");
      let doc = win.gBrowser.contentDocument;
      if (doc.getElementById("tracking-protection-message")) {
        return;
      }
      console.log("rhelmer debug2");
      let minutes = this.state.timeSaved / 1000 / 60;
      // FIXME commented out for testing
      // if (minutes >= 1 && this.blockedRequests) {
      if (this.state.totalBlockedResources) {
        console.log("rhelmer debug3");
        let message = this.newtab_message;
        message = message.replace("${blockedRequests}", this.state.totalBlockedResources);
        message = message.replace("${blockedEntities}", this.state.totalBlockedEntities);
        message = message.replace("${blockedSites}", this.state.totalBlockedSites);
        message = message.replace("${minutes}", minutes.toPrecision(3));

        let logo = doc.createElement("img");
        logo.src = "chrome://browser/skin/controlcenter/tracking-protection.svg#enabled";
        logo.style.height = 48;
        logo.style.width = 48;
        logo.style.float = "left";
        logo.style.padding = "5px";

        let span = doc.createElement("span");
        span.style.fontSize = "24px";
        span.style.fontWeight = "lighter";
        span.style.float = "right";
        span.style.padding = "5px";
        span.innerHTML = message;

        let newContainer = doc.createElement("div");
        newContainer.id = "tracking-protection-message";
        newContainer.style.padding = "24px";
        newContainer.append(logo);
        newContainer.append(span);

        let container = doc.getElementById("newtab-margin-top");
        container.append(newContainer);
      }
    }
  },

  /**
   * Open URL in new tab on desired chrome window.
   *
   * @param {ChromeWindow} win
   * @param {String} message
   * @param {String} url
   * @param {bool} foreground - true if this tab should open in the foreground.
   */
  openURL(win, message, url, foreground = true) {
    const tab = win.gBrowser.addTab(url);
    if (foreground) {
      win.gBrowser.selectedTab = tab;
    }
  },

  async init() {
    // define treatments as STRING: fn(browserWindow, url)
    this.TREATMENTS = {
      doorhanger: this.openDoorhanger,
      opentab: this.openURL,
    }

    // TODO hardcode for the moment, but get from distribution ID instead
    this.treatment = "opentab";
    this.distribution_id = "test123";
    let newtab_messages = [
      "Firefox blocked ${blockedRequests} trackers today
 from ${blockedEntities} companies that track your browsing",
      "Firefox blocked ${blockedRequests} trackers today
 and saved you ${minutes} minutes",
      "Firefox blocked ${blockedRequests} ads today from
 ${blockedSites} different websites"
    ];
    this.newtab_message = newtab_messages[0];

    // run once now on the most recent window.
    let win = Services.wm.getMostRecentWindow("navigator:browser");

    if (this.treatment === "ALL") {
      Object.keys(this.TREATMENTS).forEach((key, index) => {
        if (Object.prototype.hasOwnProperty.call(this.TREATMENTS, key)) {
          this.TREATMENTS[key](win, this.message, this.url);
        }
      });
    } else if (this.treatment in this.TREATMENTS) {
      this.TREATMENTS[this.treatment](win, this.message, this.url);
    }

    this.state = {
      timeSave: 0,
      blocklist: new Map(),
      allowedHosts: new Set(),
      reportedHosts: {},
      entityList: {},
      blockedResources: new Map(),
      totalBlockedResources: 0,
      totalAllowedResources: 0
    }

    await blocklists.loadLists(this.state);

    let filter = {urls: new MatchPattern("*://*/*")};
    this.onBeforeRequest = this.onBeforeRequest.bind(this);

    WebRequest.onBeforeRequest.addListener(this.onBeforeRequest, filter, ["blocking"]);

    let uri = Services.io.newURI(STYLESHEET_URL);
    styleSheetService.loadAndRegisterSheet(uri, styleSheetService.AGENT_SHEET);

    // Add listeners to all open windows.
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while (enumerator.hasMoreElements()) {
      let win = enumerator.getNext();
      if (win === Services.appShell.hiddenDOMWindow) {
        continue;
      }

      let gBrowser = win.gBrowser;
      this.addEventListeners(gBrowser);
    }

    // Attach to any new windows.
    Services.wm.addListener(this);
  },

  addEventListeners(gBrowser) {
    this.onTabChange = this.onTabChange.bind(this);
    this.onTabOpen = this.onTabOpen.bind(this);

    gBrowser.addTabsProgressListener(this);
    gBrowser.tabContainer.addEventListener("TabSelect", this.onTabChange);
    gBrowser.tabContainer.addEventListener("TabOpen", this.onTabOpen);
  },

  uninit() {
    // Remove listeners from all open windows.
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while (enumerator.hasMoreElements()) {
      let win = enumerator.getNext();
      if (win === Services.appShell.hiddenDOMWindow) {
        continue;
      }

      let button = win.document.getElementById("tracking-protection-study")
      if (button) {
        button.parentElement.removeChild(button);
      }

      WebRequest.onBeforeRequest.removeListener(this.onBeforeRequest);
      win.gBrowser.removeTabsProgressListener(this);
      win.gBrowser.tabContainer.removeEventListener("TabSelect", this.onTabChange);
      win.gBrowser.tabContainer.removeEventListener("TabOpen", this.onTabOpen);

      Services.wm.removeListener(this);
    }

    let uri = Services.io.newURI(STYLESHEET_URL);
    styleSheetService.unregisterSheet(uri, styleSheetService.AGENT_SHEET);
  }
}

this.shutdown = function() {
  TrackingProtectionStudy.uninit();
  Cu.unload("resource://tracking-protection-study/Blocklists.jsm");
};

this.install = function(data, reason) {};

this.startup = async function(data, reason) {
  TrackingProtectionStudy.init();
};

this.shutdown = this.uninstall = function(data, reason) {

  TrackingProtectionStudy.uninit();
}
<commit_msg>try modifying about:newtab on tabopen<commit_after>const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyModuleGetter(this, "blocklists",
  "resource://tracking-protection-study/BlockLists.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
  "resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebRequest",
  "resource://gre/modules/WebRequest.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
  "resource://gre/modules/MatchPattern.jsm");

XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
  "@mozilla.org/content/style-sheet-service;1", "nsIStyleSheetService");

Cu.importGlobalProperties(["URL"]);

// TODO disable built-in tracking protection
// const TRACKING_PROTECTION_PREF = "privacy.trackingprotection.enabled";
// const TRACKING_PROTECTION_UI_PREF = "privacy.trackingprotection.ui.enabled";
const DOORHANGER_ID = "onboarding-trackingprotection-notification";
const DOORHANGER_ICON = "chrome://browser/skin/tracking-protection-16.svg#enabled";
const STYLESHEET_URL = "resource://tracking-protection-study/tracking-protection-study.css";

this.TrackingProtectionStudy = {
  /**
   * Open doorhanger-style notification on desired chrome window.
   *
   * @param {ChromeWindow} win
   * @param {String} message
   * @param {String} url
   */
  openDoorhanger(win, message, url) {
    const options = {
      popupIconURL: DOORHANGER_ICON,
      learnMoreURL: url,
      persistent: true,
      persistWhileVisible: true,
    };

    const action = {
      label: "Got it!",
      accessKey: "G",
      callback: () => {},
    };

    win.PopupNotifications.show(win.gBrowser.selectedBrowser, DOORHANGER_ID, message,
      null, action, [], options);
  },

  onOpenWindow(xulWindow) {
    var win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
              .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
    win.addEventListener("load", () => this.addEventListeners(win.gBrowser), {once: true});
  },

  onLocationChange(browser, progress, request, uri, flags) {
    if (this.state.blockedResources.has(browser)) {
      this.showPageAction(browser.getRootNode());
      this.setPageActionCounter(browser.getRootNode(), 0);
      this.state.blockedResources.set(browser, 0);
    }
  },

  onBeforeRequest(details) {
    if (details && details.url && details.browser) {
      let browser = details.browser;
      let currentURI = browser.currentURI;

      if (!currentURI) {
        return;
      }

      if (!details.originUrl) {
        return;
      }

      if (currentURI.scheme != "http" && currentURI.scheme != "https") {
        return;
      }

      let currentHost = currentURI.host;
      let host = new URL(details.originUrl).host;

      // Block third-party requests only.
      if (currentHost != host && blocklists.hostInBlocklist(this.state.blocklist, host)) {
        let counter;
        if (this.state.blockedResources.has(details.browser)) {
          counter = this.state.blockedResources.get(details.browser);
          counter++;
        } else {
          counter = 1;
        }

        // TODO enable allowed hosts.
        if (this.state.allowedHosts.has(currentHost)) {
          this.state.totalAllowedResources += 1;
        } else {
          this.state.totalBlockedResources += 1;
        }

        this.state.blockedResources.set(details.browser, counter);

        let enumerator = Services.wm.getEnumerator("navigator:browser");
        while (enumerator.hasMoreElements()) {
          let win = enumerator.getNext();
          if (win === Services.appShell.hiddenDOMWindow) {
            continue;
          }

          if (details.browser == win.gBrowser.selectedBrowser) {
            this.showPageAction(browser.getRootNode());
            this.setPageActionCounter(browser.getRootNode(), counter);
          }
        }
        return {cancel: true};
      }
    }
  },

  /**
   * Shows the page action button for the current window.
   */
  showPageAction(doc) {
    let win = Services.wm.getMostRecentWindow("navigator:browser");
    let currentHost = win.gBrowser.currentURI.host;

    let button = doc.getElementById("tracking-protection-study-button");
    if (button) {
      button.parentElement.removeChild(button);
    }
    doc.getElementById("tracking")
    let urlbar = doc.getElementById("urlbar-icons");

    let panel = doc.createElement("panel");
    panel.setAttribute("id", "tracking-protection-study-panel");
    panel.setAttribute("type", "arrow");
    panel.setAttribute("level", "parent");
    let panelHbox = doc.createElement("hbox");

    let controls = doc.createElement("vbox");

    let group = doc.createElement("radiogroup");
    let enabled = doc.createElement("radio");
    enabled.setAttribute("label", "Enable on this site");
    enabled.addEventListener("click", () => {
      if (this.state.allowedHosts.has(currentHost)) {
        this.state.allowedHosts.delete(currentHost);
      }
      win.gBrowser.reload();
    });
    let disabled = doc.createElement("radio");
    disabled.setAttribute("label", "Disable on this site");
    disabled.addEventListener("click", () => {
      this.state.allowedHosts.add(currentHost);
      win.gBrowser.reload();
    });
    if (this.state.allowedHosts.has(currentHost)) {
      disabled.setAttribute("selected", true);
    } else {
      enabled.setAttribute("selected", true);
    }
    group.append(enabled);
    group.append(disabled);
    controls.append(group);

    let footer = doc.createElement("vbox");
    footer.setAttribute("value", "If the website appears broken, consider disabling" +
                                  "tracking protection and refreshing the page.");

    panelHbox.append(controls);
    panelHbox.append(footer);
    panel.append(panelHbox);

    button = doc.createElement("toolbarbutton");
    if (this.state.allowedHosts.has(currentHost)) {
      button.style.backgroundColor = "yellow";
    } else {
      button.style.backgroundColor = "green";
    }
    button.setAttribute("id", "tracking-protection-study-button");
    button.setAttribute("image", "chrome://browser/skin/controlcenter/tracking-protection.svg#enabled");
    button.append(panel);
    button.addEventListener("command", event => {
      doc.getElementById("panel");
      panel.openPopup(button);
    });

    urlbar.append(button);
  },

  setPageActionCounter(doc, counter) {
    let toolbarButton = doc.getElementById("tracking-protection-study-button");
    if (toolbarButton) {
      toolbarButton.setAttribute("label", counter);
    }
  },

  hidePageAction(doc) {
    let button = doc.getElementById("tracking-protection-study-button");
    if (button) {
      button.parentElement.removeChild(button);
    }
  },

  onTabChange(evt) {
    let win = evt.target.ownerGlobal;
    let currentURI = win.gBrowser.currentURI;
    if (currentURI.scheme != "http" && currentURI.scheme != "https") {
      this.hidePageAction(win.document);
      return;
    }

    let currentWin = Services.wm.getMostRecentWindow("navigator:browser");

    if (win == currentWin) {
      this.hidePageAction(win.document);
      let counter = this.state.blockedResources.get(win.gBrowser.selectedBrowser);

      if (counter) {
        this.showPageAction(win.document);
        this.setPageActionCounter(win.document, counter);
      }
    }
  },

  onTabOpen(evt) {
    let win = evt.target.ownerGlobal;
    let currentURI = win.gBrowser.currentURI;
    if (currentURI.spec == "about:newtab") {
      console.log("rhelmer debug1");
      let doc = win.gBrowser.contentDocument;
      if (doc.getElementById("tracking-protection-message")) {
        return;
      }
      console.log("rhelmer debug2");
      let minutes = this.state.timeSaved / 1000 / 60;
      // FIXME commented out for testing
      // if (minutes >= 1 && this.blockedRequests) {
      if (this.state.totalBlockedResources) {
        console.log("rhelmer debug3", doc.getElementById("newtab-margin-top"));
        let message = this.newtab_message;
        message = message.replace("${blockedRequests}", this.state.totalBlockedResources);
        message = message.replace("${blockedEntities}", this.state.totalBlockedEntities);
        message = message.replace("${blockedSites}", this.state.totalBlockedSites);
        message = message.replace("${minutes}", minutes.toPrecision(3));
        console.log("rhelmer debug4", message);

        let logo = doc.createElement("img");
        logo.src = "chrome://browser/skin/controlcenter/tracking-protection.svg#enabled";
        logo.style.height = 48;
        logo.style.width = 48;
        logo.style.float = "left";
        logo.style.padding = "5px";

        let span = doc.createElement("span");
        span.style.fontSize = "24px";
        span.style.fontWeight = "lighter";
        span.style.float = "right";
        span.style.padding = "5px";
        span.innerHTML = message;

        let newContainer = doc.createElement("div");
        newContainer.id = "tracking-protection-message";
        newContainer.style.padding = "24px";
        newContainer.append(logo);
        newContainer.append(span);

        let container = doc.getElementById("newtab-margin-top");
        container.append(newContainer);
      }
    }
  },

  /**
   * Open URL in new tab on desired chrome window.
   *
   * @param {ChromeWindow} win
   * @param {String} message
   * @param {String} url
   * @param {bool} foreground - true if this tab should open in the foreground.
   */
  openURL(win, message, url, foreground = true) {
    const tab = win.gBrowser.addTab(url);
    if (foreground) {
      win.gBrowser.selectedTab = tab;
    }
  },

  async init() {
    // define treatments as STRING: fn(browserWindow, url)
    this.TREATMENTS = {
      doorhanger: this.openDoorhanger,
      opentab: this.openURL,
    }

    // TODO hardcode for the moment, but get from distribution ID instead
    this.treatment = "opentab";
    this.distribution_id = "test123";
    let newtab_messages = [
      "Firefox blocked ${blockedRequests} trackers today
 from ${blockedEntities} companies that track your browsing",
      "Firefox blocked ${blockedRequests} trackers today
 and saved you ${minutes} minutes",
      "Firefox blocked ${blockedRequests} ads today from
 ${blockedSites} different websites"
    ];
    this.newtab_message = newtab_messages[0];

    // run once now on the most recent window.
    let win = Services.wm.getMostRecentWindow("navigator:browser");

    if (this.treatment === "ALL") {
      Object.keys(this.TREATMENTS).forEach((key, index) => {
        if (Object.prototype.hasOwnProperty.call(this.TREATMENTS, key)) {
          this.TREATMENTS[key](win, this.message, this.url);
        }
      });
    } else if (this.treatment in this.TREATMENTS) {
      this.TREATMENTS[this.treatment](win, this.message, this.url);
    }

    this.state = {
      timeSave: 0,
      blocklist: new Map(),
      allowedHosts: new Set(),
      reportedHosts: {},
      entityList: {},
      blockedResources: new Map(),
      totalBlockedResources: 0,
      totalAllowedResources: 0
    }

    await blocklists.loadLists(this.state);

    let filter = {urls: new MatchPattern("*://*/*")};
    this.onBeforeRequest = this.onBeforeRequest.bind(this);

    WebRequest.onBeforeRequest.addListener(this.onBeforeRequest, filter, ["blocking"]);

    let uri = Services.io.newURI(STYLESHEET_URL);
    styleSheetService.loadAndRegisterSheet(uri, styleSheetService.AGENT_SHEET);

    // Add listeners to all open windows.
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while (enumerator.hasMoreElements()) {
      let win = enumerator.getNext();
      if (win === Services.appShell.hiddenDOMWindow) {
        continue;
      }

      let gBrowser = win.gBrowser;
      this.addEventListeners(gBrowser);
    }

    // Attach to any new windows.
    Services.wm.addListener(this);
  },

  addEventListeners(gBrowser) {
    this.onTabChange = this.onTabChange.bind(this);
    this.onTabOpen = this.onTabOpen.bind(this);

    gBrowser.addTabsProgressListener(this);
    gBrowser.tabContainer.addEventListener("TabSelect", this.onTabChange);
    gBrowser.tabContainer.addEventListener("TabOpen", this.onTabOpen);
  },

  uninit() {
    // Remove listeners from all open windows.
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while (enumerator.hasMoreElements()) {
      let win = enumerator.getNext();
      if (win === Services.appShell.hiddenDOMWindow) {
        continue;
      }

      let button = win.document.getElementById("tracking-protection-study")
      if (button) {
        button.parentElement.removeChild(button);
      }

      WebRequest.onBeforeRequest.removeListener(this.onBeforeRequest);
      win.gBrowser.removeTabsProgressListener(this);
      win.gBrowser.tabContainer.removeEventListener("TabSelect", this.onTabChange);
      win.gBrowser.tabContainer.removeEventListener("TabOpen", this.onTabOpen);

      Services.wm.removeListener(this);
    }

    let uri = Services.io.newURI(STYLESHEET_URL);
    styleSheetService.unregisterSheet(uri, styleSheetService.AGENT_SHEET);
  }
}

this.shutdown = function() {
  TrackingProtectionStudy.uninit();
  Cu.unload("resource://tracking-protection-study/Blocklists.jsm");
};

this.install = function(data, reason) {};

this.startup = async function(data, reason) {
  TrackingProtectionStudy.init();
};

this.shutdown = this.uninstall = function(data, reason) {

  TrackingProtectionStudy.uninit();
}
<|endoftext|>