<commit_before>describe('windowHandlePosition', function() {

    before(h.setup());

    it('should return window position', function(done) {

        var self = this,
            check = function(err, res) {
                assert.ifError(err);

                res.value.x.should.be.above(1);
                res.value.y.should.be.above(1);
            };

        this.client
            // use current tab id as a valid one
            .getCurrentTabId(function(err, tabid) {
                assert.ifError(err);

                // specified window
                self.client.windowHandlePosition(tabid, check);

                // current window
                self.client.windowHandlePosition(check);
            })
            .call(done);

    });

    it('should change window position', function(done) {

        var self = this;

        this.client
            // use current tab id as a valid one
            .getCurrentTabId(function(err, tabid) {
                assert.ifError(err);

                // specified window
                self.client.windowHandlePosition(tabid, {x: 300, y: 150});
                self.client.windowHandlePosition(tabid, function(err, res) {
                    assert.ifError(err);

                    res.value.x.should.be.exactly(300);
                    res.value.y.should.be.exactly(150);
                });

                // current window
                self.client.windowHandlePosition({x: 400, y: 250});
                self.client.windowHandlePosition(tabid, function(err, res) {
                    assert.ifError(err);

                    res.value.x.should.be.exactly(400);
                    res.value.y.should.be.exactly(250);
                });
            })
            .call(done);

    });

});<commit_msg>travis made me do that<commit_after>describe('windowHandlePosition', function() {

    before(h.setup());

    it('should return window position', function(done) {

        var self = this,
            check = function(err, res) {
                assert.ifError(err);

                res.value.x.should.be.above(process.env.TRAVIS ? -8 : 1);
                res.value.y.should.be.above(process.env.TRAVIS ? -8 : 1);
            };

        this.client
            // use current tab id as a valid one
            .getCurrentTabId(function(err, tabid) {
                assert.ifError(err);

                // specified window
                self.client.windowHandlePosition(tabid, check);

                // current window
                self.client.windowHandlePosition(check);
            })
            .call(done);

    });

    it('should change window position', function(done) {

        var self = this;

        this.client
            // use current tab id as a valid one
            .getCurrentTabId(function(err, tabid) {
                assert.ifError(err);

                // specified window
                self.client.windowHandlePosition(tabid, {x: 300, y: 150});
                self.client.windowHandlePosition(tabid, function(err, res) {
                    assert.ifError(err);

                    res.value.x.should.be.exactly(300);
                    res.value.y.should.be.exactly(150);
                });

                // current window
                self.client.windowHandlePosition({x: 400, y: 250});
                self.client.windowHandlePosition(tabid, function(err, res) {
                    assert.ifError(err);

                    res.value.x.should.be.exactly(400);
                    res.value.y.should.be.exactly(250);
                });
            })
            .call(done);

    });

});<|endoftext|>


<commit_before>import {Fragment} from "./fragment"
import {Mark} from "./mark"
import {ModelError} from "./error"
import {Slice, replace} from "./replace"

const emptyArray = [], emptyAttrs = Object.create(null)

// ;; This class represents a node in the tree that makes up a
// ProseMirror document. So a document is an instance of `Node`, with
// children that are also instances of `Node`.
//
// Nodes are persistent data structures. Instead of changing them, you
// create new ones with the content you want. Old ones keep pointing
// at the old document shape. This is made cheaper by sharing
// structure between the old and new data as much as possible, which a
// tree shape like this (without back pointers) makes easy.
//
// **Never** directly mutate the properties of a `Node` object. See
// [this guide](guide/doc.html) for more information.
export class Node {
  constructor(type, attrs, content, marks) {
    // :: NodeType
    // The type of node that this is.
    this.type = type

    // :: Object
    // An object mapping attribute names to string values. The kind of
    // attributes allowed and required are determined by the node
    // type.
    this.attrs = attrs

    // :: Fragment
    // The node's content.
    this.content = content || Fragment.empty

    // :: [Mark]
    // The marks (things like whether it is emphasized or part of a
    // link) associated with this node.
    this.marks = marks || emptyArray
  }

  // :: number
  // The size of this node. For text node, this is the amount of
  // characters. For leaf nodes, it is one. And for non-leaf nodes, it
  // is the size of the content plus two (the start and end token).
  get nodeSize() { return this.type.contains ? 2 + this.content.size : 1 }

  // :: number
  // The number of children that the node has.
  get childCount() { return this.content.childCount }

  child(index) { return this.content.child(index) }

  forEach(f) { this.content.forEach(f) }

  // :: string
  // Concatenate all the text nodes found in this fragment and its
  // children.
  get textContent() { return this.content.textContent }

  // :: ?Node
  // Returns this node's first child, or `null` if there are no
  // children.
  get firstChild() { return this.content.firstChild }

  // :: ?Node
  // Returns this node's last child, or `null` if there are no
  // children.
  get lastChild() { return this.content.lastChild }

  // :: (Node) → bool
  // Test whether two nodes represent the same content.
  eq(other) {
    return this == other || (this.sameMarkup(other) && this.content.eq(other.content))
  }

  // :: (Node) → bool
  // Compare the markup (type, attributes, and marks) of this node to
  // those of another. Returns `true` if both have the same markup.
  sameMarkup(other) {
    return this.hasMarkup(other.type, other.attrs, other.marks)
  }

  // :: (NodeType, ?Object, ?[Mark]) → bool
  // Check whether this node's markup correspond to the given type,
  // attributes, and marks.
  hasMarkup(type, attrs, marks) {
    return this.type == type && Node.sameAttrs(this.attrs, attrs || emptyAttrs) && Mark.sameSet(this.marks, marks || emptyArray)
  }

  static sameAttrs(a, b) {
    if (a == b) return true
    for (let prop in a)
      if (a[prop] !== b[prop]) return false
    return true
  }

  // :: (?Fragment) → Node
  // Create a new node with the same markup as this node, containing
  // the given content (or empty, if no content is given).
  copy(content = null) {
    if (content == this.content) return this
    return new this.constructor(this.type, this.attrs, content, this.marks)
  }

  // :: ([Mark]) → Node
  // Create a copy of this node, with the given set of marks instead
  // of the node's own marks.
  mark(marks) {
    return new this.constructor(this.type, this.attrs, this.content, marks)
  }

  // :: (number, ?number) → Node
  // Create a copy of this node with only the content between the
  // given offsets. If `to` is not given, it defaults to the end of
  // the node.
  cut(from, to) {
    if (from == 0 && to == this.content.size) return this
    return this.copy(this.content.cut(from, to))
  }

  slice(from, to = this.content.size) {
    if (from == to) return Slice.empty

    from = this.resolve(from)
    to = this.resolve(to)
    let depth = from.sameDepth(to), start = from.start(depth)
    let content = from.node[depth].content.cut(from.pos - start, to.pos - start)
    return new Slice(content, from.depth - depth, to.depth - depth)
  }

  replace(from, to, slice) {
    return replace(this.resolve(from), this.resolve(to), slice)
  }

  // :: (number) → Node
  // Find the node after the given position.
  nodeAt(pos) {
    for (let node = this;;) {
      let index = findIndex(node.content, pos)
      node = node.child(index)
      if (foundOffset == pos || node.isText) return node
      pos -= foundOffset + 1
    }
  }

  nodeAfter(pos) {
    let index = findIndex(this.content, pos)
    return {node: this.content.maybeChild(index), index, offset: foundOffset}
  }

  nodeBefore(pos) {
    let index = findIndex(this.content, pos)
    if (foundOffset > pos || pos == 0) return {node: this.content.child(index), index, offset: foundOffset}
    let node = this.content.maybeChild(index - 1)
    return {node, index: index - 1, offset: foundOffset - node.childSize}
  }

  // :: (?number, ?number, (node: Node, pos: number, parent: Node))
  // Iterate over all nodes between the given two positions, calling
  // the callback with the node, its position, and its parent
  // node. `from` and `to` may be left off, to denote
  // starting at the start of the node or ending at its end.
  nodesBetween(from, to, f, pos = 0) {
    this.content.nodesBetween(from, to, f, pos, this)
  }

  resolve(pos, cache) {
    return cache === false ? ResolvedPos.resolve(this, pos) : resolveCached(this, pos)
  }

  // :: (number) → [Mark]
  // Get the marks of the node before the given position or, if that
  // position is at the start of a non-empty node, those of the node
  // after it.
  marksAt(pos) {
    let r = this.resolve(pos), top = r.parent, index = r.index[r.depth]
    let leaf = index ? top.child(index - 1) : index < top.childCount ? top.child(index) : null
    return leaf ? leaf.marks : emptyArray
  }

  // :: (?number, ?number, MarkType) → bool
  // Test whether a mark of the given type occurs in this document
  // between the two given positions.
  rangeHasMark(from, to, type) {
    let found = false
    this.nodesBetween(from, to, node => {
      if (type.isInSet(node.marks)) found = true
      return !found
    })
    return found
  }

  // :: bool
  // True when this is a block (non-inline node)
  get isBlock() { return this.type.isBlock }

  // :: bool
  // True when this is a textblock node, a block node with inline
  // content.
  get isTextblock() { return this.type.isTextblock }

  // :: bool
  // True when this is an inline node (a text node or a node that can
  // appear among text).
  get isInline() { return this.type.isInline }

  // :: bool
  // True when this is a text node.
  get isText() { return this.type.isText }

  // :: () → string
  // Return a string representation of this node for debugging
  // purposes.
  toString() {
    let name = this.type.name
    if (this.content.size)
      name += "(" + this.content.toStringInner() + ")"
    return wrapMarks(this.marks, name)
  }

  // :: () → Object
  // Return a JSON-serializeable representation of this node.
  toJSON() {
    let obj = {type: this.type.name}
    for (let _ in this.attrs) {
      obj.attrs = this.attrs
      break
    }
    if (this.content.size)
      obj.content = this.content.toJSON()
    if (this.marks.length)
      obj.marks = this.marks.map(n => n.toJSON())
    return obj
  }

  // :: (Schema, Object) → Node
  // Deserialize a node from its JSON representation.
  static fromJSON(schema, json) {
    let type = schema.nodeType(json.type)
    let content = json.text != null ? json.text : Fragment.fromJSON(schema, json.content)
    return type.create(json.attrs, content, json.marks && json.marks.map(schema.markFromJSON))
  }

  // This is a hack to be able to treat a node object as an iterator result
  get value() { return this }
}

// ;; #forward=Node
export class TextNode extends Node {
  constructor(type, attrs, content, marks) {
    super(type, attrs, null, marks)

    if (!content) throw new ModelError("Empty text nodes are not allowed")

    // :: ?string
    // For text nodes, this contains the node's text content.
    this.text = content
  }

  toString() { return wrapMarks(this.marks, JSON.stringify(this.text)) }

  get textContent() { return this.text }

  get nodeSize() { return this.text.length }

  mark(marks) {
    return new TextNode(this.type, this.attrs, this.text, marks)
  }

  cut(from = 0, to = this.text.length) {
    if (from == 0 && to == this.text.length) return this
    return this.copy(this.text.slice(from, to))
  }

  eq(other) {
    return this.sameMarkup(other) && this.text == other.text
  }

  toJSON() {
    let base = super.toJSON()
    base.text = this.text
    return base
  }
}

function wrapMarks(marks, str) {
  for (let i = marks.length - 1; i >= 0; i--)
    str = marks[i].type.name + "(" + str + ")"
  return str
}

let foundOffset = 0
function findIndex(fragment, pos, round = -1) {
  if (pos == 0) { foundOffset = pos; return 0 }
  if (pos == fragment.size) { foundOffset = pos; return fragment.content.length }
  if (pos > fragment.size || pos < 0) throw new ModelError(`Position ${pos} outside of fragment (${fragment})`)
  for (let i = 0, curPos = 0;; i++) {
    let cur = fragment.child(i), end = curPos + cur.nodeSize
    if (end >= pos) {
      if (end == pos || round > 0) { foundOffset = end; return i + 1 }
      foundOffset = curPos; return i
    }
    curPos = end
  }
}

export class ResolvedPos {
  constructor(pos, node, index, offset, parentOffset) {
    this.pos = pos
    this.node = node
    this.index = index
    this.offset = offset
    this.parentOffset = parentOffset
  }

  get parent() { return this.node[this.depth] }

  get depth() { return this.node.length - 1 }

  get nodeAfter() {
    let parent = this.parent, index = this.index[this.depth]
    if (index == parent.childCount) return null
    let dOff = this.parentOffset - this.offset[this.depth], child = parent.child(index)
    return dOff ? parent.child(index).cut(dOff) : child
  }

  get nodeBefore() {
    let index = this.index[this.depth]
    let dOff = this.parentOffset - this.offset[this.depth]
    if (dOff) return this.parent.child(index).cut(0, dOff)
    return index == 0 ? null : this.parent.child(index - 1)
  }

  sameDepth(other) {
    let depth = 0, max = Math.min(this.depth, other.depth)
    while (depth < max && this.index[depth] == other.index[depth]) ++depth
    return depth
  }

  sameParent(other) {
    return this.pos - this.parentOffset == other.pos - other.parentOffset
  }

  start(depth) {
    let pos = 0
    for (let i = 0; i < depth; i++) pos += this.offset[i] + 1
    return pos
  }

  end(depth) {
    return this.start(depth) + this.node[depth].content.size
  }

  before(depth) { return depth == this.offset.length ? this.pos : this.start(depth) - 1 }
  after(depth) { return depth == this.offset.length ? this.pos : this.end(depth) + 1 }

  toString() {
    let str = ""
    for (let i = 1; i < this.index.length; i++)
      str += (str ? "/" : "") + this.node[i].type.name + "_" + this.index[i - 1]
    return str + ":" + this.parentOffset
  }

  static resolve(doc, pos) {
    if (!(pos >= 0 && pos <= doc.content.size)) throw new ModelError("Position " + pos + " out of range")
    let nodes = [], index = [], offset = [], parentOffset = pos
    for (let node = doc;;) {
      let i = findIndex(node.content, parentOffset)
      let rem = parentOffset - foundOffset
      nodes.push(node)
      offset.push(foundOffset)
      index.push(i)
      if (!rem) break
      node = node.child(i)
      if (node.isText) break
      parentOffset = rem - 1
    }
    return new ResolvedPos(pos, nodes, index, offset, parentOffset)
  }
}

let resolveCache = [], resolveCachePos = 0, resolveCacheSize = 6
function resolveCached(doc, pos) {
  for (let i = 0; i < resolveCache.length; i++) {
    let cached = resolveCache[i]
    if (cached.pos == pos && cached.node[0] == doc) return cached
  }
  let result = resolveCache[resolveCachePos] = ResolvedPos.resolve(doc, pos)
  resolveCachePos = (resolveCachePos + 1) % resolveCacheSize
  return result
}
<commit_msg>Start porting browser tests (and fixing bugs they find)<commit_after>import {Fragment} from "./fragment"
import {Mark} from "./mark"
import {ModelError} from "./error"
import {Slice, replace} from "./replace"

const emptyArray = [], emptyAttrs = Object.create(null)

// ;; This class represents a node in the tree that makes up a
// ProseMirror document. So a document is an instance of `Node`, with
// children that are also instances of `Node`.
//
// Nodes are persistent data structures. Instead of changing them, you
// create new ones with the content you want. Old ones keep pointing
// at the old document shape. This is made cheaper by sharing
// structure between the old and new data as much as possible, which a
// tree shape like this (without back pointers) makes easy.
//
// **Never** directly mutate the properties of a `Node` object. See
// [this guide](guide/doc.html) for more information.
export class Node {
  constructor(type, attrs, content, marks) {
    // :: NodeType
    // The type of node that this is.
    this.type = type

    // :: Object
    // An object mapping attribute names to string values. The kind of
    // attributes allowed and required are determined by the node
    // type.
    this.attrs = attrs

    // :: Fragment
    // The node's content.
    this.content = content || Fragment.empty

    // :: [Mark]
    // The marks (things like whether it is emphasized or part of a
    // link) associated with this node.
    this.marks = marks || emptyArray
  }

  // :: number
  // The size of this node. For text node, this is the amount of
  // characters. For leaf nodes, it is one. And for non-leaf nodes, it
  // is the size of the content plus two (the start and end token).
  get nodeSize() { return this.type.contains ? 2 + this.content.size : 1 }

  // :: number
  // The number of children that the node has.
  get childCount() { return this.content.childCount }

  child(index) { return this.content.child(index) }

  maybeChild(index) { return this.content.maybeChild(index) }

  forEach(f) { this.content.forEach(f) }

  // :: string
  // Concatenate all the text nodes found in this fragment and its
  // children.
  get textContent() { return this.content.textContent }

  // :: ?Node
  // Returns this node's first child, or `null` if there are no
  // children.
  get firstChild() { return this.content.firstChild }

  // :: ?Node
  // Returns this node's last child, or `null` if there are no
  // children.
  get lastChild() { return this.content.lastChild }

  // :: (Node) → bool
  // Test whether two nodes represent the same content.
  eq(other) {
    return this == other || (this.sameMarkup(other) && this.content.eq(other.content))
  }

  // :: (Node) → bool
  // Compare the markup (type, attributes, and marks) of this node to
  // those of another. Returns `true` if both have the same markup.
  sameMarkup(other) {
    return this.hasMarkup(other.type, other.attrs, other.marks)
  }

  // :: (NodeType, ?Object, ?[Mark]) → bool
  // Check whether this node's markup correspond to the given type,
  // attributes, and marks.
  hasMarkup(type, attrs, marks) {
    return this.type == type && Node.sameAttrs(this.attrs, attrs || emptyAttrs) && Mark.sameSet(this.marks, marks || emptyArray)
  }

  static sameAttrs(a, b) {
    if (a == b) return true
    for (let prop in a)
      if (a[prop] !== b[prop]) return false
    return true
  }

  // :: (?Fragment) → Node
  // Create a new node with the same markup as this node, containing
  // the given content (or empty, if no content is given).
  copy(content = null) {
    if (content == this.content) return this
    return new this.constructor(this.type, this.attrs, content, this.marks)
  }

  // :: ([Mark]) → Node
  // Create a copy of this node, with the given set of marks instead
  // of the node's own marks.
  mark(marks) {
    return new this.constructor(this.type, this.attrs, this.content, marks)
  }

  // :: (number, ?number) → Node
  // Create a copy of this node with only the content between the
  // given offsets. If `to` is not given, it defaults to the end of
  // the node.
  cut(from, to) {
    if (from == 0 && to == this.content.size) return this
    return this.copy(this.content.cut(from, to))
  }

  slice(from, to = this.content.size) {
    if (from == to) return Slice.empty

    from = this.resolve(from)
    to = this.resolve(to)
    let depth = from.sameDepth(to), start = from.start(depth)
    let content = from.node[depth].content.cut(from.pos - start, to.pos - start)
    return new Slice(content, from.depth - depth, to.depth - depth)
  }

  replace(from, to, slice) {
    return replace(this.resolve(from), this.resolve(to), slice)
  }

  // :: (number) → Node
  // Find the node after the given position.
  nodeAt(pos) {
    for (let node = this;;) {
      let index = findIndex(node.content, pos)
      node = node.child(index)
      if (foundOffset == pos || node.isText) return node
      pos -= foundOffset + 1
    }
  }

  nodeAfter(pos) {
    let index = findIndex(this.content, pos)
    return {node: this.content.maybeChild(index), index, offset: foundOffset}
  }

  nodeBefore(pos) {
    let index = findIndex(this.content, pos)
    if (foundOffset > pos || pos == 0) return {node: this.content.child(index), index, offset: foundOffset}
    let node = this.content.maybeChild(index - 1)
    return {node, index: index - 1, offset: foundOffset - node.childSize}
  }

  // :: (?number, ?number, (node: Node, pos: number, parent: Node))
  // Iterate over all nodes between the given two positions, calling
  // the callback with the node, its position, and its parent
  // node. `from` and `to` may be left off, to denote
  // starting at the start of the node or ending at its end.
  nodesBetween(from, to, f, pos = 0) {
    this.content.nodesBetween(from, to, f, pos, this)
  }

  resolve(pos, cache) {
    return cache === false ? ResolvedPos.resolve(this, pos) : resolveCached(this, pos)
  }

  // :: (number) → [Mark]
  // Get the marks of the node before the given position or, if that
  // position is at the start of a non-empty node, those of the node
  // after it.
  marksAt(pos) {
    let r = this.resolve(pos), top = r.parent, index = r.index[r.depth]
    let leaf = index ? top.child(index - 1) : index < top.childCount ? top.child(index) : null
    return leaf ? leaf.marks : emptyArray
  }

  // :: (?number, ?number, MarkType) → bool
  // Test whether a mark of the given type occurs in this document
  // between the two given positions.
  rangeHasMark(from, to, type) {
    let found = false
    this.nodesBetween(from, to, node => {
      if (type.isInSet(node.marks)) found = true
      return !found
    })
    return found
  }

  // :: bool
  // True when this is a block (non-inline node)
  get isBlock() { return this.type.isBlock }

  // :: bool
  // True when this is a textblock node, a block node with inline
  // content.
  get isTextblock() { return this.type.isTextblock }

  // :: bool
  // True when this is an inline node (a text node or a node that can
  // appear among text).
  get isInline() { return this.type.isInline }

  // :: bool
  // True when this is a text node.
  get isText() { return this.type.isText }

  // :: () → string
  // Return a string representation of this node for debugging
  // purposes.
  toString() {
    let name = this.type.name
    if (this.content.size)
      name += "(" + this.content.toStringInner() + ")"
    return wrapMarks(this.marks, name)
  }

  // :: () → Object
  // Return a JSON-serializeable representation of this node.
  toJSON() {
    let obj = {type: this.type.name}
    for (let _ in this.attrs) {
      obj.attrs = this.attrs
      break
    }
    if (this.content.size)
      obj.content = this.content.toJSON()
    if (this.marks.length)
      obj.marks = this.marks.map(n => n.toJSON())
    return obj
  }

  // :: (Schema, Object) → Node
  // Deserialize a node from its JSON representation.
  static fromJSON(schema, json) {
    let type = schema.nodeType(json.type)
    let content = json.text != null ? json.text : Fragment.fromJSON(schema, json.content)
    return type.create(json.attrs, content, json.marks && json.marks.map(schema.markFromJSON))
  }

  // This is a hack to be able to treat a node object as an iterator result
  get value() { return this }
}

// ;; #forward=Node
export class TextNode extends Node {
  constructor(type, attrs, content, marks) {
    super(type, attrs, null, marks)

    if (!content) throw new ModelError("Empty text nodes are not allowed")

    // :: ?string
    // For text nodes, this contains the node's text content.
    this.text = content
  }

  toString() { return wrapMarks(this.marks, JSON.stringify(this.text)) }

  get textContent() { return this.text }

  get nodeSize() { return this.text.length }

  mark(marks) {
    return new TextNode(this.type, this.attrs, this.text, marks)
  }

  cut(from = 0, to = this.text.length) {
    if (from == 0 && to == this.text.length) return this
    return this.copy(this.text.slice(from, to))
  }

  eq(other) {
    return this.sameMarkup(other) && this.text == other.text
  }

  toJSON() {
    let base = super.toJSON()
    base.text = this.text
    return base
  }
}

function wrapMarks(marks, str) {
  for (let i = marks.length - 1; i >= 0; i--)
    str = marks[i].type.name + "(" + str + ")"
  return str
}

let foundOffset = 0
function findIndex(fragment, pos, round = -1) {
  if (pos == 0) { foundOffset = pos; return 0 }
  if (pos == fragment.size) { foundOffset = pos; return fragment.content.length }
  if (pos > fragment.size || pos < 0) throw new ModelError(`Position ${pos} outside of fragment (${fragment})`)
  for (let i = 0, curPos = 0;; i++) {
    let cur = fragment.child(i), end = curPos + cur.nodeSize
    if (end >= pos) {
      if (end == pos || round > 0) { foundOffset = end; return i + 1 }
      foundOffset = curPos; return i
    }
    curPos = end
  }
}

export class ResolvedPos {
  constructor(pos, node, index, offset, parentOffset) {
    this.pos = pos
    this.node = node
    this.index = index
    this.offset = offset
    this.parentOffset = parentOffset
  }

  get parent() { return this.node[this.depth] }

  get depth() { return this.node.length - 1 }

  get nodeAfter() {
    let parent = this.parent, index = this.index[this.depth]
    if (index == parent.childCount) return null
    let dOff = this.parentOffset - this.offset[this.depth], child = parent.child(index)
    return dOff ? parent.child(index).cut(dOff) : child
  }

  get nodeBefore() {
    let index = this.index[this.depth]
    let dOff = this.parentOffset - this.offset[this.depth]
    if (dOff) return this.parent.child(index).cut(0, dOff)
    return index == 0 ? null : this.parent.child(index - 1)
  }

  sameDepth(other) {
    let depth = 0, max = Math.min(this.depth, other.depth)
    while (depth < max && this.index[depth] == other.index[depth]) ++depth
    return depth
  }

  sameParent(other) {
    return this.pos - this.parentOffset == other.pos - other.parentOffset
  }

  start(depth) {
    let pos = 0
    for (let i = 0; i < depth; i++) pos += this.offset[i] + 1
    return pos
  }

  end(depth) {
    return this.start(depth) + this.node[depth].content.size
  }

  before(depth) { return depth == this.offset.length ? this.pos : this.start(depth) - 1 }
  after(depth) { return depth == this.offset.length ? this.pos : this.end(depth) + 1 }

  toString() {
    let str = ""
    for (let i = 1; i < this.index.length; i++)
      str += (str ? "/" : "") + this.node[i].type.name + "_" + this.index[i - 1]
    return str + ":" + this.parentOffset
  }

  static resolve(doc, pos) {
    if (!(pos >= 0 && pos <= doc.content.size)) throw new ModelError("Position " + pos + " out of range")
    let nodes = [], index = [], offset = [], parentOffset = pos
    for (let node = doc;;) {
      let i = findIndex(node.content, parentOffset)
      let rem = parentOffset - foundOffset
      nodes.push(node)
      offset.push(foundOffset)
      index.push(i)
      if (!rem) break
      node = node.child(i)
      if (node.isText) break
      parentOffset = rem - 1
    }
    return new ResolvedPos(pos, nodes, index, offset, parentOffset)
  }
}

let resolveCache = [], resolveCachePos = 0, resolveCacheSize = 6
function resolveCached(doc, pos) {
  for (let i = 0; i < resolveCache.length; i++) {
    let cached = resolveCache[i]
    if (cached.pos == pos && cached.node[0] == doc) return cached
  }
  let result = resolveCache[resolveCachePos] = ResolvedPos.resolve(doc, pos)
  resolveCachePos = (resolveCachePos + 1) % resolveCacheSize
  return result
}
<|endoftext|>


<commit_before>var commonTools = require('../base/commonTools');
var request = require('request');
require('../base/ExampleTestGlobeParameter');

module.exports = {
    'leaflet_04_interpolationAnalystService_Kriging': function (browser) {
        var type = 'leaflet';
        var exampleName = '04_interpolationAnalystService_Kriging';
        commonTools.openExampleAndLoadMap(browser, type, exampleName);
        /*check elements exist*/
        browser.waitForElementPresent('.leaflet-pane.leaflet-map-pane', 10000);
        browser.waitForElementPresent('.leaflet-pane.leaflet-tile-pane', 10000);
        browser.waitForElementPresent('.leaflet-layer', 10000);
        browser.pause(10000);
        browser.elements('css selector', '.leaflet-layer', function (result) {
            browser.assert.equal(result.value.length, 2, "expect Number of leaflet-layer to be 2, actual is " + result.value.length);
        });
        browser.elements('css selector', '.leaflet-tile-container', function (result) {
            browser.assert.equal(result.value.length, 2, "expect Number of leaflet-tile-container to be 2, actual is " + result.value.length);
        });
        //测试版权点击的正确性
        //commonTools.verifyCopyrightOfLeaflet(browser);

        //删除测试中产生的数据集
        browser.pause(1000, function () {
            var exampleTestResult = "Kriging_Result";
            request.delete(GlobeParameter.dataspatialAnalystURL + exampleTestResult);
        });
        browser.end();
    }
};<commit_msg>Update 04_interpolationAnalystService_KrigingIT.js<commit_after>var commonTools = require('../base/commonTools');
var request = require('request');
require('../base/ExampleTestGlobeParameter');

module.exports = {
    'leaflet_04_interpolationAnalystService_Kriging': function (browser) {
        var type = 'leaflet';
        var exampleName = '04_interpolationAnalystService_Kriging';
        commonTools.openExampleAndLoadMap(browser, type, exampleName);
        /*check elements exist*/
        browser.waitForElementPresent('.leaflet-pane.leaflet-map-pane', 10000);
        browser.waitForElementPresent('.leaflet-pane.leaflet-tile-pane', 10000);
        browser.waitForElementPresent('.leaflet-layer', 10000);
        browser.pause(20000);
        browser.elements('css selector', '.leaflet-layer', function (result) {
            browser.assert.equal(result.value.length, 2, "expect Number of leaflet-layer to be 2, actual is " + result.value.length);
        });
        browser.elements('css selector', '.leaflet-tile-container', function (result) {
            browser.assert.equal(result.value.length, 2, "expect Number of leaflet-tile-container to be 2, actual is " + result.value.length);
        });
        //测试版权点击的正确性
        //commonTools.verifyCopyrightOfLeaflet(browser);

        //删除测试中产生的数据集
        browser.pause(1000, function () {
            var exampleTestResult = "Kriging_Result";
            request.delete(GlobeParameter.dataspatialAnalystURL + exampleTestResult);
        });
        browser.end();
    }
};
<|endoftext|>


<commit_before>define(["js/ui/View", "js/core/List"], function (View, List) {

        return View.inherit('designer.view.SimpleHorizontalColorPickerClass', {

            defaults: {
                color: null,
                colors: List,
                height: 30,
                itemWidth: 20,
                _previewColor: null,
                _showPreview: false,
                _numColors: "{colors.size()}"
            },

            events: ['on:colorSelect'],

            $excludedStyleAttributes: ["color"],

            _onPointerDown: function (e) {

                var self = this;
                if (!this.$moveHandler) {
                    this.$moveHandler = function (e) {
                        e.preventDefault();
                        e.stopPropagation();
                        if (e.changedTouches) {
                            e = e.changedTouches[0];
                        }
                        self._updatePanel(e);

                    };
                }

                if (!this.$upHandler) {
                    this.$upHandler = function (e) {
                        e.preventDefault();
                        e.stopPropagation();

                        self.$pointerDown = false;
                        self.set('color', self.$._previewColor);
                        self.dom(self.$stage.$window).unbindDomEvent('pointermove', self.$moveHandler);
                        self.dom(self.$stage.$window).unbindDomEvent('pointerup', self.$upHandler);
                        self.set('_showPreview', false);
                        self.trigger('on:colorSelect', self.$.color);
                    };
                }

                e.stopPropagation();

                this.$pointerDown = true;
                this.set('_showPreview', true);
                this.$tableWidth = this.$.colorList.$el.offsetWidth;

                this.$columnWidth = this.$tableWidth / this.$._numColors;

                this.dom(this.$stage.$window).bindDomEvent('pointermove', this.$moveHandler);
                this.dom(this.$stage.$window).bindDomEvent('pointerup', this.$upHandler);

                this._updatePanel(e.pointerEvent);
            },
            _updatePanel: function (e) {
                var pos = this.$.colorList.globalToLocal({x: e.pageX, y: e.pageY});

                var numColors = this.$._numColors,
                    colorIndex = Math.floor((pos.x / this.$tableWidth) * numColors);

                this.set({
                    '_previewColor': this.$.colors.at(colorIndex)
                });
            },

            _updateColorPickerZoom: function (colorIndex, itemWidth) {
                this.$.colorPickerZoom.$el.style["left"] = (colorIndex + 0.5) * itemWidth + "px";
            },

            _render_previewColor: function(color){
                if (color && this.$.colors) {
                    var index = this.$.colors.indexOf(color);
                    if (index > -1) {
                        this._updateColorPickerZoom(index, this.$.itemWidth);
                    }
                } else {
                    // TODO:
                }
            },

            _commitColor: function (color) {
                if (color) {
                    this.set('_previewColor', color);
                }
            }

        });
    }
);<commit_msg>DEV-65623 - Dragging the color drag handler to much will cause the color to disapear<commit_after>define(["js/ui/View", "js/core/List"], function (View, List) {

        return View.inherit('designer.view.SimpleHorizontalColorPickerClass', {

            defaults: {
                color: null,
                colors: List,
                height: 30,
                itemWidth: 20,
                _previewColor: null,
                _showPreview: false,
                _numColors: "{colors.size()}"
            },

            events: ['on:colorSelect'],

            $excludedStyleAttributes: ["color"],

            _onPointerDown: function (e) {

                var self = this;
                if (!this.$moveHandler) {
                    this.$moveHandler = function (e) {
                        e.preventDefault();
                        e.stopPropagation();
                        if (e.changedTouches) {
                            e = e.changedTouches[0];
                        }
                        self._updatePanel(e);

                    };
                }

                if (!this.$upHandler) {
                    this.$upHandler = function (e) {
                        e.preventDefault();
                        e.stopPropagation();

                        self.$pointerDown = false;
                        self.set('color', self.$._previewColor);
                        self.dom(self.$stage.$window).unbindDomEvent('pointermove', self.$moveHandler);
                        self.dom(self.$stage.$window).unbindDomEvent('pointerup', self.$upHandler);
                        self.set('_showPreview', false);
                        self.trigger('on:colorSelect', self.$.color);
                    };
                }

                e.stopPropagation();

                this.$pointerDown = true;
                this.set('_showPreview', true);
                this.$tableWidth = this.$.colorList.$el.offsetWidth;

                this.$columnWidth = this.$tableWidth / this.$._numColors;

                this.dom(this.$stage.$window).bindDomEvent('pointermove', this.$moveHandler);
                this.dom(this.$stage.$window).bindDomEvent('pointerup', this.$upHandler);

                this._updatePanel(e.pointerEvent);
            },
            _updatePanel: function (e) {
                var pos = this.$.colorList.globalToLocal({x: e.pageX, y: e.pageY});

                var numColors = this.$._numColors,
                    colorIndex = Math.max(Math.min(this.$._numColors-1,Math.floor((pos.x / this.$tableWidth) * numColors)),0);

                this.set({
                    '_previewColor': this.$.colors.at(colorIndex)
                });
            },

            _updateColorPickerZoom: function (colorIndex, itemWidth) {
                this.$.colorPickerZoom.$el.style["left"] = (colorIndex + 0.5) * itemWidth + "px";
            },

            _render_previewColor: function(color){
                if (color && this.$.colors) {
                    var index = this.$.colors.indexOf(color);
                    if (index > -1) {
                        this._updateColorPickerZoom(index, this.$.itemWidth);
                    }
                } else {
                    // TODO:
                }
            },

            _commitColor: function (color) {
                if (color) {
                    this.set('_previewColor', color);
                }
            }

        });
    }
);<|endoftext|>


<commit_before>const webpack = require('webpack');
const path = require('path');
const utils = require('./lib/utils');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');

module.exports = function (
	{
		jsCompileItem,
		externals,
		commonConfig,
		babelSettings,
		np,
		staticDirectory,
		srcPrefix,
		sfPrefix,
		deployPrefix
	},
	callback
) {
	const contextPath = path.join(staticDirectory, srcPrefix, 'js');
	const staticFilesSourceDir = path.join(staticDirectory, srcPrefix);
	const entryItem = jsCompileItem.path.replace(
		utils.normalizePath(contextPath) + '/',
		''
	);
	const entryPath = './' + entryItem;
	const config = {
		context: contextPath,
		entry: entryPath,
		plugins: [new NodePolyfillPlugin()],
		output: {
			chunkLoadingGlobal: utils.uniqueVal(),
			path: path.join(
				staticDirectory,
				deployPrefix,
				'js',
				utils
					.normalizePath(path.dirname(jsCompileItem.path))
					.replace(utils.normalizePath(contextPath), '')
			),
			filename: 'bundle.js',
			assetModuleFilename: '[name][ext]',
			chunkFilename: '[name].bundle.js',
			publicPath: utils.normalizePath(
				path.join(
					sfPrefix,
					utils.normalizePath(
						path.join(
							deployPrefix,
							'js',
							utils
								.normalizePath(path.dirname(jsCompileItem.path))
								.replace(utils.normalizePath(contextPath), '')
						)
					),
					'/'
				)
			)
		},
		optimization: {
			chunkIds: 'named',
			moduleIds: 'named'
		}
	};

	config.externals = externals;
	config.module = { rules: utils.getRules() };

	utils.extendConfig(config, commonConfig);

	const jsRules = {
		test: /\.(js|jsx)$/,
		use: [{ loader: 'babel-loader', options: babelSettings }],
		exclude: /(node_modules|bower_components)/,
		include: [staticFilesSourceDir]
	};
	if (np) {
		//针对模板工程需要引用工程下前端公用私有npm包
		jsRules.exclude = [
			path.join(__dirname, 'node_modules'),
			/bower_components/
		];
		jsRules.include = [
			path.join(staticDirectory, '../node_modules/@ares'),
			staticDirectory,
			/clientLib/
		];
	}

	const tsRules = {
		test: /\.tsx?$/,
		use: [
			// tsc编译后，再用babel处理
			{ loader: 'babel-loader', options: babelSettings },
			{
				loader: 'ts-loader',
				options: {
					transpileOnly: true, // discard semantic checker & faster builds
					configFile: path.resolve(
						staticDirectory,
						'../tsconfig.json'
					) // 各个项目独立配置   用于 ts server 代码检查
				}
			}
		],
		exclude: /node_modules/
	};

	config.module.rules.push(jsRules);
	config.module.rules.push(tsRules);

	let rebuildCompile = false;
	const compiler = webpack(config);

	compiler.watch(
		{
			aggregateTimeout: 300,
			poll: true
		},
		function (err, stats) {
			if (err) {
				throw err;
			}

			const hasWarnings = stats.hasWarnings();
			const hasErrors = stats.hasErrors();

			if (!(hasWarnings || hasErrors)) {
				if (rebuildCompile) {
					console.log(
						'=== rebuild complete start! ',
						stats.endTime - stats.startTime + 'ms! stats info: ==='
					);
					console.log(stats.toString());
					console.log('=== rebuild complete end! ===');
				} else {
					console.log('=== build success start! stats info: ===');
					console.log(stats.toString());
					console.log('=== build success end! ===');
				}
			} else {
				if (hasWarnings) {
					console.log('=== WARNINGS start! stats info: ===');
					console.log(stats.toString());
					console.log('=== WARNINGS end! ===');
				}

				if (hasErrors) {
					console.log('=== ERRORS start ===');
					console.log(stats.toString());
					console.log('=== ERRORS end ===');
				}
			}

			if (!rebuildCompile) {
				rebuildCompile = true;
				callback();
			}
		}
	);
};
<commit_msg>fix: 输出产物固定为ES5<commit_after>const webpack = require('webpack');
const path = require('path');
const utils = require('./lib/utils');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');

module.exports = function (
	{
		jsCompileItem,
		externals,
		commonConfig,
		babelSettings,
		np,
		staticDirectory,
		srcPrefix,
		sfPrefix,
		deployPrefix
	},
	callback
) {
	const contextPath = path.join(staticDirectory, srcPrefix, 'js');
	const staticFilesSourceDir = path.join(staticDirectory, srcPrefix);
	const entryItem = jsCompileItem.path.replace(
		utils.normalizePath(contextPath) + '/',
		''
	);
	const entryPath = './' + entryItem;
	const config = {
		context: contextPath,
		entry: entryPath,
		plugins: [new NodePolyfillPlugin()],
		output: {
			chunkLoadingGlobal: utils.uniqueVal(),
			path: path.join(
				staticDirectory,
				deployPrefix,
				'js',
				utils
					.normalizePath(path.dirname(jsCompileItem.path))
					.replace(utils.normalizePath(contextPath), '')
			),
			filename: 'bundle.js',
			assetModuleFilename: '[name][ext]',
			chunkFilename: '[name].bundle.js',
			publicPath: utils.normalizePath(
				path.join(
					sfPrefix,
					utils.normalizePath(
						path.join(
							deployPrefix,
							'js',
							utils
								.normalizePath(path.dirname(jsCompileItem.path))
								.replace(utils.normalizePath(contextPath), '')
						)
					),
					'/'
				)
			)
		},
		optimization: {
			chunkIds: 'named',
			moduleIds: 'named'
		},
		target: ['web', 'es5']
	};

	config.externals = externals;
	config.module = { rules: utils.getRules() };

	utils.extendConfig(config, commonConfig);

	const jsRules = {
		test: /\.(js|jsx)$/,
		use: [{ loader: 'babel-loader', options: babelSettings }],
		exclude: /(node_modules|bower_components)/,
		include: [staticFilesSourceDir]
	};
	if (np) {
		//针对模板工程需要引用工程下前端公用私有npm包
		jsRules.exclude = [
			path.join(__dirname, 'node_modules'),
			/bower_components/
		];
		jsRules.include = [
			path.join(staticDirectory, '../node_modules/@ares'),
			staticDirectory,
			/clientLib/
		];
	}

	const tsRules = {
		test: /\.tsx?$/,
		use: [
			// tsc编译后，再用babel处理
			{ loader: 'babel-loader', options: babelSettings },
			{
				loader: 'ts-loader',
				options: {
					transpileOnly: true, // discard semantic checker & faster builds
					configFile: path.resolve(
						staticDirectory,
						'../tsconfig.json'
					) // 各个项目独立配置   用于 ts server 代码检查
				}
			}
		],
		exclude: /node_modules/
	};

	config.module.rules.push(jsRules);
	config.module.rules.push(tsRules);

	let rebuildCompile = false;
	const compiler = webpack(config);

	compiler.watch(
		{
			aggregateTimeout: 300,
			poll: true
		},
		function (err, stats) {
			if (err) {
				throw err;
			}

			const hasWarnings = stats.hasWarnings();
			const hasErrors = stats.hasErrors();

			if (!(hasWarnings || hasErrors)) {
				if (rebuildCompile) {
					console.log(
						'=== rebuild complete start! ',
						stats.endTime - stats.startTime + 'ms! stats info: ==='
					);
					console.log(stats.toString());
					console.log('=== rebuild complete end! ===');
				} else {
					console.log('=== build success start! stats info: ===');
					console.log(stats.toString());
					console.log('=== build success end! ===');
				}
			} else {
				if (hasWarnings) {
					console.log('=== WARNINGS start! stats info: ===');
					console.log(stats.toString());
					console.log('=== WARNINGS end! ===');
				}

				if (hasErrors) {
					console.log('=== ERRORS start ===');
					console.log(stats.toString());
					console.log('=== ERRORS end ===');
				}
			}

			if (!rebuildCompile) {
				rebuildCompile = true;
				callback();
			}
		}
	);
};
<|endoftext|>


<commit_before>/**
 * @flow
 */

import styled from 'styled-components'
import { colors, animations } from '../theme'

const Loader = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  background: ${colors.primary};
  animation: ${animations.load} 1s infinite ease-in-out;
  width: 1em;
  height: 4em;
  color: ${colors.primary};
  text-indent: -9999em;
  margin: 88px auto;
  position: relative;
  font-size: 11px;
  transform: translateZ(0);
  animation-delay: -0.16s;

  &:before,
  &:after {
    background: ${colors.primary};
    animation: ${animations.load} 1s infinite ease-in-out;
    width: 1em;
    height: 4em;
    position: absolute;
    top: 0;
    content: '';
  }

  &:before {
    left: -1.5em;
    animation-delay: -0.32s;
  }

  &:after {
    left: 1.5em;
  }
`

export default Loader
<commit_msg>make loader check for error and delay<commit_after>/**
 * @flow
 */

import styled from 'styled-components'
import { colors, animations } from '../theme'

const Bars = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  background: ${colors.primary};
  animation: ${animations.load} 1s infinite ease-in-out;
  width: 1em;
  height: 4em;
  color: ${colors.primary};
  text-indent: -9999em;
  margin: 88px auto;
  position: relative;
  font-size: 11px;
  transform: translateZ(0);
  animation-delay: -0.16s;

  &:before,
  &:after {
    background: ${colors.primary};
    animation: ${animations.load} 1s infinite ease-in-out;
    width: 1em;
    height: 4em;
    position: absolute;
    top: 0;
    content: '';
  }

  &:before {
    left: -1.5em;
    animation-delay: -0.32s;
  }

  &:after {
    left: 1.5em;
  }
`

type Props = {
  error?: boolean,
  timedOut?: boolean,
  pastDelay?: number
}

function Loader({ error, timedOut, pastDelay }: Props) {
  if (error) {
    return
Error!

  } else if (timedOut) {
    return
Taking a long time...

  } else if (pastDelay) {
    return <Bars />
  } else {
    return null
  }
}

export default Loader
<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular
        .module('signup')
        .controller('SignupController', SignupController);

    SignupController.$inject = ['$scope'];

    function SignupController($scope) {

    }
})();<commit_msg>example controller<commit_after>(function () {
    'use strict';

    angular
        .module('signup')
        .controller('SignupController', SignupController);

    SignupController.$inject = ['$scope'];

    function SignupController($scope) {
        // Attach functions to the controller here.
		var vm = this;
		vm.loadData = _loadData;
		vm.editData = _editData;
		vm.deleteData = _deleteData;
		vm.createData = _createData;
		vm.saveData = _saveData;

		// Any logic that needs to run when the controller loads should be placed here.
		vm.loadData();

		// Define functions here.
		function _loadData(id){
			$scope.getData = dataService.getData();
			$scope.getData.then(
					function(data){
						$scope.data = data;
					},
					function(data, status){
					}
			);
		};

		function _editData(id) {
			dataService.getData(id);
			$location.path('/productdetail/' + id);
		};

		function _deleteData(id, name) {
			dataService.deleteData(id, name);
			$scope.Data = dataService.getData();
			$route.reload();
			$location.path('/products');
		};

		function _createData() {
			$route.reload();
			$location.path('/productcreate');
		};

		function _saveData() {
			$scope.data = {};
			$scope.data.name = $scope.name;
			$scope.data.description = $scope.description;
			dataService.createData($scope.data);
			$location.path('/products');
			$route.reload();
		};

    }
})();<|endoftext|>


<commit_before>const DrawCard = require('../../drawcard.js');

class SeptonMeribald extends DrawCard {
    setupCardAbilities(ability) {
        this.action({
            title: 'Stand up to 3 characters',
            cost: ability.costs.kneelSelf(),
            target: {
                mode: 'upTo',
                numCards: 3,
                cardCondition: card => card.location === 'play area' && card.getType() === 'character' &&
                                       card.getPrintedStrength() <= 1 && card.kneeled,
                gameAction: 'stand'
            },
            handler: context => {
                for(let card of context.target) {
                    card.controller.standCard(card);
                }
                this.game.addMessage('{0} kneels {1} to stand {2}', context.player, this, context.target);
            }
        });
    }
}

SeptonMeribald.code = '10040';

module.exports = SeptonMeribald;
<commit_msg>Add missing limit for Septon Meribald (#2547)<commit_after>const DrawCard = require('../../drawcard');

class SeptonMeribald extends DrawCard {
    setupCardAbilities(ability) {
        this.action({
            title: 'Stand up to 3 characters',
            cost: ability.costs.kneelSelf(),
            target: {
                mode: 'upTo',
                numCards: 3,
                cardCondition: card => card.location === 'play area' && card.getType() === 'character' &&
                                       card.getPrintedStrength() <= 1 && card.kneeled,
                gameAction: 'stand'
            },
            message: '{player} kneels {source} to stand {target}',
            handler: context => {
                for(let card of context.target) {
                    card.controller.standCard(card);
                }
            },
            limit: ability.limit.perRound(1)
        });
    }
}

SeptonMeribald.code = '10040';

module.exports = SeptonMeribald;
<|endoftext|>


<commit_before>const DrawCard = require('../../drawcard.js');

class WyllaManderly extends DrawCard {
    setupCardAbilities(ability) {
        this.reaction({
            when: {
                onCharacterKilled: event => event.card.controller === this.controller,
                onSacrificed: event => event.card.controller === this.controller && event.card.getType() === 'character'
            },
            target: {
                cardCondition: (card, context) => card.location === 'discard pile' && card.controller === this.controller && card !== context.event.card
            },
            message: '{player} uses {source} to move {target} to the bottom of their deck',
            handler: context => {
                this.game.placeOnBottomOfDeck(context.target);
            },
            limit: ability.limit.perPhase(1)
        });
    }
}

WyllaManderly.code = '15035';

module.exports = WyllaManderly;
<commit_msg>Trigger Wylla based on original card state (#2966)<commit_after>const DrawCard = require('../../drawcard.js');

class WyllaManderly extends DrawCard {
    setupCardAbilities(ability) {
        this.reaction({
            when: {
                onCharacterKilled: event => event.cardStateWhenKilled.controller === this.controller,
                onSacrificed: event => event.cardStateWhenSacrificed.controller === this.controller && event.cardStateWhenSacrificed.getType() === 'character'
            },
            target: {
                cardCondition: (card, context) => card.location === 'discard pile' && card.controller === this.controller && card !== context.event.card
            },
            message: '{player} uses {source} to move {target} to the bottom of their deck',
            handler: context => {
                this.game.placeOnBottomOfDeck(context.target);
            },
            limit: ability.limit.perPhase(1)
        });
    }
}

WyllaManderly.code = '15035';

module.exports = WyllaManderly;
<|endoftext|>


<commit_before>import request from 'utils/request';
import moment from 'moment';

const REQUEST_URL = process.env.GFW_API;
const GLAD_ISO_DATASET = process.env.GLAD_ISO_DATASET;
const GLAD_ADM1_DATASET = process.env.GLAD_ADM1_DATASET;
const GLAD_ADM2_DATASET = process.env.GLAD_ADM2_DATASET;
const FIRES_ISO_DATASET = process.env.FIRES_ISO_DATASET;
const FIRES_ADM1_DATASET = process.env.FIRES_ADM1_DATASET;
const FIRES_ADM2_DATASET = process.env.FIRES_ADM2_DATASET;

const QUERIES = {
  gladIntersectionAlerts:
    "SELECT iso, adm1, adm2, week, year, alerts as count, area_ha, polyname FROM data WHERE {location} AND polyname = '{polyname}'",
  firesIntersectionAlerts:
    "SELECT iso, adm1, adm2, week, year, alerts as count, area_ha, polyname FROM data WHERE {location} AND polyname = '{polyname}' AND fire_type = '{dataset}'",
  firesGrouped:
    "SELECT iso, adm1, adm2, week, year, alerts as count, area_ha, polyname FROM data WHERE {location} AND polyname = '{polyname}' AND fire_type = '{dataset}'",
  viirsAlerts: '{location}?group=true&period={period}&thresh=0',
  firesStats:
    '{location}?period={period}&aggregate_by=day&aggregate_values=true&fire_type=viirs',
  alertsLatest:
    'SELECT year, week FROM data GROUP BY year, week ORDER BY year DESC, week DESC LIMIT 1'
};

const getLocation = (adm0, adm1, adm2) =>
  `iso = '${adm0}'${adm1 ? ` AND adm1 = ${adm1}` : ''}${
    adm2 ? ` AND adm2 = ${adm2}` : ''
  }`;

export const fetchGladAlerts = ({ adm0, adm1, adm2 }) => {
  let glad_summary_table = GLAD_ISO_DATASET;
  if (adm2) {
    glad_summary_table = GLAD_ADM2_DATASET;
  } else if (adm1) {
    glad_summary_table = GLAD_ADM1_DATASET;
  }
  const url = `${REQUEST_URL}/query/${glad_summary_table}?sql=${
    QUERIES.gladIntersectionAlerts
  }`
    .replace('{location}', getLocation(adm0, adm1, adm2))
    .replace('{polyname}', 'admin');
  return request.get(url, 3600, 'gladRequest');
};

export const fetchFiresAlerts = ({ adm0, adm1, adm2, dataset }) => {
  let fires_summary_table = FIRES_ISO_DATASET;
  if (adm2) {
    fires_summary_table = FIRES_ADM2_DATASET;
  } else if (adm1) {
    fires_summary_table = FIRES_ADM1_DATASET;
  }
  const url = `${REQUEST_URL}/query/${fires_summary_table}?sql=${
    QUERIES.firesIntersectionAlerts
  }`
    .replace('{location}', getLocation(adm0, adm1, adm2))
    .replace('{polyname}', 'admin')
    .replace('{dataset}', dataset);
  return request.get(url, 3600, 'firesRequest');
};

export const fetchFiresAlertsGrouped = ({ adm0, adm1, adm2, dataset }) => {
  let fires_summary_table = FIRES_ADM1_DATASET;
  if (adm1) {
    fires_summary_table = FIRES_ADM2_DATASET;
  }
  const url = `${REQUEST_URL}/query/${fires_summary_table}?sql=${
    QUERIES.firesIntersectionAlerts
  }`
    .replace('{location}', getLocation(adm0, adm1, adm2))
    .replace('{polyname}', 'admin')
    .replace('{dataset}', dataset);
  return request.get(url, 3600, 'firesRequest');
};

export const fetchFiresLatest = ({ adm1, adm2 }) => {
  let fires_summary_table = FIRES_ISO_DATASET;
  if (adm2) {
    fires_summary_table = FIRES_ADM2_DATASET;
  } else if (adm1) {
    fires_summary_table = FIRES_ADM1_DATASET;
  }

  const url = `${REQUEST_URL}/query/${fires_summary_table}?sql=${
    QUERIES.alertsLatest
  }`;
  return request
    .get(url, 3600, 'firesRequest')
    .then(response => {
      const { week, year } = response.data.data[0];
      const date = moment()
        .year(year)
        .day('Monday')
        .week(week)
        .format('YYYY-MM-DD');

      return {
        attributes: { updatedAt: date },
        id: null,
        type: 'glad-alerts'
      };
    })
    .catch(error => {
      console.error('Error in firesRequest:', error);
      return new Promise(resolve =>
        resolve({
          data: {
            data: {
              attributes: { updatedAt: lastFriday },
              id: null,
              type: 'fires-alerts'
            }
          }
        })
      );
    });
};

export const fetchLatestDate = url =>
  request.get(url, 3600, 'gladRequest').catch(error => {
    console.error('Error in latest request:', error);
    return new Promise(resolve =>
      resolve({
        data: {
          data: [
            {
              attributes: { date: lastFriday }
            }
          ]
        }
      })
    );
  });

// Latest Dates for Alerts
const lastFriday = moment()
  .day(-2)
  .format('YYYY-MM-DD');

export const fetchGLADLatest = () => {
  const url = `${process.env.GFW_API}/glad-alerts/latest`;
  return request
    .get(url)
    .then(response => {
      const { date } = response.data.data[0].attributes;

      return {
        attributes: { updatedAt: date },
        id: null,
        type: 'glad-alerts'
      };
    })
    .catch(error => {
      console.error('Error in gladRequest', error);
      return new Promise(resolve =>
        resolve({
          attributes: { updatedAt: lastFriday },
          id: null,
          type: 'glad-alerts'
        })
      );
    });
};
<commit_msg>allow glad alerts grouped from old requests<commit_after>import request from 'utils/request';
import moment from 'moment';

const REQUEST_URL = process.env.GFW_API;
const GLAD_ISO_DATASET = process.env.GLAD_ISO_DATASET;
const GLAD_ADM1_DATASET = process.env.GLAD_ADM1_DATASET;
const GLAD_ADM2_DATASET = process.env.GLAD_ADM2_DATASET;
const FIRES_ISO_DATASET = process.env.FIRES_ISO_DATASET;
const FIRES_ADM1_DATASET = process.env.FIRES_ADM1_DATASET;
const FIRES_ADM2_DATASET = process.env.FIRES_ADM2_DATASET;

const QUERIES = {
  gladIntersectionAlerts:
    "SELECT iso, adm1, adm2, week, year, alerts as count, area_ha, polyname FROM data WHERE {location} AND polyname = '{polyname}'",
  firesIntersectionAlerts:
    "SELECT iso, adm1, adm2, week, year, alerts as count, area_ha, polyname FROM data WHERE {location} AND polyname = '{polyname}' AND fire_type = '{dataset}'",
  firesGrouped:
    "SELECT iso, adm1, adm2, week, year, alerts as count, area_ha, polyname FROM data WHERE {location} AND polyname = '{polyname}' AND fire_type = '{dataset}'",
  viirsAlerts: '{location}?group=true&period={period}&thresh=0',
  firesStats:
    '{location}?period={period}&aggregate_by=day&aggregate_values=true&fire_type=viirs',
  alertsLatest:
    'SELECT year, week FROM data GROUP BY year, week ORDER BY year DESC, week DESC LIMIT 1'
};

const getLocation = (adm0, adm1, adm2) =>
  `iso = '${adm0}'${adm1 ? ` AND adm1 = ${adm1}` : ''}${
    adm2 ? ` AND adm2 = ${adm2}` : ''
  }`;

export const fetchGladAlerts = ({ adm0, adm1, adm2, grouped }) => {
  let glad_summary_table = GLAD_ISO_DATASET;
  if ((adm0 && grouped) || adm1) {
    glad_summary_table = GLAD_ADM1_DATASET;
  }
  if ((adm1 && grouped) || adm2) {
    glad_summary_table = GLAD_ADM2_DATASET;
  }
  const url = `${REQUEST_URL}/query/${glad_summary_table}?sql=${
    QUERIES.gladIntersectionAlerts
  }`
    .replace('{location}', getLocation(adm0, adm1, adm2))
    .replace('{polyname}', 'admin');
  return request.get(url, 3600, 'gladRequest');
};

export const fetchFiresAlerts = ({ adm0, adm1, adm2, dataset }) => {
  let fires_summary_table = FIRES_ISO_DATASET;
  if (adm2) {
    fires_summary_table = FIRES_ADM2_DATASET;
  } else if (adm1) {
    fires_summary_table = FIRES_ADM1_DATASET;
  }
  const url = `${REQUEST_URL}/query/${fires_summary_table}?sql=${
    QUERIES.firesIntersectionAlerts
  }`
    .replace('{location}', getLocation(adm0, adm1, adm2))
    .replace('{polyname}', 'admin')
    .replace('{dataset}', dataset);
  return request.get(url, 3600, 'firesRequest');
};

export const fetchFiresAlertsGrouped = ({ adm0, adm1, adm2, dataset }) => {
  let fires_summary_table = FIRES_ADM1_DATASET;
  if (adm1) {
    fires_summary_table = FIRES_ADM2_DATASET;
  }
  const url = `${REQUEST_URL}/query/${fires_summary_table}?sql=${
    QUERIES.firesIntersectionAlerts
  }`
    .replace('{location}', getLocation(adm0, adm1, adm2))
    .replace('{polyname}', 'admin')
    .replace('{dataset}', dataset);
  return request.get(url, 3600, 'firesRequest');
};

export const fetchFiresLatest = ({ adm1, adm2 }) => {
  let fires_summary_table = FIRES_ISO_DATASET;
  if (adm2) {
    fires_summary_table = FIRES_ADM2_DATASET;
  } else if (adm1) {
    fires_summary_table = FIRES_ADM1_DATASET;
  }

  const url = `${REQUEST_URL}/query/${fires_summary_table}?sql=${
    QUERIES.alertsLatest
  }`;
  return request
    .get(url, 3600, 'firesRequest')
    .then(response => {
      const { week, year } = response.data.data[0];
      const date = moment()
        .year(year)
        .day('Monday')
        .week(week)
        .format('YYYY-MM-DD');

      return {
        attributes: { updatedAt: date },
        id: null,
        type: 'glad-alerts'
      };
    })
    .catch(error => {
      console.error('Error in firesRequest:', error);
      return new Promise(resolve =>
        resolve({
          data: {
            data: {
              attributes: { updatedAt: lastFriday },
              id: null,
              type: 'fires-alerts'
            }
          }
        })
      );
    });
};

export const fetchLatestDate = url =>
  request.get(url, 3600, 'gladRequest').catch(error => {
    console.error('Error in latest request:', error);
    return new Promise(resolve =>
      resolve({
        data: {
          data: [
            {
              attributes: { date: lastFriday }
            }
          ]
        }
      })
    );
  });

// Latest Dates for Alerts
const lastFriday = moment()
  .day(-2)
  .format('YYYY-MM-DD');

export const fetchGLADLatest = () => {
  const url = `${process.env.GFW_API}/glad-alerts/latest`;
  return request
    .get(url)
    .then(response => {
      const { date } = response.data.data[0].attributes;

      return {
        attributes: { updatedAt: date },
        id: null,
        type: 'glad-alerts'
      };
    })
    .catch(error => {
      console.error('Error in gladRequest', error);
      return new Promise(resolve =>
        resolve({
          attributes: { updatedAt: lastFriday },
          id: null,
          type: 'glad-alerts'
        })
      );
    });
};
<|endoftext|>


<commit_before>/*jslint evil: false, bitwise:false, strict: true, undef: true, white:true, onevar:false, browser:true, plusplus:false */
/*global jQuery, console, Capture:true, window */
/**
 *  Capture.js
 *  v0.9.2
 *  Copyright 2011 Adam Craven
 *  Freely distributable under the MIT License.
 *  https://github.com/AdmCrvn/Capture
 */
(function ($) {
    "use strict";

    var bind = Function.prototype.bind;
    var slice = Array.prototype.slice;
    var toString = Object.prototype.toString;

    // All properties with prefix of 'on' are treated as event delegates
    var eventMethodPrefix = /^on(.+)/;

    /**
     *  Native ECMAScript 5 bind if supported or passes to jQuery proxy.
     *  @param  {function}  fn    Function to bind.
     *  @param  {object}  scope Scope in which to bind the function.
     */
    function bindScope(fn, scope) {
        if (bind && fn.bind === bind) {
            return bind.apply(fn, slice.call(arguments, 1));
        }
        return $.proxy(fn, scope);
    }

    /**
     *  logs errors to console
     *  @param {string} error The error message to log
     */
    function logError(error) {
        if (console && console.error) {
            console.error(error);
        } else {
            throw new Error(error);
        }
    }

    /**
     *  Validates the view when captured, checking for basic errors
     */
    function validate(view) {
        if (!view) {
            return logError('Capture.js; No view passed. You must attach a view object or constructor. e.g. $("el").capture(view)');
        }

        if (toString.call(view) !== '[object Object]') {
            return logError('Capture.js; Invalid view type. The view must be an object or a constructor.');
        }
    }

    /**
     *  Adds listeners to the view.
     *  By default all methods that have the valid prefix 'on', their selectors will be bound
     *
     *  @example view controller method the function will bind event delegates with
     *    onclick : {
     *      '.selector' : function(e) {
     *
     *      }
     *    }
     */
    function addEventListeners(view) {
        var eventHolder;
        var eventType;
        var method;
        var selector;
        var listenerElement = view.element;
        var handlerFn;

        // Loop through all methods looking for event delegates
        for (method in view) {
            if (view.hasOwnProperty(method) && method.match(eventMethodPrefix) && typeof view[method] === "object") {
                eventHolder = view[method];
                // e.g. click
                eventType = method.match(eventMethodPrefix)[1];

                for (selector in eventHolder) {
                    if (eventHolder.hasOwnProperty(selector)) {
                        handlerFn = eventHolder[selector];
                        // If the selector is the same as the listenerElement
                        // Or using special 'element' property
                        if (listenerElement.is(selector) || handlerFn === eventHolder.element) {
                            // Attach event to current element
                            listenerElement.bind(eventType, bindScope(handlerFn, view));
                        } else {
                            // Bind event delegate to the element. Setting the context to the view
                            listenerElement.delegate(selector, eventType, bindScope(handlerFn, view));
                        }
                    }
                }
            }
        }
    }

    /**
     *  Connect the view controller and element together
     *  @returns {object} New instance of an initalised view controller
     */
    function connectView(element, view, optionalArgs) {
        view.element = element;

        if (view.init && view.hasOwnProperty('init')) {
            if (optionalArgs) {
                view.init.apply(view, optionalArgs);
            } else {
                view.init.call(view);
            }
        }

        addEventListeners(view);

        return view;
    }

    if (typeof Capture !== "undefined") {
        return logError('Capture is already defined');
    }

    /**
     *  Capture loosely attaches a view to an element via event delegates.
     *  @param  {object}  view  The View to be initialised from on the element
     *  @public
     */
    $.fn.capture = function (view) {
        if (this.length === 0 || !this.each) {
            return;
        }

        var args = [this].concat(slice.call(arguments, 0));

        return window.Capture.apply(null, args);
    };

    /**
     * View Controller base constructor.
     * When an object is wrapped with Capture.view, it inherits methods directly
     * from this constructor
     * @constructor
     */
    function ViewBaseClass() {}

    ViewBaseClass.prototype = {
        // Remove all event handlers
        removeEventListeners : function () {
            this.element.unbind();
            this.element.undelegate();
        },
        // Reattach all event listners
        reattachEventListeners : function () {
            this.removeEventListeners();
            addEventListeners(this);
        }
    };

    /**
     * Allows for object-oriented style calls.
     *
     * @param {String}          element Element string to be parsed
     * @param {Object/Function} View    To be instantiated as a capture view
     *
     * @example
     *  Capture('#gallery', view)
     */
    window.Capture = function (element, view) {
        // If a constructor has been passed (A function wrapped by capture.view)
        view = (typeof view === "function") ? view() : view;

        validate(view);

        var optionalArgs = (arguments.length > 2) ? slice.call(arguments, 2) : undefined;

        return connectView($(element).eq(0), view, optionalArgs);
    };

    /**
     * This wraps the view until called by capture.
     * It instantiates the view controller and sets inheritance to view base class.
     *
     * @param {Object/Function} View    To be instantiated as a capture view
     *
     * @returns {function} The view to be initalised by capture
     */
    Capture.view = function (View) {
        return function () {
            // If a constructor has been passed
            var view = (typeof View === "function") ? new View() : View;

            // Deep copy view into instance of ViewBaseClass
            return $.extend(true, new ViewBaseClass(), view);
        };
    };

})(jQuery);
<commit_msg>Capture() call will create an exception if element doesn't exist.<commit_after>/*jslint evil: false, bitwise:false, strict: true, undef: true, white:true, onevar:false, browser:true, plusplus:false */
/*global jQuery, console, Capture:true, window */
/**
 *  Capture.js
 *  v0.9.2
 *  Copyright 2011 Adam Craven
 *  Freely distributable under the MIT License.
 *  https://github.com/AdmCrvn/Capture
 */
(function ($) {
    "use strict";

    var bind = Function.prototype.bind;
    var slice = Array.prototype.slice;
    var toString = Object.prototype.toString;

    // All properties with prefix of 'on' are treated as event delegates
    var eventMethodPrefix = /^on(.+)/;

    /**
     *  Native ECMAScript 5 bind if supported or passes to jQuery proxy.
     *  @param  {function}  fn    Function to bind.
     *  @param  {object}  scope Scope in which to bind the function.
     */
    function bindScope(fn, scope) {
        if (bind && fn.bind === bind) {
            return bind.apply(fn, slice.call(arguments, 1));
        }
        return $.proxy(fn, scope);
    }

    /**
     *  logs errors to console
     *  @param {string} error The error message to log
     */
    function logError(error) {
        if (console && console.error) {
            console.error(error);
        } else {
            throw new Error(error);
        }
    }

    /**
     *  Validates the view when captured, checking for basic errors
     */
    function validate(view) {
        if (!view) {
            return logError('Capture.js; No view passed. You must attach a view object or constructor. e.g. $("el").capture(view)');
        }

        if (toString.call(view) !== '[object Object]') {
            return logError('Capture.js; Invalid view type. The view must be an object or a constructor.');
        }
    }

    /**
     *  Adds listeners to the view.
     *  By default all methods that have the valid prefix 'on', their selectors will be bound
     *
     *  @example view controller method the function will bind event delegates with
     *    onclick : {
     *      '.selector' : function(e) {
     *
     *      }
     *    }
     */
    function addEventListeners(view) {
        var eventHolder;
        var eventType;
        var method;
        var selector;
        var listenerElement = view.element;
        var handlerFn;

        // Loop through all methods looking for event delegates
        for (method in view) {
            if (view.hasOwnProperty(method) && method.match(eventMethodPrefix) && typeof view[method] === "object") {
                eventHolder = view[method];
                // e.g. click
                eventType = method.match(eventMethodPrefix)[1];

                for (selector in eventHolder) {
                    if (eventHolder.hasOwnProperty(selector)) {
                        handlerFn = eventHolder[selector];
                        // If the selector is the same as the listenerElement
                        // Or using special 'element' property
                        if (listenerElement.is(selector) || handlerFn === eventHolder.element) {
                            // Attach event to current element
                            listenerElement.bind(eventType, bindScope(handlerFn, view));
                        } else {
                            // Bind event delegate to the element. Setting the context to the view
                            listenerElement.delegate(selector, eventType, bindScope(handlerFn, view));
                        }
                    }
                }
            }
        }
    }

    /**
     *  Connect the view controller and element together
     *  @returns {object} New instance of an initalised view controller
     */
    function connectView(element, view, optionalArgs) {
        view.element = element;

        if (view.init && view.hasOwnProperty('init')) {
            if (optionalArgs) {
                view.init.apply(view, optionalArgs);
            } else {
                view.init.call(view);
            }
        }

        addEventListeners(view);

        return view;
    }

    if (typeof Capture !== "undefined") {
        return logError('Capture is already defined');
    }

    /**
     *  Capture loosely attaches a view to an element via event delegates.
     *  @param  {object}  view  The View to be initialised from on the element
     *  @public
     */
    $.fn.capture = function (view) {
        if (this.length === 0 || !this.each) {
            return;
        }

        var args = [this].concat(slice.call(arguments, 0));

        return window.Capture.apply(null, args);
    };

    /**
     * View Controller base constructor.
     * When an object is wrapped with Capture.view, it inherits methods directly
     * from this constructor
     * @constructor
     */
    function ViewBaseClass() {}

    ViewBaseClass.prototype = {
        // Remove all event handlers
        removeEventListeners : function () {
            this.element.unbind();
            this.element.undelegate();
        },
        // Reattach all event listners
        reattachEventListeners : function () {
            this.removeEventListeners();
            addEventListeners(this);
        }
    };

    /**
     * Allows for object-oriented style calls.
     *
     * @param {String}          element Element string to be parsed
     * @param {Object/Function} View    To be instantiated as a capture view
     *
     * @example
     *  Capture('#gallery', view)
     */
    window.Capture = function (element, view) {
        // If a constructor has been passed (A function wrapped by capture.view)
        view = (typeof view === "function") ? view() : view;

        element = $(element);

        if (element.length === 0 || !element.each) {
            return logError('Capture.js; No element found. You must attach the view to an element');
        }

        validate(view);

        return connectView(element.eq(0), view, (arguments.length > 2) ? slice.call(arguments, 2) : undefined);
    };

    /**
     * This wraps the view until called by capture.
     * It instantiates the view controller and sets inheritance to view base class.
     *
     * @param {Object/Function} View    To be instantiated as a capture view
     *
     * @returns {function} The view to be initalised by capture
     */
    Capture.view = function (View) {
        return function () {
            // If a constructor has been passed
            var view = (typeof View === "function") ? new View() : View;

            // Deep copy view into instance of ViewBaseClass
            return $.extend(true, new ViewBaseClass(), view);
        };
    };

})(jQuery);
<|endoftext|>


<commit_before>'use strict';
angular.module('arethusa.core').controller('MainCtrl', [
  '$scope',
  '$injector',
  'configurator',
  'state',
  'documentStore',
  function ($scope, $injector, configurator, state, documentStore) {
    documentStore.reset();
    $scope.aU = arethusaUtil;
    $scope.debug = false;
    $scope.toggleDebugMode = function () {
      $scope.debug = !$scope.debug;
    };
    $scope.save = function() {
      // This is NOT the right place for this code, retrievers are handled
      // in the state, but that also is not good - we'll change this later.
      console.log($scope.persisters);

      console.log('save');
    };

    var conf = configurator.configurationFor('main');

    var partitionPlugins = function (plugins) {
      $scope.mainPlugins = [];
      $scope.subPlugins = [];
      $scope.pluginsWithMenu = [];
      angular.forEach(plugins, function (plugin, name) {
        $scope.registerPlugin(plugin);
      });
    };

    $scope.retrievePlugins = function (plugins) {
      var obj = {};
      angular.forEach(plugins, function (name, i) {
        obj[name] = $scope.retrievePlugin(name);
      });
      return obj;
    };

    $scope.registerPlugin = function (plugin) {
      $scope.pushPlugin(plugin);
      $scope.registerListener(plugin);
    };

    $scope.retrievePlugin = function (name) {
      var pluginConf = configurator.configurationFor(name);
      if (pluginConf.external) {
        // We copy because this object will be extended once the plugin
        // is really initialized through the inclusion of its template
        // by the plugin directive.
        return angular.copy(pluginConf);
      } else {
        return $injector.get(name);
      }
    };

    function hasView(plugin) {
      return !(!plugin.template || plugin.noView);
    }

    $scope.pushPlugin = function (plugin) {
      if (hasView(plugin)) {
        if (plugin.main) {
          $scope.mainPlugins.push(plugin);
        } else {
          $scope.subPlugins.push(plugin);
        }
      }
      if (plugin.contextMenu) {
        $scope.pluginsWithMenu.push(plugin);
      }
    };

    // Working on the assumptions that plugins will generally be grouped
    // in something like a tabset - impossible to show them all at the same
    // time, we expose some variables and functions to get info about their
    // visibility.
    // Templates can use this to implement ngIf, which removes elements
    // temporarily from the DOM.
    // Very useful - example use case:
    //   We have the morph plugin included in our configuration, but have
    //   selected a different tab at the moment, i.e. we are displaying
    //   the view of another plugin. The morph view wouldn't have to be
    //   rendered in the background!
    //
    // As we will mostly likely listen to click events for this, we have
    // to declare a first visible plugin in the init() function of this
    // controller, otherwise a user wouldn't be able to see something when
    // he first loads the page.
    //
    // Some plugins might have to rely on working in the background too.
    // Generally this shouldn't be the case, because all business logic
    // should be out of the DOM anyway. If a plugin still needs this, it can
    // do so by setting its alwaysActiveproperty to true.
    $scope.activePlugin = '';

    $scope.declareActive = function (name) {
      $scope.activePlugin = name;
    };

    $scope.declareFirstPluginActive = function () {
      $scope.declareActive($scope.subPlugins[0].name);
    };

    $scope.isActive = function (plugin) {
      return plugin.name === $scope.activePlugin && !plugin.alwaysActive;
    };

    // This is a really really bad solution right now. Using the controller
    // to insert stuff into the state object is not good. Can only stay as
    // a temporary prototype solution.
    $scope.registerListener = function (plugin) {
      if (plugin.listener) {
        state.registerListener(plugin);
      }
    };

    $scope.state = state;
    $scope.template = conf.template;

    // The application has to fulfil a specific load order.
    // The MainCtrl starts his work only when the configurator has received
    // its main configuration file (handled by the MAIN_ROUTE constant).
    //
    // The configurator might need some time to bring in external additional
    // files - asynchronously. We don't want the application to continue until
    // all configuration files are loaded.
    // We therefore wait for an event broadcast by the configurator to get a
    // green light.
    // Loading all state retrievers is another asynchronous step we want to see
    // completed before going on.
    // State broadcasts another event when it is done, after that the MainCtrl
    // can finally start to initialize itself and all all participating plugins.
    //
    // Everytime the state is reloaded, we need to reinitialize plugins (if they
    // declare to do so by implementing an init() function- it's not a necessity),
    // so that they can update their internal state after the main state tokens
    // have changed. There is no need to reinit the MainCtrl - the arethusaLoaded
    // variable takes care of this.
    // However if we reload a configuration, MainCtrl needs to be re-initialized
    // as well - the plugins participating in an editing session might have
    // changed completely. Therefore, the confLoaded event sets arethusaLoaded to
    // false every time it's triggered.
    $scope.$on('confLoaded', function () {
      $scope.arethusaLoaded = false;
      $scope.state.init();
    });

    $scope.$on('stateLoaded', function () {
      $scope.state.postInit();
      if ($scope.arethusaLoaded) {
        // We don't have to retrieve all plugins again, but we have
        // to reload them so that they can update their internal state
        $scope.initPlugins();
      } else {
        $scope.init();
      }
    });

    $scope.initPlugins = function () {
      for (var plugin in $scope.plugins) {
        try {
          $scope.plugins[plugin].init();
        } catch (err) {
        }
      }
    };

    $scope.init = function () {
      // cf. the comment for $scope.save(), this is just a temporary solution
      $scope.persisters = configurator.getPersisters(conf.persisters);

      $scope.plugins = $scope.retrievePlugins(conf.plugins);
      partitionPlugins($scope.plugins);
      $scope.initPlugins();
      $scope.declareFirstPluginActive();
      $scope.arethusaLoaded = true;
    };
  }
]);
<commit_msg>Log a success message in mainCtrl<commit_after>'use strict';
angular.module('arethusa.core').controller('MainCtrl', [
  '$scope',
  '$injector',
  'configurator',
  'state',
  'documentStore',
  function ($scope, $injector, configurator, state, documentStore) {
    documentStore.reset();
    $scope.aU = arethusaUtil;
    $scope.debug = false;
    $scope.toggleDebugMode = function () {
      $scope.debug = !$scope.debug;
    };
    $scope.save = function() {
      // This is NOT the right place for this code, retrievers are handled
      // in the state, but that also is not good - we'll change this later.
      angular.forEach($scope.persisters, function(persister, name) {
        persister.saveData(function(data) {
          arethusaLogger.log('Success');
        });
      });
    };

    var conf = configurator.configurationFor('main');

    var partitionPlugins = function (plugins) {
      $scope.mainPlugins = [];
      $scope.subPlugins = [];
      $scope.pluginsWithMenu = [];
      angular.forEach(plugins, function (plugin, name) {
        $scope.registerPlugin(plugin);
      });
    };

    $scope.retrievePlugins = function (plugins) {
      var obj = {};
      angular.forEach(plugins, function (name, i) {
        obj[name] = $scope.retrievePlugin(name);
      });
      return obj;
    };

    $scope.registerPlugin = function (plugin) {
      $scope.pushPlugin(plugin);
      $scope.registerListener(plugin);
    };

    $scope.retrievePlugin = function (name) {
      var pluginConf = configurator.configurationFor(name);
      if (pluginConf.external) {
        // We copy because this object will be extended once the plugin
        // is really initialized through the inclusion of its template
        // by the plugin directive.
        return angular.copy(pluginConf);
      } else {
        return $injector.get(name);
      }
    };

    function hasView(plugin) {
      return !(!plugin.template || plugin.noView);
    }

    $scope.pushPlugin = function (plugin) {
      if (hasView(plugin)) {
        if (plugin.main) {
          $scope.mainPlugins.push(plugin);
        } else {
          $scope.subPlugins.push(plugin);
        }
      }
      if (plugin.contextMenu) {
        $scope.pluginsWithMenu.push(plugin);
      }
    };

    // Working on the assumptions that plugins will generally be grouped
    // in something like a tabset - impossible to show them all at the same
    // time, we expose some variables and functions to get info about their
    // visibility.
    // Templates can use this to implement ngIf, which removes elements
    // temporarily from the DOM.
    // Very useful - example use case:
    //   We have the morph plugin included in our configuration, but have
    //   selected a different tab at the moment, i.e. we are displaying
    //   the view of another plugin. The morph view wouldn't have to be
    //   rendered in the background!
    //
    // As we will mostly likely listen to click events for this, we have
    // to declare a first visible plugin in the init() function of this
    // controller, otherwise a user wouldn't be able to see something when
    // he first loads the page.
    //
    // Some plugins might have to rely on working in the background too.
    // Generally this shouldn't be the case, because all business logic
    // should be out of the DOM anyway. If a plugin still needs this, it can
    // do so by setting its alwaysActiveproperty to true.
    $scope.activePlugin = '';

    $scope.declareActive = function (name) {
      $scope.activePlugin = name;
    };

    $scope.declareFirstPluginActive = function () {
      $scope.declareActive($scope.subPlugins[0].name);
    };

    $scope.isActive = function (plugin) {
      return plugin.name === $scope.activePlugin && !plugin.alwaysActive;
    };

    // This is a really really bad solution right now. Using the controller
    // to insert stuff into the state object is not good. Can only stay as
    // a temporary prototype solution.
    $scope.registerListener = function (plugin) {
      if (plugin.listener) {
        state.registerListener(plugin);
      }
    };

    $scope.state = state;
    $scope.template = conf.template;

    // The application has to fulfil a specific load order.
    // The MainCtrl starts his work only when the configurator has received
    // its main configuration file (handled by the MAIN_ROUTE constant).
    //
    // The configurator might need some time to bring in external additional
    // files - asynchronously. We don't want the application to continue until
    // all configuration files are loaded.
    // We therefore wait for an event broadcast by the configurator to get a
    // green light.
    // Loading all state retrievers is another asynchronous step we want to see
    // completed before going on.
    // State broadcasts another event when it is done, after that the MainCtrl
    // can finally start to initialize itself and all all participating plugins.
    //
    // Everytime the state is reloaded, we need to reinitialize plugins (if they
    // declare to do so by implementing an init() function- it's not a necessity),
    // so that they can update their internal state after the main state tokens
    // have changed. There is no need to reinit the MainCtrl - the arethusaLoaded
    // variable takes care of this.
    // However if we reload a configuration, MainCtrl needs to be re-initialized
    // as well - the plugins participating in an editing session might have
    // changed completely. Therefore, the confLoaded event sets arethusaLoaded to
    // false every time it's triggered.
    $scope.$on('confLoaded', function () {
      $scope.arethusaLoaded = false;
      $scope.state.init();
    });

    $scope.$on('stateLoaded', function () {
      $scope.state.postInit();
      if ($scope.arethusaLoaded) {
        // We don't have to retrieve all plugins again, but we have
        // to reload them so that they can update their internal state
        $scope.initPlugins();
      } else {
        $scope.init();
      }
    });

    $scope.initPlugins = function () {
      for (var plugin in $scope.plugins) {
        try {
          $scope.plugins[plugin].init();
        } catch (err) {
        }
      }
    };

    $scope.init = function () {
      // cf. the comment for $scope.save(), this is just a temporary solution
      $scope.persisters = configurator.getPersisters(conf.persisters);

      $scope.plugins = $scope.retrievePlugins(conf.plugins);
      partitionPlugins($scope.plugins);
      $scope.initPlugins();
      $scope.declareFirstPluginActive();
      $scope.arethusaLoaded = true;
    };
  }
]);
<|endoftext|>