<commit_before>var L = require('leaflet'),
	Nominatim = require('./geocoders/nominatim').class;

module.exports = {
	class: L.Control.extend({
		options: {
			showResultIcons: false,
			collapsed: true,
			expand: 'click',
			position: 'topright',
			placeholder: 'Search...',
			errorMessage: 'Nothing found.',
			suggestMinLength: 3,
			suggestTimeout: 250,
			defaultMarkGeocode: true
		},

		includes: L.Mixin.Events,

		initialize: function (options) {
			L.Util.setOptions(this, options);
			if (!this.options.geocoder) {
				this.options.geocoder = new Nominatim();
			}
		},

		onAdd: function (map) {
			var className = 'leaflet-control-geocoder',
			    container = L.DomUtil.create('div', className + ' leaflet-bar'),
			    icon = L.DomUtil.create('button', className + '-icon', container),
			    form = this._form = L.DomUtil.create('div', className + '-form', container),
			    input;

			this._map = map;
			this._container = container;

			icon.innerHTML = ' ';
			icon.type = 'button';

			input = this._input = L.DomUtil.create('input', '', form);
			input.type = 'text';
			input.placeholder = this.options.placeholder;

			this._errorElement = L.DomUtil.create('div', className + '-form-no-error', container);
			this._errorElement.innerHTML = this.options.errorMessage;

			this._alts = L.DomUtil.create('ul',
				className + '-alternatives leaflet-control-geocoder-alternatives-minimized',
				container);

			L.DomEvent.addListener(input, 'keydown', this._keydown, this);
			L.DomEvent.addListener(input, 'blur', function() {
				if (this.options.collapsed) {
					this._collapse();
				}
			}, this);

			if (this.options.collapsed) {
				if (this.options.expand === 'click') {
					L.DomEvent.addListener(icon, 'click', function(e) {
						// TODO: touch
						if (e.button === 0 && e.detail !== 2) {
							this._toggle();
						}
					}, this);
				} else {
					L.DomEvent.addListener(icon, 'mouseover', this._expand, this);
					L.DomEvent.addListener(icon, 'mouseout', this._collapse, this);
					this._map.on('movestart', this._collapse, this);
				}
			} else {
				L.DomEvent.addListener(icon, 'click', function(e) {
					this._geocode(e);
				}, this);
				this._expand();
			}

			if (this.options.defaultMarkGeocode) {
				this.on('markgeocode', this.markGeocode, this);
			}

			L.DomEvent.disableClickPropagation(container);

			return container;
		},

		_geocodeResult: function (results, suggest) {
			L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');
			if (!suggest && results.length === 1) {
				this._geocodeResultSelected(results[0]);
			} else if (results.length > 0) {
				this._alts.innerHTML = '';
				this._results = results;
				L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
				for (var i = 0; i < results.length; i++) {
					this._alts.appendChild(this._createAlt(results[i], i));
				}
			} else {
				L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');
			}
		},

		markGeocode: function(result) {
			result = result.geocode || result;

			this._map.fitBounds(result.bbox);

			if (this._geocodeMarker) {
				this._map.removeLayer(this._geocodeMarker);
			}

			this._geocodeMarker = new L.Marker(result.center)
				.bindPopup(result.html || result.name)
				.addTo(this._map)
				.openPopup();

			return this;
		},

		_geocode: function(suggest) {
			this._lastGeocode = this._input.value;
			L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');
			if (!suggest) {
				this._clearResults();
			}

			this.fire(suggest ? 'startgeocode' : 'startsuggest');
			this.options.geocoder[suggest ? 'suggest' : 'geocode'](this._input.value, function(results) {
				this.fire(suggest ? 'finishgeocode' : 'finishsuggest');
				this._geocodeResult(results, suggest);
			}, this);
		},

		_geocodeResultSelected: function(result) {
			if (this.options.collapsed) {
				this._collapse();
			} else {
				this._clearResults();
			}

			this.fire('markgeocode', {geocode: result});
		},

		_toggle: function() {
			if (this._container.className.indexOf('leaflet-control-geocoder-expanded') >= 0) {
				this._collapse();
			} else {
				this._expand();
			}
		},

		_expand: function () {
			L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');
			this._input.select();
			this.fire('expand');
		},

		_collapse: function () {
			this._container.className = this._container.className.replace(' leaflet-control-geocoder-expanded', '');
			L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
			L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
			this.fire('collapse');
		},

		_clearResults: function () {
			L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
			this._selection = null;
			L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
		},

		_createAlt: function(result, index) {
			var li = L.DomUtil.create('li', ''),
				a = L.DomUtil.create('a', '', li),
			    icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null,
			    text = result.html ? undefined : document.createTextNode(result.name),
			    clickHandler = function clickHandler(e) {
					L.DomEvent.preventDefault(e);
					this._geocodeResultSelected(result);
				};

			if (icon) {
				icon.src = result.icon;
			}

			li.setAttribute('data-result-index', index);

			if (result.html) {
				a.innerHTML = a.innerHTML + result.html;
			} else {
				a.appendChild(text);
			}

			L.DomEvent.addListener(li, 'mousedown', clickHandler, this);

			return li;
		},

		_keydown: function(e) {
			var _this = this,
			    select = function select(dir) {
					if (_this._selection) {
						L.DomUtil.removeClass(_this._selection, 'leaflet-control-geocoder-selected');
						_this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
					}
					if (!_this._selection) {
						_this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
					}

					if (_this._selection) {
						L.DomUtil.addClass(_this._selection, 'leaflet-control-geocoder-selected');
					}
				};

			switch (e.keyCode) {
			// Escape
			case 27:
				if (this.options.collapsed) {
					this._collapse();
				}
				break;
			// Up
			case 38:
				select(-1);
				L.DomEvent.preventDefault(e);
				break;
			// Up
			case 40:
				select(1);
				L.DomEvent.preventDefault(e);
				break;
			// Enter
			case 13:
				if (this._selection) {
					var index = parseInt(this._selection.getAttribute('data-result-index'), 10);
					this._geocodeResultSelected(this._results[index]);
					this._clearResults();
				} else {
					this._geocode();
				}
				L.DomEvent.preventDefault(e);
				break;
			default:
				var v = this._input.value;
				if (this.options.geocoder.suggest && v !== this._lastGeocode) {
					clearTimeout(this._suggestTimeout);
					if (v.length >= this.options.suggestMinLength) {
						this._suggestTimeout = setTimeout(L.bind(function() {
							this._geocode(true);
						}, this), this.options.suggestTimeout);
					} else {
						this._clearResults();
					}
				}
			}
		}
	}),
	factory: function(options) {
		return new L.Control.Geocoder(options);
	}
};
<commit_msg>Fix broken suggest/geocode logic; ignore old requests<commit_after>var L = require('leaflet'),
	Nominatim = require('./geocoders/nominatim').class;

module.exports = {
	class: L.Control.extend({
		options: {
			showResultIcons: false,
			collapsed: true,
			expand: 'click',
			position: 'topright',
			placeholder: 'Search...',
			errorMessage: 'Nothing found.',
			suggestMinLength: 3,
			suggestTimeout: 250,
			defaultMarkGeocode: true
		},

		includes: L.Mixin.Events,

		initialize: function (options) {
			L.Util.setOptions(this, options);
			if (!this.options.geocoder) {
				this.options.geocoder = new Nominatim();
			}

			this._requestCount = 0;
		},

		onAdd: function (map) {
			var className = 'leaflet-control-geocoder',
			    container = L.DomUtil.create('div', className + ' leaflet-bar'),
			    icon = L.DomUtil.create('button', className + '-icon', container),
			    form = this._form = L.DomUtil.create('div', className + '-form', container),
			    input;

			this._map = map;
			this._container = container;

			icon.innerHTML = ' ';
			icon.type = 'button';

			input = this._input = L.DomUtil.create('input', '', form);
			input.type = 'text';
			input.placeholder = this.options.placeholder;

			this._errorElement = L.DomUtil.create('div', className + '-form-no-error', container);
			this._errorElement.innerHTML = this.options.errorMessage;

			this._alts = L.DomUtil.create('ul',
				className + '-alternatives leaflet-control-geocoder-alternatives-minimized',
				container);

			L.DomEvent.addListener(input, 'keydown', this._keydown, this);
			L.DomEvent.addListener(input, 'blur', function() {
				if (this.options.collapsed) {
					this._collapse();
				}
			}, this);

			if (this.options.collapsed) {
				if (this.options.expand === 'click') {
					L.DomEvent.addListener(icon, 'click', function(e) {
						// TODO: touch
						if (e.button === 0 && e.detail !== 2) {
							this._toggle();
						}
					}, this);
				} else {
					L.DomEvent.addListener(icon, 'mouseover', this._expand, this);
					L.DomEvent.addListener(icon, 'mouseout', this._collapse, this);
					this._map.on('movestart', this._collapse, this);
				}
			} else {
				L.DomEvent.addListener(icon, 'click', function(e) {
					this._geocode(e);
				}, this);
				this._expand();
			}

			if (this.options.defaultMarkGeocode) {
				this.on('markgeocode', this.markGeocode, this);
			}

			this.on('startgeocode', function() {
				L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');
			}, this);
			this.on('finishgeocode', function() {
				L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');
			}, this);

			L.DomEvent.disableClickPropagation(container);

			return container;
		},

		_geocodeResult: function (results, suggest) {
			if (!suggest && results.length === 1) {
				this._geocodeResultSelected(results[0]);
			} else if (results.length > 0) {
				this._alts.innerHTML = '';
				this._results = results;
				L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
				for (var i = 0; i < results.length; i++) {
					this._alts.appendChild(this._createAlt(results[i], i));
				}
			} else {
				L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');
			}
		},

		markGeocode: function(result) {
			result = result.geocode || result;

			this._map.fitBounds(result.bbox);

			if (this._geocodeMarker) {
				this._map.removeLayer(this._geocodeMarker);
			}

			this._geocodeMarker = new L.Marker(result.center)
				.bindPopup(result.html || result.name)
				.addTo(this._map)
				.openPopup();

			return this;
		},

		_geocode: function(suggest) {
			var requestCount = ++this._requestCount,
				mode = suggest ? 'suggest' : 'geocode';

			this._lastGeocode = this._input.value;
			if (!suggest) {
				this._clearResults();
			}

			this.fire('start' + mode);
			this.options.geocoder[mode](this._input.value, function(results) {
				if (requestCount === this._requestCount) {
					this.fire('finish' + mode);
					this._geocodeResult(results, suggest);
				}
			}, this);
		},

		_geocodeResultSelected: function(result) {
			if (this.options.collapsed) {
				this._collapse();
			} else {
				this._clearResults();
			}

			this.fire('markgeocode', {geocode: result});
		},

		_toggle: function() {
			if (this._container.className.indexOf('leaflet-control-geocoder-expanded') >= 0) {
				this._collapse();
			} else {
				this._expand();
			}
		},

		_expand: function () {
			L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');
			this._input.select();
			this.fire('expand');
		},

		_collapse: function () {
			this._container.className = this._container.className.replace(' leaflet-control-geocoder-expanded', '');
			L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
			L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
			this.fire('collapse');
		},

		_clearResults: function () {
			L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
			this._selection = null;
			L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
		},

		_createAlt: function(result, index) {
			var li = L.DomUtil.create('li', ''),
				a = L.DomUtil.create('a', '', li),
			    icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null,
			    text = result.html ? undefined : document.createTextNode(result.name),
			    clickHandler = function clickHandler(e) {
					L.DomEvent.preventDefault(e);
					this._geocodeResultSelected(result);
				};

			if (icon) {
				icon.src = result.icon;
			}

			li.setAttribute('data-result-index', index);

			if (result.html) {
				a.innerHTML = a.innerHTML + result.html;
			} else {
				a.appendChild(text);
			}

			L.DomEvent.addListener(li, 'mousedown', clickHandler, this);

			return li;
		},

		_keydown: function(e) {
			var _this = this,
			    select = function select(dir) {
					if (_this._selection) {
						L.DomUtil.removeClass(_this._selection, 'leaflet-control-geocoder-selected');
						_this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
					}
					if (!_this._selection) {
						_this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
					}

					if (_this._selection) {
						L.DomUtil.addClass(_this._selection, 'leaflet-control-geocoder-selected');
					}
				};

			switch (e.keyCode) {
			// Escape
			case 27:
				if (this.options.collapsed) {
					this._collapse();
				}
				break;
			// Up
			case 38:
				select(-1);
				L.DomEvent.preventDefault(e);
				break;
			// Up
			case 40:
				select(1);
				L.DomEvent.preventDefault(e);
				break;
			// Enter
			case 13:
				if (this._selection) {
					var index = parseInt(this._selection.getAttribute('data-result-index'), 10);
					this._geocodeResultSelected(this._results[index]);
					this._clearResults();
				} else {
					this._geocode();
				}
				L.DomEvent.preventDefault(e);
				break;
			default:
				var v = this._input.value;
				if (this.options.geocoder.suggest && v !== this._lastGeocode) {
					clearTimeout(this._suggestTimeout);
					if (v.length >= this.options.suggestMinLength) {
						this._suggestTimeout = setTimeout(L.bind(function() {
							this._geocode(true);
						}, this), this.options.suggestTimeout);
					} else {
						this._clearResults();
					}
				}
			}
		}
	}),
	factory: function(options) {
		return new L.Control.Geocoder(options);
	}
};
<|endoftext|>


<commit_before>class RoughSpriteGenerator
{
  constructor(game) {
    this.game = game;
  }

  getRectangle(bmd, width, height, config) {
    let rc = rough.canvas(bmd.canvas);
    rc.rectangle(0, 0, width, height, config);
  }

  getCircle(bmd, center, radius, config) {
    let rc = rough.canvas(bmd.canvas);
    rc.circle(center.x, center.y, radius * 2, config);
  }

  getLine(bmd, dist, config) {
    this.getRectangle(bmd, dist, 2, config);
  }

  getCircleSprite(x, y, radius, config = {}) {
    const defaultConfig = {
      fill: "rgb(10,150,10)",
      fillWeight: 5 // thicker lines for hachure
    };
    const configs = Object.assign({}, defaultConfig, config);
    const realRadius = radius + (config.fillWeight|| 0);
    let bmd = this.game.add.bitmapData(realRadius * 2, realRadius * 2);
    this.getCircle(bmd, {x: realRadius, y: realRadius}, radius, configs);
    return new Phaser.Sprite(this.game, x, y, bmd);
  }

  getRectangleSprite(x, y, width, height, config = {}) {
    let bmd = this.game.add.bitmapData(width, height);
    const defaultConfig = {
        fill: 'orange',
        stroke: 'black',
        hachureAngle: 60,
        hachureGap: 10,
        fillWeight: 5,
        strokeWidth: 5
      };
    const configs = Object.assign({}, defaultConfig, config);
    this.getRectangle(bmd, width, height, configs);
    return new Phaser.Sprite(this.game, x, y, bmd);
  }

  getLineSprite(x, y, x1, y1, x2, y2, config = {}) {
    const dist = Math.sqrt( ((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)) );
    let bmd = this.game.add.bitmapData(dist, 2);
    this.getLine(bmd, dist, config);
    const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
    let sprite = new Phaser.Sprite(this.game, x, y, bmd);
    sprite.angle =- angle;
    return sprite;
  }
}
export default RoughSpriteGenerator;<commit_msg>add tool method<commit_after>class RoughSpriteGenerator
{
  constructor(game) {
    this.game = game;
  }

  getRectangle(bmd, width, height, config) {
    let rc = rough.canvas(bmd.canvas);
    rc.rectangle(0, 0, width, height, config);
  }

  getCircle(bmd, center, radius, config) {
    let rc = rough.canvas(bmd.canvas);
    rc.circle(center.x, center.y, radius * 2, config);
  }

  getLine(bmd, dist, config) {
    this.getRectangle(bmd, dist, 2, config);
  }

  getCircleSprite(x, y, radius, config = {}) {
    const defaultConfig = {
      fill: "rgb(10,150,10)",
      fillWeight: 5 // thicker lines for hachure
    };
    const configs = Object.assign({}, defaultConfig, config);
    const realRadius = radius + (config.fillWeight|| 0);
    let bmd = this.game.add.bitmapData(realRadius * 2, realRadius * 2);
    this.getCircle(bmd, {x: realRadius, y: realRadius}, radius, configs);
    return new Phaser.Sprite(this.game, x, y, bmd);
  }

  getRectangleSprite(x, y, width, height, config = {}) {
    let bmd = this.game.add.bitmapData(width, height);
    const defaultConfig = {
        fill: 'orange',
        stroke: 'black',
        hachureAngle: 60,
        hachureGap: 10,
        fillWeight: 5,
        strokeWidth: 5
      };
    const configs = Object.assign({}, defaultConfig, config);
    this.getRectangle(bmd, width, height, configs);
    return new Phaser.Sprite(this.game, x, y, bmd);
  }

  getLineSprite(x, y, x1, y1, x2, y2, config = {}) {
    const dist = this.lengthFromPoints(x1, y1, x2, y2);
    let bmd = this.game.add.bitmapData(dist, 2);
    this.getLine(bmd, dist, config);
    const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
    let sprite = new Phaser.Sprite(this.game, x, y, bmd);
    sprite.angle =- angle;
    return sprite;
  }

  lengthFromPoints(x1, y1, x2, y2) {
     return Math.sqrt( ((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)) );
  }
}
export default RoughSpriteGenerator;<|endoftext|>


<commit_before>const r = require('./../../../db');
const client = require('./../../');

function guildStats(guild) {
	const name = guild.name;
	const id = guild.id;
	const users = guild.members.filter(member => !member.user.bot).size;
	const total = guild.members.size;
	const bots = total - users;
	const percentage = Math.floor((bots / total) * 100);
	const pass = !(percentage > 50 && total > 20);

	return { name, id, users, bots, total, percentage, pass };
}

function banGuild(id) {
	client.guilds.get(id).leave();
	r.table('collection')
		.insert({
			guild: id
		})
		.run(r.conn, (err) => {
			if (err) throw new Error('Couldn\'t save banned guild.');
			console.log(`Banned ${client.guilds.get(id).name}`);
		});
}

client.on('guildCreate', (guild) => {
	const report = guildStats(guild);

	if (!report.pass) {
		console.log(`${guild.name} failed the authentication test`);
		banGuild(guild.id);
	} else {
		r.table('collection')
			.filter({ guild: guild.id })
			.run(r.conn, (err1, cursor) => {
				if (err1) throw new Error('Failed to search banned guild database.');
				cursor.toArray((err2, result) => {
					if (err2) throw new Error('Failed to convert banned cursor to results.');
					if (result.length === 0) {
						console.log(`${guild.name} passed the authentication test`);
					} else {
						console.log(`${guild.name} was already banned!`);
						guild.leave();
					}
				});
			});
	}
});

module.exports = banGuild;
<commit_msg>automatically check for bot collection servers every 30 minutes<commit_after>const r = require('./../../../db');
const client = require('./../../');

function guildStats(guild) {
	const name = guild.name;
	const id = guild.id;
	const users = guild.members.filter(member => !member.user.bot).size;
	const total = guild.members.size;
	const bots = total - users;
	const percentage = Math.floor((bots / total) * 100);
	const pass = !(percentage > 50 && total > 20);
	const fail = !pass;

	return { name, id, users, bots, total, percentage, pass, fail };
}

function banGuild(id) {
	client.guilds.get(id).leave();
	r.table('collection')
		.insert({
			guild: id
		})
		.run(r.conn, (err) => {
			if (err) throw new Error('Couldn\'t save banned guild.');
			console.log(`Banned ${client.guilds.get(id).name}`);
		});
}

function checkGuilds(bot) {
	bot.guilds.map(guild => guildStats(guild).fail).forEach(guild => banGuild(guild.id));
}

client.on('guildCreate', (guild) => {
	const report = guildStats(guild);

	if (report.fail) {
		console.log(`${guild.name} failed the authentication test`);
		banGuild(guild.id);
	} else {
		r.table('collection')
			.filter({ guild: guild.id })
			.run(r.conn, (err1, cursor) => {
				if (err1) throw new Error('Failed to search banned guild database.');
				cursor.toArray((err2, result) => {
					if (err2) throw new Error('Failed to convert banned cursor to results.');
					if (result.length === 0) {
						console.log(`${guild.name} passed the authentication test`);
					} else {
						console.log(`${guild.name} was already banned!`);
						guild.leave();
					}
				});
			});
	}
});

client.on('ready', () => {
	checkGuilds(client);
	setInterval(() => {
		checkGuilds(client);
	}, 30000);
});

module.exports = banGuild;
<|endoftext|>


<commit_before>/*!
 * viewport-units-buggyfill v0.3.1
 * @web: https://github.com/rodneyrehm/viewport-units-buggyfill/
 * @author: Rodney Rehm - http://rodneyrehm.de/en/
 */

(function (root, factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([], factory);
  } else if (typeof exports === 'object') {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like enviroments that support module.exports,
    // like Node.
    module.exports = factory();
  } else {
    // Browser globals (root is window)
    root.viewportUnitsBuggyfill = factory();
  }
}(this, function () {
  'use strict';
  /*global document, window, location, XMLHttpRequest, XDomainRequest*/

  var initialized = false;
  var options;
  var refreshDebounce;
  var viewportUnitExpression = /([+-]?[0-9.]+)(vh|vw|vmin|vmax)/g;
  var calcExpression = /calc\(/g;
  var quoteExpression = /[\"\']/g;
  var urlExpression = /url\([^\)]*\)/g;
  var forEach = [].forEach;
  var dimensions;
  var declarations;
  var styleNode;
  var is_safari_or_uiwebview = /(iPhone|iPod|iPad).+AppleWebKit/i.test(window.navigator.userAgent);
  var is_bad_IE = false;
  var no_vmin_vmax = false;
  var no_vmin_in_calc = false;
  var use_css_content_hack = false;
  var use_css_behavior_hack = false;

  // WARNING!
  // Do not remove the following conditional comment.
  // It is required to identify the current version of IE

  /*@cc_on

  @if (@_jscript_version <= 10)
    is_bad_IE = true;
    no_vmin_in_calc = true;
    no_vmin_vmax = true;
  @end

  @*/

  // Debounce function from http://davidwalsh.name/javascript-debounce-function
  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds. If `immediate` is passed, trigger the function on the
  // leading edge, instead of the trailing.
  function debounce(func, wait, immediate) {
    var timeout;
    return function() {
      var context = this;
      var args = arguments;
      clearTimeout(timeout);
      timeout = setTimeout(function() {
        timeout = null;
        if (!immediate) {
          func.apply(context, args);
        }
      }, wait);

      if (immediate && !timeout) {
        func.apply(context, args);
      }
    };
  }

  function initialize(initOptions) {
    options = initOptions || {};
    if (initialized || (!options.force && !is_safari_or_uiwebview && !is_bad_IE)) {
      // this buggyfill only applies to mobile safari
      return;
    }

    // Test viewport units support in calc() expressions
    var div = document.createElement('div');
    div.style.width = '1vmax';
    if (div.style.width === '') {
      no_vmin_vmax = true;
    }

    // there is no accurate way to detect this programmatically.
    no_vmin_in_calc = no_vmin_in_calc || is_safari_or_uiwebview;
    use_css_behavior_hack = !!options.use_css_behavior_hack;
    use_css_content_hack = !!options.use_css_content_hack;
    initialized = true;
    styleNode = document.createElement('style');
    styleNode.id = 'patched-viewport';
    document.head.appendChild(styleNode);

    // Issue #6: Cross Origin Stylesheets are not accessible through CSSOM,
    // therefore download and inject them as <style> to circumvent SOP.
    importCrossOriginLinks(function() {
      // doing a full refresh rather than updateStyles because an orientationchange
      // could activate different stylesheets
      window.addEventListener('orientationchange', refresh, true);
      // we must add a pageShow event here, since a user could have gone to a
      // different page, rotated the device, and then went back to the original
      // page, which doesn't update the viewport units.
      window.addEventListener('pageshow', refresh, true);
      if (is_bad_IE || options.force || inIframe()) {
        if (options.use_resize_debounce) {
          if (typeof options.use_resize_debounce === 'number') {
            refreshDebounce = debounce(refresh, options.use_resize_debounce);
          } else {
            refreshDebounce = debounce(refresh, 250);
          }

          window.addEventListener('resize', refreshDebounce, true);
        } else {
          window.addEventListener('resize', refresh, true);
        }
      }

      refresh();
    });
  }

  // from http://stackoverflow.com/questions/326069/how-to-identify-if-a-webpage-is-being-loaded-inside-an-iframe-or-directly-into-t
  function inIframe() {
    try {
      return window.self !== window.top;
    } catch (e) {
      return true;
    }
  }

  function updateStyles() {
    styleNode.textContent = getReplacedViewportUnits();
  }

  function refresh() {
    if (!initialized) {
      return;
    }

    findProperties();

    // iOS Safari will report window.innerWidth and .innerHeight as 0
    // unless a timeout is used here.
    // TODO: figure out WHY innerWidth === 0
    setTimeout(function() {
      updateStyles();
    }, 1);
  }

  function findProperties() {
    declarations = [];
    forEach.call(document.styleSheets, function(sheet) {
      if (sheet.ownerNode.id === 'patched-viewport' || !sheet.cssRules) {
        // skip entire sheet because no rules ara present or it's the target-element of the buggyfill
        return;
      }

      if (sheet.media && sheet.media.mediaText && window.matchMedia && !window.matchMedia(sheet.media.mediaText).matches) {
        // skip entire sheet because media attribute doesn't match
        return;
      }

      forEach.call(sheet.cssRules, findDeclarations);
    });

    return declarations;
  }

  function findDeclarations(rule) {
    if (rule.type === 7) {
      var value = rule.cssText;
      viewportUnitExpression.lastIndex = 0;
      if (viewportUnitExpression.test(value)) {
        // KeyframesRule does not have a CSS-PropertyName
        // checkHacks(rule, null, value);
        declarations.push([rule, null, value]);
      }

      return;
    }

    if (!rule.style) {
      if (!rule.cssRules) {
        return;
      }

      forEach.call(rule.cssRules, function(_rule) {
        findDeclarations(_rule);
      });

      return;
    }

    forEach.call(rule.style, function(name) {
      var value = rule.style.getPropertyValue(name);
      viewportUnitExpression.lastIndex = 0;
      if (viewportUnitExpression.test(value)) {
        checkHacks(rule, name, value);
        declarations.push([rule, name, value]);
      }
    });
  }

  // iOS SAFARI, IE9: abuse "content" if "use_css_content_hack" specified
  // IE9: abuse "behavior" if "use_css_behavior_hack" specified
  function checkHacks(rule, name, value) {
    if (!use_css_content_hack && !use_css_behavior_hack) {
      return;
    }

    if (name !== 'content' && name !== 'behavior') {
      return;
    }

    var needsCalcFix = (use_css_content_hack && no_vmin_in_calc && name === 'content' && value.indexOf('use_css_content_hack') > -1);
    var needsVminVmaxFix = (use_css_behavior_hack && no_vmin_vmax && name === 'behavior' && value.indexOf('use_css_behavior_hack') > -1);
    if (!needsCalcFix && !needsVminVmaxFix) {
      return;
    }

    var fakeRules = value.replace(quoteExpression, '');
    if (needsVminVmaxFix) {
      fakeRules = fakeRules.replace(urlExpression, '');
    }

    fakeRules.split(';').forEach(function(fakeRuleElement) {
      var fakeRule = fakeRuleElement.split(':');
      if (fakeRule.length !== 2) {
        return;
      }

      var name = fakeRule[0].trim();
      var value = fakeRule[1].trim();
      if (name === 'use_css_content_hack' || name === 'use_css_behavior_hack') {
        return;
      }

      declarations.push([rule, name, value]);
      if (calcExpression.test(value)) {
        var webkitValue = value.replace(calcExpression, '-webkit-calc(');
        declarations.push([rule, name, webkitValue]);
      }
    });
  }

  function getReplacedViewportUnits() {
    dimensions = getViewport();

    var css = [];
    var buffer = [];
    var open;
    var close;

    declarations.forEach(function(item) {
      var _item = overwriteDeclaration.apply(null, item);
      var _open = _item.selector.length ? (_item.selector.join(' {\n') + ' {\n') : '';
      var _close = new Array(_item.selector.length + 1).join('\n}');

      if (!_open || _open !== open) {
        if (buffer.length) {
          css.push(open + buffer.join('\n') + close);
          buffer.length = 0;
        }

        if (_open) {
          open = _open;
          close = _close;
          buffer.push(_item.content);
        } else {
          css.push(_item.content);
          open = null;
          close = null;
        }

        return;
      }

      if (_open && !open) {
        open = _open;
        close = _close;
      }

      buffer.push(_item.content);
    });

    if (buffer.length) {
      css.push(open + buffer.join('\n') + close);
    }

    return css.join('\n\n');
  }

  function overwriteDeclaration(rule, name, value) {
    var _value = value.replace(viewportUnitExpression, replaceValues);
    var  _selectors = [];

    if (is_bad_IE && name === 'filter') {
      // remove unit "px"
      _value = parseInt(_value, 10);
    }

    if (name) {
      // skipping KeyframesRule
      _selectors.push(rule.selectorText);
      _value = name + ': ' + _value + ';';
    }

    var _rule = rule.parentRule;
    while (_rule) {
      _selectors.unshift('@media ' + _rule.media.mediaText);
      _rule = _rule.parentRule;
    }

    return {
      selector: _selectors,
      content: _value
    };
  }

  function replaceValues(match, number, unit) {
    var _base = dimensions[unit];
    var _number = parseFloat(number) / 100;
    return (_number * _base) + 'px';
  }

  function getViewport() {
    var vh = window.innerHeight;
    var vw = window.innerWidth;

    return {
      vh: vh,
      vw: vw,
      vmax: Math.max(vw, vh),
      vmin: Math.min(vw, vh)
    };
  }

  function importCrossOriginLinks(next) {
    var _waiting = 0;
    var decrease = function() {
      _waiting--;
      if (!_waiting) {
        next();
      }
    };

    forEach.call(document.styleSheets, function(sheet) {
      if (!sheet.href || origin(sheet.href) === origin(location.href)) {
        // skip <style> and <link> from same origin
        return;
      }

      _waiting++;
      convertLinkToStyle(sheet.ownerNode, decrease);
    });

    if (!_waiting) {
      next();
    }
  }

  function origin(url) {
    return url.slice(0, url.indexOf('/', url.indexOf('://') + 3));
  }

  function convertLinkToStyle(link, next) {
    getCors(link.href, function() {
      var style = document.createElement('style');
      style.media = link.media;
      style.setAttribute('data-href', link.href);
      style.textContent = this.responseText;
      link.parentNode.replaceChild(style, link);
      next();
    }, next);
  }

  function getCors(url, success, error) {
    var xhr = new XMLHttpRequest();
    if ('withCredentials' in xhr) {
      // XHR for Chrome/Firefox/Opera/Safari.
      xhr.open('GET', url, true);
    } else if (typeof XDomainRequest !== 'undefined') {
      // XDomainRequest for IE.
      xhr = new XDomainRequest();
      xhr.open('GET', url);
    } else {
      throw new Error('cross-domain XHR not supported');
    }

    xhr.onload = success;
    xhr.onerror = error;
    xhr.send();
    return xhr;
  }

  return {
    version: '0.3.1',
    findProperties: findProperties,
    getCss: getReplacedViewportUnits,
    init: initialize,
    refresh: refresh
  };

}));
<commit_msg>simplify debounced refresh() handling<commit_after>/*!
 * viewport-units-buggyfill v0.3.1
 * @web: https://github.com/rodneyrehm/viewport-units-buggyfill/
 * @author: Rodney Rehm - http://rodneyrehm.de/en/
 */

(function (root, factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([], factory);
  } else if (typeof exports === 'object') {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like enviroments that support module.exports,
    // like Node.
    module.exports = factory();
  } else {
    // Browser globals (root is window)
    root.viewportUnitsBuggyfill = factory();
  }
}(this, function () {
  'use strict';
  /*global document, window, location, XMLHttpRequest, XDomainRequest*/

  var initialized = false;
  var options;
  var refreshDebounce;
  var viewportUnitExpression = /([+-]?[0-9.]+)(vh|vw|vmin|vmax)/g;
  var calcExpression = /calc\(/g;
  var quoteExpression = /[\"\']/g;
  var urlExpression = /url\([^\)]*\)/g;
  var forEach = [].forEach;
  var dimensions;
  var declarations;
  var styleNode;
  var is_safari_or_uiwebview = /(iPhone|iPod|iPad).+AppleWebKit/i.test(window.navigator.userAgent);
  var is_bad_IE = false;
  var no_vmin_vmax = false;
  var no_vmin_in_calc = false;
  var use_css_content_hack = false;
  var use_css_behavior_hack = false;

  // WARNING!
  // Do not remove the following conditional comment.
  // It is required to identify the current version of IE

  /*@cc_on

  @if (@_jscript_version <= 10)
    is_bad_IE = true;
    no_vmin_in_calc = true;
    no_vmin_vmax = true;
  @end

  @*/

  function debounce(func, wait) {
    var timeout;
    return function() {
      var context = this;
      var args = arguments;
      var callback = function() {
        func.apply(context, args);
      };

      clearTimeout(timeout);
      timeout = setTimeout(callback, wait);
    };
  }

  function initialize(initOptions) {
    options = initOptions || {};
    if (initialized || (!options.force && !is_safari_or_uiwebview && !is_bad_IE)) {
      // this buggyfill only applies to mobile safari
      return;
    }

    // previous option names
    !options.refreshDebounceWait && (options.refreshDebounceWait = options.use_resize_debounce);

    // Test viewport units support in calc() expressions
    var div = document.createElement('div');
    div.style.width = '1vmax';
    if (div.style.width === '') {
      no_vmin_vmax = true;
    }

    // there is no accurate way to detect this programmatically.
    no_vmin_in_calc = no_vmin_in_calc || is_safari_or_uiwebview;
    use_css_behavior_hack = !!options.use_css_behavior_hack;
    use_css_content_hack = !!options.use_css_content_hack;
    initialized = true;
    styleNode = document.createElement('style');
    styleNode.id = 'patched-viewport';
    document.head.appendChild(styleNode);

    // Issue #6: Cross Origin Stylesheets are not accessible through CSSOM,
    // therefore download and inject them as <style> to circumvent SOP.
    importCrossOriginLinks(function() {
      var _refresh = debounce(refresh, options.refreshDebounceWait || 100);
      // doing a full refresh rather than updateStyles because an orientationchange
      // could activate different stylesheets
      window.addEventListener('orientationchange', _refresh, true);
      // orientationchange might have happened while in a different window
      window.addEventListener('pageshow', _refresh, true);

      if (is_bad_IE || options.force || inIframe()) {
        window.addEventListener('resize', _refresh, true);
      }

      refresh();
    });
  }

  // from http://stackoverflow.com/questions/326069/how-to-identify-if-a-webpage-is-being-loaded-inside-an-iframe-or-directly-into-t
  function inIframe() {
    try {
      return window.self !== window.top;
    } catch (e) {
      return true;
    }
  }

  function updateStyles() {
    styleNode.textContent = getReplacedViewportUnits();
  }

  function refresh() {
    if (!initialized) {
      return;
    }

    findProperties();

    // iOS Safari will report window.innerWidth and .innerHeight as 0
    // unless a timeout is used here.
    // TODO: figure out WHY innerWidth === 0
    setTimeout(function() {
      updateStyles();
    }, 1);
  }

  function findProperties() {
    declarations = [];
    forEach.call(document.styleSheets, function(sheet) {
      if (sheet.ownerNode.id === 'patched-viewport' || !sheet.cssRules) {
        // skip entire sheet because no rules ara present or it's the target-element of the buggyfill
        return;
      }

      if (sheet.media && sheet.media.mediaText && window.matchMedia && !window.matchMedia(sheet.media.mediaText).matches) {
        // skip entire sheet because media attribute doesn't match
        return;
      }

      forEach.call(sheet.cssRules, findDeclarations);
    });

    return declarations;
  }

  function findDeclarations(rule) {
    if (rule.type === 7) {
      var value = rule.cssText;
      viewportUnitExpression.lastIndex = 0;
      if (viewportUnitExpression.test(value)) {
        // KeyframesRule does not have a CSS-PropertyName
        // checkHacks(rule, null, value);
        declarations.push([rule, null, value]);
      }

      return;
    }

    if (!rule.style) {
      if (!rule.cssRules) {
        return;
      }

      forEach.call(rule.cssRules, function(_rule) {
        findDeclarations(_rule);
      });

      return;
    }

    forEach.call(rule.style, function(name) {
      var value = rule.style.getPropertyValue(name);
      viewportUnitExpression.lastIndex = 0;
      if (viewportUnitExpression.test(value)) {
        checkHacks(rule, name, value);
        declarations.push([rule, name, value]);
      }
    });
  }

  // iOS SAFARI, IE9: abuse "content" if "use_css_content_hack" specified
  // IE9: abuse "behavior" if "use_css_behavior_hack" specified
  function checkHacks(rule, name, value) {
    if (!use_css_content_hack && !use_css_behavior_hack) {
      return;
    }

    if (name !== 'content' && name !== 'behavior') {
      return;
    }

    var needsCalcFix = (use_css_content_hack && no_vmin_in_calc && name === 'content' && value.indexOf('use_css_content_hack') > -1);
    var needsVminVmaxFix = (use_css_behavior_hack && no_vmin_vmax && name === 'behavior' && value.indexOf('use_css_behavior_hack') > -1);
    if (!needsCalcFix && !needsVminVmaxFix) {
      return;
    }

    var fakeRules = value.replace(quoteExpression, '');
    if (needsVminVmaxFix) {
      fakeRules = fakeRules.replace(urlExpression, '');
    }

    fakeRules.split(';').forEach(function(fakeRuleElement) {
      var fakeRule = fakeRuleElement.split(':');
      if (fakeRule.length !== 2) {
        return;
      }

      var name = fakeRule[0].trim();
      var value = fakeRule[1].trim();
      if (name === 'use_css_content_hack' || name === 'use_css_behavior_hack') {
        return;
      }

      declarations.push([rule, name, value]);
      if (calcExpression.test(value)) {
        var webkitValue = value.replace(calcExpression, '-webkit-calc(');
        declarations.push([rule, name, webkitValue]);
      }
    });
  }

  function getReplacedViewportUnits() {
    dimensions = getViewport();

    var css = [];
    var buffer = [];
    var open;
    var close;

    declarations.forEach(function(item) {
      var _item = overwriteDeclaration.apply(null, item);
      var _open = _item.selector.length ? (_item.selector.join(' {\n') + ' {\n') : '';
      var _close = new Array(_item.selector.length + 1).join('\n}');

      if (!_open || _open !== open) {
        if (buffer.length) {
          css.push(open + buffer.join('\n') + close);
          buffer.length = 0;
        }

        if (_open) {
          open = _open;
          close = _close;
          buffer.push(_item.content);
        } else {
          css.push(_item.content);
          open = null;
          close = null;
        }

        return;
      }

      if (_open && !open) {
        open = _open;
        close = _close;
      }

      buffer.push(_item.content);
    });

    if (buffer.length) {
      css.push(open + buffer.join('\n') + close);
    }

    return css.join('\n\n');
  }

  function overwriteDeclaration(rule, name, value) {
    var _value = value.replace(viewportUnitExpression, replaceValues);
    var  _selectors = [];

    if (is_bad_IE && name === 'filter') {
      // remove unit "px"
      _value = parseInt(_value, 10);
    }

    if (name) {
      // skipping KeyframesRule
      _selectors.push(rule.selectorText);
      _value = name + ': ' + _value + ';';
    }

    var _rule = rule.parentRule;
    while (_rule) {
      _selectors.unshift('@media ' + _rule.media.mediaText);
      _rule = _rule.parentRule;
    }

    return {
      selector: _selectors,
      content: _value
    };
  }

  function replaceValues(match, number, unit) {
    var _base = dimensions[unit];
    var _number = parseFloat(number) / 100;
    return (_number * _base) + 'px';
  }

  function getViewport() {
    var vh = window.innerHeight;
    var vw = window.innerWidth;

    return {
      vh: vh,
      vw: vw,
      vmax: Math.max(vw, vh),
      vmin: Math.min(vw, vh)
    };
  }

  function importCrossOriginLinks(next) {
    var _waiting = 0;
    var decrease = function() {
      _waiting--;
      if (!_waiting) {
        next();
      }
    };

    forEach.call(document.styleSheets, function(sheet) {
      if (!sheet.href || origin(sheet.href) === origin(location.href)) {
        // skip <style> and <link> from same origin
        return;
      }

      _waiting++;
      convertLinkToStyle(sheet.ownerNode, decrease);
    });

    if (!_waiting) {
      next();
    }
  }

  function origin(url) {
    return url.slice(0, url.indexOf('/', url.indexOf('://') + 3));
  }

  function convertLinkToStyle(link, next) {
    getCors(link.href, function() {
      var style = document.createElement('style');
      style.media = link.media;
      style.setAttribute('data-href', link.href);
      style.textContent = this.responseText;
      link.parentNode.replaceChild(style, link);
      next();
    }, next);
  }

  function getCors(url, success, error) {
    var xhr = new XMLHttpRequest();
    if ('withCredentials' in xhr) {
      // XHR for Chrome/Firefox/Opera/Safari.
      xhr.open('GET', url, true);
    } else if (typeof XDomainRequest !== 'undefined') {
      // XDomainRequest for IE.
      xhr = new XDomainRequest();
      xhr.open('GET', url);
    } else {
      throw new Error('cross-domain XHR not supported');
    }

    xhr.onload = success;
    xhr.onerror = error;
    xhr.send();
    return xhr;
  }

  return {
    version: '0.3.1',
    findProperties: findProperties,
    getCss: getReplacedViewportUnits,
    init: initialize,
    refresh: refresh
  };

}));
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('./app');
var http = require('http');

/**
 * Set listening from loopback adresses only
 */
var address = ['127.0.0.1', '::1'];
app.set('trust proxy', 'loopback');

/**
 * Get port from environment and store in Express.
 */
var port = normalizePort(process.env.PORT || '28524');
app.set('port', port);

/**
 * Create HTTP server on provided port, on loopback interfaces.
 */
for(var addr in address) {
    if (address[addr] !== undefined) {
        (new HttpServer(app)).listen(port, address[addr]);
    }
}

/**
 * Normalize a port into a number, string, or false.
 */
function normalizePort(val) {
    var port = parseInt(val, 10);

    if (isNaN(port)) {
        // named pipe
        return val;
    }

    if (port >= 0) {
        // port number
        return port;
    }

    return false;
}

function HttpServer (app) {
    'use strict';

    var my = this,  // Public scope
        priv = {    // Private scope
            app: app,
            address: '0.0.0.0',
            port: 3000
        };

    /**
     * Event listener for HTTP server "error" event.
     */
    this.onError = function(error) {
        if (error.syscall !== 'listen') {
            throw error;
        }

        var bind = (typeof priv.port === 'string') ? ('Pipe ' + priv.port) : ('Port ' + priv.port);

        // handle specific listen errors with friendly messages
        switch (error.code) {
            case 'EACCES':
                console.error(bind + ' requires elevated privileges');
                process.exit(1);
                break;
            case 'EADDRINUSE':
                console.error(bind + ' is already in use');
                process.exit(1);
                break;
            default:
                throw error;
        }
    };

    /**
     * Event listener for HTTP server "listening" event.
     */
    this.onListening = function() {
        var addr = priv.server.address();
        var bind = (typeof addr === 'string') ? ('pipe ' + addr) : ('port ' + addr.port);
        console.log('Listening on ' + addr.address + ', ' + bind);
    };

    this.listen = function(port, address) {
        priv.address = address || priv.address;
        priv.port = port || priv.port;
        priv.server = http.createServer(priv.app).listen(priv.port, priv.address).on('error', my.onError).on('listening', my.onListening);
    };
}<commit_msg>Simplify node binding<commit_after>#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('./app');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */
var port = normalizePort(process.env.PORT || '28524');
app.set('port', port);

/**
 * Set listening from loopback adresses only
 */
var address = process.env.PORT ? ['0.0.0.0'] : ['127.0.0.1', '::1'];
app.set('trust proxy', 'loopback');

/**
 * Create HTTP server on provided port, on loopback interfaces.
 */
for(var addr in address) {
    if (address[addr] !== undefined) {
        (new HttpServer(app)).listen(port, address[addr]);
    }
}

/**
 * Normalize a port into a number, string, or false.
 */
function normalizePort(val) {
    var port = parseInt(val, 10);

    if (isNaN(port)) {
        // named pipe
        return val;
    }

    if (port >= 0) {
        // port number
        return port;
    }

    return false;
}

function HttpServer (app) {
    'use strict';

    var my = this,  // Public scope
        priv = {    // Private scope
            app: app,
            address: '0.0.0.0',
            port: 3000
        };

    /**
     * Event listener for HTTP server "error" event.
     */
    this.onError = function(error) {
        if (error.syscall !== 'listen') {
            throw error;
        }

        var bind = (typeof priv.port === 'string') ? ('Pipe ' + priv.port) : ('Port ' + priv.port);

        // handle specific listen errors with friendly messages
        switch (error.code) {
            case 'EACCES':
                console.error(bind + ' requires elevated privileges');
                process.exit(1);
                break;
            case 'EADDRINUSE':
                console.error(bind + ' is already in use');
                process.exit(1);
                break;
            default:
                throw error;
        }
    };

    /**
     * Event listener for HTTP server "listening" event.
     */
    this.onListening = function() {
        var addr = priv.server.address();
        var bind = (typeof addr === 'string') ? ('pipe ' + addr) : ('port ' + addr.port);
        console.log('Listening on ' + addr.address + ', ' + bind);
    };

    this.listen = function(port, address) {
        priv.address = address || priv.address;
        priv.port = port || priv.port;
        priv.server = http.createServer(priv.app).listen(priv.port, priv.address).on('error', my.onError).on('listening', my.onListening);
    };
}<|endoftext|>


<commit_before>import angular from 'angular-fix';

export default addFormlyNgModelAttrsManipulator;

// @ngInject
function addFormlyNgModelAttrsManipulator(formlyConfig, $interpolate) {
  if (formlyConfig.extras.disableNgModelAttrsManipulator) {
    return;
  }
  formlyConfig.templateManipulators.preWrapper.push(ngModelAttrsManipulator);

  function ngModelAttrsManipulator(template, options, scope) {
    /* jshint maxcomplexity:6 */
    var el = document.createElement('div');
    var data = options.data;
    var elementFilter = ':not([formly-skip-ng-model-attrs-manipulator])';
    if (data.skipNgModelAttrsManipulator === true) {
      return template;
    }
    else if (data.skipNgModelAttrsManipulator !== false) {
      elementFilter += ':not(' + data.skipNgModelAttrsManipulator + ')';
    }

    el.innerHTML = template;

    var modelNodes = el.querySelectorAll('[ng-model]' + elementFilter +
                    ', [data-ng-model]' + elementFilter);
    if (!modelNodes || !modelNodes.length) {
      return template;
    }

    addIfNotPresent(modelNodes, 'id', scope.id);
    addIfNotPresent(modelNodes, 'name', scope.name || scope.id);

    addValidation();
    addModelOptions();
    addTemplateOptionsAttrs();

    return el.innerHTML;

    function addValidation() {
      if (angular.isDefined(options.validators) || angular.isDefined(options.validation.messages)) {
        addIfNotPresent(modelNodes, 'formly-custom-validation', '');
      }
    }

    function addModelOptions() {
      if (angular.isDefined(options.modelOptions)) {
        addIfNotPresent(modelNodes, 'ng-model-options', 'options.modelOptions');
        if (options.modelOptions.getterSetter) {
          angular.forEach(modelNodes, node => {
            node.setAttribute('ng-model', 'options.value');
          });
        }
      }
    }

    function addTemplateOptionsAttrs() {
      if (!options.templateOptions && !options.expressionProperties) {
        // no need to run these if there are no templateOptions or expressionProperties
        return;
      }
      const to = options.templateOptions || {};
      const ep = options.expressionProperties || {};

      let ngModelAttributes = getBuiltInAttributes();

      // extend with the user's specifications winning
      angular.extend(ngModelAttributes, options.ngModelAttrs);

      // Feel free to make this more simple :-)
      angular.forEach(ngModelAttributes, (val, name) => {
        /* eslint complexity:[2, 14] */
        let attrVal;
        let attrName;
        const ref = `options.templateOptions['${name}']`;
        const toVal = to[name];
        const epVal = getEpValue(ep, name);

        const inTo = angular.isDefined(toVal);
        const inEp = angular.isDefined(epVal);
        if (val.value) {
          // I realize this looks backwards, but it's right, trust me...
          attrName = val.value;
          attrVal = name;
        } else if (val.expression && inTo) {
          attrName = val.expression;
          if (angular.isString(to[name])) {
            attrVal = `$eval(${ref})`;
          } else if (angular.isFunction(to[name])) {
            attrVal = `${ref}(model[options.key], options, this, $event)`;
          } else {
            throw new Error(
              `options.templateOptions.${name} must be a string or function: ${JSON.stringify(options)}`
            );
          }
        } else if (val.bound && inEp) {
          attrName = val.bound;
          attrVal = ref;
        } else if ((val.attribute || val.boolean) && inEp) {
          attrName = val.attribute || val.boolean;
          attrVal = `${$interpolate.startSymbol()}${ref}${$interpolate.endSymbol()}`;
        } else if (val.attribute && inTo) {
          attrName = val.attribute;
          attrVal = toVal;
        } else if (val.boolean) {
          if (inTo && !inEp && toVal) {
            attrName = val.boolean;
            attrVal = true;
          } else {
            /* eslint no-empty:0 */
            // empty to illustrate that a boolean will not be added via val.bound
            // if you want it added via val.bound, then put it in expressionProperties
          }
        } else if (val.bound && inTo) {
          attrName = val.bound;
          attrVal = ref;
        }

        if (angular.isDefined(attrName) && angular.isDefined(attrVal)) {
          addIfNotPresent(modelNodes, attrName, attrVal);
        }
      });
    }
  }

  // Utility functions
  function getBuiltInAttributes() {
    let ngModelAttributes = {
      focus: {
        attribute: 'formly-focus'
      }
    };
    const boundOnly = [];
    const bothBooleanAndBound = ['required', 'disabled'];
    const bothAttributeAndBound = ['pattern', 'minlength'];
    const expressionOnly = ['change', 'keydown', 'keyup', 'keypress', 'click', 'focus', 'blur'];
    const attributeOnly = ['placeholder', 'min', 'max', 'tabindex', 'type'];
    if (formlyConfig.extras.ngModelAttrsManipulatorPreferUnbound) {
      bothAttributeAndBound.push('maxlength');
    } else {
      boundOnly.push('maxlength');
    }

    angular.forEach(boundOnly, item => {
      ngModelAttributes[item] = {bound: 'ng-' + item};
    });

    angular.forEach(bothBooleanAndBound, item => {
      ngModelAttributes[item] = {boolean: item, bound: 'ng-' + item};
    });

    angular.forEach(bothAttributeAndBound, item => {
      ngModelAttributes[item] = {attribute: item, bound: 'ng-' + item};
    });

    angular.forEach(expressionOnly, item => {
      var propName = 'on' + item.substr(0, 1).toUpperCase() + item.substr(1);
      ngModelAttributes[propName] = {expression: 'ng-' + item};
    });

    angular.forEach(attributeOnly, item => {
      ngModelAttributes[item] = {attribute: item};
    });
    return ngModelAttributes;
  }

  function getEpValue(ep, name) {
    return ep['templateOptions.' + name] ||
      ep[`templateOptions['${name}']`] ||
      ep[`templateOptions["${name}"]`];
  }

  function addIfNotPresent(nodes, attr, val) {
    angular.forEach(nodes, node => {
      if (!node.getAttribute(attr)) {
        node.setAttribute(attr, val);
      }
    });
  }
}
<commit_msg>codestyle fix<commit_after>import angular from 'angular-fix';

export default addFormlyNgModelAttrsManipulator;

// @ngInject
function addFormlyNgModelAttrsManipulator(formlyConfig, $interpolate) {
  if (formlyConfig.extras.disableNgModelAttrsManipulator) {
    return;
  }
  formlyConfig.templateManipulators.preWrapper.push(ngModelAttrsManipulator);

  function ngModelAttrsManipulator(template, options, scope) {
    /* jshint maxcomplexity:6 */
    var el = document.createElement('div');
    var data = options.data;
    var elementFilter = ':not([formly-skip-ng-model-attrs-manipulator])';
    if (data.skipNgModelAttrsManipulator === true) {
      return template;
    } else if (data.skipNgModelAttrsManipulator !== false) {
      elementFilter += ':not(' + data.skipNgModelAttrsManipulator + ')';
    }

    el.innerHTML = template;

    var modelNodes = el.querySelectorAll('[ng-model]' + elementFilter +
                    ', [data-ng-model]' + elementFilter);
    if (!modelNodes || !modelNodes.length) {
      return template;
    }

    addIfNotPresent(modelNodes, 'id', scope.id);
    addIfNotPresent(modelNodes, 'name', scope.name || scope.id);

    addValidation();
    addModelOptions();
    addTemplateOptionsAttrs();

    return el.innerHTML;

    function addValidation() {
      if (angular.isDefined(options.validators) || angular.isDefined(options.validation.messages)) {
        addIfNotPresent(modelNodes, 'formly-custom-validation', '');
      }
    }

    function addModelOptions() {
      if (angular.isDefined(options.modelOptions)) {
        addIfNotPresent(modelNodes, 'ng-model-options', 'options.modelOptions');
        if (options.modelOptions.getterSetter) {
          angular.forEach(modelNodes, node => {
            node.setAttribute('ng-model', 'options.value');
          });
        }
      }
    }

    function addTemplateOptionsAttrs() {
      if (!options.templateOptions && !options.expressionProperties) {
        // no need to run these if there are no templateOptions or expressionProperties
        return;
      }
      const to = options.templateOptions || {};
      const ep = options.expressionProperties || {};

      let ngModelAttributes = getBuiltInAttributes();

      // extend with the user's specifications winning
      angular.extend(ngModelAttributes, options.ngModelAttrs);

      // Feel free to make this more simple :-)
      angular.forEach(ngModelAttributes, (val, name) => {
        /* eslint complexity:[2, 14] */
        let attrVal;
        let attrName;
        const ref = `options.templateOptions['${name}']`;
        const toVal = to[name];
        const epVal = getEpValue(ep, name);

        const inTo = angular.isDefined(toVal);
        const inEp = angular.isDefined(epVal);
        if (val.value) {
          // I realize this looks backwards, but it's right, trust me...
          attrName = val.value;
          attrVal = name;
        } else if (val.expression && inTo) {
          attrName = val.expression;
          if (angular.isString(to[name])) {
            attrVal = `$eval(${ref})`;
          } else if (angular.isFunction(to[name])) {
            attrVal = `${ref}(model[options.key], options, this, $event)`;
          } else {
            throw new Error(
              `options.templateOptions.${name} must be a string or function: ${JSON.stringify(options)}`
            );
          }
        } else if (val.bound && inEp) {
          attrName = val.bound;
          attrVal = ref;
        } else if ((val.attribute || val.boolean) && inEp) {
          attrName = val.attribute || val.boolean;
          attrVal = `${$interpolate.startSymbol()}${ref}${$interpolate.endSymbol()}`;
        } else if (val.attribute && inTo) {
          attrName = val.attribute;
          attrVal = toVal;
        } else if (val.boolean) {
          if (inTo && !inEp && toVal) {
            attrName = val.boolean;
            attrVal = true;
          } else {
            /* eslint no-empty:0 */
            // empty to illustrate that a boolean will not be added via val.bound
            // if you want it added via val.bound, then put it in expressionProperties
          }
        } else if (val.bound && inTo) {
          attrName = val.bound;
          attrVal = ref;
        }

        if (angular.isDefined(attrName) && angular.isDefined(attrVal)) {
          addIfNotPresent(modelNodes, attrName, attrVal);
        }
      });
    }
  }

  // Utility functions
  function getBuiltInAttributes() {
    let ngModelAttributes = {
      focus: {
        attribute: 'formly-focus'
      }
    };
    const boundOnly = [];
    const bothBooleanAndBound = ['required', 'disabled'];
    const bothAttributeAndBound = ['pattern', 'minlength'];
    const expressionOnly = ['change', 'keydown', 'keyup', 'keypress', 'click', 'focus', 'blur'];
    const attributeOnly = ['placeholder', 'min', 'max', 'tabindex', 'type'];
    if (formlyConfig.extras.ngModelAttrsManipulatorPreferUnbound) {
      bothAttributeAndBound.push('maxlength');
    } else {
      boundOnly.push('maxlength');
    }

    angular.forEach(boundOnly, item => {
      ngModelAttributes[item] = {bound: 'ng-' + item};
    });

    angular.forEach(bothBooleanAndBound, item => {
      ngModelAttributes[item] = {boolean: item, bound: 'ng-' + item};
    });

    angular.forEach(bothAttributeAndBound, item => {
      ngModelAttributes[item] = {attribute: item, bound: 'ng-' + item};
    });

    angular.forEach(expressionOnly, item => {
      var propName = 'on' + item.substr(0, 1).toUpperCase() + item.substr(1);
      ngModelAttributes[propName] = {expression: 'ng-' + item};
    });

    angular.forEach(attributeOnly, item => {
      ngModelAttributes[item] = {attribute: item};
    });
    return ngModelAttributes;
  }

  function getEpValue(ep, name) {
    return ep['templateOptions.' + name] ||
      ep[`templateOptions['${name}']`] ||
      ep[`templateOptions["${name}"]`];
  }

  function addIfNotPresent(nodes, attr, val) {
    angular.forEach(nodes, node => {
      if (!node.getAttribute(attr)) {
        node.setAttribute(attr, val);
      }
    });
  }
}
<|endoftext|>


<commit_before>define([
    '/api/config?cb=' + Math.random().toString(16).substring(2),
    '/common/messages.js',
    '/common/crypto.js',
    '/_socket/realtime-input.js',
    '/common/convert.js',
    '/_socket/toolbar.js',
    '/common/cursor.js',
    '/common/json-ot.js',
    '/bower_components/diff-dom/diffDOM.js',
    '/bower_components/jquery/dist/jquery.min.js',
    '/customize/pad.js'
], function (Config, Messages, Crypto, realtimeInput, Convert, Toolbar, Cursor, JsonOT) {
    var $ = window.jQuery;
    var ifrw = $('#pad-iframe')[0].contentWindow;
    var Ckeditor; // to be initialized later...
    var DiffDom = window.diffDOM;

    window.Convert = Convert;

    window.Toolbar = Toolbar;

    var userName = Crypto.rand64(8),
        toolbar;

    var module = {};

    var isNotMagicLine = function (el) {
        // factor as:
        // return !(el.tagName === 'SPAN' && el.contentEditable === 'false');
        var filter = (el.tagName === 'SPAN' && el.contentEditable === 'false');
        if (filter) {
            console.log("[hyperjson.serializer] prevented an element" +
                "from being serialized:", el);
            return false;
        }
        return true;
    };

    var setRandomizedInterval = function (func, target, range) {
        var timeout;
        var again = function () {
            setTimeout(function () {
                again();
                func();
            }, target - (range / 2) + Math.random() * range);
        };
        again();
        return {
            cancel: function () {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = undefined;
                }
            }
        };
    }

    var andThen = function (Ckeditor) {
        $(window).on('hashchange', function() {
            window.location.reload();
        });
        if (window.location.href.indexOf('#') === -1) {
            window.location.href = window.location.href + '#' + Crypto.genKey();
            return;
        }

        var fixThings = false;
        var key = Crypto.parseKey(window.location.hash.substring(1));
        var editor = window.editor = Ckeditor.replace('editor1', {
            // https://dev.ckeditor.com/ticket/10907
            needsBrFiller: fixThings,
            needsNbspFiller: fixThings,
            removeButtons: 'Source,Maximize',
            // magicline plugin inserts html crap into the document which is not part of the
            // document itself and causes problems when it's sent across the wire and reflected back
            // but we filter it now, so that's ok.
            removePlugins: 'resize'
        });

        editor.on('instanceReady', function (Ckeditor) {
            editor.execCommand('maximize');
            var documentBody = ifrw.$('iframe')[0].contentDocument.body;

            documentBody.innerHTML = Messages.initialState;

            var inner = window.inner = documentBody;
            var cursor = window.cursor = Cursor(inner);

            var $textarea = $('#feedback');

            var setEditable = function (bool) {
                // inner.style.backgroundColor = bool? 'unset': 'grey';
                inner.setAttribute('contenteditable', bool);
            };

            // don't let the user edit until the pad is ready
            setEditable(false);

            var diffOptions = {
                preDiffApply: function (info) {
                    /* TODO DiffDOM will filter out magicline plugin elements
                        in practice this will make it impossible to use it
                        while someone else is typing, which could be annoying

                        we should check when such an element is going to be
                        removed, and prevent that from happening. */

                    // no use trying to recover the cursor if it doesn't exist
                    if (!cursor.exists()) { return; }

                    /*  frame is either 0, 1, 2, or 3, depending on which
                        cursor frames were affected: none, first, last, or both
                    */
                    var frame = info.frame = cursor.inNode(info.node);

                    if (!frame) { return; }

                    if (typeof info.diff.oldValue === 'string' && typeof info.diff.newValue === 'string') {
                        var pushes = cursor.pushDelta(info.diff.oldValue, info.diff.newValue);

                        if (frame & 1) {
                            // push cursor start if necessary
                            if (pushes.commonStart < cursor.Range.start.offset) {
                                cursor.Range.start.offset += pushes.delta;
                            }
                        }
                        if (frame & 2) {
                            // push cursor end if necessary
                            if (pushes.commonStart < cursor.Range.end.offset) {
                                cursor.Range.end.offset += pushes.delta;
                            }
                        }
                    }
                },
                postDiffApply: function (info) {
                    if (info.frame) {
                        if (info.node) {
                            if (info.frame & 1) { cursor.fixStart(info.node); }
                            if (info.frame & 2) { cursor.fixEnd(info.node); }
                        } else { console.error("info.node did not exist"); }

                        var sel = cursor.makeSelection();
                        var range = cursor.makeRange();

                        cursor.fixSelection(sel, range);
                    }
                }
            };

            var initializing = true;

            var assertStateMatches = function () {
                var userDocState = module.realtimeInput.realtime.getUserDoc();
                var currentState = $textarea.val();
                if (currentState !== userDocState) {
                    console.log({
                        userDocState: userDocState,
                        currentState: currentState
                    });
                    throw new Error("currentState !== userDocState");
                }
            };

            var updateDebugTextarea = function (shjson) {
                window.setTimeout(function () {
                    $textarea.val(shjson);
                }, 0);
            };

            var now = function () { return new Date().getTime() };

            var DD = new DiffDom(diffOptions);
            // apply patches, and try not to lose the cursor in the process!
            var applyHjson = function (shjson) {
                //setEditable(false);
                console.log(now());
                var userDocStateDom = Convert.hjson.to.dom(JSON.parse(shjson));
                userDocStateDom.setAttribute("contenteditable", "true"); // lol wtf
                console.log(now());
                //assertStateMatches();
                var patch = (DD).diff(inner, userDocStateDom);
                (DD).apply(inner, patch);
                console.log(now());
                // push back to the textarea so we get a userDocState
                //setEditable(true);
            };

            var onRemote = function (info) {
                if (initializing) { return; }

                var shjson = info.realtime.getUserDoc();

                // remember where the cursor is
                cursor.update();

                // build a dom from HJSON, diff, and patch the editor
                applyHjson(shjson);
                //updateDebugTextarea(shjson);

                var shjson2 = JSON.stringify(Convert.core.hyperjson.fromDOM(inner));
                if (shjson2 !== shjson) {
                    rti.propogate(shjson2);
                }
            };

            var onInit = function (info) {
                var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox');
                toolbar = info.realtime.toolbar = Toolbar.create($bar, userName, info.realtime);
                /* TODO handle disconnects and such*/
            };

            var onReady = function (info) {
                console.log("Unlocking editor");
                initializing = false;
                setEditable(true);

                var shjson = info.realtime.getUserDoc();

                applyHjson(shjson);
            };

            var onAbort = function (info) {
                console.log("Aborting the session!");
                // stop the user from continuing to edit
                setEditable(false);
                // TODO inform them that the session was torn down
                toolbar.failed();
            };

            var realtimeOptions = {
                // configuration :D
                doc: inner,
                // first thing called
                onInit: onInit,

                onReady: onReady,

                // when remote changes occur
                onRemote: onRemote,

                // handle aborts
                onAbort: onAbort,

                // provide initialstate...
                initialState: JSON.stringify(Convert.core.hyperjson.fromDOM(inner, isNotMagicLine)),

                // really basic operational transform
                // reject patch if it results in invalid JSON
                transformFunction : JsonOT.validate,

                // websocketURL, ofc
                websocketURL: Config.websocketURL,

                // username
                userName: userName,

                // communication channel name
                channel: key.channel,

                // encryption key
                cryptKey: key.cryptKey
            };

            var rti = module.realtimeInput = window.rti = realtimeInput.start(realtimeOptions);

            var propogate = window.cryptpad_propogate = function () {
                var shjson = JSON.stringify(Convert.core.hyperjson.fromDOM(inner, isNotMagicLine));
                if (!rti.propogate(shjson)) { return; }
                rti.onEvent(shjson);
            };

            var testInput = window.testInput = function (el, offset) {
                var i = 0,
                    j = offset,
                    input = "The quick red fox jumped over the lazy brown dog. ",
                    l = input.length,
                    errors = 0,
                    max_errors = 15,
                    interval;
                var cancel = function () {
                    //if (interval) { interval.cancel(); }
                };

                interval = setRandomizedInterval(function () {
                    propogate();
                    try {
                        el.replaceData(j, 0, input.charAt(i));
                    } catch (err) {
                        errors++;
                        if (errors >= max_errors) {
                            console.log("Max error number exceeded");
                            cancel();
                        }

                        console.error(err);
                        var next = document.createTextNode("-");
                        window.inner.appendChild(next);
                        el = next;
                        j = -1;
                    }
                    i = (i + 1) % l;
                    j++;
                }, 200, 50);

                return {
                    cancel: cancel
                };
            };

            var easyTest = window.easyTest = function () {
                cursor.update();
                var start = cursor.Range.start;
                var test = testInput(start.el, start.offset);
                propogate();
                return test;
            };

            editor.on('change', propogate);
        });
    };

    var interval = 100;
    var first = function () {
        Ckeditor = ifrw.CKEDITOR;
        if (Ckeditor) {
            andThen(Ckeditor);
        } else {
            console.log("Ckeditor was not defined. Trying again in %sms",interval);
            setTimeout(first, interval);
        }
    };

    $(first);
});
<commit_msg>fix quick red fox and make test.cancel work again<commit_after>define([
    '/api/config?cb=' + Math.random().toString(16).substring(2),
    '/common/messages.js',
    '/common/crypto.js',
    '/_socket/realtime-input.js',
    '/common/convert.js',
    '/_socket/toolbar.js',
    '/common/cursor.js',
    '/common/json-ot.js',
    '/bower_components/diff-dom/diffDOM.js',
    '/bower_components/jquery/dist/jquery.min.js',
    '/customize/pad.js'
], function (Config, Messages, Crypto, realtimeInput, Convert, Toolbar, Cursor, JsonOT) {
    var $ = window.jQuery;
    var ifrw = $('#pad-iframe')[0].contentWindow;
    var Ckeditor; // to be initialized later...
    var DiffDom = window.diffDOM;

    window.Convert = Convert;

    window.Toolbar = Toolbar;

    var userName = Crypto.rand64(8),
        toolbar;

    var module = {};

    var isNotMagicLine = function (el) {
        // factor as:
        // return !(el.tagName === 'SPAN' && el.contentEditable === 'false');
        var filter = (el.tagName === 'SPAN' && el.contentEditable === 'false');
        if (filter) {
            console.log("[hyperjson.serializer] prevented an element" +
                "from being serialized:", el);
            return false;
        }
        return true;
    };

    var setRandomizedInterval = function (func, target, range) {
        var timeout;
        var again = function () {
            timeout = setTimeout(function () {
                again();
                func();
            }, target - (range / 2) + Math.random() * range);
        };
        again();
        return {
            cancel: function () {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = undefined;
                }
            }
        };
    }

    var andThen = function (Ckeditor) {
        $(window).on('hashchange', function() {
            window.location.reload();
        });
        if (window.location.href.indexOf('#') === -1) {
            window.location.href = window.location.href + '#' + Crypto.genKey();
            return;
        }

        var fixThings = false;
        var key = Crypto.parseKey(window.location.hash.substring(1));
        var editor = window.editor = Ckeditor.replace('editor1', {
            // https://dev.ckeditor.com/ticket/10907
            needsBrFiller: fixThings,
            needsNbspFiller: fixThings,
            removeButtons: 'Source,Maximize',
            // magicline plugin inserts html crap into the document which is not part of the
            // document itself and causes problems when it's sent across the wire and reflected back
            // but we filter it now, so that's ok.
            removePlugins: 'resize'
        });

        editor.on('instanceReady', function (Ckeditor) {
            editor.execCommand('maximize');
            var documentBody = ifrw.$('iframe')[0].contentDocument.body;

            documentBody.innerHTML = Messages.initialState;

            var inner = window.inner = documentBody;
            var cursor = window.cursor = Cursor(inner);

            var $textarea = $('#feedback');

            var setEditable = function (bool) {
                // inner.style.backgroundColor = bool? 'unset': 'grey';
                inner.setAttribute('contenteditable', bool);
            };

            // don't let the user edit until the pad is ready
            setEditable(false);

            var diffOptions = {
                preDiffApply: function (info) {
                    /* TODO DiffDOM will filter out magicline plugin elements
                        in practice this will make it impossible to use it
                        while someone else is typing, which could be annoying

                        we should check when such an element is going to be
                        removed, and prevent that from happening. */

                    // no use trying to recover the cursor if it doesn't exist
                    if (!cursor.exists()) { return; }

                    /*  frame is either 0, 1, 2, or 3, depending on which
                        cursor frames were affected: none, first, last, or both
                    */
                    var frame = info.frame = cursor.inNode(info.node);

                    if (!frame) { return; }

                    if (typeof info.diff.oldValue === 'string' && typeof info.diff.newValue === 'string') {
                        var pushes = cursor.pushDelta(info.diff.oldValue, info.diff.newValue);

                        if (frame & 1) {
                            // push cursor start if necessary
                            if (pushes.commonStart < cursor.Range.start.offset) {
                                cursor.Range.start.offset += pushes.delta;
                            }
                        }
                        if (frame & 2) {
                            // push cursor end if necessary
                            if (pushes.commonStart < cursor.Range.end.offset) {
                                cursor.Range.end.offset += pushes.delta;
                            }
                        }
                    }
                },
                postDiffApply: function (info) {
                    if (info.frame) {
                        if (info.node) {
                            if (info.frame & 1) { cursor.fixStart(info.node); }
                            if (info.frame & 2) { cursor.fixEnd(info.node); }
                        } else { console.error("info.node did not exist"); }

                        var sel = cursor.makeSelection();
                        var range = cursor.makeRange();

                        cursor.fixSelection(sel, range);
                    }
                }
            };

            var initializing = true;

            var assertStateMatches = function () {
                var userDocState = module.realtimeInput.realtime.getUserDoc();
                var currentState = $textarea.val();
                if (currentState !== userDocState) {
                    console.log({
                        userDocState: userDocState,
                        currentState: currentState
                    });
                    throw new Error("currentState !== userDocState");
                }
            };

            var updateDebugTextarea = function (shjson) {
                window.setTimeout(function () {
                    $textarea.val(shjson);
                }, 0);
            };

            var now = function () { return new Date().getTime() };

            var DD = new DiffDom(diffOptions);
            // apply patches, and try not to lose the cursor in the process!
            var applyHjson = function (shjson) {
                //setEditable(false);
                console.log(now());
                var userDocStateDom = Convert.hjson.to.dom(JSON.parse(shjson));
                userDocStateDom.setAttribute("contenteditable", "true"); // lol wtf
                console.log(now());
                //assertStateMatches();
                var patch = (DD).diff(inner, userDocStateDom);
                (DD).apply(inner, patch);
                console.log(now());
                // push back to the textarea so we get a userDocState
                //setEditable(true);
            };

            var onRemote = function (info) {
                if (initializing) { return; }

                var shjson = info.realtime.getUserDoc();

                // remember where the cursor is
                cursor.update();

                // build a dom from HJSON, diff, and patch the editor
                applyHjson(shjson);
                //updateDebugTextarea(shjson);

                var shjson2 = JSON.stringify(Convert.core.hyperjson.fromDOM(inner));
                if (shjson2 !== shjson) {
                    rti.propogate(shjson2);
                }
            };

            var onInit = function (info) {
                var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox');
                toolbar = info.realtime.toolbar = Toolbar.create($bar, userName, info.realtime);
                /* TODO handle disconnects and such*/
            };

            var onReady = function (info) {
                console.log("Unlocking editor");
                initializing = false;
                setEditable(true);

                var shjson = info.realtime.getUserDoc();

                applyHjson(shjson);
            };

            var onAbort = function (info) {
                console.log("Aborting the session!");
                // stop the user from continuing to edit
                setEditable(false);
                // TODO inform them that the session was torn down
                toolbar.failed();
            };

            var realtimeOptions = {
                // configuration :D
                doc: inner,
                // first thing called
                onInit: onInit,

                onReady: onReady,

                // when remote changes occur
                onRemote: onRemote,

                // handle aborts
                onAbort: onAbort,

                // provide initialstate...
                initialState: JSON.stringify(Convert.core.hyperjson.fromDOM(inner, isNotMagicLine)),

                // really basic operational transform
                // reject patch if it results in invalid JSON
                transformFunction : JsonOT.validate,

                // websocketURL, ofc
                websocketURL: Config.websocketURL,

                // username
                userName: userName,

                // communication channel name
                channel: key.channel,

                // encryption key
                cryptKey: key.cryptKey
            };

            var rti = module.realtimeInput = window.rti = realtimeInput.start(realtimeOptions);

            var propogate = window.cryptpad_propogate = function () {
                var shjson = JSON.stringify(Convert.core.hyperjson.fromDOM(inner, isNotMagicLine));
                if (!rti.propogate(shjson)) { return; }
                rti.onEvent(shjson);
            };

            var testInput = window.testInput = function (el, offset) {
                var i = 0,
                    j = offset,
                    input = "The quick red fox jumps over the lazy brown dog. ",
                    l = input.length,
                    errors = 0,
                    max_errors = 15,
                    interval;
                var cancel = function () {
                    if (interval) { interval.cancel(); }
                };

                interval = setRandomizedInterval(function () {
                    propogate();
                    try {
                        el.replaceData(j, 0, input.charAt(i));
                    } catch (err) {
                        errors++;
                        if (errors >= max_errors) {
                            console.log("Max error number exceeded");
                            cancel();
                        }

                        console.error(err);
                        var next = document.createTextNode("-");
                        window.inner.appendChild(next);
                        el = next;
                        j = -1;
                    }
                    i = (i + 1) % l;
                    j++;
                }, 200, 50);

                return {
                    cancel: cancel
                };
            };

            var easyTest = window.easyTest = function () {
                cursor.update();
                var start = cursor.Range.start;
                var test = testInput(start.el, start.offset);
                propogate();
                return test;
            };

            editor.on('change', propogate);
        });
    };

    var interval = 100;
    var first = function () {
        Ckeditor = ifrw.CKEDITOR;
        if (Ckeditor) {
            andThen(Ckeditor);
        } else {
            console.log("Ckeditor was not defined. Trying again in %sms",interval);
            setTimeout(first, interval);
        }
    };

    $(first);
});
<|endoftext|>


<commit_before><commit_msg>lint compliance and minor refactor<commit_after><|endoftext|>