<commit_before>(function($, $$) {

var _ = Mavo.DOMExpression = $.Class({
	constructor: function(o = {}) {
		this.mavo = o.mavo;
		this.template = o.template && o.template.template || o.template;

		for (let prop of ["item", "path", "syntax", "fallback", "attribute", "originalAttribute", "expression", "parsed", "identifiers"]) {
			this[prop] = o[prop] === undefined && this.template? this.template[prop] : o[prop];
		}

		this.node = o.node;

		if (!this.node) {
			// No node provided, figure it out from path
			this.node = Mavo.elementPath(this.item.element, this.path);
		}

		this.element = this.node;
		this.attribute = this.attribute || null;

		Mavo.hooks.run("domexpression-init-start", this);

		if (this.attribute == "mv-value") {
			this.originalAttribute = "mv-value";
			this.attribute = Mavo.Primitive.getValueAttribute(this.element);
			this.fallback = this.fallback || Mavo.Primitive.getValue(this.element, {attribute: this.attribute});
			var expression = this.element.getAttribute("mv-value");
			this.element.removeAttribute("mv-value");
			this.parsed = [new Mavo.Expression(expression)];
			this.expression = this.syntax.start + expression + this.syntax.end;
		}

		if (this.node.nodeType === 3 && this.element === this.node) {
			this.element = this.node.parentNode;

			// If no element siblings make this.node the element, which is more robust
			// Same if attribute, there are no attributes on a text node!
			if (!this.node.parentNode.children.length || this.attribute) {
				this.node = this.element;
				this.element.normalize();
			}
		}

		if (!this.expression) { // Still unhandled?
			if (this.attribute) {
				// Some web components (e.g. AFrame) hijack getAttribute()
				var value = Element.prototype.getAttribute.call(this.node, this.attribute);

				this.expression = (value || "").trim();
			}
			else {
				// Move whitespace outside to prevent it from messing with types
				this.node.normalize();

				if (this.node.firstChild && this.node.childNodes.length === 1 && this.node.firstChild.nodeType === 3) {
					var whitespace = this.node.firstChild.textContent.match(/^\s*|\s*$/g);

					if (whitespace[1]) {
						this.node.firstChild.splitText(this.node.firstChild.textContent.length - whitespace[1].length);
						this.node.after(this.node.lastChild);
					}

					if (whitespace[0]) {
						this.node.firstChild.splitText(whitespace[0].length);
						this.node.parentNode.insertBefore(this.node.firstChild, this.node);
					}
				}

				this.expression = this.node.textContent;
			}

			this.parsed = this.template? this.template.parsed : this.syntax.tokenize(this.expression);
		}

		this.oldValue = this.value = this.parsed.map(x => x instanceof Mavo.Expression? x.expression : x);

		// Cache identifiers
		this.identifiers = this.identifiers || Mavo.flatten(this.parsed.map(x => x.identifiers || []));

		// Any identifiers that need additional updating?
		_.special.add(this);

		this.mavo.treeBuilt.then(() => {
			if (!this.template && !this.item) {
				// Only collection items and groups can have their own expressions arrays
				this.item = Mavo.Node.getClosestItem(this.element);
			}

			if (this.originalAttribute == "mv-value" && this.mavoNode && this.mavoNode == this.item.collection) {
				Mavo.delete(this.item.expressions, this);
			}

			this.mavo.expressions.register(this);

			Mavo.hooks.run("domexpression-init-treebuilt", this);
		});

		Mavo.hooks.run("domexpression-init-end", this);

		_.elements.set(this.element, [...(_.elements.get(this.element) || []), this]);
	},

	destroy: function() {
		_.special.delete(this);
		this.mavo.expressions.unregister(this);
	},

	get isDynamicObject() {
		return this.originalAttribute == "mv-value"
		       && this.mavoNode
			   && !(this.mavoNode instanceof Mavo.Primitive);
	},

	changedBy: function(evt) {
		if (this.isDynamicObject) {
			// Just prevent the same node from triggering changes, everything else is game
			return !evt || !this.mavoNode.contains(evt.node);
		}

		return Mavo.Expression.changedBy(this.identifiers, evt);
	},

	update: function() {
		var env = {context: this};
		var parentEnv = env;

		if (this.item) {
			var scope = this.isDynamicObject? this.item.parent : this.item;
			var data = this.data = scope.getLiveData();
		}
		else {
			var data = this.data === undefined? Mavo.Data.stub : this.data;
		}

		Mavo.hooks.run("domexpression-update-start", env);

		this.oldValue = this.value;
		var changed = false;

		env.value = this.value = this.parsed.map((expr, i) => {
			if (expr instanceof Mavo.Expression) {
				var env = {context: this, expr, parentEnv};

				Mavo.hooks.run("domexpression-update-beforeeval", env);

				env.value = Mavo.value(env.expr.eval(data));

				Mavo.hooks.run("domexpression-update-aftereval", env);

				changed = true;

				if (env.value instanceof Error) {
					return this.fallback !== undefined? this.fallback : this.syntax.start + env.expr.expression + this.syntax.end;
				}

				if (env.value === undefined || env.value === null) {
					// Don’t print things like "undefined" or "null"
					return "";
				}

				return env.value;
			}

			return expr;
		});

		if (!changed) {
			// If nothing changed, no need to do anything
			return;
		}

		if (env.value.length === 1) {
			env.value = env.value[0];
		}
		else {
			env.value = env.value.map(v => Mavo.Primitive.format(v, {
				attribute: this.attribute,
				element: this.element
			})).join("");
		}

		this.output(env.value);

		Mavo.hooks.run("domexpression-update-end", env);
	},

	output: function(value) {
		if (this.primitive) {
			if (Mavo.in(Mavo.isProxy, value)) {
				value = Mavo.clone(value); // Drop proxy
			}

			this.primitive.value = value;
		}
		else if (this.mavoNode) {
			this.mavoNode.render(value);
		}
		else {
			Mavo.Primitive.setValue(this.node, value, {attribute: this.attribute});
		}
	},

	live: {
		item: function(item) {
			if (item && this._item != item) {
				if (this._item) {
					// Previous item, delete from its expressions
					Mavo.delete(this._item.expressions, this);
				}

				item.expressions = item.expressions || [];
				item.expressions.push(this);
			}
		}
	},

	static: {
		elements: new WeakMap(),

		/**
		 * Search for Mavo.DOMExpression object(s) associated with a given element
		 * and optionally an attribute.
		 *
		 * @return If one argument, array of matching DOMExpression objects.
		 *         If two arguments, the matching DOMExpression object or null
		 */
		search: function (element, attribute) {
			if (element === null) {
				return element;
			}

			var all = _.elements.get(element) || [];

			if (arguments.length > 1) {
				if (!all.length) {
					return null;
				}

				return all.filter(et => et.attribute === attribute)[0] || null;
			}

			return all;
		},

		special: {
			add: function(domexpression, name) {
				if (name) {
					var o = this.vars[name];

					if (o && domexpression.identifiers.indexOf(name) > -1) {
						o.all = o.all || new Set();
						o.all.add(domexpression);

						if (o.all.size === 1) {
							o.observe();
						}
						else if (!o.all.size) {
							o.unobserve();
						}
					}
				}
				else {
					// All names
					for (var name in this.vars) {
						this.add(domexpression, name);
					}
				}
			},

			delete: function(domexpression, name) {
				if (name) {
					var o = this.vars[name];

					o.all = o.all || new Set();
					o.all.delete(domexpression);

					if (!o.all.size) {
						o.unobserve();
					}
				}
				else {
					// All names
					for (var name in this.vars) {
						this.delete(domexpression, name);
					}
				}
			},

			update: function() {
				if (this.update) {
					this.update(...arguments);
				}

				this.all.forEach(domexpression => domexpression.update());
			},

			event: function(name, {type, update, target = document} = {}) {
				this.vars[name] = {
					observe: function() {
						this.callback = this.callback || _.special.update.bind(this);
						target.addEventListener(type, this.callback);
					},
					unobserve: function() {
						target.removeEventListener(type, this.callback);
					}
				};

				if (update) {
					this.vars[name].update = function(evt) {
						Mavo.Functions[name] = update(evt);
					};
				}
			},

			vars: {
				"$now": {
					observe: function() {
						var callback = () => {
							_.special.update.call(this);
							this.timer = requestAnimationFrame(callback);
						};

						this.timer = requestAnimationFrame(callback);
					},
					unobserve: function() {
						cancelAnimationFrame(this.timer);
					}
				}
			}
		}
	}
});

_.special.event("$mouse", {
	type: "mousemove",
	update: function(evt) {
		return {x: evt.clientX, y: evt.clientY};
	}
});

_.special.event("$hash", {
	type: "hashchange",
	target: window
});

})(Bliss, Bliss.$);
<commit_msg>Check for unprefixed special properties to listen to (#468)<commit_after>(function($, $$) {

var _ = Mavo.DOMExpression = $.Class({
	constructor: function(o = {}) {
		this.mavo = o.mavo;
		this.template = o.template && o.template.template || o.template;

		for (let prop of ["item", "path", "syntax", "fallback", "attribute", "originalAttribute", "expression", "parsed", "identifiers"]) {
			this[prop] = o[prop] === undefined && this.template? this.template[prop] : o[prop];
		}

		this.node = o.node;

		if (!this.node) {
			// No node provided, figure it out from path
			this.node = Mavo.elementPath(this.item.element, this.path);
		}

		this.element = this.node;
		this.attribute = this.attribute || null;

		Mavo.hooks.run("domexpression-init-start", this);

		if (this.attribute == "mv-value") {
			this.originalAttribute = "mv-value";
			this.attribute = Mavo.Primitive.getValueAttribute(this.element);
			this.fallback = this.fallback || Mavo.Primitive.getValue(this.element, {attribute: this.attribute});
			var expression = this.element.getAttribute("mv-value");
			this.element.removeAttribute("mv-value");
			this.parsed = [new Mavo.Expression(expression)];
			this.expression = this.syntax.start + expression + this.syntax.end;
		}

		if (this.node.nodeType === 3 && this.element === this.node) {
			this.element = this.node.parentNode;

			// If no element siblings make this.node the element, which is more robust
			// Same if attribute, there are no attributes on a text node!
			if (!this.node.parentNode.children.length || this.attribute) {
				this.node = this.element;
				this.element.normalize();
			}
		}

		if (!this.expression) { // Still unhandled?
			if (this.attribute) {
				// Some web components (e.g. AFrame) hijack getAttribute()
				var value = Element.prototype.getAttribute.call(this.node, this.attribute);

				this.expression = (value || "").trim();
			}
			else {
				// Move whitespace outside to prevent it from messing with types
				this.node.normalize();

				if (this.node.firstChild && this.node.childNodes.length === 1 && this.node.firstChild.nodeType === 3) {
					var whitespace = this.node.firstChild.textContent.match(/^\s*|\s*$/g);

					if (whitespace[1]) {
						this.node.firstChild.splitText(this.node.firstChild.textContent.length - whitespace[1].length);
						this.node.after(this.node.lastChild);
					}

					if (whitespace[0]) {
						this.node.firstChild.splitText(whitespace[0].length);
						this.node.parentNode.insertBefore(this.node.firstChild, this.node);
					}
				}

				this.expression = this.node.textContent;
			}

			this.parsed = this.template? this.template.parsed : this.syntax.tokenize(this.expression);
		}

		this.oldValue = this.value = this.parsed.map(x => x instanceof Mavo.Expression? x.expression : x);

		// Cache identifiers
		this.identifiers = this.identifiers || Mavo.flatten(this.parsed.map(x => x.identifiers || []));

		// Any identifiers that need additional updating?
		_.special.add(this);

		this.mavo.treeBuilt.then(() => {
			if (!this.template && !this.item) {
				// Only collection items and groups can have their own expressions arrays
				this.item = Mavo.Node.getClosestItem(this.element);
			}

			if (this.originalAttribute == "mv-value" && this.mavoNode && this.mavoNode == this.item.collection) {
				Mavo.delete(this.item.expressions, this);
			}

			this.mavo.expressions.register(this);

			Mavo.hooks.run("domexpression-init-treebuilt", this);
		});

		Mavo.hooks.run("domexpression-init-end", this);

		_.elements.set(this.element, [...(_.elements.get(this.element) || []), this]);
	},

	destroy: function() {
		_.special.delete(this);
		this.mavo.expressions.unregister(this);
	},

	get isDynamicObject() {
		return this.originalAttribute == "mv-value"
		       && this.mavoNode
			   && !(this.mavoNode instanceof Mavo.Primitive);
	},

	changedBy: function(evt) {
		if (this.isDynamicObject) {
			// Just prevent the same node from triggering changes, everything else is game
			return !evt || !this.mavoNode.contains(evt.node);
		}

		return Mavo.Expression.changedBy(this.identifiers, evt);
	},

	update: function() {
		var env = {context: this};
		var parentEnv = env;

		if (this.item) {
			var scope = this.isDynamicObject? this.item.parent : this.item;
			var data = this.data = scope.getLiveData();
		}
		else {
			var data = this.data === undefined? Mavo.Data.stub : this.data;
		}

		Mavo.hooks.run("domexpression-update-start", env);

		this.oldValue = this.value;
		var changed = false;

		env.value = this.value = this.parsed.map((expr, i) => {
			if (expr instanceof Mavo.Expression) {
				var env = {context: this, expr, parentEnv};

				Mavo.hooks.run("domexpression-update-beforeeval", env);

				env.value = Mavo.value(env.expr.eval(data));

				Mavo.hooks.run("domexpression-update-aftereval", env);

				changed = true;

				if (env.value instanceof Error) {
					return this.fallback !== undefined? this.fallback : this.syntax.start + env.expr.expression + this.syntax.end;
				}

				if (env.value === undefined || env.value === null) {
					// Don’t print things like "undefined" or "null"
					return "";
				}

				return env.value;
			}

			return expr;
		});

		if (!changed) {
			// If nothing changed, no need to do anything
			return;
		}

		if (env.value.length === 1) {
			env.value = env.value[0];
		}
		else {
			env.value = env.value.map(v => Mavo.Primitive.format(v, {
				attribute: this.attribute,
				element: this.element
			})).join("");
		}

		this.output(env.value);

		Mavo.hooks.run("domexpression-update-end", env);
	},

	output: function(value) {
		if (this.primitive) {
			if (Mavo.in(Mavo.isProxy, value)) {
				value = Mavo.clone(value); // Drop proxy
			}

			this.primitive.value = value;
		}
		else if (this.mavoNode) {
			this.mavoNode.render(value);
		}
		else {
			Mavo.Primitive.setValue(this.node, value, {attribute: this.attribute});
		}
	},

	live: {
		item: function(item) {
			if (item && this._item != item) {
				if (this._item) {
					// Previous item, delete from its expressions
					Mavo.delete(this._item.expressions, this);
				}

				item.expressions = item.expressions || [];
				item.expressions.push(this);
			}
		}
	},

	static: {
		elements: new WeakMap(),

		/**
		 * Search for Mavo.DOMExpression object(s) associated with a given element
		 * and optionally an attribute.
		 *
		 * @return If one argument, array of matching DOMExpression objects.
		 *         If two arguments, the matching DOMExpression object or null
		 */
		search: function (element, attribute) {
			if (element === null) {
				return element;
			}

			var all = _.elements.get(element) || [];

			if (arguments.length > 1) {
				if (!all.length) {
					return null;
				}

				return all.filter(et => et.attribute === attribute)[0] || null;
			}

			return all;
		},

		special: {
			add: function(domexpression, name) {
				if (name) {
					var o = this.vars[name];
					var hasName = domexpression.identifiers.indexOf(name) > -1;
					var hasUnprefixedName = (name.startsWith("$") &&
						domexpression.identifiers.indexOf(name.substr(1)) > -1);

					if (o && (hasName || hasUnprefixedName)) {
						o.all = o.all || new Set();
						o.all.add(domexpression);

						if (o.all.size === 1) {
							o.observe();
						}
						else if (!o.all.size) {
							o.unobserve();
						}
					}
				}
				else {
					// All names
					for (var name in this.vars) {
						this.add(domexpression, name);
					}
				}
			},

			delete: function(domexpression, name) {
				if (name) {
					var o = this.vars[name];

					o.all = o.all || new Set();
					o.all.delete(domexpression);

					if (!o.all.size) {
						o.unobserve();
					}
				}
				else {
					// All names
					for (var name in this.vars) {
						this.delete(domexpression, name);
					}
				}
			},

			update: function() {
				if (this.update) {
					this.update(...arguments);
				}

				this.all.forEach(domexpression => domexpression.update());
			},

			event: function(name, {type, update, target = document} = {}) {
				this.vars[name] = {
					observe: function() {
						this.callback = this.callback || _.special.update.bind(this);
						target.addEventListener(type, this.callback);
					},
					unobserve: function() {
						target.removeEventListener(type, this.callback);
					}
				};

				if (update) {
					this.vars[name].update = function(evt) {
						Mavo.Functions[name] = update(evt);
					};
				}
			},

			vars: {
				"$now": {
					observe: function() {
						var callback = () => {
							_.special.update.call(this);
							this.timer = requestAnimationFrame(callback);
						};

						this.timer = requestAnimationFrame(callback);
					},
					unobserve: function() {
						cancelAnimationFrame(this.timer);
					}
				}
			}
		}
	}
});

_.special.event("$mouse", {
	type: "mousemove",
	update: function(evt) {
		return {x: evt.clientX, y: evt.clientY};
	}
});

_.special.event("$hash", {
	type: "hashchange",
	target: window
});

})(Bliss, Bliss.$);
<|endoftext|>


<commit_before>import { deleteNearSelection } from "./deleteNearSelection"
import { runInOp } from "../display/operations"
import { ensureCursorVisible } from "../display/scrolling"
import { endOfLine } from "../input/movement"
import { clipPos, Pos } from "../line/pos"
import { visualLine, visualLineEnd } from "../line/spans"
import { getLine, lineNo } from "../line/utils_line"
import { Range } from "../model/selection"
import { selectAll } from "../model/selection_updates"
import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc"
import { getOrder } from "../util/bidi"

// Commands are parameter-less actions that can be performed on an
// editor, mostly used for keybindings.
export let commands = {
  selectAll: selectAll,
  singleSelection: cm => cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll),
  killLine: cm => deleteNearSelection(cm, range => {
    if (range.empty()) {
      let len = getLine(cm.doc, range.head.line).text.length
      if (range.head.ch == len && range.head.line < cm.lastLine())
        return {from: range.head, to: Pos(range.head.line + 1, 0)}
      else
        return {from: range.head, to: Pos(range.head.line, len)}
    } else {
      return {from: range.from(), to: range.to()}
    }
  }),
  deleteLine: cm => deleteNearSelection(cm, range => ({
    from: Pos(range.from().line, 0),
    to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
  })),
  delLineLeft: cm => deleteNearSelection(cm, range => ({
    from: Pos(range.from().line, 0), to: range.from()
  })),
  delWrappedLineLeft: cm => deleteNearSelection(cm, range => {
    let top = cm.charCoords(range.head, "div").top + 5
    let leftPos = cm.coordsChar({left: 0, top: top}, "div")
    return {from: leftPos, to: range.from()}
  }),
  delWrappedLineRight: cm => deleteNearSelection(cm, range => {
    let top = cm.charCoords(range.head, "div").top + 5
    let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
    return {from: range.from(), to: rightPos }
  }),
  undo: cm => cm.undo(),
  redo: cm => cm.redo(),
  undoSelection: cm => cm.undoSelection(),
  redoSelection: cm => cm.redoSelection(),
  goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)),
  goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())),
  goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line),
    {origin: "+move", bias: 1}
  ),
  goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head),
    {origin: "+move", bias: 1}
  ),
  goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line),
    {origin: "+move", bias: -1}
  ),
  goLineRight: cm => cm.extendSelectionsBy(range => {
    let top = cm.cursorCoords(range.head, "div").top + 5
    return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
  }, sel_move),
  goLineLeft: cm => cm.extendSelectionsBy(range => {
    let top = cm.cursorCoords(range.head, "div").top + 5
    return cm.coordsChar({left: 0, top: top}, "div")
  }, sel_move),
  goLineLeftSmart: cm => cm.extendSelectionsBy(range => {
    let top = cm.charCoords(range.head, "div").top + 5
    let pos = cm.coordsChar({left: 0, top: top}, "div")
    if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head)
    return pos
  }, sel_move),
  goLineUp: cm => cm.moveV(-1, "line"),
  goLineDown: cm => cm.moveV(1, "line"),
  goPageUp: cm => cm.moveV(-1, "page"),
  goPageDown: cm => cm.moveV(1, "page"),
  goCharLeft: cm => cm.moveH(-1, "char"),
  goCharRight: cm => cm.moveH(1, "char"),
  goColumnLeft: cm => cm.moveH(-1, "column"),
  goColumnRight: cm => cm.moveH(1, "column"),
  goWordLeft: cm => cm.moveH(-1, "word"),
  goGroupRight: cm => cm.moveH(1, "group"),
  goGroupLeft: cm => cm.moveH(-1, "group"),
  goWordRight: cm => cm.moveH(1, "word"),
  delCharBefore: cm => cm.deleteH(-1, "char"),
  delCharAfter: cm => cm.deleteH(1, "char"),
  delWordBefore: cm => cm.deleteH(-1, "word"),
  delWordAfter: cm => cm.deleteH(1, "word"),
  delGroupBefore: cm => cm.deleteH(-1, "group"),
  delGroupAfter: cm => cm.deleteH(1, "group"),
  indentAuto: cm => cm.indentSelection("smart"),
  indentMore: cm => cm.indentSelection("add"),
  indentLess: cm => cm.indentSelection("subtract"),
  insertTab: cm => cm.replaceSelection("\t"),
  insertSoftTab: cm => {
    let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
    for (let i = 0; i < ranges.length; i++) {
      let pos = ranges[i].from()
      let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
      spaces.push(spaceStr(tabSize - col % tabSize))
    }
    cm.replaceSelections(spaces)
  },
  defaultTab: cm => {
    if (cm.somethingSelected()) cm.indentSelection("add")
    else cm.execCommand("insertTab")
  },
  // Swap the two chars left and right of each selection's head.
  // Move cursor behind the two swapped characters afterwards.
  //
  // Doesn't consider line feeds a character.
  // Doesn't scan more than one line above to find a character.
  // Doesn't do anything on an empty line.
  // Doesn't do anything with non-empty selections.
  transposeChars: cm => runInOp(cm, () => {
    let ranges = cm.listSelections(), newSel = []
    for (let i = 0; i < ranges.length; i++) {
      if (!ranges[i].empty()) continue
      let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
      if (line) {
        if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1)
        if (cur.ch > 0) {
          cur = new Pos(cur.line, cur.ch + 1)
          cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
                          Pos(cur.line, cur.ch - 2), cur, "+transpose")
        } else if (cur.line > cm.doc.first) {
          let prev = getLine(cm.doc, cur.line - 1).text
          if (prev) {
            cur = new Pos(cur.line, 1)
            cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
                            prev.charAt(prev.length - 1),
                            Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
          }
        }
      }
      newSel.push(new Range(cur, cur))
    }
    cm.setSelections(newSel)
  }),
  newlineAndIndent: cm => runInOp(cm, () => {
    let sels = cm.listSelections()
    for (let i = sels.length - 1; i >= 0; i--)
      cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input")
    sels = cm.listSelections()
    for (let i = 0; i < sels.length; i++)
      cm.indentLine(sels[i].from().line, null, true)
    ensureCursorVisible(cm)
  }),
  openLine: cm => cm.replaceSelection("\n", "start"),
  toggleOverwrite: cm => cm.toggleOverwrite()
}

function lineStart(cm, lineN) {
  let line = getLine(cm.doc, lineN)
  let visual = visualLine(line)
  if (visual != line) lineN = lineNo(visual)
  return endOfLine(true, cm, visual, lineN, 1)
}
function lineEnd(cm, lineN) {
  let line = getLine(cm.doc, lineN)
  let visual = visualLineEnd(line)
  if (visual != line) lineN = lineNo(visual)
  return endOfLine(true, cm, line, lineN, -1)
}
function lineStartSmart(cm, pos) {
  let start = lineStart(cm, pos.line)
  let line = getLine(cm.doc, start.line)
  let order = getOrder(line, cm.doc.direction)
  if (!order || order[0].level == 0) {
    let firstNonWS = Math.max(0, line.text.search(/\S/))
    let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
    return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
  }
  return start
}
<commit_msg>Use cursorCoords in goLineLeftSmart command<commit_after>import { deleteNearSelection } from "./deleteNearSelection"
import { runInOp } from "../display/operations"
import { ensureCursorVisible } from "../display/scrolling"
import { endOfLine } from "../input/movement"
import { clipPos, Pos } from "../line/pos"
import { visualLine, visualLineEnd } from "../line/spans"
import { getLine, lineNo } from "../line/utils_line"
import { Range } from "../model/selection"
import { selectAll } from "../model/selection_updates"
import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc"
import { getOrder } from "../util/bidi"

// Commands are parameter-less actions that can be performed on an
// editor, mostly used for keybindings.
export let commands = {
  selectAll: selectAll,
  singleSelection: cm => cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll),
  killLine: cm => deleteNearSelection(cm, range => {
    if (range.empty()) {
      let len = getLine(cm.doc, range.head.line).text.length
      if (range.head.ch == len && range.head.line < cm.lastLine())
        return {from: range.head, to: Pos(range.head.line + 1, 0)}
      else
        return {from: range.head, to: Pos(range.head.line, len)}
    } else {
      return {from: range.from(), to: range.to()}
    }
  }),
  deleteLine: cm => deleteNearSelection(cm, range => ({
    from: Pos(range.from().line, 0),
    to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
  })),
  delLineLeft: cm => deleteNearSelection(cm, range => ({
    from: Pos(range.from().line, 0), to: range.from()
  })),
  delWrappedLineLeft: cm => deleteNearSelection(cm, range => {
    let top = cm.charCoords(range.head, "div").top + 5
    let leftPos = cm.coordsChar({left: 0, top: top}, "div")
    return {from: leftPos, to: range.from()}
  }),
  delWrappedLineRight: cm => deleteNearSelection(cm, range => {
    let top = cm.charCoords(range.head, "div").top + 5
    let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
    return {from: range.from(), to: rightPos }
  }),
  undo: cm => cm.undo(),
  redo: cm => cm.redo(),
  undoSelection: cm => cm.undoSelection(),
  redoSelection: cm => cm.redoSelection(),
  goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)),
  goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())),
  goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line),
    {origin: "+move", bias: 1}
  ),
  goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head),
    {origin: "+move", bias: 1}
  ),
  goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line),
    {origin: "+move", bias: -1}
  ),
  goLineRight: cm => cm.extendSelectionsBy(range => {
    let top = cm.cursorCoords(range.head, "div").top + 5
    return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
  }, sel_move),
  goLineLeft: cm => cm.extendSelectionsBy(range => {
    let top = cm.cursorCoords(range.head, "div").top + 5
    return cm.coordsChar({left: 0, top: top}, "div")
  }, sel_move),
  goLineLeftSmart: cm => cm.extendSelectionsBy(range => {
    let top = cm.cursor(range.head, "div").top + 5
    let pos = cm.coordsChar({left: 0, top: top}, "div")
    if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head)
    return pos
  }, sel_move),
  goLineUp: cm => cm.moveV(-1, "line"),
  goLineDown: cm => cm.moveV(1, "line"),
  goPageUp: cm => cm.moveV(-1, "page"),
  goPageDown: cm => cm.moveV(1, "page"),
  goCharLeft: cm => cm.moveH(-1, "char"),
  goCharRight: cm => cm.moveH(1, "char"),
  goColumnLeft: cm => cm.moveH(-1, "column"),
  goColumnRight: cm => cm.moveH(1, "column"),
  goWordLeft: cm => cm.moveH(-1, "word"),
  goGroupRight: cm => cm.moveH(1, "group"),
  goGroupLeft: cm => cm.moveH(-1, "group"),
  goWordRight: cm => cm.moveH(1, "word"),
  delCharBefore: cm => cm.deleteH(-1, "char"),
  delCharAfter: cm => cm.deleteH(1, "char"),
  delWordBefore: cm => cm.deleteH(-1, "word"),
  delWordAfter: cm => cm.deleteH(1, "word"),
  delGroupBefore: cm => cm.deleteH(-1, "group"),
  delGroupAfter: cm => cm.deleteH(1, "group"),
  indentAuto: cm => cm.indentSelection("smart"),
  indentMore: cm => cm.indentSelection("add"),
  indentLess: cm => cm.indentSelection("subtract"),
  insertTab: cm => cm.replaceSelection("\t"),
  insertSoftTab: cm => {
    let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
    for (let i = 0; i < ranges.length; i++) {
      let pos = ranges[i].from()
      let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
      spaces.push(spaceStr(tabSize - col % tabSize))
    }
    cm.replaceSelections(spaces)
  },
  defaultTab: cm => {
    if (cm.somethingSelected()) cm.indentSelection("add")
    else cm.execCommand("insertTab")
  },
  // Swap the two chars left and right of each selection's head.
  // Move cursor behind the two swapped characters afterwards.
  //
  // Doesn't consider line feeds a character.
  // Doesn't scan more than one line above to find a character.
  // Doesn't do anything on an empty line.
  // Doesn't do anything with non-empty selections.
  transposeChars: cm => runInOp(cm, () => {
    let ranges = cm.listSelections(), newSel = []
    for (let i = 0; i < ranges.length; i++) {
      if (!ranges[i].empty()) continue
      let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
      if (line) {
        if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1)
        if (cur.ch > 0) {
          cur = new Pos(cur.line, cur.ch + 1)
          cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
                          Pos(cur.line, cur.ch - 2), cur, "+transpose")
        } else if (cur.line > cm.doc.first) {
          let prev = getLine(cm.doc, cur.line - 1).text
          if (prev) {
            cur = new Pos(cur.line, 1)
            cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
                            prev.charAt(prev.length - 1),
                            Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
          }
        }
      }
      newSel.push(new Range(cur, cur))
    }
    cm.setSelections(newSel)
  }),
  newlineAndIndent: cm => runInOp(cm, () => {
    let sels = cm.listSelections()
    for (let i = sels.length - 1; i >= 0; i--)
      cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input")
    sels = cm.listSelections()
    for (let i = 0; i < sels.length; i++)
      cm.indentLine(sels[i].from().line, null, true)
    ensureCursorVisible(cm)
  }),
  openLine: cm => cm.replaceSelection("\n", "start"),
  toggleOverwrite: cm => cm.toggleOverwrite()
}

function lineStart(cm, lineN) {
  let line = getLine(cm.doc, lineN)
  let visual = visualLine(line)
  if (visual != line) lineN = lineNo(visual)
  return endOfLine(true, cm, visual, lineN, 1)
}
function lineEnd(cm, lineN) {
  let line = getLine(cm.doc, lineN)
  let visual = visualLineEnd(line)
  if (visual != line) lineN = lineNo(visual)
  return endOfLine(true, cm, line, lineN, -1)
}
function lineStartSmart(cm, pos) {
  let start = lineStart(cm, pos.line)
  let line = getLine(cm.doc, start.line)
  let order = getOrder(line, cm.doc.direction)
  if (!order || order[0].level == 0) {
    let firstNonWS = Math.max(0, line.text.search(/\S/))
    let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
    return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
  }
  return start
}
<|endoftext|>


<commit_before>const { app, BrowserWindow, protocol } = require('electron');
const path = require('path');
const url = require('url');

// Override file path resolver to handle absolute path
app.on('ready', () => {
  protocol.interceptFileProtocol('file', (req, callback) => {
    const requestedUrl = req.url.substr(7);

    if (path.isAbsolute(requestedUrl)) {
      callback(path.normalize(path.join(__dirname, requestedUrl)));
    } else {
      callback(requestedUrl);
    }
  });
});

let win;

function createWindow() {
  win = new BrowserWindow({ width: 800, height: 600 });

  win.loadURL(url.format({
    pathname: '/index.html',
    protocol: 'file:',
    slashes: true,
  }));

  // Open the DevTools.
  if (process.env.NODE_ENV === 'development') {
    win.webContents.openDevTools();
  }

  win.on('closed', () => {
    win = null;
  });
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (win === null) {
    createWindow();
  }
});
<commit_msg>(refs #379) Remove a code showing electron dev tool<commit_after>const { app, BrowserWindow, protocol } = require('electron');
const path = require('path');
const url = require('url');

// Override file path resolver to handle absolute path
app.on('ready', () => {
  protocol.interceptFileProtocol('file', (req, callback) => {
    const requestedUrl = req.url.substr(7);

    if (path.isAbsolute(requestedUrl)) {
      callback(path.normalize(path.join(__dirname, requestedUrl)));
    } else {
      callback(requestedUrl);
    }
  });
});

let win;

function createWindow() {
  win = new BrowserWindow({ width: 800, height: 600 });

  win.loadURL(url.format({
    pathname: '/index.html',
    protocol: 'file:',
    slashes: true,
  }));

  win.on('closed', () => {
    win = null;
  });
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (win === null) {
    createWindow();
  }
});
<|endoftext|>


<commit_before>//Cache polyfil to support cacheAPI in all browsers
importScripts("./cache-polyfill.js");

var cacheName = "cache-v1";

//Files to cache
var files = [
  "/",
  "/css/main.css",
  "/js/main.js",
  "/js/menu.js",
  "/images/icons/android-chrome-192x192.png",
  "/images/icons/android-chrome-512x512.png",
  "/images/engaging.png",
  "/images/fast.png",
  "/images/reliable.png",
  "/images/pwa-codelabs.png",
  "/images/menu_black.svg",
  "/manifest.json"
];

//Adding `install` event listener
self.addEventListener('install', (event) => {
  console.info('Event: Install');

  self.skipWaiting(); //To forces the waiting service worker to become the active service worker

  event.waitUntil(
    caches.open(cacheName)
    .then((cache) => {
      //[] of files to cache & if any of the file not present `addAll` will fail
      return cache.addAll(files)
      .then(() => {
        console.info('All files are cached');
      })
      .catch((error) =>  {
        console.error('Failed to cache', error);
      })
    })
  );
});

/*
  FETCH EVENT: triggered for every request made by index page, after install.
*/

//Adding `fetch` event listener
self.addEventListener('fetch', (event) => {
  console.info('Event: Fetch');

  var request = event.request;

  //Tell the browser to wait for newtwork request and respond with below
  event.respondWith(
    //If request is already in cache, return it
    caches.match(request).then((response) => {
      if (response) {
        return response;
      }

      //if request is not cached, add it to cache
      return fetch(request).then((response) => {
        var responseToCache = response.clone();
        caches.open(cacheName).then((cache) => {
            cache.put(request, responseToCache).catch((err) => {
              console.warn(request.url + ': ' + err.message);
            });
          });

        return response;
      });
    })
  );
});

/*
  ACTIVATE EVENT: triggered once after registering, also used to clean up caches.
*/

//Adding `activate` event listener
self.addEventListener('activate', (event) => {
  console.info('Event: Activate');

  //Remove old and unwanted caches
  event.waitUntil(
    caches.keys().then(cacheNames => Promise.all(
      cacheNames.map((cache) => {
        if (cache !== cacheName) {
          return caches.delete(cache); //Deleting the old cache
        }
      })
    ))
    .then(() => {
      console.log('Old caches are cleared!');
    })
  );
});
<commit_msg>Added issues<commit_after>//Cache polyfil to support cacheAPI in all browsers
importScripts("./cache-polyfill.js");

var cacheName = "cache-v2";

//Files to cache
var files = [
  "/",
  "/css/main.css",
  "/js/main.js",
  "/js/menu.js",
  "/images/icons/android-chrome-192x192.png",
  "/images/icons/android-chrome-512x512.png",
  "/images/engaging.png",
  "/images/fast.png",
  "/images/reliable.png",
  "/images/pwa-codelabs.png",
  "/images/menu_black.svg",
  "/manifest.json"
];

//Adding `install` event listener
self.addEventListener('install', (event) => {
  console.info('Event: Install');

  self.skipWaiting(); //To forces the waiting service worker to become the active service worker

  event.waitUntil(
    caches.open(cacheName)
    .then((cache) => {
      //[] of files to cache & if any of the file not present `addAll` will fail
      return cache.addAll(files)
      .then(() => {
        console.info('All files are cached');
      })
      .catch((error) =>  {
        console.error('Failed to cache', error);
      })
    })
  );
});

/*
  FETCH EVENT: triggered for every request made by index page, after install.
*/

//Adding `fetch` event listener
self.addEventListener('fetch', (event) => {
  console.info('Event: Fetch');

  var request = event.request;

  //Tell the browser to wait for newtwork request and respond with below
  event.respondWith(
    //If request is already in cache, return it
    caches.match(request).then((response) => {
      if (response) {
        return response;
      }

      //if request is not cached, add it to cache
      return fetch(request).then((response) => {
        var responseToCache = response.clone();
        caches.open(cacheName).then((cache) => {
            cache.put(request, responseToCache).catch((err) => {
              console.warn(request.url + ': ' + err.message);
            });
          });

        return response;
      });
    })
  );
});

/*
  ACTIVATE EVENT: triggered once after registering, also used to clean up caches.
*/

//Adding `activate` event listener
self.addEventListener('activate', (event) => {
  console.info('Event: Activate');

  //Remove old and unwanted caches
  event.waitUntil(
    caches.keys().then(cacheNames => Promise.all(
      cacheNames.map((cache) => {
        if (cache !== cacheName) {
          return caches.delete(cache); //Deleting the old cache
        }
      })
    ))
    .then(() => {
      console.log('Old caches are cleared!');
    })
  );
});
<|endoftext|>


<commit_before>var dom = require('./dom'),
  ds = require('dollar-slice'),
  paneController = require('../controllers/pane'),
  publishPaneController = require('../controllers/publish-pane'),
  toolbar = dom.find('.kiln-toolbar');

/**
 * create pane
 * @param {string} header
 * @param {Element|DocumentFragment} innerEl
 * @returns {Element}
 */
function createPane(header, innerEl) {
  var template = dom.find('.kiln-pane-template'),
    el;

  // add header and contents before importing
  template.content.querySelector('.pane-header').innerHTML = header;
  template.content.querySelector('.pane-inner').appendChild(innerEl);

  // import template
  el = document.importNode(template.content, true);
  return el;
}

/**
 * open publish pane
 */
function openPublish() {
  var header = 'Schedule Publish',
    actionsEl = dom.create(`

        <button class="publish-now">Publish Now</button>

    `),
    el = createPane(header, actionsEl);

  dom.insertBefore(toolbar, el);
  // init controller for pane background
  ds.controller('pane', paneController);
  ds.get('pane', toolbar.previousElementSibling);
  // init controller for publish pane
  ds.controller('publish-pane', publishPaneController);
  ds.get('publish-pane', toolbar.previousElementSibling.querySelector('.actions'));
}

module.exports.openPublish = openPublish;
<commit_msg>use new clone every time<commit_after>var dom = require('./dom'),
  ds = require('dollar-slice'),
  paneController = require('../controllers/pane'),
  publishPaneController = require('../controllers/publish-pane'),
  toolbar = dom.find('.kiln-toolbar');

/**
 * create pane
 * @param {string} header
 * @param {Element|DocumentFragment} innerEl
 * @returns {Element}
 */
function createPane(header, innerEl) {
  var template = dom.find('.kiln-pane-template'),
    el = document.importNode(template.content, true);

  // add header and contents
  el.querySelector('.pane-header').innerHTML = header;
  el.querySelector('.pane-inner').appendChild(innerEl);

  return el;
}

/**
 * open publish pane
 */
function openPublish() {
  var header = 'Schedule Publish',
    actionsEl = dom.create(`

        <button class="publish-now">Publish Now</button>

    `),
    el = createPane(header, actionsEl);

  dom.insertBefore(toolbar, el);
  // init controller for pane background
  ds.controller('pane', paneController);
  ds.get('pane', toolbar.previousElementSibling);
  // init controller for publish pane
  ds.controller('publish-pane', publishPaneController);
  ds.get('publish-pane', toolbar.previousElementSibling.querySelector('.actions'));
}

module.exports.openPublish = openPublish;
<|endoftext|>


<commit_before>import request from 'request';
import fs from 'fs';
import yaml from 'js-yaml';
import merge from 'deepmerge';
import path from 'path';

/*
  Eureka JS client
  This module handles registration with a Eureka server, as well as heartbeats
  for reporting instance health. This module requires a eureka-client-config.js configuration
  file.
*/

export default class Eureka {

  constructor(config) {
    console.log('initializing eureka client');
    this.config = config;
    if (!config) {
      // Load up the current working directory and the environment:
      const cwd = process.cwd();
      const env = process.env.NODE_ENV || 'development';

      // Attempt to load the config file:
      try {
        config = yaml.safeLoad(fs.readFileSync(path.join(cwd, 'eureka-client.yml'), 'utf8'));
      } catch(e) {}

      try {
        const envConfig = yaml.safeLoad(fs.readFileSync(path.join(cwd, `eureka-client-${env}.yml`), 'utf8'));
        config = merge(config, envConfig);
      } catch(e) {}

      this.config = config;
    }
    if (!this.config) {
      throw new Error('missing configuration file.');
    }
    if (!this.config.instance || !this.config.eureka) {
      throw new Error('missing instance / eureka configuration.');
    }
    this.register();
  }

  /*
    Registers with the Eureka server and initializes heartbeats on registration success.
  */
  register() {
    this.config.instance.status = 'UP';
    request.post({
      url: this.baseEurekaUrl() + this.config.instance.app,
      json: true,
      body: {instance: this.config.instance}
    }, (error, response, body) => {
      if (!error && response.statusCode === 204) {
        console.log('registered with eureka: ', `${this.config.instance.app}/${this.getInstanceId()}`);
        this.startHeartbeats();
      } else {
        throw new Error('eureka registration FAILED: ' + (error ? error : `status: ${response.statusCode} body: ${body}`));
      }
    });
  }

  /*
    Sets up heartbeats on interval for the life of the application.
    Heartbeat interval by setting configuration property: eureka.heartbeatInterval
  */
  startHeartbeats() {
    this.heartbeat = setInterval(() => {
      request.put({
        url: `${this.baseEurekaUrl()}${this.config.instance.app}/${this.getInstanceId()}`
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          console.log('eureka heartbeat success');
        } else {
          console.warn('eureka heartbeat FAILED, will retry. ' + (error ? error : `status: ${response.statusCode} body: ${body}`));
        }
      });
    }, this.config.eureka.heartbeatInterval || 30000);
  }

  /*
    Base Eureka server URL + path
  */
  baseEurekaUrl() {
    return `http:\/\/${this.config.eureka.host}:${this.config.eureka.port}/eureka/v2/apps/`;
  }

  /*
    Helper method to get the instance ID. If the datacenter is AWS, this will be the
    instance-id in the metadata. Else, it's the hostName.
  */
  getInstanceId() {
    if (this.config.instance.dataCenterInfo === 'Amazon') {
      return this.config.instance.dataCenterInfo.metadata['instance-id'];
    }
    return this.config.instance.hostName;
  }

  /*
    Retrieves a list of instances from server given an appId
  */
  getInstancesByAppId(appId) {
    if (!appId) {
      throw new Error('Unable to query instances with no appId');
    }
    request.get({
      url: `${this.baseEurekaUrl()}${appId}`,
      headers: {Accept: 'application/json'}
    }, (error, response, body) => {
      if (!error && response.statusCode === 200) {
        return body;
      }
      throw new Error('Unable to retrieve instances for appId: ', appId);
    });
  }
}
<commit_msg>Removing comment.<commit_after>import request from 'request';
import fs from 'fs';
import yaml from 'js-yaml';
import merge from 'deepmerge';
import path from 'path';

/*
  Eureka JS client
  This module handles registration with a Eureka server, as well as heartbeats
  for reporting instance health.
*/

export default class Eureka {

  constructor(config) {
    console.log('initializing eureka client');
    this.config = config;
    if (!config) {
      // Load up the current working directory and the environment:
      const cwd = process.cwd();
      const env = process.env.NODE_ENV || 'development';

      // Attempt to load the config file:
      try {
        config = yaml.safeLoad(fs.readFileSync(path.join(cwd, 'eureka-client.yml'), 'utf8'));
      } catch(e) {}

      try {
        const envConfig = yaml.safeLoad(fs.readFileSync(path.join(cwd, `eureka-client-${env}.yml`), 'utf8'));
        config = merge(config, envConfig);
      } catch(e) {}

      this.config = config;
    }
    if (!this.config) {
      throw new Error('missing configuration file.');
    }
    if (!this.config.instance || !this.config.eureka) {
      throw new Error('missing instance / eureka configuration.');
    }
    this.register();
  }

  /*
    Registers with the Eureka server and initializes heartbeats on registration success.
  */
  register() {
    this.config.instance.status = 'UP';
    request.post({
      url: this.baseEurekaUrl() + this.config.instance.app,
      json: true,
      body: {instance: this.config.instance}
    }, (error, response, body) => {
      if (!error && response.statusCode === 204) {
        console.log('registered with eureka: ', `${this.config.instance.app}/${this.getInstanceId()}`);
        this.startHeartbeats();
      } else {
        throw new Error('eureka registration FAILED: ' + (error ? error : `status: ${response.statusCode} body: ${body}`));
      }
    });
  }

  /*
    Sets up heartbeats on interval for the life of the application.
    Heartbeat interval by setting configuration property: eureka.heartbeatInterval
  */
  startHeartbeats() {
    this.heartbeat = setInterval(() => {
      request.put({
        url: `${this.baseEurekaUrl()}${this.config.instance.app}/${this.getInstanceId()}`
      }, (error, response, body) => {
        if (!error && response.statusCode === 200) {
          console.log('eureka heartbeat success');
        } else {
          console.warn('eureka heartbeat FAILED, will retry. ' + (error ? error : `status: ${response.statusCode} body: ${body}`));
        }
      });
    }, this.config.eureka.heartbeatInterval || 30000);
  }

  /*
    Base Eureka server URL + path
  */
  baseEurekaUrl() {
    return `http:\/\/${this.config.eureka.host}:${this.config.eureka.port}/eureka/v2/apps/`;
  }

  /*
    Helper method to get the instance ID. If the datacenter is AWS, this will be the
    instance-id in the metadata. Else, it's the hostName.
  */
  getInstanceId() {
    if (this.config.instance.dataCenterInfo === 'Amazon') {
      return this.config.instance.dataCenterInfo.metadata['instance-id'];
    }
    return this.config.instance.hostName;
  }

  /*
    Retrieves a list of instances from server given an appId
  */
  getInstancesByAppId(appId) {
    if (!appId) {
      throw new Error('Unable to query instances with no appId');
    }
    request.get({
      url: `${this.baseEurekaUrl()}${appId}`,
      headers: {Accept: 'application/json'}
    }, (error, response, body) => {
      if (!error && response.statusCode === 200) {
        return body;
      }
      throw new Error('Unable to retrieve instances for appId: ', appId);
    });
  }
}
<|endoftext|>


<commit_before>export { startEventRecorder, stopEventRecorder };
import { ALL } from "./core.js";

const startEventRecorder = (core) => {
    const events = [];
    const listener = event => {
        events.push(event);
    };
    core.on(ALL, listener);
    return {
        events,
        listener,
    };
};

const stopEventRecorder = (core, eventRecorder) => {
    const {
        events,
        listener,
    } = eventRecorder;
    core.off(ALL, listener);
    events.length = 0;
};
<commit_msg>less restrictive input<commit_after>export { startEventRecorder, stopEventRecorder };
import { ALL } from "./core.js";

const startEventRecorder = (core) => {
    const events = [];
    const listener = event => {
        events.push(event);
    };
    core.on(ALL, listener);
    return {
        events,
        listener,
    };
};

const stopEventRecorder = (core, eventRecorder) => {
    if (!eventRecorder) {
        return;
    }
    const {
        events,
        listener,
    } = eventRecorder;
    core.off(ALL, listener);
    events.length = 0;
};
<|endoftext|>


<commit_before>"use strict";

var Document = require("substance-document");

var Figure = function(node, document) {
  Document.Composite.call(this, node, document);
};

Figure.type = {
  "parent": "content",
  "properties": {
    "url": "string",
    "caption": "paragraph"
  }
};

Figure.Prototype = function() {

  this.insertOperation = function(startChar, text) {
    return null;
  };

  this.deleteOperation = function(startChar, endChar) {
    return null;
  };

  this.hasCaption = function() {
    return (!!this.properties.caption);
  };

  this.getNodes = function() {
    var nodes = [];
    if (this.properties.caption) nodes.push(this.properties.caption);
    return nodes;
  };

  this.getCaption = function() {
    if (this.properties.caption) return this.document.get(this.properties.caption);
  };
};

Figure.Prototype.prototype = Document.Composite.prototype;
Figure.prototype = new Figure.Prototype();
Figure.prototype.constructor = Figure;

// a factory method to create nodes more conveniently
// Supported
//  - id: unique id
//  - url: a relative path or a web URL
//  - caption: a string used as caption
Figure.create = function(data) {

  var result = {};

  var figId = data.id;
  var figure = {
    id: figId,
    type: "text",
    url: data.url
  };

  if (data.caption) {
    var captionId = "caption_" + data.id;
    var caption = {
      id: captionId,
      type: "text",
      content: data.caption
    };
    result[captionId] = caption;
    figure.caption = captionId;
  }

  result[figId] = figure;
  return result;
};

Document.Node.defineProperties(Figure.prototype, ["url", "caption"]);

module.exports = Figure;
<commit_msg>Fix regression.<commit_after>"use strict";

var Document = require("substance-document");

var Figure = function(node, document) {
  Document.Composite.call(this, node, document);
};

Figure.type = {
  "parent": "content",
  "properties": {
    "url": "string",
    "caption": "paragraph"
  }
};

Figure.Prototype = function() {

  this.insertOperation = function(startChar, text) {
    return null;
  };

  this.deleteOperation = function(startChar, endChar) {
    return null;
  };

  this.hasCaption = function() {
    return (!!this.properties.caption);
  };

  this.getNodes = function() {
    var nodes = [];
    if (this.properties.caption) nodes.push(this.properties.caption);
    return nodes;
  };

  this.getCaption = function() {
    if (this.properties.caption) return this.document.get(this.properties.caption);
  };
};

Figure.Prototype.prototype = Document.Composite.prototype;
Figure.prototype = new Figure.Prototype();
Figure.prototype.constructor = Figure;

// a factory method to create nodes more conveniently
// Supported
//  - id: unique id
//  - url: a relative path or a web URL
//  - caption: a string used as caption
Figure.create = function(data) {

  var result = {};

  var figId = data.id;
  var figure = {
    id: figId,
    type: "figure",
    url: data.url
  };

  if (data.caption) {
    var captionId = "caption_" + data.id;
    var caption = {
      id: captionId,
      type: "text",
      content: data.caption
    };
    result[captionId] = caption;
    figure.caption = captionId;
  }

  result[figId] = figure;
  return result;
};

Document.Node.defineProperties(Figure.prototype, ["url", "caption"]);

module.exports = Figure;
<|endoftext|>


<commit_before>/**
 * @file        Main export of the PIXI filters library
 * @author      Mat Groves <mat@goodboydigital.com>
 * @copyright   2013-2015 GoodBoyDigital
 * @license     {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License}
 */

/**
 * @namespace PIXI.filters
 */
module.exports = {
    AsciiFilter:        require('./ascii/AsciiFilter'),
    BloomFilter:        require('./bloom/BloomFilter'),
    BlurFilter:         require('./blur/BlurFilter'),
    BlurXFilter:        require('./blur/BlurXFilter'),
    BlurYFilter:        require('./blur/BlurYFilter'),
    ColorMatrixFilter:  require('./color/ColorMatrixFilter'),
    ColorStepFilter:    require('./color/ColorStepFilter'),
    ConvolutionFilter:  require('./convolution/ConvolutionFilter'),
    CrossHatchFilter:   require('./crosshatch/CrossHatchFilter'),
    DisplacementFilter: require('./displacement/DisplacementFilter'),
    DotScreenFilter:    require('./dot/DotScreenFilter'),
    GrayFilter:         require('./gray/GrayFilter'),
    InvertFilter:       require('./invert/InvertFilter'),
    NoiseFilter:        require('./noise/NoiseFilter'),
    NormalMapFilter:    require('./normal/NormalMapFilter'),
    PixelateFilter:     require('./pixelate/PixelateFilter'),
    RGBSplitFilter:     require('./rgb/RGBSplitFilter'),
    ShockwaveFilter:    require('./shockwave/ShockwaveFilter'),
    SepiaFilter:        require('./sepia/SepiaFilter'),
    SmartBlurFilter:    require('./blur/SmartBlurFilter'),
    TiltShiftFilter:    require('./tiltshift/TiltShiftFilter'),
    TiltShiftXFilter:   require('./tiltshift/TiltShiftXFilter'),
    TiltShiftYFilter:   require('./tiltshift/TiltShiftYFilter'),
    TwistFilter:        require('./twist/TwistFilter')
};
<commit_msg>Added internal filters to filter index<commit_after>/**
 * @file        Main export of the PIXI filters library
 * @author      Mat Groves <mat@goodboydigital.com>
 * @copyright   2013-2015 GoodBoyDigital
 * @license     {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License}
 */

/**
 * @namespace PIXI.filters
 */
module.exports = {
    // expose some internal filters...
    AbstractFilter:     require('../core/renderers/webgl/filters/AbstractFilter'),
    FXAAFilter:         require('../core/renderers/webgl/filters/FXAAFilter'),
    SpriteMaskFilter:   require('../core/renderers/webgl/filters/SpriteMaskFilter'),
    // add the rest!
    AsciiFilter:        require('./ascii/AsciiFilter'),
    BloomFilter:        require('./bloom/BloomFilter'),
    BlurFilter:         require('./blur/BlurFilter'),
    BlurXFilter:        require('./blur/BlurXFilter'),
    BlurYFilter:        require('./blur/BlurYFilter'),
    ColorMatrixFilter:  require('./color/ColorMatrixFilter'),
    ColorStepFilter:    require('./color/ColorStepFilter'),
    ConvolutionFilter:  require('./convolution/ConvolutionFilter'),
    CrossHatchFilter:   require('./crosshatch/CrossHatchFilter'),
    DisplacementFilter: require('./displacement/DisplacementFilter'),
    DotScreenFilter:    require('./dot/DotScreenFilter'),
    GrayFilter:         require('./gray/GrayFilter'),
    InvertFilter:       require('./invert/InvertFilter'),
    NoiseFilter:        require('./noise/NoiseFilter'),
    NormalMapFilter:    require('./normal/NormalMapFilter'),
    PixelateFilter:     require('./pixelate/PixelateFilter'),
    RGBSplitFilter:     require('./rgb/RGBSplitFilter'),
    ShockwaveFilter:    require('./shockwave/ShockwaveFilter'),
    SepiaFilter:        require('./sepia/SepiaFilter'),
    SmartBlurFilter:    require('./blur/SmartBlurFilter'),
    TiltShiftFilter:    require('./tiltshift/TiltShiftFilter'),
    TiltShiftXFilter:   require('./tiltshift/TiltShiftXFilter'),
    TiltShiftYFilter:   require('./tiltshift/TiltShiftYFilter'),
    TwistFilter:        require('./twist/TwistFilter')
};
<|endoftext|>


<commit_before>/**
@license
Copyright 2017 The FOAM Authors. All Rights Reserved.
http://www.apache.org/licenses/LICENSE-2.0
*/

foam.CLASS({
  package: 'foam.core',
  name: 'PropertyXMLRefinement',
  refines: 'foam.core.Property',

  properties: [
    {
      class: 'Boolean',
      name: 'xmlAttribute',
      value: false
    },
    {
      class: 'Boolean',
      name: 'xmlTextNode',
      value: false
    },
    {
      name: 'fromXML',
      value: function fromXML(value, ctx, prop, xml) {
        return foam.xml.parse(value, null, ctx);
      }
    },
    {
      name: 'toXML',
      value: function toXML(value, Outputter) { return value; }
    }
  ],

  methods: [
    function outputXML(o) {
      o.output({ class: '__Property__', forClass_: this.forClass_, name: this.name });
    }
  ]
});

/** Add toXML() method to FObject. **/
foam.CLASS({
  package: 'foam.core',
  name: 'FObjectXMLStringify',
  refines: 'foam.core.FObject',

  methods: [
    /**
      Output as a pretty-printed XML-ish String.
      Use for debugging/testing purposes. If you want actual
      XML output, use foam.xml.* instead.
    */
    function toXML() {
      return foam.xml.Pretty.stringify(this);
    }
  ]
});

/** XML Outputter **/
foam.CLASS({
  package: 'foam.xml',
  name: "Outputter",

  documentation: 'XML Outputter.',

  properties: [
    {
      class: 'String',
      name: 'buf_',
      value: ''
    },
    {
      class: 'Int',
      name: 'indentLevel_',
      value: 0
    },
    {
      class: 'String',
      name: 'indentStr',
      value: '\t'
    },
    {
      class: 'String',
      name: 'nlStr',
      value: '\n'
    },
    {
      class: 'Boolean',
      name: 'outputDefaultValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'outputDefinedValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'formatDatesAsNumbers',
      value: false
    },
    {
      class: 'Boolean',
      name: 'outputClassNames',
      value: true
    },
    {
      class: 'Function',
      name: 'propertyPredicate',
      value: function(o, p) { return ! p.transient; }
    },
    {
      class: 'Boolean',
      name: 'useShortNames',
      value: false
    },
        {
      class: 'Boolean',
      name: 'sortObjectKeys',
      value: false
    },
    {
      class: 'Boolean',
      name: 'pretty',
      value: true,
      postSet: function(_, p) {
        if ( p ) {
          this.clearProperty('indentStr');
          this.clearProperty('nlStr');
          this.clearProperty('useShortNames');
        } else {
          this.indentStr = this.nlStr = null;
        }
      }
    }
  ],

  methods: [

    function reset() {
      this.indentLevel_ = 0;
      this.buf_ = '';
      return this;
    },

    function escape(str) {
        return str && str.toString()
            .replace(/&/g, '&')
            .replace(/</g, '<')
            .replace(/>/g, '>');
    },

    function maybeEscapeKey(str) {
      return this.alwaysQuoteKeys || ! /^[a-zA-Z\$_][0-9a-zA-Z$_]*$/.test(str) ?
          '"' + str + '"' :
          str ;
    },

    function escapeAttr(str) {
        return str && str.replace(/"/g, '"');
    },

    function out() {
      for ( var i = 0 ; i < arguments.length ; i++ ) this.buf_ += arguments[i];
      return this;
    },

    function start(c) {
      if ( c ) this.out(c);
      if ( this.indentStr ) {
        this.indentLevel_++;
        this.indent();
      }
    },

    function end(c) {
      if ( this.indent ) {
        this.indentLevel_--;
      }
      if ( c ) this.nl().indent().out(c);
      return this;
    },

    function nl() {
      if ( this.nlStr && this.nlStr.length ) {
        this.out(this.nlStr);
      }
      return this;
    },

    function indent() {
      for ( var i = 0 ; i < this.indentLevel_ ; i++ ) this.out(this.indentStr);
      return this;
    },

    function outputPropertyName(p) {
      this.out(this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name));
      return this;
    },

    function outputAttributes(v) {
      var attributes = v.cls_.getAxiomsByClass(foam.core.Property).filter(function (p) { return p.xmlAttribute });
      if ( attributes.length === 0 ) return this;

      for ( var i = 0 ; i < attributes.length ; i++ ) {
        this.out(' ' + attributes[i].name + '="' + this.escapeAttr(attributes[i].get(v)) + '"');
      }
      return this;
    },

    function propertyName(p) {
      return this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name)
    },

    function outputProperty_(o, p) {
      if ( ! this.propertyPredicate(o, p ) ) return;
      // don't output default values unless value is defined and outputDefinedValues set to true
      if ( this.outputDefinedValues ) {
        if ( ! o.hasOwnProperty(p.name) ) return;
      } else if ( this.outputDefaultValues ) {
        if ( p.isDefaultValue(o[p.name]) ) return;
      }

      var v = o[p.name];
      if ( ! v || ( v instanceof Array && v.length === 0 ) ) {
        return;
      }

      this.nl().indent();
      this.outputProperty(v, p);
    },

    {
      name: 'outputProperty',
      code: foam.mmethod({
        Undefined:    function(v, p) {},
        String:       function(v, p) { this.outputPrimitive(v, p); },
        Number:       function(v, p) { this.outputPrimitive(v, p); },
        Boolean:      function(v, p) { this.outputPrimitive(v, p); },
        Date:         function(v, p) { this.outputPrimitive(v, p); },
        AbstractEnum: function(v, p) { this.outputPrimitive(v.name, p); },
        Array:     function(v, p) {
          for ( var i = 0 ; i < v.length ; i++ ) {
            if ( foam.core.FObjectArray.isInstance(p) ) {
              // output FObject array
              this.start('<' + this.propertyName(p) + '>');
              this.output(p.toXML(v[i], this));
              this.end('</' + this.propertyName(p) + '>');
            } else {
              // output primitive array
              this.outputPrimitive(v[i], p);
            }

            // new line and indent except on last element
            if ( i != v.length - 1 ) this.nl().indent();
          }
        },
        FObject: function(v, p) {
          if ( v.text ) {
            // if v.text exists then we have attributes
            // check if the value is an FObject and structure XML accordingly
            if ( foam.core.FObject.isInstance(v.text) ) {
              this.start('<' + this.propertyName(p) + this.outputAttributes(v) + '>');
              this.output(p.toXML(v, this));
              this.end('</' +  this.propertyName(p) + '>');
            } else {
              this.out('<').outputPropertyName(p).outputAttributes(v).out('>');
              this.out(p.toXML(v.text, this));
              this.out('</').outputPropertyName(p).out('>');
            }
          } else {
            // assume no attributes
            this.start('<' + this.propertyName(p) + '>');
            this.output(p.toXML(v, this));
            this.end('</' +  this.propertyName(p) + '>');
          }
        }
      })
    },

    function outputPrimitive(v, p){
      this.out('<').outputPropertyName(p).out('>');
      this.output(p.toXML(v, this));
      this.out('</').outputPropertyName(p).out('>');
    },

    function outputDate(o) {
      if ( this.formatDatesAsNumbers ) {
        this.out(o.valueOf());
      } else {
        this.out(o.toISOString());
      }
    },

    function outputFunction(o) {
      if ( this.formatFunctionsAsStrings ) {
        this.output(o.toString());
      } else {
        this.out(o.toString());
      }
    },

    function outputObjectKeyValue_(key, value, first) {
      if ( ! first ) this.out(',').nl().indent();
      this.out(this.maybeEscapeKey(key), ':').output(value);
    },

    function outputObjectKeyValues_(o) {
      var first = true;
      for ( var key in o ) {
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    function outputSortedObjectKeyValues_(o) {
      var key, keys = [];

      for ( key in o ) keys.push(key);
      keys.sort();

      var first = true;
      for ( var i = 0 ; i < keys.length; i++ ) {
        key = keys[i];
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    {
      name: 'output',
      code: foam.mmethod({
        Undefined:    function(o) { this.out('null'); },
        Null:         function(o) { this.out('null'); },
        String:       function(o) { this.out(this.escape(o)); },
        Number:       function(o) { this.out(o); },
        Boolean:      function(o) { this.out(o); },
        Date:         function(o) { this.outputDate(o); },
        Function:     function(o) { this.outputFunction(o); },
        AbstractEnum: function(o) { },
        FObject:      function(o) {
          if ( o.outputXML ) {
            o.outputXML(this)
            return;
          }

          var clsName = o.cls_.id;
          // Iterate through properties and output
          var ps = o.cls_.getAxiomsByClass(foam.core.Property);
          for ( var i = 0 ; i < ps.length ; i++ ) {
            // skip outputting of attributes
            if ( ps[i].xmlAttribute ) continue;
            this.outputProperty_(o, ps[i]);
          }
        },
        Array: function(o, opt_cls) {
          this.start('<objects>\n');
          var cls = this.getCls(opt_cls);
          for ( var i = 0 ; i < o.length ; i++ ) {
            this.output(o[i], cls);
            if ( i < o.length-1 ) this.out('\n').nl().indent();
          }
          this.end('\n</objects>');
        },
        Object:        function(o) {
          if ( o.outputXML ) {
            o.outputXML(this);
          } else {
            var oName = o.name;
            this.start("<object name='" + oName + "'>");
            if ( this.sortObjectKeys ) {
              this.outputSortedObjectKeyValues_(o);
            } else {
              this.outputObjectKeyValues_(o);
            }
            this.end('</object>');
          }
        }
      })
    },

    function stringify(o) {
      // Root tags of objects for array of FObjects
      this.output(o);
      var ret = this.buf_;
      this.reset(); // reset to avoid retaining garbage
      return ret;
    },

    function objectify(doc, cls) {
      var obj = cls.create();
      var children = doc.children;

      for ( var i = 0 ; i < children.length ; i++ ) {
        // fetch property based on xml tag name since they may not be in order
        var node = children[i];
        var prop = obj.cls_.getAxiomByName(node.tagName);

        if ( foam.core.FObjectProperty.isInstance(prop) ) {
          // parse FObjectProperty
          prop.set(obj, this.objectify(node, prop.of));
        } else if ( foam.core.FObjectArray.isInstance(prop) ) {
          // parse array property
          prop.get(obj).push(this.objectify(node, foam.lookup(prop.of)));

        } else if ( foam.core.StringArray.isInstance(prop) ) {
          // parse string array
          prop.get(obj).push(node.firstChild ? node.firstChild.nodeValue : null);
        } else {
          // parse property
          prop.set(obj, node.firstChild ? node.firstChild.nodeValue : null);
        }
      }

      // check to see if text property exists
      var textProp = obj.cls_.getAxiomByName('text');
      if ( textProp ) {
        // parse attributes if they exist
        var attributes = doc.attributes;
        for ( var i = 0 ; i < attributes.length ; i++ ) {
          var attribute = attributes[i];
          var prop = obj.cls_.getAxiomByName(attribute.name);
          // don't need to check for types as attributes are always simple types
          prop.set(obj, attribute.value);
        }

        if ( foam.core.FObjectProperty.isInstance(textProp) ) {
          textProp.set(obj, this.objectify(doc, textProp.of));
        } else {
          textProp.set(obj, doc.firstChild ? doc.firstChild.nodeValue : null);
        }
      }

      return obj;
    },

    function parseString(str, opt_class) {
      // create DOM
      var parser = new DOMParser();
      var doc = parser.parseFromString(str, 'text/xml');
      var root = doc.firstChild;

      var rootClass = root.getAttribute('class');
      if ( rootClass )
        return this.objectify(root, foam.lookup(rootClass));

      if ( opt_class ) {
        // lookup class if given a string
        if ( typeof(opt_class) === 'string' )
          opt_class = foam.lookup(opt_class);
        return this.objectify(root, opt_class);
      }

      throw new Error('Class not provided');
    },

    function getCls(opt_cls) {
      return foam.String.isInstance(opt_cls);
    }
  ]
});

/** Library of pre-configured XML Outputters. **/
foam.LIB({
  name: 'foam.xml',

  constants: {
    // Pretty Print
    Pretty: foam.xml.Outputter.create({
      outputDefaultValues: false,
      outputDefinedValues: true
    }),

    // Compact output (not pretty)
    Compact: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: false
    }),

    // Shorter than Compact (uses short-names if available)
    Short: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: false,
      // TODO: No deserialization support for shortnames yet.
      //      useShortNames: true,
      useShortNames: false,
    })
  },

  methods: [
    function stringify(o) {
      return foam.xml.Compact.stringify(o);
    },

    function objectify(o) {
      return foam.xml.Compact.objectify(o);
    }
  ]
});
<commit_msg>Add Object XML output support.<commit_after>/**
@license
Copyright 2017 The FOAM Authors. All Rights Reserved.
http://www.apache.org/licenses/LICENSE-2.0
*/

foam.CLASS({
  package: 'foam.core',
  name: 'PropertyXMLRefinement',
  refines: 'foam.core.Property',

  properties: [
    {
      class: 'Boolean',
      name: 'xmlAttribute',
      value: false
    },
    {
      class: 'Boolean',
      name: 'xmlTextNode',
      value: false
    },
    {
      name: 'fromXML',
      value: function fromXML(value, ctx, prop, xml) {
        return foam.xml.parse(value, null, ctx);
      }
    },
    {
      name: 'toXML',
      value: function toXML(value, Outputter) { return value; }
    }
  ],

  methods: [
    function outputXML(o) {
      o.output({ class: '__Property__', forClass_: this.forClass_, name: this.name });
    }
  ]
});

/** Add toXML() method to FObject. **/
foam.CLASS({
  package: 'foam.core',
  name: 'FObjectXMLStringify',
  refines: 'foam.core.FObject',

  methods: [
    /**
      Output as a pretty-printed XML-ish String.
      Use for debugging/testing purposes. If you want actual
      XML output, use foam.xml.* instead.
    */
    function toXML() {
      return foam.xml.Pretty.stringify(this);
    }
  ]
});

/** XML Outputter **/
foam.CLASS({
  package: 'foam.xml',
  name: "Outputter",

  documentation: 'XML Outputter.',

  properties: [
    {
      class: 'String',
      name: 'buf_',
      value: ''
    },
    {
      class: 'Int',
      name: 'indentLevel_',
      value: 0
    },
    {
      class: 'String',
      name: 'indentStr',
      value: '\t'
    },
    {
      class: 'String',
      name: 'nlStr',
      value: '\n'
    },
    {
      class: 'Boolean',
      name: 'outputDefaultValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'outputDefinedValues',
      value: true
    },
    {
      class: 'Boolean',
      name: 'formatDatesAsNumbers',
      value: false
    },
    {
      class: 'Boolean',
      name: 'outputClassNames',
      value: true
    },
    {
      class: 'Function',
      name: 'propertyPredicate',
      value: function(o, p) { return ! p.transient; }
    },
    {
      class: 'Boolean',
      name: 'useShortNames',
      value: false
    },
        {
      class: 'Boolean',
      name: 'sortObjectKeys',
      value: false
    },
    {
      class: 'Boolean',
      name: 'pretty',
      value: true,
      postSet: function(_, p) {
        if ( p ) {
          this.clearProperty('indentStr');
          this.clearProperty('nlStr');
          this.clearProperty('useShortNames');
        } else {
          this.indentStr = this.nlStr = null;
        }
      }
    }
  ],

  methods: [

    function reset() {
      this.indentLevel_ = 0;
      this.buf_ = '';
      return this;
    },

    function escape(str) {
        return str && str.toString()
            .replace(/&/g, '&')
            .replace(/</g, '<')
            .replace(/>/g, '>');
    },

    function maybeEscapeKey(str) {
      return this.alwaysQuoteKeys || ! /^[a-zA-Z\$_][0-9a-zA-Z$_]*$/.test(str) ?
          '"' + str + '"' :
          str ;
    },

    function escapeAttr(str) {
        return str && str.replace(/"/g, '"');
    },

    function out() {
      for ( var i = 0 ; i < arguments.length ; i++ ) this.buf_ += arguments[i];
      return this;
    },

    function start(c) {
      if ( c ) this.out(c);
      if ( this.indentStr ) {
        this.indentLevel_++;
        this.indent();
      }
    },

    function end(c) {
      if ( this.indent ) {
        this.indentLevel_--;
      }
      if ( c ) this.nl().indent().out(c);
      return this;
    },

    function nl() {
      if ( this.nlStr && this.nlStr.length ) {
        this.out(this.nlStr);
      }
      return this;
    },

    function indent() {
      for ( var i = 0 ; i < this.indentLevel_ ; i++ ) this.out(this.indentStr);
      return this;
    },

    function outputPropertyName(p) {
      this.out(this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name));
      return this;
    },

    function outputAttributes(v) {
      var attributes = v.cls_.getAxiomsByClass(foam.core.Property).filter(function (p) { return p.xmlAttribute });
      if ( attributes.length === 0 ) return this;

      for ( var i = 0 ; i < attributes.length ; i++ ) {
        this.out(' ' + attributes[i].name + '="' + this.escapeAttr(attributes[i].get(v)) + '"');
      }
      return this;
    },

    function propertyName(p) {
      return this.maybeEscapeKey(this.useShortNames && p.shortName ? p.shortName : p.name)
    },

    function outputProperty_(o, p) {
      if ( ! this.propertyPredicate(o, p ) ) return;
      // don't output default values unless value is defined and outputDefinedValues set to true
      if ( this.outputDefinedValues ) {
        if ( ! o.hasOwnProperty(p.name) ) return;
      } else if ( this.outputDefaultValues ) {
        if ( p.isDefaultValue(o[p.name]) ) return;
      }

      var v = o[p.name];
      if ( ! v || ( v instanceof Array && v.length === 0 ) ) {
        return;
      }

      this.nl().indent();
      this.outputProperty(v, p);
    },

    {
      name: 'outputProperty',
      code: foam.mmethod({
        Undefined:    function(v, p) {},
        String:       function(v, p) { this.outputPrimitive(v, p); },
        Number:       function(v, p) { this.outputPrimitive(v, p); },
        Boolean:      function(v, p) { this.outputPrimitive(v, p); },
        Date:         function(v, p) { this.outputPrimitive(v, p); },
        AbstractEnum: function(v, p) { this.outputPrimitive(v.name, p); },
        Array:     function(v, p) {
          for ( var i = 0 ; i < v.length ; i++ ) {
            if ( foam.core.FObjectArray.isInstance(p) ) {
              // output FObject array
              this.start('<' + this.propertyName(p) + '>');
              this.output(p.toXML(v[i], this));
              this.end('</' + this.propertyName(p) + '>');
            } else {
              // output primitive array
              this.outputPrimitive(v[i], p);
            }

            // new line and indent except on last element
            if ( i != v.length - 1 ) this.nl().indent();
          }
        },
        FObject: function(v, p) {
          if ( v.text ) {
            // if v.text exists then we have attributes
            // check if the value is an FObject and structure XML accordingly
            if ( foam.core.FObject.isInstance(v.text) ) {
              this.start('<' + this.propertyName(p) + this.outputAttributes(v) + '>');
              this.output(p.toXML(v, this));
              this.end('</' +  this.propertyName(p) + '>');
            } else {
              this.out('<').outputPropertyName(p).outputAttributes(v).out('>');
              this.out(p.toXML(v.text, this));
              this.out('</').outputPropertyName(p).out('>');
            }
          } else {
            // assume no attributes
            this.start('<' + this.propertyName(p) + '>');
            this.output(p.toXML(v, this));
            this.end('</' +  this.propertyName(p) + '>');
          }
        },
        Object: function(v, p) { this.outputPrimitive(v, p); },
      })
    },

    function outputPrimitive(v, p){
      this.out('<').outputPropertyName(p).out('>');
      this.output(p.toXML(v, this));
      this.out('</').outputPropertyName(p).out('>');
    },

    function outputDate(o) {
      if ( this.formatDatesAsNumbers ) {
        this.out(o.valueOf());
      } else {
        this.out(o.toISOString());
      }
    },

    function outputFunction(o) {
      if ( this.formatFunctionsAsStrings ) {
        this.output(o.toString());
      } else {
        this.out(o.toString());
      }
    },

    function outputObjectKeyValue_(key, value, first) {
      if ( ! first ) this.out(',').nl().indent();
      this.out(this.maybeEscapeKey(key), ':').output(value);
    },

    function outputObjectKeyValues_(o) {
      var first = true;
      for ( var key in o ) {
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    function outputSortedObjectKeyValues_(o) {
      var key, keys = [];

      for ( key in o ) keys.push(key);
      keys.sort();

      var first = true;
      for ( var i = 0 ; i < keys.length; i++ ) {
        key = keys[i];
        this.outputObjectKeyValue_(key, o[key], first);
        first = false;
      }
    },

    {
      name: 'output',
      code: foam.mmethod({
        Undefined:    function(o) { this.out('null'); },
        Null:         function(o) { this.out('null'); },
        String:       function(o) { this.out(this.escape(o)); },
        Number:       function(o) { this.out(o); },
        Boolean:      function(o) { this.out(o); },
        Date:         function(o) { this.outputDate(o); },
        Function:     function(o) { this.outputFunction(o); },
        AbstractEnum: function(o) { },
        FObject:      function(o) {
          if ( o.outputXML ) {
            o.outputXML(this)
            return;
          }

          var clsName = o.cls_.id;
          // Iterate through properties and output
          var ps = o.cls_.getAxiomsByClass(foam.core.Property);
          for ( var i = 0 ; i < ps.length ; i++ ) {
            // skip outputting of attributes
            if ( ps[i].xmlAttribute ) continue;
            this.outputProperty_(o, ps[i]);
          }
        },
        Array: function(o, opt_cls) {
          this.start('<objects>\n');
          var cls = this.getCls(opt_cls);
          for ( var i = 0 ; i < o.length ; i++ ) {
            this.output(o[i], cls);
            if ( i < o.length-1 ) this.out('\n').nl().indent();
          }
          this.end('\n</objects>');
        },
        Object:        function(o) {
          if ( o.outputXML ) {
            o.outputXML(this);
          } else {
            var oName = o.name;
            this.start("<object name='" + oName + "'>");
            if ( this.sortObjectKeys ) {
              this.outputSortedObjectKeyValues_(o);
            } else {
              this.outputObjectKeyValues_(o);
            }
            this.end('</object>');
          }
        }
      })
    },

    function stringify(o) {
      // Root tags of objects for array of FObjects
      this.output(o);
      var ret = this.buf_;
      this.reset(); // reset to avoid retaining garbage
      return ret;
    },

    function objectify(doc, cls) {
      var obj = cls.create();
      var children = doc.children;

      for ( var i = 0 ; i < children.length ; i++ ) {
        // fetch property based on xml tag name since they may not be in order
        var node = children[i];
        var prop = obj.cls_.getAxiomByName(node.tagName);

        if ( foam.core.FObjectProperty.isInstance(prop) ) {
          // parse FObjectProperty
          prop.set(obj, this.objectify(node, prop.of));
        } else if ( foam.core.FObjectArray.isInstance(prop) ) {
          // parse array property
          prop.get(obj).push(this.objectify(node, foam.lookup(prop.of)));

        } else if ( foam.core.StringArray.isInstance(prop) ) {
          // parse string array
          prop.get(obj).push(node.firstChild ? node.firstChild.nodeValue : null);
        } else {
          // parse property
          prop.set(obj, node.firstChild ? node.firstChild.nodeValue : null);
        }
      }

      // check to see if text property exists
      var textProp = obj.cls_.getAxiomByName('text');
      if ( textProp ) {
        // parse attributes if they exist
        var attributes = doc.attributes;
        for ( var i = 0 ; i < attributes.length ; i++ ) {
          var attribute = attributes[i];
          var prop = obj.cls_.getAxiomByName(attribute.name);
          // don't need to check for types as attributes are always simple types
          prop.set(obj, attribute.value);
        }

        if ( foam.core.FObjectProperty.isInstance(textProp) ) {
          textProp.set(obj, this.objectify(doc, textProp.of));
        } else {
          textProp.set(obj, doc.firstChild ? doc.firstChild.nodeValue : null);
        }
      }

      return obj;
    },

    function parseString(str, opt_class) {
      // create DOM
      var parser = new DOMParser();
      var doc = parser.parseFromString(str, 'text/xml');
      var root = doc.firstChild;

      var rootClass = root.getAttribute('class');
      if ( rootClass )
        return this.objectify(root, foam.lookup(rootClass));

      if ( opt_class ) {
        // lookup class if given a string
        if ( typeof(opt_class) === 'string' )
          opt_class = foam.lookup(opt_class);
        return this.objectify(root, opt_class);
      }

      throw new Error('Class not provided');
    },

    function getCls(opt_cls) {
      return foam.String.isInstance(opt_cls);
    }
  ]
});

/** Library of pre-configured XML Outputters. **/
foam.LIB({
  name: 'foam.xml',

  constants: {
    // Pretty Print
    Pretty: foam.xml.Outputter.create({
      outputDefaultValues: false,
      outputDefinedValues: true
    }),

    // Compact output (not pretty)
    Compact: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: false
    }),

    // Shorter than Compact (uses short-names if available)
    Short: foam.xml.Outputter.create({
      pretty: false,
      formatDatesAsNumbers: true,
      outputDefaultValues: false,
      outputDefinedValues: false,
      // TODO: No deserialization support for shortnames yet.
      //      useShortNames: true,
      useShortNames: false,
    })
  },

  methods: [
    function stringify(o) {
      return foam.xml.Compact.stringify(o);
    },

    function objectify(o) {
      return foam.xml.Compact.objectify(o);
    }
  ]
});
<|endoftext|>