<commit_before>moduleAid.VERSION = '1.0.0';

this.__defineGetter__('sights', function() {
	var sights = linkedPanel.querySelectorAll('[anonid="findSights"]');
	if(sights.length > 0) {
		return sights[0];
	}

	// First the grid itself
	var boxNode = document.createElement('hbox');
	boxNode.setAttribute('anonid', 'findSights');

	// It shouldn't depend on the stylesheet being loaded, it could error and the browser would be unusable
	boxNode.setAttribute('style', 'pointer-events: none;');

	// Insert the box into the tab
	boxNode = (!viewSource) ? gBrowser.mCurrentBrowser.parentNode.appendChild(boxNode) : linkedPanel.appendChild(boxNode);

	// We need to make sure the box is resized to the proper window size
	sightsResizeViewSource();

	return boxNode;
});

// x and y are the center of the box
this.buildSights = function(x, y, scrollLeft, scrollTop, current) {
	var box = document.createElement('box');
	box.setAttribute('anonid', 'highlightSights');
	box.style.height = '400px';
	box.style.width = '400px';

	box._sights = {
		top: y,
		left: x,
		scrollTop: scrollTop,
		scrollLeft: scrollLeft,
		current: current || false,
		timer: null
	};

	// Method for the sight to auto-update itself
	box.updateSights = function() {
		// A few failsafes
		if(typeof(linkedPanel) == 'undefined' || typeof(timerAid) == 'undefined' || UNLOADED || this.hidden) {
			this.parentNode.removeChild(this);
			return;
		}

		var newSize = this.clientWidth /1.5;

		// Remove the sight when it gets too small
		if(newSize < 40) {
			this.parentNode.removeChild(this);
			return;
		}

		// Let's make sure the document actually exists
		try {
			var scrollTop = contentDocument.getElementsByTagName('html')[0].scrollTop || contentDocument.getElementsByTagName('body')[0].scrollTop;
			var scrollLeft = contentDocument.getElementsByTagName('html')[0].scrollLeft || contentDocument.getElementsByTagName('body')[0].scrollLeft;
		}
		catch(ex) { return; }

		var newTop = (this._sights.top -(newSize /2));
		var newLeft = (this._sights.left -(newSize /2));
		if(scrollTop != this._sights.scrollTop) { newTop -= (scrollTop -this._sights.scrollTop); }
		if(scrollLeft != this._sights.scrollLeft) { newLeft -= (scrollLeft -this._sights.scrollLeft); }

		this.style.top = newTop+'px';
		this.style.left = newLeft+'px';
		this.style.height = newSize+'px';
		this.style.width = newSize+'px';

		this._sights.timer = timerAid.create(this.updateSights, 100, 'once', this);
	}

	sights.appendChild(box);
	box.updateSights();
};

this.currentSights = function(e) {
	// Hide the current sights
	for(var i=0; i<sights.childNodes.length; i++) {
		if(sights.childNodes[i]._sights.current) {
			sights.childNodes[i].hidden = true;
		}
	}

	if(!gFindBar._findField.value || e.detail.retValue == gFindBar.nsITypeAheadFind.FIND_NOTFOUND) { return; }

	var contentWindow = gFindBar.browser._fastFind.currentWindow || gFindBar.browser.contentWindow;
	if(!contentWindow) { return; } // Usually triggered when a selection is on a frame and the frame closes

	// Let's make sure the document actually exists
	try {
		var scrollTop = contentDocument.getElementsByTagName('html')[0].scrollTop || contentDocument.getElementsByTagName('body')[0].scrollTop;
		var scrollLeft = contentDocument.getElementsByTagName('html')[0].scrollLeft || contentDocument.getElementsByTagName('body')[0].scrollLeft;
	}
	catch(ex) { return; }

	var editableNode = gFindBar.browser._fastFind.foundEditable;
	var controller = (editableNode && editableNode.editor) ? editableNode.editor.selectionController : null;
	if(controller) {
		var sel = controller.getSelection(gFindBar.nsISelectionController.SELECTION_NORMAL);
	} else {
		var sel = gFindBar._getSelectionController(contentWindow).getSelection(gFindBar.nsISelectionController.SELECTION_NORMAL);
	}

	if(sel.rangeCount == 1) {
		var dimensions = sel.getRangeAt(0).getClientRects()[0];
		buildSights(dimensions.right -(dimensions.width /2), dimensions.bottom -(dimensions.height /2), scrollLeft, scrollTop, true);
	}
}

this.sightsColor = function() {
	var m = prefAid.highlightColor.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i);
	if(!m) { return; }
	if(m[1].length === 6) { // 6-char notation
		var rgb = {
			r: parseInt(m[1].substr(0,2),16),
			g: parseInt(m[1].substr(2,2),16),
			b: parseInt(m[1].substr(4,2),16)
		};
	} else { // 3-char notation
		var rgb = {
			r: parseInt(m[1].charAt(0)+m[1].charAt(0),16),
			g: parseInt(m[1].charAt(1)+m[1].charAt(1),16),
			b: parseInt(m[1].charAt(2)+m[1].charAt(2),16)
		};
	}

	var c = rgb.r+','+rgb.g+','+rgb.b;
	var o = (0.213 * (rgb.r /255) + 0.715 * (rgb.g /255) + 0.072 * (rgb.b /255) < 0.5) ? '255,255,255' : '0,0,0';

	styleAid.unload('sightsColor');

	var sscode = '/*FindBar Tweak CSS declarations of variable values*/\n';
	sscode += '@namespace url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);\n';
	sscode += '@-moz-document url("chrome://browser/content/browser.xul"), url("chrome://global/content/viewSource.xul") {\n';
	sscode += ' box[anonid="highlightSights"] {\n';
	sscode += '  -moz-border-top-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += '  -moz-border-bottom-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += '  -moz-border-left-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += '  -moz-border-right-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += ' }\n';
	sscode += '}';

	styleAid.load('sightsColor', sscode, true);
};

this.delaySightsResizeViewSource = function() {
	timerAid.init('resizeViewSource', sightsResizeViewSource, 0);
};

this.sightsResizeViewSource = function() {
	if(!viewSource) { return; }

	var styleString = 'top: '+$('viewSource-toolbox').clientHeight+'px;';
	styleString += ' height: '+$('content').clientHeight+'px;';
	setAttribute($$('[anonid="findSights"]')[0], 'style', styleString);
	listenerAid.add(viewSource, 'resize', delaySightsResizeViewSource);
};

moduleAid.LOADMODULE = function() {
	prefAid.listen('highlightColor', sightsColor);
	sightsColor();

	listenerAid.add(gFindBar, 'FoundFindBar', currentSights);
	listenerAid.add(gFindBar, 'FoundAgain', currentSights);
}

moduleAid.UNLOADMODULE = function() {
	listenerAid.remove(gFindBar, 'FoundFindBar', currentSights);
	listenerAid.remove(gFindBar, 'FoundAgain', currentSights);

	if(!viewSource) {
		for(var t=0; t<gBrowser.mTabs.length; t++) {
			var panel = $(gBrowser.mTabs[t].linkedPanel);
			var innerSights = panel.querySelectorAll('[anonid="findSights"]');
			if(innerSights.length > 0) {
				innerSights[0].parentNode.removeChild(innerSights[0]);
			}
		}
	}
	else {
		var innerSights = $$('[anonid="findSights"]');
		if(innerSights.length > 0) {
			innerSights[0].parentNode.removeChild(innerSights[0]);
		}
		listenerAid.remove(viewSource, 'resize', delaySightsResizeViewSource);
	}

	prefAid.unlisten('highlightColor', sightsColor);

	if(UNLOADED || !prefAid.sightsCurrent) {
		styleAid.unload('sightsColor');
	}
};
<commit_msg>bugfix for sights in frames<commit_after>moduleAid.VERSION = '1.0.1';

this.__defineGetter__('sights', function() {
	var sights = linkedPanel.querySelectorAll('[anonid="findSights"]');
	if(sights.length > 0) {
		return sights[0];
	}

	// First the grid itself
	var boxNode = document.createElement('hbox');
	boxNode.setAttribute('anonid', 'findSights');

	// It shouldn't depend on the stylesheet being loaded, it could error and the browser would be unusable
	boxNode.setAttribute('style', 'pointer-events: none;');

	// Insert the box into the tab
	boxNode = (!viewSource) ? gBrowser.mCurrentBrowser.parentNode.appendChild(boxNode) : linkedPanel.appendChild(boxNode);

	// We need to make sure the box is resized to the proper window size
	sightsResizeViewSource();

	return boxNode;
});

// x and y are the center of the box
this.buildSights = function(x, y, scrollLeft, scrollTop, current) {
	var box = document.createElement('box');
	box.setAttribute('anonid', 'highlightSights');
	box.style.height = '400px';
	box.style.width = '400px';

	box._sights = {
		top: y,
		left: x,
		scrollTop: scrollTop,
		scrollLeft: scrollLeft,
		current: current || false,
		timer: null
	};

	// Method for the sight to auto-update itself
	box.updateSights = function() {
		// A few failsafes
		if(typeof(linkedPanel) == 'undefined' || typeof(timerAid) == 'undefined' || UNLOADED || this.hidden) {
			this.parentNode.removeChild(this);
			return;
		}

		var newSize = this.clientWidth /1.5;

		// Remove the sight when it gets too small
		if(newSize < 40) {
			this.parentNode.removeChild(this);
			return;
		}

		// Let's make sure the document actually exists
		try {
			var scrollTop = contentDocument.getElementsByTagName('html')[0].scrollTop || contentDocument.getElementsByTagName('body')[0].scrollTop;
			var scrollLeft = contentDocument.getElementsByTagName('html')[0].scrollLeft || contentDocument.getElementsByTagName('body')[0].scrollLeft;
		}
		catch(ex) { return; }

		var newTop = (this._sights.top -(newSize /2));
		var newLeft = (this._sights.left -(newSize /2));
		if(scrollTop != this._sights.scrollTop) { newTop -= (scrollTop -this._sights.scrollTop); }
		if(scrollLeft != this._sights.scrollLeft) { newLeft -= (scrollLeft -this._sights.scrollLeft); }

		this.style.top = newTop+'px';
		this.style.left = newLeft+'px';
		this.style.height = newSize+'px';
		this.style.width = newSize+'px';

		this._sights.timer = timerAid.create(this.updateSights, 100, 'once', this);
	}

	sights.appendChild(box);
	box.updateSights();
};

this.currentSights = function(e) {
	// Hide the current sights
	for(var i=0; i<sights.childNodes.length; i++) {
		if(sights.childNodes[i]._sights.current) {
			sights.childNodes[i].hidden = true;
		}
	}

	if(!gFindBar._findField.value || e.detail.retValue == gFindBar.nsITypeAheadFind.FIND_NOTFOUND) { return; }

	var contentWindow = gFindBar.browser._fastFind.currentWindow || gFindBar.browser.contentWindow;
	if(!contentWindow) { return; } // Usually triggered when a selection is on a frame and the frame closes

	// Let's make sure the document actually exists
	try {
		var scrollTop = contentDocument.getElementsByTagName('html')[0].scrollTop || contentDocument.getElementsByTagName('body')[0].scrollTop;
		var scrollLeft = contentDocument.getElementsByTagName('html')[0].scrollLeft || contentDocument.getElementsByTagName('body')[0].scrollLeft;
	}
	catch(ex) { return; }

	var editableNode = gFindBar.browser._fastFind.foundEditable;
	var controller = (editableNode && editableNode.editor) ? editableNode.editor.selectionController : null;
	if(controller) {
		var sel = controller.getSelection(gFindBar.nsISelectionController.SELECTION_NORMAL);
	} else {
		var sel = gFindBar._getSelectionController(contentWindow).getSelection(gFindBar.nsISelectionController.SELECTION_NORMAL);
	}

	if(sel.rangeCount == 1) {
		var retRange = sel.getRangeAt(0);

		// We need to account for frames positions as well, as the ranges values are relative to them
		var xMod = 0;
		var yMod = 0;

		var ownDoc = retRange.startContainer.ownerDocument;
		while(ownDoc != contentDocument) {
			try {
				var frameElement = ownDoc.defaultView.frameElement.getBoundingClientRect();
				xMod += frameElement.left;
				yMod += frameElement.top;

				ownDoc = ownDoc.defaultView.parent.document;
			}
			catch(ex) { return; } // failsafe
		}

		var dimensions = retRange.getClientRects()[0];
		buildSights(dimensions.right -(dimensions.width /2) +xMod, dimensions.bottom -(dimensions.height /2) +yMod, scrollLeft, scrollTop, true);
	}
}

this.sightsColor = function() {
	if(!prefAid.sightsCurrent) { return; }

	var m = prefAid.highlightColor.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i);
	if(!m) { return; }
	if(m[1].length === 6) { // 6-char notation
		var rgb = {
			r: parseInt(m[1].substr(0,2),16),
			g: parseInt(m[1].substr(2,2),16),
			b: parseInt(m[1].substr(4,2),16)
		};
	} else { // 3-char notation
		var rgb = {
			r: parseInt(m[1].charAt(0)+m[1].charAt(0),16),
			g: parseInt(m[1].charAt(1)+m[1].charAt(1),16),
			b: parseInt(m[1].charAt(2)+m[1].charAt(2),16)
		};
	}

	var c = rgb.r+','+rgb.g+','+rgb.b;
	var o = (0.213 * (rgb.r /255) + 0.715 * (rgb.g /255) + 0.072 * (rgb.b /255) < 0.5) ? '255,255,255' : '0,0,0';

	styleAid.unload('sightsColor');

	var sscode = '/*FindBar Tweak CSS declarations of variable values*/\n';
	sscode += '@namespace url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);\n';
	sscode += '@-moz-document url("chrome://browser/content/browser.xul"), url("chrome://global/content/viewSource.xul") {\n';
	sscode += ' box[anonid="highlightSights"] {\n';
	sscode += '  -moz-border-top-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += '  -moz-border-bottom-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += '  -moz-border-left-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += '  -moz-border-right-colors: rgba('+c+',0.25) rgba('+c+',0.95) rgba('+o+',0.7) rgb('+c+') rgba('+c+', 0.85) rgba('+o+', 0.5) rgba('+c+', 0.4) rgba('+c+', 0.15) !important;\n';
	sscode += ' }\n';
	sscode += '}';

	styleAid.load('sightsColor', sscode, true);
};

this.delaySightsResizeViewSource = function() {
	timerAid.init('resizeViewSource', sightsResizeViewSource, 0);
};

this.sightsResizeViewSource = function() {
	if(!viewSource) { return; }

	var styleString = 'top: '+$('viewSource-toolbox').clientHeight+'px;';
	styleString += ' height: '+$('content').clientHeight+'px;';
	setAttribute($$('[anonid="findSights"]')[0], 'style', styleString);
	listenerAid.add(viewSource, 'resize', delaySightsResizeViewSource);
};

moduleAid.LOADMODULE = function() {
	prefAid.listen('highlightColor', sightsColor);
	sightsColor();

	listenerAid.add(gFindBar, 'FoundFindBar', currentSights);
	listenerAid.add(gFindBar, 'FoundAgain', currentSights);
}

moduleAid.UNLOADMODULE = function() {
	listenerAid.remove(gFindBar, 'FoundFindBar', currentSights);
	listenerAid.remove(gFindBar, 'FoundAgain', currentSights);

	if(!viewSource) {
		for(var t=0; t<gBrowser.mTabs.length; t++) {
			var panel = $(gBrowser.mTabs[t].linkedPanel);
			var innerSights = panel.querySelectorAll('[anonid="findSights"]');
			if(innerSights.length > 0) {
				innerSights[0].parentNode.removeChild(innerSights[0]);
			}
		}
	}
	else {
		var innerSights = $$('[anonid="findSights"]');
		if(innerSights.length > 0) {
			innerSights[0].parentNode.removeChild(innerSights[0]);
		}
		listenerAid.remove(viewSource, 'resize', delaySightsResizeViewSource);
	}

	prefAid.unlisten('highlightColor', sightsColor);

	if(UNLOADED || !prefAid.sightsCurrent) {
		styleAid.unload('sightsColor');
	}
};
<|endoftext|>


<commit_before>$( document ).ready(function($){

	// Materialize Setup
	$(".button-collapse").sideNav();
	$('.modal-trigger').leanModal();
	$('.tooltipped').tooltip({delay: 50});
	$('.parallax').parallax();

	// Smooth Scrolling
    $(document.body).on('click', 'a[href*=#]:not([href=#])', function (e) {
        e.preventDefault();
        if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') || location.hostname == this.hostname) {

            var target = $(this.hash);
            target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
            if (target.length) {
                var offset = 0;
                if (target[0].id.indexOf("feature-") == 0) {
                    offset = 100;
                }
                $('html,body').animate({
                    scrollTop: target.offset().top - offset
                }, 1000);
                return false;
            }
        }
    });

    // Webcal Link Rewrite
    if( $(".calendar-button").length ){

    	$(".calendar-button").each(function(){
	    	var link = $(this).attr('href');

			link = link.replace("http://", "webcal://");

			link = link.replace("https://", "webcal://");

			if(ua.indexOf("android") == -1 && ( navigator.platform.toUpperCase().indexOf('LINUX')>=0 || navigator.platform.toUpperCase().indexOf('WIN')>=0 ) ){
				link = "https://www.google.com/calendar/render?cid=" + link;
			}

			$(this).attr('href', link);
    	});
    }
});

/*
Tracks changes made to checkboxes on subscription page
 */
function addToList( id ){

	var currentText = $('input[name=allSubscriptions]').val();
	if( currentText.search(new RegExp(" ?" + id + " ?")) >= 0 ){
		// If the number's already in the string, remove it

		$('input[name=allSubscriptions]').val(
			currentText.replace( new RegExp(" ?" + id + " ?"), " ")
		);
	} else {
		// If the checkbox has been clicked for the first time, add it to the input
		$('input[name=allSubscriptions]').val( currentText + " " + id );
	}
}<commit_msg>finally<commit_after>$( document ).ready(function($){

	// Materialize Setup
	$(".button-collapse").sideNav();
	$('.modal-trigger').leanModal();
	$('.tooltipped').tooltip({delay: 50});
	$('.parallax').parallax();

	// Smooth Scrolling
    $(document.body).on('click', 'a[href*=#]:not([href=#])', function (e) {
        e.preventDefault();
        if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') || location.hostname == this.hostname) {

            var target = $(this.hash);
            target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
            if (target.length) {
                var offset = 0;
                if (target[0].id.indexOf("feature-") == 0) {
                    offset = 100;
                }
                $('html,body').animate({
                    scrollTop: target.offset().top - offset
                }, 1000);
                return false;
            }
        }
    });

    // Webcal Link Rewrite
    if( $(".calendar-button").length ){

    	$(".calendar-button").each(function(){
	    	var link = $(this).attr('href');

			link = link.replace("http://", "webcal://");

			link = link.replace("https://", "webcal://");

			if(navigator.userAgent.indexOf("android") == -1 && ( navigator.platform.toUpperCase().indexOf('LINUX')>=0 || navigator.platform.toUpperCase().indexOf('WIN')>=0 ) ){
				link = "https://www.google.com/calendar/render?cid=" + link;
			}

			$(this).attr('href', link);
    	});
    }
});

/*
Tracks changes made to checkboxes on subscription page
 */
function addToList( id ){

	var currentText = $('input[name=allSubscriptions]').val();
	if( currentText.search(new RegExp(" ?" + id + " ?")) >= 0 ){
		// If the number's already in the string, remove it

		$('input[name=allSubscriptions]').val(
			currentText.replace( new RegExp(" ?" + id + " ?"), " ")
		);
	} else {
		// If the checkbox has been clicked for the first time, add it to the input
		$('input[name=allSubscriptions]').val( currentText + " " + id );
	}
}<|endoftext|>


<commit_before>
BenchmarkStream = Backbone.Model.extend({
  initialize: function () {
    var self = this;
    self.socket = new WebSocket(this.get('addr'));
    self.socket.onopen = function (e) {
      self.trigger('open', e);
    };
    self.socket.onmessage = function (e) {
      self.trigger('message', e);
      self.trigger('data', e.data);

      var parsed = JSON.parse(e.data);
      self.trigger('jsonData', parsed);
      self.trigger('dtype-' + parsed.dtype, parsed.data);
    };
    self.socket.onclose = function (e) {
      self.trigger('close', e);
    };
    self.socket.onerror = function (e) {
      self.trigger('error', e);
    };
  }
});

ConsoleView = Backbone.View.extend({
  initialize: function () {
    this.$el = $(this.el);
    this.maxMessages = 1000;
    this.consoleEnabledBox = $('#console-enabled');
  },
  append: function(msg) {
    if (!this.consoleEnabledBox.attr('checked')) {
      return;
    }

    this.$el.append('
' + msg + '
');

    var children = this.$el.children();
    if (children.length > this.maxMessages) {
      var garbage = children.slice(0, children.length - this.maxMessages);
      $(garbage).remove();
    }

    this.scrollBottom();
  },
  scrollBottom: function () {
    this.$el

    var lastTop = $(consoleView.$el.children().last()).offset().top;
    this.$el.scrollTop(lastTop);
  },
  logEvents: function (obj, eventType) {
    var self = this;
    obj.bind(eventType, function (e) {
      self.append(e);
    });
  }
});

Benchmarker = Backbone.Model.extend({
  url: "/benchmarker",
  initialize: function () {
    this.fetch();
  },
  start: function (url, concurrency, requests) {
    var self = this;
    $.post("/benchmarker",
      {state: "started",
       concurrency: concurrency,
       requests: requests,
       url: url
      },
      function (data) {
        self.set({state: "started"});
        console.log(data);
    });
  },
  stop: function () {
    var self = this;
    $.post("/benchmarker", {state: "stopped"}, function () {
      self.set({state: "stopped"});
    });
  },
  bindToStream: function (stream) {
    var self = this;
    stream.bind('dtype-stats', function (data) {
      self.set(data)
    });

    stream.bind('dtype-state', function (data) {
      self.set({state: data});
    });
  }
});

ControlsView = Backbone.View.extend({
  initialize: function () {
    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);
  },
  events: {
    "click #start-ctl": "start",
    "click #stop-ctl":  "stop",
    "change": "render"
  },
  start: function (e) {
    console.log("Starting");
    var self = this;

    self.disableInputs();

    var url = this.$el.find('#url').val();
    var concurrency = this.$el.find('#concurrency').val();
    var requests = this.$el.find('#requests').val();
    self.model.start(url, concurrency, requests);
  },
  stop: function (e) {
    this.model.stop();
  },
  render: function () {
    if (this.model.get('state') === 'stopped') {
      this.renderStartable();
    } else {
      this.renderStoppable();
    }
  },
  renderStartable: function () {
    this.enableInputs();

    var startCtl = this.$el.find('#start-ctl');
    startCtl.removeAttr('disabled');
    startCtl.show();

    this.$el.find('#stop-ctl').hide();
  },
  renderStoppable: function () {
    this.disableInputs();

    var stopCtl = this.$el.find('#stop-ctl');
    stopCtl.removeAttr('disabled');
    stopCtl.show();

    this.$el.find('#start-ctl').hide();
  },
  disableInputs: function () {
    this.$el.find('input').attr('disabled', true);
  },
  enableInputs: function () {
    this.$el.find('input').removeAttr('disabled');
  }
});

AggregateStatsView = Backbone.View.extend({
   initialize: function () {
    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);

    this.renderElements = {
      completed: this.$el.find('#runs-total'),
      succeeded: this.$el.find('#runs-succeeded'),
      failed: this.$el.find('#runs-failed'),
      runtime: this.$el.find('#runtime'),
      medianRuntime: this.$el.find('#median-runtime'),
      runsSec: this.$el.find('#runs-sec'),
      responseCodes: this.$el.find('#response-code-stats tbody')
    }
  },
  render: function () {
    $('h1').text("(engulf " + $('#url').val().toLowerCase() + ")");

    var res = this.renderElements;
    res.completed.text(this.model.get('runs-total'));
    res.succeeded.text(this.model.get('runs-succeeded'));
    res.failed.text(this.model.get('runs-failed'));
    res.runtime.text(this.formatMillis(this.model.get('runtime')));

    var medianRuntime = this.model.get('median-runtime');
    if (medianRuntime) {
      res.medianRuntime.text(medianRuntime + " ms");
    }

    var runsSec = this.model.get('runs-sec');
    if (runsSec) {
      res.runsSec.text(parseInt(runsSec) + ' / sec');
    }

    this.renderResponseCodes(this.model.get('response-code-counts'));
  },
  renderResponseCodes: function (codeCounts) {
    var self = this;

    var tbody = self.renderElements.responseCodes;
    tbody.html('');

    if (!codeCounts) { return };

    if (!self.tmpl) {
      self.tmpl = _.template("
<%= code %>	<%= count %>
");
    }

    for (code in codeCounts) {
      var count = codeCounts[code];
      tbody.append(self.tmpl({code: code, count: count}));
    }
  },
  formatMillis: function(millis) {
    totalSec = parseInt(millis / 1000);
    hours = parseInt( totalSec / 3600 ) % 24;
    minutes = parseInt( totalSec / 60 ) % 60;
    seconds = totalSec % 60;
    millis = millis % 1000;

    return (hours < 10 ? "0" + hours : hours) +
            ":" + (minutes < 10 ? "0" + minutes : minutes) +
            ":" + (seconds  < 10 ? "0" + seconds : seconds) +
            "." + millis;
  }
});

ChartsView = Backbone.View.extend({
  initialize: function () {
    var self = this;

    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);

    var chartW = 595;
    var w = this.w = (parseInt(chartW/100));
    var h = this.h = 100;

    // Which field in the results to use as data
    this.yField = "avg";

    var data = _.range(100).map(function (i) { return {avg: 0, idx: i} });

    this.x = d3.scale.linear().
               domain([0, 1]).
               range([0, w]);

    this.setYMax = function(upper) {
      self.y = d3.scale.linear().
                  domain([0, upper]).
                  rangeRound([0, h-40]);
    };

    this.setYMax(100);

    this.rtPercentiles = d3.select("#resp-time-percentiles").
         append("svg").
         attr("class", "chart").
         attr("width", w * 100).
         attr("height", h);

    this.rtPercentiles.selectAll("rect").
         data(data).
         enter().append("rect").
         attr("x", function(d, i) { return self.x(i) - .5; }).
         attr("y", function(d) { return h - self.y(d[self.yField]) - .5; }).
         attr("width", w).
         attr("height", function(d) { return self.y(d[self.yField]); });

    this.rtPercentiles.append("line")
         .attr("x1", 0)
         .attr("x2", w * data.length)
         .attr("y1", h - .5)
         .attr("y2", h - .5)
         .style("stroke", "#000");

    this.rtPercentiles.selectAll("text")
         .data(data)
       .enter().append("text")
         .attr("x", function (d, i) { return self.x(i); })
         .attr("y", function(d, i) { return 1; })
         .attr("dx", -3) // padding-right
         .attr("dy", ".35em") // vertical-align: middle
         .attr("text-anchor", "end") // text-align: right
         .attr("class", function (d,i) { return (i === 0 || ((i+1) % 10 === 0)) ? "decile" : "non-decile" })
         .text("");

    window.rtp = this.rtPercentiles;

  },
  render: function () {
   var self = this;
   var rtpData = _.map(this.model.get('runtime-percentiles'), function (d) { return d.avg})
   var decileData = _.filter(this.model.get('runtime-percentiles'),
                           function(d) { return (d.idx === 0 || ((d.idx+1) % 10 === 0)) }).
                           map(function (d) { return d.avg});

   if (!rtpData) {
    return
   }

   self.setYMax(rtpData[rtpData.length-1]);

   self.rtPercentiles.selectAll("rect").
       data(rtpData).
      transition().
       duration(50).
       attr("y", function(d) { return self.h - self.y(d) - .5; }).
       attr("height", function(d) { return self.y(d); });

    self.rtPercentiles.selectAll(".decile")
         .data(decileData)
         .transition().duration(100)
         .attr("x", function (d, i) { return self.x((i+1) * self.w * 1.83) - 8 ; })
         .attr("y", function(d, i) { return 20; })
         .attr("dx", -3) // padding-right
         .attr("dy", ".35em") // vertical-align: middle
         .attr("text-anchor", "end") // text-align: right
         .text(function (d, i) { return d + 'ms' } );
  }
});

ResponseTimeSeriesView = Backbone.View.extend({
  initialize: function () {
    var self = this;

    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);

    var chartW = self.chartW = 500;
    var data = _.range(100).map(function (i) { return {value: 0, count: 0}});
    var data = [];

    var w = this.w = chartW;
    var h = this.h = 80;

    self.setYMax = function(upper) {
      self.y = d3.scale.linear().
                  domain([0, upper]).
                  rangeRound([0, h]);
    };

    self.setXMax = function(upper) {
      self.x = d3.scale.linear()
              .domain([0, upper])
              .range([0, w]);
    }

    this.lastTotal = 0;
  },
  render: function () {
    var self = this;

    var data = [];
    var raw = this.model.get('avg-runtime-by-start-time');

    var valMax = 0;
    var rawTotal = 0;
    for (time in raw) {
      var d = raw[time];

      rawTotal += d.total;

      if (d.avg > valMax) {
        valMax = d.avg;
      }

      d.time = time;
      d.value = d.avg;
      data.push(d);
    }

    if (rawTotal != self.lastTotal) {
      self.lastTotal = rawTotal;
    } else {
      return;
    }

    $('#resp-time-series').html('');

     var chart = d3.select("#resp-time-series").append("svg")
      .attr("class", "chart")
      .attr("width", self.chartW)
      .attr("height", self.h);

    chart.append("line")
      .attr("x1", 0)
      .attr("x2", self.chartW)
      .attr("y1", self.h - .5)
      .attr("y2", self.h - .5)
      .style("stroke", "#000");

      this.chart = chart;

    this.setYMax(valMax);
    this.setXMax(data.length);

    if (!data) {
      return
    }

    var w = this.w;
    var h = this.h;

    var x = this.x;
    var y = this.y;

    var rect = chart.selectAll("rect")
      .data(data, function(d) { return d.time; });

    rect.enter().append("rect", "line")
      .attr("x", function(d, i) { return x(i + 1) - .5; })
      .attr("y", function(d) { return h - y(d.value) - .5; })
      .attr("width", 2)
      .attr("height", function(d) { return y(d.value); })
    .transition()
      .duration(1)
      .attr("x", function(d, i) { return x(i) - .5; });

    /*
    rect.transition()
        .duration(1)
        .attr("x", function(d, i) { return x(i) - .5; });

    rect.exit().transition()
        .duration(1)
        .attr("x", function(d, i) { return x(i - 1) - .5; })
        .emove();
        */

  }
});

$(function () {
  var benchmarkStream = window.benchmarkStream = new BenchmarkStream(
      {addr: 'ws://' + location.host + '/benchmarker/stream'}
  );

  var consoleView  = window.consoleView = new ConsoleView(
    {
      el: $('#console')
    }
  );
  consoleView.logEvents(benchmarkStream, 'data');

  var benchmarker  = window.benchmarker = new Benchmarker();
  benchmarker.bindToStream(benchmarkStream);

  var controlsView = window.controlsView = new ControlsView(
    {
      el: $('#controls')[0],
      model: benchmarker,
    }
  );

  var statsView = window.statsView = new AggregateStatsView(
    {
      el: $('#stats')[0],
      model: benchmarker,
    }
  );

  var chartsView = window.chartsView = new ChartsView(
    {
      el: $('#resp-time-percentiles')[0],
      model: benchmarker,
    }
  );

  var responseTimeSeriesView = window.responseTimeSeriesView = new ResponseTimeSeriesView(
    {
      el: $('#resp-time-series')[0],
      model: benchmarker,
    }
  );
});
<commit_msg>Minor JS cleanup<commit_after>
BenchmarkStream = Backbone.Model.extend({
  initialize: function () {
    var self = this;
    self.socket = new WebSocket(this.get('addr'));
    self.socket.onopen = function (e) {
      self.trigger('open', e);
    };
    self.socket.onmessage = function (e) {
      self.trigger('message', e);
      self.trigger('data', e.data);

      var parsed = JSON.parse(e.data);
      self.trigger('jsonData', parsed);
      self.trigger('dtype-' + parsed.dtype, parsed.data);
    };
    self.socket.onclose = function (e) {
      self.trigger('close', e);
    };
    self.socket.onerror = function (e) {
      self.trigger('error', e);
    };
  }
});

ConsoleView = Backbone.View.extend({
  initialize: function () {
    this.$el = $(this.el);
    this.maxMessages = 1000;
    this.consoleEnabledBox = $('#console-enabled');
  },
  append: function(msg) {
    if (!this.consoleEnabledBox.attr('checked')) {
      return;
    }

    this.$el.append('
' + msg + '
');

    var children = this.$el.children();
    if (children.length > this.maxMessages) {
      var garbage = children.slice(0, children.length - this.maxMessages);
      $(garbage).remove();
    }

    this.scrollBottom();
  },
  scrollBottom: function () {
    this.$el

    var lastTop = $(consoleView.$el.children().last()).offset().top;
    this.$el.scrollTop(lastTop);
  },
  logEvents: function (obj, eventType) {
    var self = this;
    obj.bind(eventType, function (e) {
      self.append(e);
    });
  }
});

Benchmarker = Backbone.Model.extend({
  url: "/benchmarker",
  initialize: function () {
    this.fetch();
  },
  start: function (url, concurrency, requests) {
    var self = this;
    $.post("/benchmarker",
      {state: "started",
       concurrency: concurrency,
       requests: requests,
       url: url
      },
      function (data) {
        self.set({state: "started"});
        console.log(data);
    });
  },
  stop: function () {
    var self = this;
    $.post("/benchmarker", {state: "stopped"}, function () {
      self.set({state: "stopped"});
    });
  },
  bindToStream: function (stream) {
    var self = this;
    stream.bind('dtype-stats', function (data) {
      self.set(data)
    });

    stream.bind('dtype-state', function (data) {
      self.set({state: data});
    });
  }
});

ControlsView = Backbone.View.extend({
  initialize: function () {
    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);
  },
  events: {
    "click #start-ctl": "start",
    "click #stop-ctl":  "stop",
    "change": "render"
  },
  start: function (e) {
    console.log("Starting");
    var self = this;

    self.disableInputs();

    var url = this.$el.find('#url').val();
    var concurrency = this.$el.find('#concurrency').val();
    var requests = this.$el.find('#requests').val();
    self.model.start(url, concurrency, requests);
  },
  stop: function (e) {
    this.model.stop();
  },
  render: function () {
    if (this.model.get('state') === 'stopped') {
      this.renderStartable();
    } else {
      this.renderStoppable();
    }
  },
  renderStartable: function () {
    this.enableInputs();

    var startCtl = this.$el.find('#start-ctl');
    startCtl.removeAttr('disabled');
    startCtl.show();

    this.$el.find('#stop-ctl').hide();
  },
  renderStoppable: function () {
    this.disableInputs();

    var stopCtl = this.$el.find('#stop-ctl');
    stopCtl.removeAttr('disabled');
    stopCtl.show();

    this.$el.find('#start-ctl').hide();
  },
  disableInputs: function () {
    this.$el.find('input').attr('disabled', true);
  },
  enableInputs: function () {
    this.$el.find('input').removeAttr('disabled');
  }
});

AggregateStatsView = Backbone.View.extend({
   initialize: function () {
    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);

    this.renderElements = {
      completed: this.$el.find('#runs-total'),
      succeeded: this.$el.find('#runs-succeeded'),
      failed: this.$el.find('#runs-failed'),
      runtime: this.$el.find('#runtime'),
      medianRuntime: this.$el.find('#median-runtime'),
      runsSec: this.$el.find('#runs-sec'),
      responseCodes: this.$el.find('#response-code-stats tbody')
    }
  },
  render: function () {
    $('h1').text("(engulf " + $('#url').val().toLowerCase() + ")");

    var res = this.renderElements;
    res.completed.text(this.model.get('runs-total'));
    res.succeeded.text(this.model.get('runs-succeeded'));
    res.failed.text(this.model.get('runs-failed'));
    res.runtime.text(this.formatMillis(this.model.get('runtime')));

    var medianRuntime = this.model.get('median-runtime');
    if (medianRuntime) {
      res.medianRuntime.text(medianRuntime + " ms");
    }

    var runsSec = this.model.get('runs-sec');
    if (runsSec) {
      res.runsSec.text(parseInt(runsSec) + ' / sec');
    }

    this.renderResponseCodes(this.model.get('response-code-counts'));
  },
  renderResponseCodes: function (codeCounts) {
    var self = this;

    var tbody = self.renderElements.responseCodes;
    tbody.html('');

    if (!codeCounts) { return };

    if (!self.tmpl) {
      self.tmpl = _.template("
<%= code %>	<%= count %>
");
    }

    for (code in codeCounts) {
      var count = codeCounts[code];
      tbody.append(self.tmpl({code: code, count: count}));
    }
  },
  formatMillis: function(millis) {
    totalSec = parseInt(millis / 1000);
    hours = parseInt( totalSec / 3600 ) % 24;
    minutes = parseInt( totalSec / 60 ) % 60;
    seconds = totalSec % 60;
    millis = millis % 1000;

    return (hours < 10 ? "0" + hours : hours) +
            ":" + (minutes < 10 ? "0" + minutes : minutes) +
            ":" + (seconds  < 10 ? "0" + seconds : seconds) +
            "." + millis;
  }
});

ChartsView = Backbone.View.extend({
  filterData: function (data) {
    return _.map(data, function (d) { return d.avg} );
  },
  yScale: null,
  setYScale: function(newYMax) {
    this.yScale = d3.scale.linear().
      domain([0, newYMax]).
      rangeRound([0, this.h-40]);
  },
  xScale: null,
  setXScale: function (width) {
    this.xScale = d3.scale.linear().
      domain([0, 1]).
      range([0, width]);
  },
  initialize: function () {
    var self = this;
    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);

    var chartW = 595;
    var w = this.w = (parseInt(chartW/100));
    var h = this.h = 100;

    // Which field in the results to use as data
    this.yField = "avg";

    var data = _.range(100).map(function (i) {return {}});

    this.setYScale(100);
    this.setXScale(w);

    this.rtPercentiles = d3.select("#resp-time-percentiles").
         append("svg").
         attr("class", "chart").
         attr("width", w * 100).
         attr("height", h);

    // Setup bars
    this.rtPercentiles.selectAll("rect").
         data(data).
         enter().append("rect").
         attr("x", function(d, i) { return self.xScale(i) - .5; }).
         attr("y", function(d) { return h - self.yScale(d[self.yField]) - .5; }).
         attr("width", w).
         attr("height", function(d) { return self.yScale(d[self.yField]); });

    // Create bottom line
    this.rtPercentiles.append("line").
         attr("x1", 0).
         attr("x2", w * data.length).
         attr("y1", h - .5).
         attr("y2", h - .5).
         style("stroke", "#000");

    // Setup percentile labels
    this.rtPercentiles.selectAll("text").
         data(data).
         enter().append("text").
         attr("x", function (d, i) { return self.xScale(i); }).
         attr("y", function(d, i) { return 1; }).
         attr("dx", -3). // padding-right
         attr("dy", ".35em"). // vertical-align: middle
         attr("text-anchor", "end"). // text-align: right
         attr("class", function (d,i) { return (i === 0 || ((i+1) % 10 === 0)) ? "decile" : "non-decile" }).
         text("");
  },
  render: function () {
    var self = this;
    var rawData = this.model.get('runtime-percentiles');
    if (! rawData) return;

    var percentiles = this.filterData(rawData);

    // Grab every 10th percentile for the avg time overlay
    var deciles = [];
    _.each(percentiles, function (p, i) {
      if (i === 0 || ((i+1) % 10 === 0)) {
        deciles.push(p)
      }
    });

    this.setYScale(percentiles[percentiles.length-1]);

    self.rtPercentiles.selectAll("rect").
         data(percentiles).
         transition().
         duration(50).
         attr("y", function(d) { return self.h - self.yScale(d) - .5; }).
         attr("height", function(d) { return self.yScale(d); });

    self.rtPercentiles.selectAll(".decile").
         data(deciles).
         transition().
         duration(100).
         attr("x", function (d, i) {
           return self.xScale((i+1) * self.w * 1.83) - 8;
         }).
         attr("y", function(d, i) { return 20; }).
         attr("dx", -3). // padding-right
         attr("dy", ".35em"). // vertical-align: middle
         attr("text-anchor", "end"). // text-align: right
         text(function (d, i) { return d + 'ms' } );

  }

});

ResponseTimeSeriesView = Backbone.View.extend({
  initialize: function () {
    var self = this;

    this.$el = $(this.el);

    _.bindAll(this, "render");
    this.model.bind('change', this.render);

    var chartW = self.chartW = 500;
    var data = _.range(100).map(function (i) { return {value: 0, count: 0}});
    var data = [];

    var w = this.w = chartW;
    var h = this.h = 80;

    self.setYMax = function(upper) {
      self.y = d3.scale.linear().
                  domain([0, upper]).
                  rangeRound([0, h]);
    };

    self.setXMax = function(upper) {
      self.x = d3.scale.linear()
              .domain([0, upper])
              .range([0, w]);
    }

    this.lastTotal = 0;
  },
  render: function () {
    var self = this;

    var data = [];
    var raw = this.model.get('avg-runtime-by-start-time');

    var valMax = 0;
    var rawTotal = 0;
    for (time in raw) {
      var d = raw[time];

      rawTotal += d.total;

      if (d.avg > valMax) {
        valMax = d.avg;
      }

      d.time = time;
      d.value = d.avg;
      data.push(d);
    }

    if (rawTotal != self.lastTotal) {
      self.lastTotal = rawTotal;
    } else {
      return;
    }

    $('#resp-time-series').html('');

     var chart = d3.select("#resp-time-series").append("svg")
      .attr("class", "chart")
      .attr("width", self.chartW)
      .attr("height", self.h);

    chart.append("line")
      .attr("x1", 0)
      .attr("x2", self.chartW)
      .attr("y1", self.h - .5)
      .attr("y2", self.h - .5)
      .style("stroke", "#000");

      this.chart = chart;

    this.setYMax(valMax);
    this.setXMax(data.length);

    if (!data) {
      return
    }

    var w = this.w;
    var h = this.h;

    var x = this.x;
    var y = this.y;

    var rect = chart.selectAll("rect")
      .data(data, function(d) { return d.time; });

    rect.enter().append("rect", "line")
      .attr("x", function(d, i) { return x(i + 1) - .5; })
      .attr("y", function(d) { return h - y(d.value) - .5; })
      .attr("width", 2)
      .attr("height", function(d) { return y(d.value); })
    .transition()
      .duration(1)
      .attr("x", function(d, i) { return x(i) - .5; });

    /*
    rect.transition()
        .duration(1)
        .attr("x", function(d, i) { return x(i) - .5; });

    rect.exit().transition()
        .duration(1)
        .attr("x", function(d, i) { return x(i - 1) - .5; })
        .emove();
        */

  }
});

$(function () {
  var benchmarkStream = window.benchmarkStream = new BenchmarkStream(
      {addr: 'ws://' + location.host + '/benchmarker/stream'}
  );

  var consoleView  = window.consoleView = new ConsoleView(
    {
      el: $('#console')
    }
  );
  consoleView.logEvents(benchmarkStream, 'data');

  var benchmarker  = window.benchmarker = new Benchmarker();
  benchmarker.bindToStream(benchmarkStream);

  var controlsView = window.controlsView = new ControlsView(
    {
      el: $('#controls')[0],
      model: benchmarker,
    }
  );

  var statsView = window.statsView = new AggregateStatsView(
    {
      el: $('#stats')[0],
      model: benchmarker,
    }
  );

  var chartsView = window.chartsView = new ChartsView(
    {
      el: $('#resp-time-percentiles')[0],
      model: benchmarker,
    }
  );

  var responseTimeSeriesView = window.responseTimeSeriesView = new ResponseTimeSeriesView(
    {
      el: $('#resp-time-series')[0],
      model: benchmarker,
    }
  );
});
<|endoftext|>


<commit_before>import TestWrapper from './TestWrapper'

export default class CheerioTestWrapper extends TestWrapper {
  constructor (wrapper) {
    super()
    this.wrapper = wrapper
  }

  get el () {
    if (!this.__el) {
      if (this.wrapper.first()['0'].type === 'root') {
        this.__el = this.wrapper.children().first()
      } else {
        this.__el = this.wrapper.first()
      }
    }

    return this.__el
  }

  inspect () {
    return 'the node in <??? />'
  }

  isEmpty () {
    if (this.tagName() === 'noscript') {
      return true
    }

    return super.isEmpty()
  }

  attr (name) {
    return this.el.attr(name)
  }

  html () {
    return this.el.toString()
  }

  style (name) {
    return this.el.css(name)
  }

  tagName () {
    return this.el[0].name
  }

  data (name) {
    return this.el.data(name)
  }

  hasClass (name) {
    return this.el.hasClass(name)
  }

  classNames () {
    return this.el.attr('class')
  }

  value () {
    return this.el.attr('value')
  }

  id () {
    return this.el.attr('id')
  }

  isChecked () {
    return this.is(':checked')
  }

  isDisabled () {
    return this.is(':disabled')
  }

  isSelected () {
    return this.is(':selected')
  }

  is (selector) {
    return this.el.is(selector)
  }

  hasNode () {
    throw new Error('static rendering does not support `contain` yet')
  }

  state () {
    throw new Error('static rendering does not support `state` yet')
  }

  prop () {
    throw new Error('static rendering does not support `prop` yet')
  }

  hasRef () {
    throw new Error('static rendering does not support refs')
  }
}
<commit_msg>React 15.x returns no noscript element for null components anymore<commit_after>import TestWrapper from './TestWrapper'

export default class CheerioTestWrapper extends TestWrapper {
  constructor (wrapper) {
    super()
    this.wrapper = wrapper
  }

  get el () {
    if (!this.__el) {
      if (this.wrapper.first()['0'].type === 'root') {
        this.__el = this.wrapper.children().first()
      } else {
        this.__el = this.wrapper.first()
      }
    }

    return this.__el
  }

  inspect () {
    return 'the node in <??? />'
  }

  isEmpty () {
    // React 0.14.x returns an element with tag name <noscript />, while
    // 15.x returns no element at all.

    if (this.tagName() === null || this.tagName() === 'noscript') {
      return true
    }

    return super.isEmpty()
  }

  attr (name) {
    return this.el.attr(name)
  }

  html () {
    return this.el.toString()
  }

  style (name) {
    return this.el.css(name)
  }

  tagName () {
    const el = this.el[0]

    if (el) {
      return el.name
    }

    return null
  }

  data (name) {
    return this.el.data(name)
  }

  hasClass (name) {
    return this.el.hasClass(name)
  }

  classNames () {
    return this.el.attr('class')
  }

  value () {
    return this.el.attr('value')
  }

  id () {
    return this.el.attr('id')
  }

  isChecked () {
    return this.is(':checked')
  }

  isDisabled () {
    return this.is(':disabled')
  }

  isSelected () {
    return this.is(':selected')
  }

  is (selector) {
    return this.el.is(selector)
  }

  hasNode () {
    throw new Error('static rendering does not support `contain` yet')
  }

  state () {
    throw new Error('static rendering does not support `state` yet')
  }

  prop () {
    throw new Error('static rendering does not support `prop` yet')
  }

  hasRef () {
    throw new Error('static rendering does not support refs')
  }
}
<|endoftext|>


<commit_before>import {
  Control,
  DomEvent,
  DomUtil,
  Evented,
  Util,
  latLngBounds
} from 'leaflet';
import { geosearchCore } from '../Classes/GeosearchCore';
import { arcgisOnlineProvider } from '../Providers/ArcgisOnlineGeocoder';
import { Util as EsriUtil } from 'esri-leaflet';

export var Geosearch = Control.extend({
  includes: Evented.prototype,

  options: {
    position: 'topleft',
    collapseAfterResult: true,
    expanded: false,
    allowMultipleResults: true,
    placeholder: 'Search for places or addresses',
    title: 'Location Search'
  },

  initialize: function (options) {
    Util.setOptions(this, options);

    if (!options || !options.providers || !options.providers.length) {
      if (!options) {
        options = {};
      }
      options.providers = [ arcgisOnlineProvider() ];
    }

    // instantiate the underlying class and pass along options
    this._geosearchCore = geosearchCore(this, options);
    this._geosearchCore._providers = options.providers;

    // bubble each providers events to the control
    this._geosearchCore.addEventParent(this);
    for (var i = 0; i < this._geosearchCore._providers.length; i++) {
      this._geosearchCore._providers[i].addEventParent(this);
    }

    this._geosearchCore._pendingSuggestions = [];

    Control.prototype.initialize.call(this, options);
  },

  _renderSuggestions: function (suggestions) {
    var currentGroup;

    if (suggestions.length > 0) {
      this._suggestions.style.display = 'block';
    }

    var list;
    var header;
    var suggestionTextArray = [];

    for (var i = 0; i < suggestions.length; i++) {
      var suggestion = suggestions[i];
      if (!header && this._geosearchCore._providers.length > 1 && currentGroup !== suggestion.provider.options.label) {
        header = DomUtil.create('div', 'geocoder-control-header', suggestion.provider._contentsElement);
        header.textContent = suggestion.provider.options.label;
        header.innerText = suggestion.provider.options.label;
        currentGroup = suggestion.provider.options.label;
      }

      if (!list) {
        list = DomUtil.create('ul', 'geocoder-control-list', suggestion.provider._contentsElement);
      }

      if (suggestionTextArray.indexOf(suggestion.text) === -1) {
        var suggestionItem = DomUtil.create('li', 'geocoder-control-suggestion', list);

        suggestionItem.innerHTML = suggestion.text;
        suggestionItem.provider = suggestion.provider;
        suggestionItem['data-magic-key'] = suggestion.magicKey;
        suggestionItem.unformattedText = suggestion.unformattedText;
      } else {
        for (var j = 0; j < list.childNodes.length; j++) {
          // if the same text already appears in the list of suggestions, append an additional ObjectID to its magicKey instead
          if (list.childNodes[j].innerHTML === suggestion.text) {
            list.childNodes[j]['data-magic-key'] += ',' + suggestion.magicKey;
          }
        }
      }
      suggestionTextArray.push(suggestion.text);
    }

    // when the geocoder position is either "topleft" or "topright":
    // set the maxHeight of the suggestions box to:
    //  map height
    //  - suggestions offset (distance from top of suggestions to top of control)
    //  - control offset (distance from top of control to top of map)
    //  - 10 (extra padding)
    if (this.getPosition().indexOf('top') > -1) {
      this._suggestions.style.maxHeight = (this._map.getSize().y - this._suggestions.offsetTop - this._wrapper.offsetTop - 10) + 'px';
    }

    // when the geocoder position is either "bottomleft" or "bottomright":
    // 1. set the maxHeight of the suggestions box to:
    //  map height
    //  - corner control container offsetHeight (height of container of bottom corner)
    //  - control offsetHeight (height of geocoder control wrapper, the main expandable button)
    // 2. to move it up, set the top of the suggestions box to:
    //  negative offsetHeight of suggestions box (its own negative height now that it has children elements
    //  - control offsetHeight (height of geocoder control wrapper, the main expandable button)
    //  + 20 (extra spacing)
    if (this.getPosition().indexOf('bottom') > -1) {
      this._setSuggestionsBottomPosition();
    }
  },

  _setSuggestionsBottomPosition: function () {
    this._suggestions.style.maxHeight = (this._map.getSize().y - this._map._controlCorners[this.getPosition()].offsetHeight - this._wrapper.offsetHeight) + 'px';
    this._suggestions.style.top = (-this._suggestions.offsetHeight - this._wrapper.offsetHeight + 20) + 'px';
  },

  _boundsFromResults: function (results) {
    if (!results.length) {
      return;
    }

    var nullIsland = latLngBounds([0, 0], [0, 0]);
    var resultBounds = [];
    var resultLatlngs = [];

    // collect the bounds and center of each result
    for (var i = results.length - 1; i >= 0; i--) {
      var result = results[i];

      resultLatlngs.push(result.latlng);

      // make sure bounds are valid and not 0,0. sometimes bounds are incorrect or not present
      if (result.bounds && result.bounds.isValid() && !result.bounds.equals(nullIsland)) {
        resultBounds.push(result.bounds);
      }
    }

    // form a bounds object containing all center points
    var bounds = latLngBounds(resultLatlngs);

    // and extend it to contain all bounds objects
    for (var j = 0; j < resultBounds.length; j++) {
      bounds.extend(resultBounds[j]);
    }

    return bounds;
  },

  clear: function () {
    this._clearAllSuggestions();

    if (this.options.collapseAfterResult) {
      this._input.value = '';
      this._lastValue = '';
      this._input.placeholder = '';
      DomUtil.removeClass(this._wrapper, 'geocoder-control-expanded');
    }

    if (!this._map.scrollWheelZoom.enabled() && this._map.options.scrollWheelZoom) {
      this._map.scrollWheelZoom.enable();
    }
  },

  _clearAllSuggestions: function () {
    this._suggestions.style.display = 'none';

    for (var i = 0; i < this.options.providers.length; i++) {
      this._clearProviderSuggestions(this.options.providers[i]);
    }
  },

  _clearProviderSuggestions: function (provider) {
    provider._contentsElement.innerHTML = '';
  },

  _finalizeSuggestions: function (activeRequests, suggestionsLength) {
    // check if all requests are finished to remove the loading indicator
    if (!activeRequests) {
      DomUtil.removeClass(this._input, 'geocoder-control-loading');

      // when the geocoder position is either "bottomleft" or "bottomright",
      // it is necessary in some cases to recalculate the maxHeight and top values of the this._suggestions element,
      // even though this is also being done after each provider returns their own suggestions
      if (this.getPosition().indexOf('bottom') > -1) {
        this._setSuggestionsBottomPosition();
      }

      // also check if there were 0 total suggest results to clear the parent suggestions element
      // otherwise its display value may be "block" instead of "none"
      if (!suggestionsLength) {
        this._clearAllSuggestions();
      }
    }
  },

  _setupClick: function () {
    DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    this._input.focus();
  },

  disable: function () {
    this._input.disabled = true;
    DomUtil.addClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.removeListener(this._wrapper, 'click', this._setupClick, this);
  },

  enable: function () {
    this._input.disabled = false;
    DomUtil.removeClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);
  },

  getAttribution: function () {
    var attribs = [];

    for (var i = 0; i < this._providers.length; i++) {
      if (this._providers[i].options.attribution) {
        attribs.push(this._providers[i].options.attribution);
      }
    }

    return attribs.join(', ');
  },

  geocodeSuggestion: function (e) {
    var suggestionItem = e.target || e.srcElement;

    if (
      suggestionItem.classList.contains('geocoder-control-suggestions') ||
      suggestionItem.classList.contains('geocoder-control-header')
    ) {
      return;
    }

    // make sure and point at the actual 'geocoder-control-suggestion'
    if (suggestionItem.classList.length < 1) {
      suggestionItem = suggestionItem.parentNode;
    }

    this._geosearchCore._geocode(suggestionItem.unformattedText, suggestionItem['data-magic-key'], suggestionItem.provider);
    this.clear();
  },

  onAdd: function (map) {
    // include 'Powered by Esri' in map attribution
    EsriUtil.setEsriAttribution(map);

    this._map = map;
    this._wrapper = DomUtil.create('div', 'geocoder-control');
    this._input = DomUtil.create('input', 'geocoder-control-input leaflet-bar', this._wrapper);
    this._input.title = this.options.title;

    if (this.options.expanded) {
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
      this._input.placeholder = this.options.placeholder;
    }

    // create the main suggested results container element
    this._suggestions = DomUtil.create('div', 'geocoder-control-suggestions leaflet-bar', this._wrapper);

    // create a child contents container element for each provider inside of this._suggestions
    // to maintain the configured order of providers for suggested results
    for (var i = 0; i < this.options.providers.length; i++) {
      this.options.providers[i]._contentsElement = DomUtil.create('div', null, this._suggestions);
    }

    var credits = this._geosearchCore._getAttribution();

    if (map.attributionControl) {
      map.attributionControl.addAttribution(credits);
    }

    DomEvent.addListener(this._input, 'focus', function (e) {
      this._input.placeholder = this.options.placeholder;
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    }, this);

    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);

    // make sure both click and touch spawn an address/poi search
    DomEvent.addListener(this._suggestions, 'mousedown', this.geocodeSuggestion, this);

    DomEvent.addListener(this._input, 'blur', function (e) {
      // TODO: this is too greedy and should not "clear"
      // when trying to use the scrollbar or clicking on a non-suggestion item (such as a provider header)
      this.clear();
    }, this);

    DomEvent.addListener(this._input, 'keydown', function (e) {
      var text = (e.target || e.srcElement).value;

      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');

      var list = this._suggestions.querySelectorAll('.' + 'geocoder-control-suggestion');
      var selected = this._suggestions.querySelectorAll('.' + 'geocoder-control-selected')[0];
      var selectedPosition;

      for (var i = 0; i < list.length; i++) {
        if (list[i] === selected) {
          selectedPosition = i;
          break;
        }
      }

      switch (e.keyCode) {
        case 13:
          /*
            if an item has been selected, geocode it
            if focus is on the input textbox, geocode only if multiple results are allowed and more than two characters are present, or if a single suggestion is displayed.
            if less than two characters have been typed, abort the geocode
          */
          if (selected) {
            this._input.value = selected.innerText;
            this._geosearchCore._geocode(selected.unformattedText, selected['data-magic-key'], selected.provider);
            this.clear();
          } else if (this.options.allowMultipleResults && text.length >= 2) {
            this._geosearchCore._geocode(this._input.value, undefined);
            this.clear();
          } else {
            if (list.length === 1) {
              DomUtil.addClass(list[0], 'geocoder-control-selected');
              this._geosearchCore._geocode(list[0].innerHTML, list[0]['data-magic-key'], list[0].provider);
            } else {
              this.clear();
              this._input.blur();
            }
          }
          DomEvent.preventDefault(e);
          break;
        case 38:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var previousItem = list[selectedPosition - 1];

          if (selected && previousItem) {
            DomUtil.addClass(previousItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[list.length - 1], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        case 40:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var nextItem = list[selectedPosition + 1];

          if (selected && nextItem) {
            DomUtil.addClass(nextItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[0], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        default:
          // when the input changes we should cancel all pending suggestion requests if possible to avoid result collisions
          for (var x = 0; x < this._geosearchCore._pendingSuggestions.length; x++) {
            var request = this._geosearchCore._pendingSuggestions[x];
            if (request && request.abort && !request.id) {
              request.abort();
            }
          }
          break;
      }
    }, this);

    DomEvent.addListener(this._input, 'keyup', Util.throttle(function (e) {
      var key = e.which || e.keyCode;
      var text = (e.target || e.srcElement).value;

      // require at least 2 characters for suggestions
      if (text.length < 2) {
        this._lastValue = this._input.value;
        this._clearAllSuggestions();
        DomUtil.removeClass(this._input, 'geocoder-control-loading');
        return;
      }

      // if this is the escape key it will clear the input so clear suggestions
      if (key === 27) {
        this._clearAllSuggestions();
        return;
      }

      // if this is NOT the up/down arrows or enter make a suggestion
      if (key !== 13 && key !== 38 && key !== 40) {
        if (this._input.value !== this._lastValue) {
          this._lastValue = this._input.value;
          DomUtil.addClass(this._input, 'geocoder-control-loading');
          this._geosearchCore._suggest(text);
        }
      }
    }, 50, this), this);

    DomEvent.disableClickPropagation(this._wrapper);

    // when mouse moves over suggestions disable scroll wheel zoom if its enabled
    DomEvent.addListener(this._suggestions, 'mouseover', function (e) {
      if (map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.disable();
      }
    });

    // when mouse moves leaves suggestions enable scroll wheel zoom if its disabled
    DomEvent.addListener(this._suggestions, 'mouseout', function (e) {
      if (!map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.enable();
      }
    });

    this._geosearchCore.on('load', function (e) {
      DomUtil.removeClass(this._input, 'geocoder-control-loading');
      this.clear();
      this._input.blur();
    }, this);

    return this._wrapper;
  }
});

export function geosearch (options) {
  return new Geosearch(options);
}

export default geosearch;
<commit_msg>linting<commit_after>import {
  Control,
  DomEvent,
  DomUtil,
  Evented,
  Util,
  latLngBounds
} from 'leaflet';
import { geosearchCore } from '../Classes/GeosearchCore';
import { arcgisOnlineProvider } from '../Providers/ArcgisOnlineGeocoder';
import { Util as EsriUtil } from 'esri-leaflet';

export var Geosearch = Control.extend({
  includes: Evented.prototype,

  options: {
    position: 'topleft',
    collapseAfterResult: true,
    expanded: false,
    allowMultipleResults: true,
    placeholder: 'Search for places or addresses',
    title: 'Location Search'
  },

  initialize: function (options) {
    Util.setOptions(this, options);

    if (!options || !options.providers || !options.providers.length) {
      if (!options) {
        options = {};
      }
      options.providers = [ arcgisOnlineProvider() ];
    }

    // instantiate the underlying class and pass along options
    this._geosearchCore = geosearchCore(this, options);
    this._geosearchCore._providers = options.providers;

    // bubble each providers events to the control
    this._geosearchCore.addEventParent(this);
    for (var i = 0; i < this._geosearchCore._providers.length; i++) {
      this._geosearchCore._providers[i].addEventParent(this);
    }

    this._geosearchCore._pendingSuggestions = [];

    Control.prototype.initialize.call(this, options);
  },

  _renderSuggestions: function (suggestions) {
    var currentGroup;

    if (suggestions.length > 0) {
      this._suggestions.style.display = 'block';
    }

    var list;
    var header;
    var suggestionTextArray = [];

    for (var i = 0; i < suggestions.length; i++) {
      var suggestion = suggestions[i];
      if (!header && this._geosearchCore._providers.length > 1 && currentGroup !== suggestion.provider.options.label) {
        header = DomUtil.create('div', 'geocoder-control-header', suggestion.provider._contentsElement);
        header.textContent = suggestion.provider.options.label;
        header.innerText = suggestion.provider.options.label;
        currentGroup = suggestion.provider.options.label;
      }

      if (!list) {
        list = DomUtil.create('ul', 'geocoder-control-list', suggestion.provider._contentsElement);
      }

      if (suggestionTextArray.indexOf(suggestion.text) === -1) {
        var suggestionItem = DomUtil.create('li', 'geocoder-control-suggestion', list);

        suggestionItem.innerHTML = suggestion.text;
        suggestionItem.provider = suggestion.provider;
        suggestionItem['data-magic-key'] = suggestion.magicKey;
        suggestionItem.unformattedText = suggestion.unformattedText;
      } else {
        for (var j = 0; j < list.childNodes.length; j++) {
          // if the same text already appears in the list of suggestions, append an additional ObjectID to its magicKey instead
          if (list.childNodes[j].innerHTML === suggestion.text) {
            list.childNodes[j]['data-magic-key'] += ',' + suggestion.magicKey;
          }
        }
      }
      suggestionTextArray.push(suggestion.text);
    }

    // when the geocoder position is either "topleft" or "topright":
    // set the maxHeight of the suggestions box to:
    //  map height
    //  - suggestions offset (distance from top of suggestions to top of control)
    //  - control offset (distance from top of control to top of map)
    //  - 10 (extra padding)
    if (this.getPosition().indexOf('top') > -1) {
      this._suggestions.style.maxHeight = (this._map.getSize().y - this._suggestions.offsetTop - this._wrapper.offsetTop - 10) + 'px';
    }

    // when the geocoder position is either "bottomleft" or "bottomright":
    // 1. set the maxHeight of the suggestions box to:
    //  map height
    //  - corner control container offsetHeight (height of container of bottom corner)
    //  - control offsetHeight (height of geocoder control wrapper, the main expandable button)
    // 2. to move it up, set the top of the suggestions box to:
    //  negative offsetHeight of suggestions box (its own negative height now that it has children elements
    //  - control offsetHeight (height of geocoder control wrapper, the main expandable button)
    //  + 20 (extra spacing)
    if (this.getPosition().indexOf('bottom') > -1) {
      this._setSuggestionsBottomPosition();
    }
  },

  _setSuggestionsBottomPosition: function () {
    this._suggestions.style.maxHeight = (this._map.getSize().y - this._map._controlCorners[this.getPosition()].offsetHeight - this._wrapper.offsetHeight) + 'px';
    this._suggestions.style.top = (-this._suggestions.offsetHeight - this._wrapper.offsetHeight + 20) + 'px';
  },

  _boundsFromResults: function (results) {
    if (!results.length) {
      return;
    }

    var nullIsland = latLngBounds([0, 0], [0, 0]);
    var resultBounds = [];
    var resultLatlngs = [];

    // collect the bounds and center of each result
    for (var i = results.length - 1; i >= 0; i--) {
      var result = results[i];

      resultLatlngs.push(result.latlng);

      // make sure bounds are valid and not 0,0. sometimes bounds are incorrect or not present
      if (result.bounds && result.bounds.isValid() && !result.bounds.equals(nullIsland)) {
        resultBounds.push(result.bounds);
      }
    }

    // form a bounds object containing all center points
    var bounds = latLngBounds(resultLatlngs);

    // and extend it to contain all bounds objects
    for (var j = 0; j < resultBounds.length; j++) {
      bounds.extend(resultBounds[j]);
    }

    return bounds;
  },

  clear: function () {
    this._clearAllSuggestions();

    if (this.options.collapseAfterResult) {
      this._input.value = '';
      this._lastValue = '';
      this._input.placeholder = '';
      DomUtil.removeClass(this._wrapper, 'geocoder-control-expanded');
    }

    if (!this._map.scrollWheelZoom.enabled() && this._map.options.scrollWheelZoom) {
      this._map.scrollWheelZoom.enable();
    }
  },

  _clearAllSuggestions: function () {
    this._suggestions.style.display = 'none';

    for (var i = 0; i < this.options.providers.length; i++) {
      this._clearProviderSuggestions(this.options.providers[i]);
    }
  },

  _clearProviderSuggestions: function (provider) {
    provider._contentsElement.innerHTML = '';
  },

  _finalizeSuggestions: function (activeRequests, suggestionsLength) {
    // check if all requests are finished to remove the loading indicator
    if (!activeRequests) {
      DomUtil.removeClass(this._input, 'geocoder-control-loading');

      // when the geocoder position is either "bottomleft" or "bottomright",
      // it is necessary in some cases to recalculate the maxHeight and top values of the this._suggestions element,
      // even though this is also being done after each provider returns their own suggestions
      if (this.getPosition().indexOf('bottom') > -1) {
        this._setSuggestionsBottomPosition();
      }

      // also check if there were 0 total suggest results to clear the parent suggestions element
      // otherwise its display value may be "block" instead of "none"
      if (!suggestionsLength) {
        this._clearAllSuggestions();
      }
    }
  },

  _setupClick: function () {
    DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    this._input.focus();
  },

  disable: function () {
    this._input.disabled = true;
    DomUtil.addClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.removeListener(this._wrapper, 'click', this._setupClick, this);
  },

  enable: function () {
    this._input.disabled = false;
    DomUtil.removeClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);
  },

  getAttribution: function () {
    var attribs = [];

    for (var i = 0; i < this._providers.length; i++) {
      if (this._providers[i].options.attribution) {
        attribs.push(this._providers[i].options.attribution);
      }
    }

    return attribs.join(', ');
  },

  geocodeSuggestion: function (e) {
    var suggestionItem = e.target || e.srcElement;

    if (
      suggestionItem.classList.contains('geocoder-control-suggestions') ||
      suggestionItem.classList.contains('geocoder-control-header')
    ) {
      return;
    }

    // make sure and point at the actual 'geocoder-control-suggestion'
    if (suggestionItem.classList.length < 1) {
      suggestionItem = suggestionItem.parentNode;
    }

    this._geosearchCore._geocode(suggestionItem.unformattedText, suggestionItem['data-magic-key'], suggestionItem.provider);
    this.clear();
  },

  onAdd: function (map) {
    // include 'Powered by Esri' in map attribution
    EsriUtil.setEsriAttribution(map);

    this._map = map;
    this._wrapper = DomUtil.create('div', 'geocoder-control');
    this._input = DomUtil.create('input', 'geocoder-control-input leaflet-bar', this._wrapper);
    this._input.title = this.options.title;

    if (this.options.expanded) {
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
      this._input.placeholder = this.options.placeholder;
    }

    // create the main suggested results container element
    this._suggestions = DomUtil.create('div', 'geocoder-control-suggestions leaflet-bar', this._wrapper);

    // create a child contents container element for each provider inside of this._suggestions
    // to maintain the configured order of providers for suggested results
    for (var i = 0; i < this.options.providers.length; i++) {
      this.options.providers[i]._contentsElement = DomUtil.create('div', null, this._suggestions);
    }

    var credits = this._geosearchCore._getAttribution();

    if (map.attributionControl) {
      map.attributionControl.addAttribution(credits);
    }

    DomEvent.addListener(this._input, 'focus', function (e) {
      this._input.placeholder = this.options.placeholder;
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    }, this);

    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);

    // make sure both click and touch spawn an address/poi search
    DomEvent.addListener(this._suggestions, 'mousedown', this.geocodeSuggestion, this);

    DomEvent.addListener(this._input, 'blur', function (e) {
      // TODO: this is too greedy and should not "clear"
      // when trying to use the scrollbar or clicking on a non-suggestion item (such as a provider header)
      this.clear();
    }, this);

    DomEvent.addListener(this._input, 'keydown', function (e) {
      var text = (e.target || e.srcElement).value;

      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');

      var list = this._suggestions.querySelectorAll('.' + 'geocoder-control-suggestion');
      var selected = this._suggestions.querySelectorAll('.' + 'geocoder-control-selected')[0];
      var selectedPosition;

      for (var i = 0; i < list.length; i++) {
        if (list[i] === selected) {
          selectedPosition = i;
          break;
        }
      }

      switch (e.keyCode) {
        case 13:
          /*
            if an item has been selected, geocode it
            if focus is on the input textbox, geocode only if multiple results are allowed and more than two characters are present, or if a single suggestion is displayed.
            if less than two characters have been typed, abort the geocode
          */
          if (selected) {
            this._input.value = selected.innerText;
            this._geosearchCore._geocode(selected.unformattedText, selected['data-magic-key'], selected.provider);
            this.clear();
          } else if (this.options.allowMultipleResults && text.length >= 2) {
            this._geosearchCore._geocode(this._input.value, undefined);
            this.clear();
          } else {
            if (list.length === 1) {
              DomUtil.addClass(list[0], 'geocoder-control-selected');
              this._geosearchCore._geocode(list[0].innerHTML, list[0]['data-magic-key'], list[0].provider);
            } else {
              this.clear();
              this._input.blur();
            }
          }
          DomEvent.preventDefault(e);
          break;
        case 38:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var previousItem = list[selectedPosition - 1];

          if (selected && previousItem) {
            DomUtil.addClass(previousItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[list.length - 1], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        case 40:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var nextItem = list[selectedPosition + 1];

          if (selected && nextItem) {
            DomUtil.addClass(nextItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[0], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        default:
          // when the input changes we should cancel all pending suggestion requests if possible to avoid result collisions
          for (var x = 0; x < this._geosearchCore._pendingSuggestions.length; x++) {
            var request = this._geosearchCore._pendingSuggestions[x];
            if (request && request.abort && !request.id) {
              request.abort();
            }
          }
          break;
      }
    }, this);

    DomEvent.addListener(this._input, 'keyup', Util.throttle(function (e) {
      var key = e.which || e.keyCode;
      var text = (e.target || e.srcElement).value;

      // require at least 2 characters for suggestions
      if (text.length < 2) {
        this._lastValue = this._input.value;
        this._clearAllSuggestions();
        DomUtil.removeClass(this._input, 'geocoder-control-loading');
        return;
      }

      // if this is the escape key it will clear the input so clear suggestions
      if (key === 27) {
        this._clearAllSuggestions();
        return;
      }

      // if this is NOT the up/down arrows or enter make a suggestion
      if (key !== 13 && key !== 38 && key !== 40) {
        if (this._input.value !== this._lastValue) {
          this._lastValue = this._input.value;
          DomUtil.addClass(this._input, 'geocoder-control-loading');
          this._geosearchCore._suggest(text);
        }
      }
    }, 50, this), this);

    DomEvent.disableClickPropagation(this._wrapper);

    // when mouse moves over suggestions disable scroll wheel zoom if its enabled
    DomEvent.addListener(this._suggestions, 'mouseover', function (e) {
      if (map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.disable();
      }
    });

    // when mouse moves leaves suggestions enable scroll wheel zoom if its disabled
    DomEvent.addListener(this._suggestions, 'mouseout', function (e) {
      if (!map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.enable();
      }
    });

    this._geosearchCore.on('load', function (e) {
      DomUtil.removeClass(this._input, 'geocoder-control-loading');
      this.clear();
      this._input.blur();
    }, this);

    return this._wrapper;
  }
});

export function geosearch (options) {
  return new Geosearch(options);
}

export default geosearch;
<|endoftext|>


<commit_before>/**
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2015 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

describe('Bakery', function() {

  var yui;

  beforeAll(function (done) {
    var modules = ['juju-env-bakery', 'macaroon' ];
    YUI().use(modules, function (Y) {
      yui = Y;
      done();
    });
  });

  //it('can be instantiated with the proper config values', function() {
  //  var bakery = new yui.juju.environments.web.Bakery();
  //  assert.equal(
  //      bakery.webhandler instanceof yui.juju.environments.web.WebHandler,
  //      true);
  //});
  //
  //describe('_requestHandler', function() {
  //  var success, failure;
  //
  //  beforeEach(function() {
  //    success = sinon.spy();
  //    failure = sinon.spy();
  //  });
  //
  //  it('calls the failure callback if status > 400', function() {
  //    var bakery = new yui.juju.environments.web.Bakery();
  //    bakery._requestHandler(success, failure, {
  //      target: { status: 404 }
  //    });
  //    assert.equal(success.callCount, 0);
  //    assert.equal(failure.callCount, 1);
  //  });
  //
  //  it('calls the success callback if status < 400', function() {
  //    var bakery = new yui.juju.environments.web.Bakery();
  //    bakery._requestHandler(success, failure, {
  //      target: { status: 200 }
  //    });
  //    assert.equal(success.callCount, 1);
  //    assert.equal(failure.callCount, 0);
  //  });
  //});
  //
  //describe('_requestHandlerWithInteraction', function() {
  //  var success, failure;
  //
  //  beforeEach(function() {
  //    success = sinon.spy();
  //    failure = sinon.spy();
  //    xhr = sinon.useFakeXMLHttpRequest();
  //    requests = [];
  //    xhr.onCreate = function (req) { requests.push(req); };
  //  });
  //
  //  afterEach(function () {
  //      xhr.restore();
  //  });
  //
  //  it('calls getRequest after authentication', function() {
  //    var bakery = new yui.juju.environments.web.Bakery();
  //    bakery.webhandler.sendGetRequest = sinon.spy()
  //    var called = 0;
  //    //bakery.webhandler.sendPutRequest = function(a,b,c,d,e,f,g,h) {
  //    //  called++;
  //    //  h({'target' : {
  //    //    status: 200
  //    //  }});
  //    //};
  //    this._cleanups.push(getRequest.reset);
  //    var m = macaroon.export(
  //      macaroon.newMacaroon(['secret'], 'some id', 'a location')
  //    );
  //    bakery._requestHandlerWithInteraction(
  //      'path', 'set-cookie-auth', success, failure, {
  //        target: {
  //          status: 401,
  //          responseText: '{"Info": {"Macaroon": ' + JSON.stringify(m) + '}}',
  //          getResponseHeader: function(k) {return 'Macaroon';}
  //        }
  //      }
  //    );
  //    assert.equal(called, 1);
  //    assert.equal(bakery.webhandler.sendGetRequest.callCount, 1);
  //    var getArgs = getRequest.lastArguments();
  //    assert.equal(getArgs[0], 'path');
  //    assert.equal(failure.callCount(), 0);
  //  });
  //});
});
<commit_msg>Remove karma tests.<commit_after><|endoftext|>


<commit_before>;
(function(ns) {
    ns.views.WorkspaceSummarySidebar = ns.views.Base.extend({
        className: "workspace_summary_sidebar",

        setup : function() {
            this.model.bind("image:change", this.render);
            this.model.members().fetch();
            this.model.members().bind("reset", this.render);
        },

        additionalContext : function() {
            return {
                workspaceAdmin : this.model.workspaceAdmin(),
                imageUrl : this.model.imageUrl()+"&buster="+(new Date().getTime()),
                hasImage : this.model.hasImage(),
                members : this.model.members().chain().first(24).map(function(member){
                     return {
                         imageUrl : member.imageUrl({size : 'icon'}),
                         showUrl : member.showUrl()
                     };
                }).value(),
                extra_members : Math.max(this.model.members().length - 24, 0)
            };
        },

        postRender : function() {
            var self = this;
            this.$(".workspace_image").load(function() {
                self.$(".after_image").removeClass("hidden");
            });
        }
    });
})(chorus);
<commit_msg>extract variable for number of members to show in sidebar<commit_after>;
(function(ns) {
    ns.views.WorkspaceSummarySidebar = ns.views.Base.extend({
        className: "workspace_summary_sidebar",

        setup : function() {
            this.model.bind("image:change", this.render);
            this.model.members().fetch();
            this.model.members().bind("reset", this.render);
        },

        numMembers: 24,

        additionalContext : function() {
            return {
                workspaceAdmin : this.model.workspaceAdmin(),
                imageUrl : this.model.imageUrl()+"&buster="+(new Date().getTime()),
                hasImage : this.model.hasImage(),
                members : this.model.members().chain().first(this.numMembers).map(function(member){
                     return {
                         imageUrl : member.imageUrl({size : 'icon'}),
                         showUrl : member.showUrl()
                     };
                }).value(),
                extra_members : Math.max(this.model.members().length - this.numMembers, 0)
            };
        },

        postRender : function() {
            var self = this;
            this.$(".workspace_image").load(function() {
                self.$(".after_image").removeClass("hidden");
            });
        }
    });
})(chorus);

<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash'),
    fs = require('fs-extra'),
    path = require('path');

var file = require('../common/file'),
    mapping = require('../common/mapping');

var templatePath = path.join(__dirname, 'template/doc'),
    template = file.globTemplate(path.join(templatePath, '*.jst'));

var argNames = ['a', 'b', 'c', 'd'];

var templateData = {
  'mapping': mapping,
  'toArgOrder': toArgOrder,
  'toFuncList': toFuncList
};

function toArgOrder(array) {
  var reordered = [];
  _.each(array, function(newIndex, index) {
    reordered[newIndex] = argNames[index];
  });
  return '`(' + reordered.join(', ') + ')`';
}

function toFuncList(array) {
  var chunks = _.chunk(array.slice().sort(), 5),
      lastChunk = _.last(chunks),
      last = lastChunk ? lastChunk.pop() : undefined;

  chunks = _.reject(chunks, _.isEmpty);
  lastChunk = _.last(chunks);

  var result = '`' + _.map(chunks, function(chunk) {
    return chunk.join('`, `') + '`';
  }).join(',\n`');

  if (last == null) {
    return result;
  }
  if (_.size(chunks) > 1 || _.size(lastChunk) > 1) {
    result += ',';
  }
  result += ' &';
  result += _.size(lastChunk) < 5 ? ' ' : '\n';
  return result + '`' + last + '`';
}

/*----------------------------------------------------------------------------*/

function onComplete(error) {
  if (error) {
    throw error;
  }
}

function build(target) {
  target = path.resolve(target);
  fs.writeFile(target, template.wiki(templateData), onComplete);
}

build(_.last(process.argv));
<commit_msg>Update public/libs/bower_components/lodash/lib/fp/build-doc.js<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 1997-2013, SalesLogix, NA., LLC. All rights reserved.
 */
define('Mobile/SalesLogix/Views/_RightDrawerBaseMixin', [
    'dojo/_base/declare',
    'dojo/_base/array',
    'dojo/_base/lang'
], function(
    declare,
    array,
    lang
) {

    // Base Mixin for the right drawer/menu. This is responsible for creating the toggle button on the toolbar and managing the state of the right menu (loaded/unloaded).
    //
    // Lifecycles:
    // -- Loading of the right menu --
    // 1. Toggle button clicked
    // 2. setupRightDrawer
    // 3. loadRightDrawer
    //
    // -- Unloading of the right menu --
    // 1. onBeforeTransitionAway
    // 2. unloadRightDrawer
    return declare('Mobile.SalesLogix.Views._RightDrawerBaseMixin', null, {
        drawerLoaded: false,
        toolsAdded: false,

        setupRightDrawer: function() {
        },
        loadRightDrawer: function() {
            if (this.drawerLoaded) {
                return;
            }

            this.setupRightDrawer();
            var drawer = App.getView('right_drawer');
            if (drawer) {
                drawer.refresh();
                this.drawerLoaded = true;
            }
        },
        createToolLayout: function() {
            var tools = this.inherited(arguments) || {
                tbar: []
            };

            if (!this.toolsAdded) {
                tools.tbar.unshift({
                    id: 'toggleRightDrawer',
                    side: 'right',
                    fn: this.toggleRightDrawer,
                    scope: this
                });

                this.toolsAdded = true;
            }
            return tools;
        },
        toggleRightDrawer: function() {
            this._toggleDrawer('right');
        },
        _toggleDrawer: function(state) {
            var snapperState = App.snapper.state();
            if (snapperState.state === state) {
                App.snapper.close();
            } else {
                this.loadRightDrawer();
                App.snapper.open(state);
            }
        },
        unloadRightDrawer: function() {
        },
        onBeforeTransitionAway: function() {
            var drawer = App.getView('right_drawer');
            if (drawer) {
                this.unloadRightDrawer();
                drawer.clear();
                this.drawerLoaded = false;
            }
        }
    });
});

<commit_msg>[13092494] Loading of the right drawer now happens on a view's onTransitionTo lifecycle. Unload happens onTransitionAway.<commit_after>/*
 * Copyright (c) 1997-2013, SalesLogix, NA., LLC. All rights reserved.
 */
define('Mobile/SalesLogix/Views/_RightDrawerBaseMixin', [
    'dojo/_base/declare',
    'dojo/_base/array',
    'dojo/_base/lang'
], function(
    declare,
    array,
    lang
) {

    // Base Mixin for the right drawer/menu. This is responsible for creating the toggle button on the toolbar and managing the state of the right menu (loaded/unloaded).
    //
    // Lifecycles:
    // -- Loading of the right menu --
    // 1. Toggle button clicked
    // 2. setupRightDrawer
    // 3. loadRightDrawer
    //
    // -- Unloading of the right menu --
    // 1. onBeforeTransitionAway
    // 2. unloadRightDrawer
    return declare('Mobile.SalesLogix.Views._RightDrawerBaseMixin', null, {
        drawerLoaded: false,
        toolsAdded: false,

        setupRightDrawer: function() {
        },
        loadRightDrawer: function() {
            if (this.drawerLoaded) {
                return;
            }

            this.setupRightDrawer();
            var drawer = App.getView('right_drawer');
            if (drawer) {
                drawer.refresh();
                this.drawerLoaded = true;
            }
        },
        createToolLayout: function() {
            var tools = this.inherited(arguments) || {
                tbar: []
            };

            if (!this.toolsAdded) {
                tools.tbar.unshift({
                    id: 'toggleRightDrawer',
                    side: 'right',
                    fn: this.toggleRightDrawer,
                    scope: this
                });

                this.toolsAdded = true;
            }
            return tools;
        },
        toggleRightDrawer: function() {
            this._toggleDrawer('right');
        },
        _toggleDrawer: function(state) {
            var snapperState = App.snapper.state();
            if (snapperState.state === state) {
                App.snapper.close();
            } else {
                App.snapper.open(state);
            }
        },
        unloadRightDrawer: function() {
        },
        onTransitionTo: function() {
            this.loadRightDrawer();
        },
        onTransitionAway: function() {
            var drawer = App.getView('right_drawer');
            if (drawer) {
                this.unloadRightDrawer();
                drawer.clear();
                this.drawerLoaded = false;
            }
        }
    });
});

<|endoftext|>


<commit_before>$(function() {

	$(document).on('mousedown', '.page_block .resize-line', function() {
		$obj = $(this).parent();

		$(document).bind('mousemove.page_block', function(event) {
			var new_height = event.pageY - $obj.position().top;
			var max_height = 0;

			$obj.find('.block_inner > div').each(function() {
				var element_pos = $(this).position().top + $(this).height();

				if (max_height < element_pos) {
					max_height = element_pos;
				}
			});

			$obj.find('.block_inner_big > div').each(function() {
				if ($(this)[0].style.height === '100%') {
					max_height = 10;
				} else {
					var element_pos = $(this).position().top + $(this).height();

					if (max_height < element_pos) {
						max_height = element_pos;
					}
				}
			});

			if (new_height > max_height) {
				$obj.height(new_height);
			} else {
				$obj.height(max_height);
			}
		});

		$(document).bind('mouseup.page_block', function(event) {
			$(this).unbind('mousemove.page_block');
			$(this).unbind('mouseup.page_block');
		});
	});

});<commit_msg>Размер блока страницы ограничивается координатами элементов, включающих высоту<commit_after>$(function() {

	var BLOCK_MIN_HEIGHT = 10;

	$(document).on('mouseup', function(event) {
		$(this).unbind('mousemove.page_block');
	});

	$(document).on('mousedown', '.page_block .resize-line', function() {
		$block = $(this).parent();

		var new_height = event.pageY - $block.position().top;
		var max_height = BLOCK_MIN_HEIGHT;

		$block.find('.block_inner > div').each(function() {
			var element_pos = $(this).position().top + $(this).outerHeight();

			if (max_height < element_pos) {
				max_height = element_pos;
			}
		});

		$block.find('.block_inner_big > div').each(function() {
			if ($(this)[0].style.height !== '100%') {
				var element_pos = $(this).position().top + $(this).height();

				if (max_height < element_pos) {
					max_height = element_pos;
				}
			}
		});

		$(document).bind('mousemove.page_block', function(event) {
			new_height = event.pageY - $block.position().top;

			if (new_height > max_height) {
				$block.height(new_height);
			} else {
				$block.height(max_height);
			}
		});
	});

});<|endoftext|>