<commit_before>/**
 * PowerTip TooltipController
 *
 * @fileoverview  TooltipController object that manages tooltips for an instance.
 * @link          http://stevenbenner.github.com/jquery-powertip/
 * @author        Steven Benner (http://stevenbenner.com/)
 * @requires      jQuery 1.7+
 */

/**
 * Creates a new tooltip controller.
 * @private
 * @constructor
 * @param {Object} options Options object containing settings.
 */
function TooltipController(options) {
	var placementCalculator = new PlacementCalculator(),
		tipElement = $('#' + options.popupId);

	// build and append tooltip div if it does not already exist
	if (tipElement.length === 0) {
		tipElement = $('<div/>', { id: options.popupId });
		// grab body element if it was not populated when the script loaded
		// this hack exists solely for jsfiddle support
		if ($body.length === 0) {
			$body = $('body');
		}
		$body.append(tipElement);
	}

	// hook mousemove for cursor follow tooltips
	if (options.followMouse) {
		// only one positionTipOnCursor hook per tooltip element, please
		if (!tipElement.data(DATA_HASMOUSEMOVE)) {
			$document.on({
				mousemove: positionTipOnCursor,
				scroll: positionTipOnCursor
			});
			tipElement.data(DATA_HASMOUSEMOVE, true);
		}
	}

	// if we want to be able to mouse onto the tooltip then we need to
	// attach hover events to the tooltip that will cancel a close request
	// on hover and start a new close request on mouseleave
	if (options.mouseOnToPopup) {
		tipElement.on({
			mouseenter: function tipMouseEnter() {
				// we only let the mouse stay on the tooltip if it is set
				// to let users interact with it
				if (tipElement.data(DATA_MOUSEONTOTIP)) {
					// check activeHover in case the mouse cursor entered
					// the tooltip during the fadeOut and close cycle
					if (session.activeHover) {
						session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel();
					}
				}
			},
			mouseleave: function tipMouseLeave() {
				// check activeHover in case the mouse cursor entered
				// the tooltip during the fadeOut and close cycle
				if (session.activeHover) {
					session.activeHover.data(DATA_DISPLAYCONTROLLER).hide();
				}
			}
		});
	}

	/**
	 * Gives the specified element the active-hover state and queues up
	 * the showTip function.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function beginShowTip(element) {
		element.data(DATA_HASACTIVEHOVER, true);
		// show tooltip, asap
		tipElement.queue(function queueTipInit(next) {
			showTip(element);
			next();
		});
	}

	/**
	 * Shows the tooltip, as soon as possible.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function showTip(element) {
		var tipContent;

		// it is possible, especially with keyboard navigation, to move on
		// to another element with a tooltip during the queue to get to
		// this point in the code. if that happens then we need to not
		// proceed or we may have the fadeout callback for the last tooltip
		// execute immediately after this code runs, causing bugs.
		if (!element.data(DATA_HASACTIVEHOVER)) {
			return;
		}

		// if the tooltip is open and we got asked to open another one then
		// the old one is still in its fadeOut cycle, so wait and try again
		if (session.isTipOpen) {
			if (!session.isClosing) {
				hideTip(session.activeHover);
			}
			tipElement.delay(100).queue(function queueTipAgain(next) {
				showTip(element);
				next();
			});
			return;
		}

		// trigger powerTipPreRender event
		element.trigger('powerTipPreRender');

		// set tooltip content
		tipContent = getTooltipContent(element);
		if (tipContent) {
			tipElement.empty().append(tipContent);
		} else {
			// we have no content to display, give up
			return;
		}

		// trigger powerTipRender event
		element.trigger('powerTipRender');

		session.activeHover = element;
		session.isTipOpen = true;

		tipElement.data(DATA_MOUSEONTOTIP, options.mouseOnToPopup);

		// set tooltip position
		if (!options.followMouse) {
			positionTipOnElement(element);
			session.isFixedTipOpen = true;
		} else {
			positionTipOnCursor();
		}

		// fadein
		tipElement.fadeIn(options.fadeInTime, function fadeInCallback() {
			// start desync polling
			if (!session.desyncTimeout) {
				session.desyncTimeout = window.setInterval(closeDesyncedTip, 500);
			}

			// trigger powerTipOpen event
			element.trigger('powerTipOpen');
		});
	}

	/**
	 * Hides the tooltip.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function hideTip(element) {
		// reset session
		session.isClosing = true;
		session.activeHover = null;
		session.isTipOpen = false;

		// stop desync polling
		session.desyncTimeout = window.clearInterval(session.desyncTimeout);

		// reset element state
		element.data(DATA_HASACTIVEHOVER, false);
		element.data(DATA_FORCEDOPEN, false);

		// fade out
		tipElement.fadeOut(options.fadeOutTime, function fadeOutCallback() {
			var coords = new CSSCoordinates();

			// reset session and tooltip element
			session.isClosing = false;
			session.isFixedTipOpen = false;
			tipElement.removeClass();

			// support mouse-follow and fixed position tips at the same
			// time by moving the tooltip to the last known cursor location
			// after it is hidden
			coords.set('top', session.currentY + options.offset);
			coords.set('left', session.currentX + options.offset);
			tipElement.css(coords);

			// trigger powerTipClose event
			element.trigger('powerTipClose');
		});
	}

	/**
	 * Checks for a tooltip desync and closes the tooltip if one occurs.
	 * @private
	 */
	function closeDesyncedTip() {
		var isDesynced = false;
		// It is possible for the mouse cursor to leave an element without
		// firing the mouseleave or blur event. This most commonly happens
		// when the element is disabled under mouse cursor. If this happens
		// it will result in a desynced tooltip because the tooltip was
		// never asked to close. So we should periodically check for a
		// desync situation and close the tip if such a situation arises.
		if (session.isTipOpen && !session.isClosing && !session.delayInProgress) {
			// user moused onto another tip or active hover is disabled
			if (session.activeHover.data(DATA_HASACTIVEHOVER) === false || session.activeHover.is(':disabled')) {
				isDesynced = true;
			} else {
				// hanging tip - have to test if mouse position is not over
				// the active hover and not over a tooltip set to let the
				// user interact with it.
				// for keyboard navigation: this only counts if the element
				// does not have focus.
				// for tooltips opened via the api: we need to check if it
				// has the forcedOpen flag.
				if (!isMouseOver(session.activeHover) && !session.activeHover.is(":focus") && !session.activeHover.data(DATA_FORCEDOPEN)) {
					if (tipElement.data(DATA_MOUSEONTOTIP)) {
						if (!isMouseOver(tipElement)) {
							isDesynced = true;
						}
					} else {
						isDesynced = true;
					}
				}
			}

			if (isDesynced) {
				// close the desynced tip
				hideTip(session.activeHover);
			}
		}
	}

	/**
	 * Moves the tooltip to the users mouse cursor.
	 * @private
	 */
	function positionTipOnCursor() {
		// to support having fixed tooltips on the same page as cursor
		// tooltips, where both instances are referencing the same tooltip
		// element, we need to keep track of the mouse position constantly,
		// but we should only set the tip location if a fixed tip is not
		// currently open, a tip open is imminent or active, and the
		// tooltip element in question does have a mouse-follow using it.
		if (!session.isFixedTipOpen && (session.isTipOpen || (session.tipOpenImminent && tipElement.data(DATA_HASMOUSEMOVE)))) {
			// grab measurements
			var tipWidth = tipElement.outerWidth(),
				tipHeight = tipElement.outerHeight(),
				coords = new CSSCoordinates(),
				collisions, collisionCount;

			// grab collisions
			coords.set('top', session.currentY + options.offset);
			coords.set('left', session.currentX + options.offset);
			collisions = getViewportCollisions(
				coords,
				tipWidth,
				tipHeight
			);
			collisionCount = countFlags(collisions);

			// handle tooltip view port collisions
			if (collisionCount > 0) {
				if (collisionCount === 1) {
					// if there is only one collision (bottom or right) then
					// simply constrain the tooltip to the view port
					if (collisions === Collision.right) {
						coords.set('left', $window.width() - tipWidth);
					} else if (collisions === Collision.bottom) {
						coords.set('top', $window.scrollTop() + $window.height() - tipHeight);
					}
				} else {
					// if the tooltip has more than one collision then it
					// is trapped in the corner and should be flipped to
					// get it out of the users way
					coords.set('left', session.currentX - tipWidth - options.offset);
					coords.set('top', session.currentY - tipHeight - options.offset);
				}
			}

			// position the tooltip
			tipElement.css(coords);
		}
	}

	/**
	 * Sets the tooltip to the correct position relative to the specified
	 * target element. Based on options settings.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function positionTipOnElement(element) {
		var priorityList,
			finalPlacement;

		if (options.smartPlacement) {
			priorityList = $.fn.powerTip.smartPlacementLists[options.placement];

			// iterate over the priority list and use the first placement
			// option that does not collide with the view port. if they all
			// collide then the last placement in the list will be used.
			$.each(priorityList, function(idx, pos) {
				// place tooltip and find collisions
				var collisions = getViewportCollisions(
					placeTooltip(element, pos),
					tipElement.outerWidth(),
					tipElement.outerHeight()
				);

				// update the final placement variable
				finalPlacement = pos;

				// break if there were no collisions
				if (collisions === Collision.none) {
					return false;
				}
			});
		} else {
			// if we're not going to use the smart placement feature then
			// just compute the coordinates and do it
			placeTooltip(element, options.placement);
			finalPlacement = options.placement;
		}

		// add placement as class for CSS arrows
		tipElement.addClass(finalPlacement);
	}

	/**
	 * Sets the tooltip position to the appropriate values to show the tip
	 * at the specified placement. This function will iterate and test the
	 * tooltip to support elastic tooltips.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 * @param {string} placement The placement for the tooltip.
	 * @return {CSSCoordinates} A CSSCoordinates object with the top, left, and right position values.
	 */
	function placeTooltip(element, placement) {
		var iterationCount = 0,
			tipWidth,
			tipHeight,
			coords = new CSSCoordinates();

		// for the first iteration set the tip to 0,0 to get the full
		// expanded width
		coords.set('top', 0);
		coords.set('left', 0);
		tipElement.css(coords);

		// to support elastic tooltips we need to check for a change in
		// the rendered dimensions after the tooltip has been positioned
		do {
			// grab the current tip dimensions
			tipWidth = tipElement.outerWidth();
			tipHeight = tipElement.outerHeight();

			// get placement coordinates
			coords = placementCalculator.compute(
				element,
				placement,
				tipWidth,
				tipHeight,
				options.offset
			);

			// place the tooltip
			tipElement.css(coords);
		} while (
			// sanity check: limit to 5 iterations, and...
			++iterationCount <= 5 &&
			// try again if the dimensions changed after placement
			(tipWidth !== tipElement.outerWidth() || tipHeight !== tipElement.outerHeight())
		);

		return coords;
	}

	/**
	 * Fetches the tooltip content from the specified element's data attributes.
	 * @private
	 * @param {jQuery} element The element to get the tooltip content for.
	 * @return {(string|jQuery|undefined)} The text/HTML string, jQuery object, or
	 *     undefined if there was no tooltip content for the element.
	 */
	function getTooltipContent(element) {
		var tipText = element.data(DATA_POWERTIP),
			tipObject = element.data(DATA_POWERTIPJQ),
			tipTarget = element.data(DATA_POWERTIPTARGET),
			content;

		if (tipText) {
			if (typeof tipText === 'function') {
				tipText = tipText.call(element[0]);
			}
			content = tipText;
		} else if (tipObject) {
			if (typeof tipElem === 'function') {
				tipObject = tipObject.call(element[0]);
			}
			if (tipObject.length > 0) {
				content = tipObject.clone(true, true);
			}
		} else if (tipTarget) {
			var targetElement = $('#' + tipTarget);
			if (targetElement.length > 0) {
				content = $('#' + tipTarget).html();
			}
		}

		return content;
	}

	// expose methods
	this.showTip = beginShowTip;
	this.hideTip = hideTip;
	this.resetPosition = positionTipOnElement;
}
<commit_msg>Optimized collision count test in positionTipOnCursor().<commit_after>/**
 * PowerTip TooltipController
 *
 * @fileoverview  TooltipController object that manages tooltips for an instance.
 * @link          http://stevenbenner.github.com/jquery-powertip/
 * @author        Steven Benner (http://stevenbenner.com/)
 * @requires      jQuery 1.7+
 */

/**
 * Creates a new tooltip controller.
 * @private
 * @constructor
 * @param {Object} options Options object containing settings.
 */
function TooltipController(options) {
	var placementCalculator = new PlacementCalculator(),
		tipElement = $('#' + options.popupId);

	// build and append tooltip div if it does not already exist
	if (tipElement.length === 0) {
		tipElement = $('<div/>', { id: options.popupId });
		// grab body element if it was not populated when the script loaded
		// this hack exists solely for jsfiddle support
		if ($body.length === 0) {
			$body = $('body');
		}
		$body.append(tipElement);
	}

	// hook mousemove for cursor follow tooltips
	if (options.followMouse) {
		// only one positionTipOnCursor hook per tooltip element, please
		if (!tipElement.data(DATA_HASMOUSEMOVE)) {
			$document.on({
				mousemove: positionTipOnCursor,
				scroll: positionTipOnCursor
			});
			tipElement.data(DATA_HASMOUSEMOVE, true);
		}
	}

	// if we want to be able to mouse onto the tooltip then we need to
	// attach hover events to the tooltip that will cancel a close request
	// on hover and start a new close request on mouseleave
	if (options.mouseOnToPopup) {
		tipElement.on({
			mouseenter: function tipMouseEnter() {
				// we only let the mouse stay on the tooltip if it is set
				// to let users interact with it
				if (tipElement.data(DATA_MOUSEONTOTIP)) {
					// check activeHover in case the mouse cursor entered
					// the tooltip during the fadeOut and close cycle
					if (session.activeHover) {
						session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel();
					}
				}
			},
			mouseleave: function tipMouseLeave() {
				// check activeHover in case the mouse cursor entered
				// the tooltip during the fadeOut and close cycle
				if (session.activeHover) {
					session.activeHover.data(DATA_DISPLAYCONTROLLER).hide();
				}
			}
		});
	}

	/**
	 * Gives the specified element the active-hover state and queues up
	 * the showTip function.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function beginShowTip(element) {
		element.data(DATA_HASACTIVEHOVER, true);
		// show tooltip, asap
		tipElement.queue(function queueTipInit(next) {
			showTip(element);
			next();
		});
	}

	/**
	 * Shows the tooltip, as soon as possible.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function showTip(element) {
		var tipContent;

		// it is possible, especially with keyboard navigation, to move on
		// to another element with a tooltip during the queue to get to
		// this point in the code. if that happens then we need to not
		// proceed or we may have the fadeout callback for the last tooltip
		// execute immediately after this code runs, causing bugs.
		if (!element.data(DATA_HASACTIVEHOVER)) {
			return;
		}

		// if the tooltip is open and we got asked to open another one then
		// the old one is still in its fadeOut cycle, so wait and try again
		if (session.isTipOpen) {
			if (!session.isClosing) {
				hideTip(session.activeHover);
			}
			tipElement.delay(100).queue(function queueTipAgain(next) {
				showTip(element);
				next();
			});
			return;
		}

		// trigger powerTipPreRender event
		element.trigger('powerTipPreRender');

		// set tooltip content
		tipContent = getTooltipContent(element);
		if (tipContent) {
			tipElement.empty().append(tipContent);
		} else {
			// we have no content to display, give up
			return;
		}

		// trigger powerTipRender event
		element.trigger('powerTipRender');

		session.activeHover = element;
		session.isTipOpen = true;

		tipElement.data(DATA_MOUSEONTOTIP, options.mouseOnToPopup);

		// set tooltip position
		if (!options.followMouse) {
			positionTipOnElement(element);
			session.isFixedTipOpen = true;
		} else {
			positionTipOnCursor();
		}

		// fadein
		tipElement.fadeIn(options.fadeInTime, function fadeInCallback() {
			// start desync polling
			if (!session.desyncTimeout) {
				session.desyncTimeout = window.setInterval(closeDesyncedTip, 500);
			}

			// trigger powerTipOpen event
			element.trigger('powerTipOpen');
		});
	}

	/**
	 * Hides the tooltip.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function hideTip(element) {
		// reset session
		session.isClosing = true;
		session.activeHover = null;
		session.isTipOpen = false;

		// stop desync polling
		session.desyncTimeout = window.clearInterval(session.desyncTimeout);

		// reset element state
		element.data(DATA_HASACTIVEHOVER, false);
		element.data(DATA_FORCEDOPEN, false);

		// fade out
		tipElement.fadeOut(options.fadeOutTime, function fadeOutCallback() {
			var coords = new CSSCoordinates();

			// reset session and tooltip element
			session.isClosing = false;
			session.isFixedTipOpen = false;
			tipElement.removeClass();

			// support mouse-follow and fixed position tips at the same
			// time by moving the tooltip to the last known cursor location
			// after it is hidden
			coords.set('top', session.currentY + options.offset);
			coords.set('left', session.currentX + options.offset);
			tipElement.css(coords);

			// trigger powerTipClose event
			element.trigger('powerTipClose');
		});
	}

	/**
	 * Checks for a tooltip desync and closes the tooltip if one occurs.
	 * @private
	 */
	function closeDesyncedTip() {
		var isDesynced = false;
		// It is possible for the mouse cursor to leave an element without
		// firing the mouseleave or blur event. This most commonly happens
		// when the element is disabled under mouse cursor. If this happens
		// it will result in a desynced tooltip because the tooltip was
		// never asked to close. So we should periodically check for a
		// desync situation and close the tip if such a situation arises.
		if (session.isTipOpen && !session.isClosing && !session.delayInProgress) {
			// user moused onto another tip or active hover is disabled
			if (session.activeHover.data(DATA_HASACTIVEHOVER) === false || session.activeHover.is(':disabled')) {
				isDesynced = true;
			} else {
				// hanging tip - have to test if mouse position is not over
				// the active hover and not over a tooltip set to let the
				// user interact with it.
				// for keyboard navigation: this only counts if the element
				// does not have focus.
				// for tooltips opened via the api: we need to check if it
				// has the forcedOpen flag.
				if (!isMouseOver(session.activeHover) && !session.activeHover.is(":focus") && !session.activeHover.data(DATA_FORCEDOPEN)) {
					if (tipElement.data(DATA_MOUSEONTOTIP)) {
						if (!isMouseOver(tipElement)) {
							isDesynced = true;
						}
					} else {
						isDesynced = true;
					}
				}
			}

			if (isDesynced) {
				// close the desynced tip
				hideTip(session.activeHover);
			}
		}
	}

	/**
	 * Moves the tooltip to the users mouse cursor.
	 * @private
	 */
	function positionTipOnCursor() {
		// to support having fixed tooltips on the same page as cursor
		// tooltips, where both instances are referencing the same tooltip
		// element, we need to keep track of the mouse position constantly,
		// but we should only set the tip location if a fixed tip is not
		// currently open, a tip open is imminent or active, and the
		// tooltip element in question does have a mouse-follow using it.
		if (!session.isFixedTipOpen && (session.isTipOpen || (session.tipOpenImminent && tipElement.data(DATA_HASMOUSEMOVE)))) {
			// grab measurements
			var tipWidth = tipElement.outerWidth(),
				tipHeight = tipElement.outerHeight(),
				coords = new CSSCoordinates(),
				collisions, collisionCount;

			// grab collisions
			coords.set('top', session.currentY + options.offset);
			coords.set('left', session.currentX + options.offset);
			collisions = getViewportCollisions(
				coords,
				tipWidth,
				tipHeight
			);

			// handle tooltip view port collisions
			if (collisions !== Collision.none) {
				collisionCount = countFlags(collisions);
				if (collisionCount === 1) {
					// if there is only one collision (bottom or right) then
					// simply constrain the tooltip to the view port
					if (collisions === Collision.right) {
						coords.set('left', $window.width() - tipWidth);
					} else if (collisions === Collision.bottom) {
						coords.set('top', $window.scrollTop() + $window.height() - tipHeight);
					}
				} else {
					// if the tooltip has more than one collision then it
					// is trapped in the corner and should be flipped to
					// get it out of the users way
					coords.set('left', session.currentX - tipWidth - options.offset);
					coords.set('top', session.currentY - tipHeight - options.offset);
				}
			}

			// position the tooltip
			tipElement.css(coords);
		}
	}

	/**
	 * Sets the tooltip to the correct position relative to the specified
	 * target element. Based on options settings.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 */
	function positionTipOnElement(element) {
		var priorityList,
			finalPlacement;

		if (options.smartPlacement) {
			priorityList = $.fn.powerTip.smartPlacementLists[options.placement];

			// iterate over the priority list and use the first placement
			// option that does not collide with the view port. if they all
			// collide then the last placement in the list will be used.
			$.each(priorityList, function(idx, pos) {
				// place tooltip and find collisions
				var collisions = getViewportCollisions(
					placeTooltip(element, pos),
					tipElement.outerWidth(),
					tipElement.outerHeight()
				);

				// update the final placement variable
				finalPlacement = pos;

				// break if there were no collisions
				if (collisions === Collision.none) {
					return false;
				}
			});
		} else {
			// if we're not going to use the smart placement feature then
			// just compute the coordinates and do it
			placeTooltip(element, options.placement);
			finalPlacement = options.placement;
		}

		// add placement as class for CSS arrows
		tipElement.addClass(finalPlacement);
	}

	/**
	 * Sets the tooltip position to the appropriate values to show the tip
	 * at the specified placement. This function will iterate and test the
	 * tooltip to support elastic tooltips.
	 * @private
	 * @param {jQuery} element The element that the tooltip should target.
	 * @param {string} placement The placement for the tooltip.
	 * @return {CSSCoordinates} A CSSCoordinates object with the top, left, and right position values.
	 */
	function placeTooltip(element, placement) {
		var iterationCount = 0,
			tipWidth,
			tipHeight,
			coords = new CSSCoordinates();

		// for the first iteration set the tip to 0,0 to get the full
		// expanded width
		coords.set('top', 0);
		coords.set('left', 0);
		tipElement.css(coords);

		// to support elastic tooltips we need to check for a change in
		// the rendered dimensions after the tooltip has been positioned
		do {
			// grab the current tip dimensions
			tipWidth = tipElement.outerWidth();
			tipHeight = tipElement.outerHeight();

			// get placement coordinates
			coords = placementCalculator.compute(
				element,
				placement,
				tipWidth,
				tipHeight,
				options.offset
			);

			// place the tooltip
			tipElement.css(coords);
		} while (
			// sanity check: limit to 5 iterations, and...
			++iterationCount <= 5 &&
			// try again if the dimensions changed after placement
			(tipWidth !== tipElement.outerWidth() || tipHeight !== tipElement.outerHeight())
		);

		return coords;
	}

	/**
	 * Fetches the tooltip content from the specified element's data attributes.
	 * @private
	 * @param {jQuery} element The element to get the tooltip content for.
	 * @return {(string|jQuery|undefined)} The text/HTML string, jQuery object, or
	 *     undefined if there was no tooltip content for the element.
	 */
	function getTooltipContent(element) {
		var tipText = element.data(DATA_POWERTIP),
			tipObject = element.data(DATA_POWERTIPJQ),
			tipTarget = element.data(DATA_POWERTIPTARGET),
			content;

		if (tipText) {
			if (typeof tipText === 'function') {
				tipText = tipText.call(element[0]);
			}
			content = tipText;
		} else if (tipObject) {
			if (typeof tipElem === 'function') {
				tipObject = tipObject.call(element[0]);
			}
			if (tipObject.length > 0) {
				content = tipObject.clone(true, true);
			}
		} else if (tipTarget) {
			var targetElement = $('#' + tipTarget);
			if (targetElement.length > 0) {
				content = $('#' + tipTarget).html();
			}
		}

		return content;
	}

	// expose methods
	this.showTip = beginShowTip;
	this.hideTip = hideTip;
	this.resetPosition = positionTipOnElement;
}
<|endoftext|>


<commit_before>/**
 * Created by doug on 9/17/16.
 */

enyo.kind({
    name: "FileApis",
    layoutKind: "FittableRowsLayout",
    components: [
        { kind: "onyx.Toolbar", layoutKind: "FittableColumnsLayout", components: [
            {fit: true, content: $L("File APIs")}
        ]},
        {
            fit: true,
            kind: "Scroller",
            touch: true,
            horizontal: "hidden",
            components: [
                {style: "margin-left: auto; margin-right: auto; max-width: 35em; padding: 1rem;", components: [
                    // {kind: "FittableColumns", components: [
                    //     {content: $L("Permanence: "), style: "color: white; line-height: 2.5rem;"},
                    //     {kind: "onyx.PickerDecorator", style: "margin-left: 1rem;", components: [
                    //         {},   // PickerButton
                    //         {name: "permanencePckr", kind: "onyx.Picker", components: [
                    //             {content: $L("Persistent"), active: true, value: window.PERSISTENT},
                    //             {content: $L("Temporary"), value: window.TEMPORARY}
                    //         ]}
                    //     ]}
                    // ]},
                    {kind: "FittableColumns", style: "margin-top: 1rem;", components: [
                        {content: $L("Size: "), style: "color: white; line-height: 2rem;"},
                        {kind: "onyx.InputDecorator", style: "margin-left: 1rem;", components: [
                            {name: "sizeInpt", kind: "onyx.Input", type: "number", value: "200000", attributes: {min: "0", step: "100000"}, style: "width: 11rem;"}
                        ]},
                        {content: $L("bytes"), style: "color: white; line-height: 2rem; margin-left: 1rem;"}
                    ]},
                    {kind: "onyx.Groupbox", style: "margin-top: 1rem;", components: [
                        {kind: "onyx.Button", style: "width: 100%", content: $L("Chrome sandbox - write file"), ontap: "chromeSandboxWriteFile"},
                        {name: "webkitChromeOut", content: "   ", allowHtml: true, style: "padding: 5px; color: white"}
                    ]},
                    {kind: "onyx.Groupbox", style: "margin-top: 1rem;", components: [
                        {kind: "onyx.Button", style: "width: 100%", content: $L("Chrome sandbox - delete all"), ontap: "chromeSandboxDeleteAll"},
                        {name: "sandboxDeleteOut", content: "   ", allowHtml: true, style: "padding: 5px; color: white"}
                    ]}
                ]}
            ]
        }
    ],

    chromeSandboxWriteFile: function (inSender, inEvent) {
        var panel = this;
        var requestedBytes;
        try {
            requestedBytes = parseInt(this.$.sizeInpt.getValue(), 10);
            this.log(typeof requestedBytes, requestedBytes);
            var msg = "requesting " + requestedBytes + " bytes persistent storage:";
            panel.$.webkitChromeOut.set('content', msg);

            navigator.webkitPersistentStorage.requestQuota(requestedBytes, gotQuota, fileSystemFail);

            // var permanence = this.$.permanencePckr.getSelected().value;
            // this.log(typeof permanence, permanence, typeof requestedBytes, requestedBytes);
            // window.webkitRequestFileSystem(permanence, requestedBytes, success, fileSystemFail);
        } catch (err) {
            this.error(err);
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + panel.errorToMessage(err));
        }

        function gotQuota(grantedBytes) {
            console.log("got FileSystem quota:", grantedBytes);
            var msg = "granted " + grantedBytes + " bytes";
            msg += "

requesting file system:"
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

            window.webkitRequestFileSystem(window.PERSISTENT, grantedBytes, gotFileSystem, fileSystemFail);
        }
        function gotFileSystem(fileSystem) {
            console.log(fileSystem);
            var msg = [];
            msg.push("name: " + fileSystem.name);
            msg.push("
getting directory:");
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg.join('
'));

            fileSystem.root.getDirectory('Testr', {create: true}, gotDirectory, fileSystemFail);
        }
        function gotDirectory(dirEntry) {
            console.log(dirEntry);
            var msg = "path: " + dirEntry.fullPath;
            msg += "

getting file:"
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

            var filePath;
            var appInfo = webos.fetchAppInfo();
            if (appInfo && appInfo.id) {
                filePath = appInfo.id + ".txt";
            } else {
                filePath = "unknown_id.txt";
            }
            dirEntry.getFile(filePath, {create: true, exclusive: false}, gotFile, fileSystemFail);
        }
        function gotFile(fileEntry) {
            console.log(fileEntry);
            var msg = "path: " + fileEntry.fullPath;
            msg += "

creating fileWriter:"
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

            fileEntry.createWriter(gotFileWriter, fileSystemFail);

            function gotFileWriter(fileWriter) {
                console.log(fileWriter);
                var msg = "length " + fileWriter.length;

                var strArr = [];
                while ((strArr.length+1) * 100 <= requestedBytes) {
                    strArr.push("........10........20........30........40........50........60........70........80........90......100\n");
                }
                var blob = new Blob(strArr, {type: "text/plain"});

                msg += "

writing " + blob.size + " bytes:";
                panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

                fileWriter.onwriteend = gotTruncatedFile;
                fileWriter.onerror = fileSystemFail;
                fileWriter.truncate(0);

                function gotTruncatedFile(progressEvt) {
                    fileWriter.onwriteend = function(progressEvt) {
                        var msg = "wrote " + fileWriter.position + " bytes to " + fileEntry.fullPath;
                        console.log(msg);
                        panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);
                    };

                    fileWriter.write(blob);
                }
            }
        }

        function fileSystemFail(fileError) {
            console.error(fileError);
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + panel.errorToMessage(fileError) + '
');
        }
    },

    chromeSandboxDeleteAll: function (inSender, inEvent) {
        var panel = this;
        var dirReader;
        var totalEntries = 0;
        try {
            var msg = "requesting filesystem:";
            this.log(msg);
            panel.$.sandboxDeleteOut.set('content', msg);

            window.webkitRequestFileSystem(window.PERSISTENT, 1024, gotFileSystem, fileSystemFail);
        } catch (err) {
            this.error(err);
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + panel.errorToMessage(err));
        }

        function gotFileSystem(fileSystem) {
            console.log(fileSystem);
            var msg = [];
            msg.push("name: " + fileSystem.name);
            msg.push("
reading root directory:");
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg.join('
'));

            dirReader = fileSystem.root.createReader();
            dirReader.readEntries(gotEntries, fileSystemFail);
        }
        function gotEntries(entries) {
            console.log(entries);
            totalEntries += entries.length;
            var msg = [];
            for (var i = 0; i < entries.length; i++) {
                var entry = entries[i];
                if (entry.isDirectory){
                    console.log('recursively removing: ' + entry.fullPath);
                    msg.push('recursively removing: ' + entry.fullPath);
                    entry.removeRecursively(removedEntry, fileSystemFail);
                }
                else if (entry.isFile){
                    console.log('removing: ' + entry.fullPath);
                    msg.push('removing: ' + entry.fullPath);
                    entry.remove(removedEntry, fileSystemFail);
                }
            }
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg.join('
'));

            if (entries.length > 0) {
                dirReader.readEntries(gotEntries, fileSystemFail);
            } else {
                var msg2 = 'attempted removal of ' + totalEntries + ' entries';
                panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg2);
            }
        }
        function removedEntry() {
            var msg = "entry removed";
            console.log(msg);
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg);
        }

        function fileSystemFail(fileError) {
            console.error(fileError);
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + panel.errorToMessage(fileError) + '
');
        }
    },

    errorToMessage: function errorToMessage(error, fallbackMsg) {
        var msg = [];
        if (! error) {
            if (fallbackMsg) {
                return fallbackMsg;
            } else {
                return Object.prototype.toString.call(error);
            }
        }
        if (error.message) {
            msg.unshift(error.message);
        }
        if (error.name) {
            msg.unshift(error.name);
        }

        if (error instanceof ProgressEvent) {
            if (error.type === "error" && error.target.error) {
                msg.push(errorToMessage(error.target.error));
            } else {
                msg.push("ProgressEvent");
                msg.push("type: " + error.type);
                msg.push("total: " + error.total);
                msg.push("loaded: " + error.loaded);
            }
        }

        if (msg.length === 0) {
            if (fallbackMsg) {
                msg.push(fallbackMsg);
            } else {
                try {
                    msg.push(JSON.stringify(error));
                } catch (e) {
                    console.error(e);
                }
            }
        }

        return msg.join('
');
    }

});
<commit_msg>File APIs: tweaked "write file" test, to allow for overhead.<commit_after>/**
 * Created by doug on 9/17/16.
 */

enyo.kind({
    name: "FileApis",
    layoutKind: "FittableRowsLayout",
    components: [
        { kind: "onyx.Toolbar", layoutKind: "FittableColumnsLayout", components: [
            {fit: true, content: $L("File APIs")}
        ]},
        {
            fit: true,
            kind: "Scroller",
            touch: true,
            horizontal: "hidden",
            components: [
                {style: "margin-left: auto; margin-right: auto; max-width: 35em; padding: 1rem;", components: [
                    // {kind: "FittableColumns", components: [
                    //     {content: $L("Permanence: "), style: "color: white; line-height: 2.5rem;"},
                    //     {kind: "onyx.PickerDecorator", style: "margin-left: 1rem;", components: [
                    //         {},   // PickerButton
                    //         {name: "permanencePckr", kind: "onyx.Picker", components: [
                    //             {content: $L("Persistent"), active: true, value: window.PERSISTENT},
                    //             {content: $L("Temporary"), value: window.TEMPORARY}
                    //         ]}
                    //     ]}
                    // ]},
                    {kind: "FittableColumns", style: "margin-top: 1rem;", components: [
                        {content: $L("Size: "), style: "color: white; line-height: 2rem;"},
                        {kind: "onyx.InputDecorator", style: "margin-left: 1rem;", components: [
                            {name: "sizeInpt", kind: "onyx.Input", type: "number", value: "200000", attributes: {min: "0", step: "100000"}, style: "width: 11rem;"}
                        ]},
                        {content: $L("bytes"), style: "color: white; line-height: 2rem; margin-left: 1rem;"}
                    ]},
                    {kind: "onyx.Groupbox", style: "margin-top: 1rem;", components: [
                        {kind: "onyx.Button", style: "width: 100%", content: $L("Chrome sandbox - write file"), ontap: "chromeSandboxWriteFile"},
                        {name: "webkitChromeOut", content: "   ", allowHtml: true, style: "padding: 5px; color: white"}
                    ]},
                    {kind: "onyx.Groupbox", style: "margin-top: 1rem;", components: [
                        {kind: "onyx.Button", style: "width: 100%", content: $L("Chrome sandbox - delete all"), ontap: "chromeSandboxDeleteAll"},
                        {name: "sandboxDeleteOut", content: "   ", allowHtml: true, style: "padding: 5px; color: white"}
                    ]}
                ]}
            ]
        }
    ],

    chromeSandboxWriteFile: function (inSender, inEvent) {
        var panel = this;
        var targetBytes;
        try {
            targetBytes = parseInt(this.$.sizeInpt.getValue(), 10);
            var requestedBytes = Math.round(targetBytes * 1.01);
            var msg = "requesting " + requestedBytes + " bytes persistent storage:";
            this.log(msg);
            panel.$.webkitChromeOut.set('content', msg);

            navigator.webkitPersistentStorage.requestQuota(requestedBytes, gotQuota, fileSystemFail);

            // var permanence = this.$.permanencePckr.getSelected().value;
            // this.log(typeof permanence, permanence, typeof targetBytes, targetBytes);
            // window.webkitRequestFileSystem(permanence, requestedBytes, success, fileSystemFail);
        } catch (err) {
            this.error(err);
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + panel.errorToMessage(err));
        }

        function gotQuota(grantedBytes) {
            console.log("got FileSystem quota:", grantedBytes);
            var msg = "granted " + grantedBytes + " bytes";
            msg += "

requesting file system:";
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

            window.webkitRequestFileSystem(window.PERSISTENT, grantedBytes, gotFileSystem, fileSystemFail);
        }
        function gotFileSystem(fileSystem) {
            console.log(fileSystem);
            var msg = [];
            msg.push("name: " + fileSystem.name);
            msg.push("
getting directory:");
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg.join('
'));

            fileSystem.root.getDirectory('Testr', {create: true}, gotDirectory, fileSystemFail);
        }
        function gotDirectory(dirEntry) {
            console.log(dirEntry);
            var msg = "path: " + dirEntry.fullPath;
            msg += "

getting file:"
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

            var filePath;
            var appInfo = webos.fetchAppInfo();
            if (appInfo && appInfo.id) {
                filePath = appInfo.id + ".txt";
            } else {
                filePath = "unknown_id.txt";
            }
            dirEntry.getFile(filePath, {create: true, exclusive: false}, gotFile, fileSystemFail);
        }
        function gotFile(fileEntry) {
            console.log(fileEntry);
            var msg = "path: " + fileEntry.fullPath;
            msg += "

creating fileWriter:"
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

            fileEntry.createWriter(gotFileWriter, fileSystemFail);

            function gotFileWriter(fileWriter) {
                console.log(fileWriter);
                var msg = "length " + fileWriter.length;

                var strArr = [];
                while ((strArr.length+1) * 100 <= targetBytes) {
                    strArr.push("........10........20........30........40........50........60........70........80........90......100\n");
                }
                var blob = new Blob(strArr, {type: "text/plain"});

                msg += "

writing " + blob.size + " bytes:";
                panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);

                fileWriter.onwriteend = gotTruncatedFile;
                fileWriter.onerror = fileSystemFail;
                fileWriter.truncate(0);

                function gotTruncatedFile(progressEvt) {
                    fileWriter.onwriteend = function(progressEvt) {
                        var msg = "wrote " + fileWriter.position + " bytes to " + fileEntry.fullPath;
                        console.log(msg);
                        panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + msg);
                    };

                    fileWriter.write(blob);
                }
            }
        }

        function fileSystemFail(fileError) {
            console.error(fileError);
            panel.$.webkitChromeOut.set('content', panel.$.webkitChromeOut.get('content') + '
' + panel.errorToMessage(fileError) + '
');
        }
    },

    chromeSandboxDeleteAll: function (inSender, inEvent) {
        var panel = this;
        var dirReader;
        var totalEntries = 0;
        try {
            var msg = "requesting filesystem:";
            this.log(msg);
            panel.$.sandboxDeleteOut.set('content', msg);

            window.webkitRequestFileSystem(window.PERSISTENT, 1024, gotFileSystem, fileSystemFail);
        } catch (err) {
            this.error(err);
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + panel.errorToMessage(err));
        }

        function gotFileSystem(fileSystem) {
            console.log(fileSystem);
            var msg = [];
            msg.push("name: " + fileSystem.name);
            msg.push("
reading root directory:");
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg.join('
'));

            dirReader = fileSystem.root.createReader();
            dirReader.readEntries(gotEntries, fileSystemFail);
        }
        function gotEntries(entries) {
            console.log(entries);
            totalEntries += entries.length;
            var msg = [];
            for (var i = 0; i < entries.length; i++) {
                var entry = entries[i];
                if (entry.isDirectory){
                    console.log('recursively removing: ' + entry.fullPath);
                    msg.push('recursively removing: ' + entry.fullPath);
                    entry.removeRecursively(removedEntry, fileSystemFail);
                }
                else if (entry.isFile){
                    console.log('removing: ' + entry.fullPath);
                    msg.push('removing: ' + entry.fullPath);
                    entry.remove(removedEntry, fileSystemFail);
                }
            }
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg.join('
'));

            if (entries.length > 0) {
                dirReader.readEntries(gotEntries, fileSystemFail);
            } else {
                var msg2 = 'attempted removal of ' + totalEntries + ' entries';
                panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg2);
            }
        }
        function removedEntry() {
            var msg = "entry removed";
            console.log(msg);
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + msg);
        }

        function fileSystemFail(fileError) {
            console.error(fileError);
            panel.$.sandboxDeleteOut.set('content', panel.$.sandboxDeleteOut.get('content') + '
' + panel.errorToMessage(fileError) + '
');
        }
    },

    errorToMessage: function errorToMessage(error, fallbackMsg) {
        var msg = [];
        if (! error) {
            if (fallbackMsg) {
                return fallbackMsg;
            } else {
                return Object.prototype.toString.call(error);
            }
        }
        if (error.message) {
            msg.unshift(error.message);
        }
        if (error.name) {
            msg.unshift(error.name);
        }

        if (error instanceof ProgressEvent) {
            if (error.type === "error" && error.target.error) {
                msg.push(errorToMessage(error.target.error));
            } else {
                msg.push("ProgressEvent");
                msg.push("type: " + error.type);
                msg.push("total: " + error.total);
                msg.push("loaded: " + error.loaded);
            }
        }

        if (msg.length === 0) {
            if (fallbackMsg) {
                msg.push(fallbackMsg);
            } else {
                try {
                    msg.push(JSON.stringify(error));
                } catch (e) {
                    console.error(e);
                }
            }
        }

        return msg.join('
');
    }

});
<|endoftext|>


<commit_before>//@flow
import Transport from "@ledgerhq/hw-transport";

/**
 * HTTP transport implementation
 */
// NOTE in the future we might want to do WebSocket, because we could have the disconnect lifecycle hooked.
export default class HttpTransport extends Transport<string> {
  // this transport is not discoverable
  static list = (): * => Promise.resolve([]);
  static discover = () => ({ unsubscribe: () => {} });

  static async open(url: string, timeout?: number) {
    const response = await fetch(url, { timeout });
    if (response.status !== 200) {
      throw new Error(
        "failed to access HttpTransport(" + url + "): status " + response.status
      );
    }
    return new HttpTransport(url);
  }

  url: string;

  /**
   * The full url of an http server.
   * * a GET is expected to return a 200 status on this url.
   * * a POST is expected to accept { apduHex, statusList } object in body and
       returns { data, error } where either data is filled with an hex result
       or error is filled with a text error.
   * @example
   * new HttpTransport("http://192.168.0.1/")
   */
  constructor(url: string) {
    super();
    this.url = url;
  }

  async exchange(apduHex: string, statusList: Array<number>): Promise<string> {
    const response = await fetch(this.url, {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ apduHex, statusList })
    });
    if (response.status !== 200) {
      throw "failed to communicate to server. code=" + response.status;
    }
    const body = await response.json();
    if (body.error) throw body.error;
    return body.data;
  }

  setScrambleKey() {}

  close(): Promise<void> {
    return Promise.resolve();
  }
}
<commit_msg>fix flow<commit_after>//@flow
import Transport from "@ledgerhq/hw-transport";

/**
 * HTTP transport implementation
 */
// NOTE in the future we might want to do WebSocket, because we could have the disconnect lifecycle hooked.
export default class HttpTransport extends Transport<string> {
  // this transport is not discoverable
  static list = (): * => Promise.resolve([]);
  static discover = (_cb: *) => ({ unsubscribe: () => {} });

  static async open(url: string, timeout?: number) {
    const response = await fetch(url, { timeout });
    if (response.status !== 200) {
      throw new Error(
        "failed to access HttpTransport(" + url + "): status " + response.status
      );
    }
    return new HttpTransport(url);
  }

  url: string;

  /**
   * The full url of an http server.
   * * a GET is expected to return a 200 status on this url.
   * * a POST is expected to accept { apduHex, statusList } object in body and
       returns { data, error } where either data is filled with an hex result
       or error is filled with a text error.
   * @example
   * new HttpTransport("http://192.168.0.1/")
   */
  constructor(url: string) {
    super();
    this.url = url;
  }

  async exchange(apduHex: string, statusList: Array<number>): Promise<string> {
    const response = await fetch(this.url, {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ apduHex, statusList })
    });
    if (response.status !== 200) {
      throw "failed to communicate to server. code=" + response.status;
    }
    const body = await response.json();
    if (body.error) throw body.error;
    return body.data;
  }

  setScrambleKey() {}

  close(): Promise<void> {
    return Promise.resolve();
  }
}
<|endoftext|>


<commit_before>// Dependencies
var binPath = require('./bin-path')
var fs = require('fs')
var pkgConfig = require('pkg-config')

function checkDevDeps (filePath) {
  // This get devDependencies property
  // from the nearest package.json
  var options = { cwd: filePath, root: 'devDependencies', cache: false }
  var devDeps = pkgConfig(null, options)

  // Check if there are linters defined in
  // package.json devDependencies
  if (devDeps && (devDeps.standard || devDeps.semistandard || devDeps.happiness)) {
    // standard style
    if (devDeps.standard) {
      return {
        linterName: 'standard',
        executablePath: binPath.standard
      }
    }

    // happiness style
    if (devDeps.happiness) {
      return {
        linterName: 'happiness',
        executablePath: binPath.happiness
      }
    }

    // semistandard style
    return {
      linterName: 'semistandard',
      executablePath: binPath.semiStandard
    }
  }

  // fallback
  return {
    linterName: false,
    executablePath: false
  }
}

module.export = function selectStyle (config, filePath) {
  // Default values
  var linterName = false
  var executablePath = false

  // see if setting 'checkStyleDevDependencies' is true
  // if not fallback to style value to decide which bin
  // we should use
  if (config.checkStyleDevDependencies) {
    var devDeps = checkDevDeps(filePath)

    linterName = devDeps.linterName ? devDeps.linterName : linterName
    executablePath = devDeps.executablePath ? devDeps.executablePath : executablePath
  } else {
    if (config.style === 'standard') {
      linterName = 'standard'
      executablePath = binPath.standard
    } else if (config.style === 'happiness') {
      linterName = 'happiness'
      executablePath = binPath.happiness
    } else {
      linterName = 'semistandard'
      executablePath = binPath.semiStandard
    }
  }

  // Sane check
  if (executablePath && !fs.existsSync(executablePath)) {
    throw new Error('Standard or Semistandard wasn\'t' +
      'installed properly with linter-js-standard,' +
      'please re-install the plugin.')
  }

  return {
    name: linterName,
    execPath: executablePath
  }
}
<commit_msg>fix typo in select-style<commit_after>// Dependencies
var binPath = require('./bin-path')
var fs = require('fs')
var pkgConfig = require('pkg-config')

function checkDevDeps (filePath) {
  // This get devDependencies property
  // from the nearest package.json
  var options = { cwd: filePath, root: 'devDependencies', cache: false }
  var devDeps = pkgConfig(null, options)

  // Check if there are linters defined in
  // package.json devDependencies
  if (devDeps && (devDeps.standard || devDeps.semistandard || devDeps.happiness)) {
    // standard style
    if (devDeps.standard) {
      return {
        linterName: 'standard',
        executablePath: binPath.standard
      }
    }

    // happiness style
    if (devDeps.happiness) {
      return {
        linterName: 'happiness',
        executablePath: binPath.happiness
      }
    }

    // semistandard style
    return {
      linterName: 'semistandard',
      executablePath: binPath.semiStandard
    }
  }

  // fallback
  return {
    linterName: false,
    executablePath: false
  }
}

module.exports = function selectStyle (config, filePath) {
  // Default values
  var linterName = false
  var executablePath = false

  // see if setting 'checkStyleDevDependencies' is true
  // if not fallback to style value to decide which bin
  // we should use
  if (config.checkStyleDevDependencies) {
    var devDeps = checkDevDeps(filePath)

    linterName = devDeps.linterName ? devDeps.linterName : linterName
    executablePath = devDeps.executablePath ? devDeps.executablePath : executablePath
  } else {
    if (config.style === 'standard') {
      linterName = 'standard'
      executablePath = binPath.standard
    } else if (config.style === 'happiness') {
      linterName = 'happiness'
      executablePath = binPath.happiness
    } else {
      linterName = 'semistandard'
      executablePath = binPath.semiStandard
    }
  }

  // Sane check
  if (executablePath && !fs.existsSync(executablePath)) {
    throw new Error('Standard or Semistandard wasn\'t' +
      'installed properly with linter-js-standard,' +
      'please re-install the plugin.')
  }

  return {
    name: linterName,
    execPath: executablePath
  }
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.node.ws',
  name: 'Frame',
  properties: [
    ['fin', 1],
    ['rsv1',0],
    ['rsv2',0],
    ['rsv3',0],
    ['opcode',1],
    ['mask',0],
    'maskingKey',
    'buffer',
    ['bufferPos', 0],
    ['needed', 0],
    {
      type: 'Boolean',
      name: 'framing',
      defaultValue: true
    },
    {
      type: 'Boolean',
      name: 'finished',
      defaultValue: false
    },
  ],
  methods: [
    function toData() {
      this.length = this.buffer.length;
      var headerSize = this.buffer.length > 65535 ? 10 :
          (this.buffer.length > 125 ? 4 : 2);

      var i = 0;
      var buffer = new Buffer(this.buffer.length + headerSize);
      // FIN = 1, RSV1-3 = 0
      buffer.writeUInt8(
        0x80 +
          this.opcode, i++);

      var length = this.length;
      if ( length > 0xffffffff ) {
        console.error("Too large a frame to support in JS");
      } else if ( length > 65535 ) {
        buffer.writeUInt8(127, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8((length >> 24) & 0xff, i++)
        buffer.writeUInt8((length >> 16) & 0xff, i++)
        buffer.writeUInt8((length >> 8) & 0xff, i++)
        buffer.writeUInt8((length & 0xff), i++)
      } else if ( length > 125 ) {
        buffer.writeUInt8(126, i++);
        buffer.writeUInt8((length & 0xff00) >> 8, i++)
        buffer.writeUInt8(length & 0xff, i++)
      } else {
        buffer.writeUInt8(length & 0x7f, i++);
      }

      this.buffer.copy(buffer, i);

      return buffer;
    },
    function onData(data, i) {
      if ( this.framing ) {
        var byte = data.readUInt8(i++);
        this.opcode = byte & 0x0f;
        this.fin = !! ( byte & 0x80 );
        this.rsv1 = !! ( byte & 0x40 );
        this.rsv2  = !! ( byte & 0x20 );
        this.rsv3 = !! ( byte & 0x10 );

        byte = data.readUInt8(i++);
        this.mask = !! ( byte & 0x80 );
        var length = byte & 0x7f;

        if ( length == 126 ) {
          length = 0;
          byte = data.readUInt8(i++);
          length += byte << 8;
          byte = data.readUInt8(i++);
          length += byte;
        } else if ( length == 127 ) {
          length = 0;
          var tolarge = false;
          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 56;

          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 48;

          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 40;

          byte = data.readUInt8(i++);
          if ( byte !== 0 ) tolarge = true;
          //length += byte << 32;

          byte = data.readUInt8(i++);
          length += byte << 24;
          byte = data.readUInt8(i++);
          length += byte << 16;
          byte = data.readUInt8(i++);
          length += byte << 8;
          byte = data.readUInt8(i++);
          length += byte;

          if ( tolarge ) {
            console.error("Payload too large.");
            this.socket.end();
            return;
          }
        }
        this.length = length;
        this.buffer = new Buffer(this.length);
        this.bufferPos = 0;
        this.needed = this.length;
        this.framing = false;

        if ( this.mask ) {
          this.masking_key = [];
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
          byte = data.readUInt8(i++);
          this.masking_key.push(byte);
        }
      }

      var amount = Math.min(data.length - i, this.needed);
      data.copy(this.buffer, this.bufferPos, i, i + amount);

      if ( this.mask ) {
        for ( var j = this.bufferPos ; j < this.bufferPos + amount; j++ ) {
          this.buffer.writeUInt8(this.buffer.readUInt8(j) ^ this.masking_key[j % 4], j);
        }
      }

      this.bufferPos += amount;
      this.needed -= amount;
      i += amount;

      if ( this.needed == 0 ) {
        this.finished = true;
      }

      return i;
    }
  ]
});
<commit_msg>Convert WebSocket Frame reading to be a per-byte state machine for reading headers.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.node.ws',
  name: 'Frame',
  properties: [
    ['fin', 1],
    ['rsv1',0],
    ['rsv2',0],
    ['rsv3',0],
    ['opcode',1],
    ['mask',0],
    'maskingKey',
    'buffer',
    ['bufferPos', 0],
    ['needed', 0],
    {
      name: 'headerState',
      postSet: function(old, f) {
        if ( typeof f !== 'function' ) {
          console.log("Set from", old, "to", f);
        }
      }
    },
    'state',
    ['length_', 0],
    {
      type: 'Boolean',
      name: 'framing',
      defaultValue: true
    },
    {
      type: 'Boolean',
      name: 'finished',
      defaultValue: false
    },
  ],
  methods: [
    function init() {
      console.log("Setting header state");
      this.headerState = this.frameHeader;
      console.log("Set to", this.headerState);
      this.state = this.readHeader;
    },
    function toData() {
      this.length = this.buffer.length;
      var headerSize = this.buffer.length > 65535 ? 10 :
          (this.buffer.length > 125 ? 4 : 2);

      var i = 0;
      var buffer = new Buffer(this.buffer.length + headerSize);
      // FIN = 1, RSV1-3 = 0
      buffer.writeUInt8(
        0x80 +
          this.opcode, i++);

      var length = this.length;
      if ( length > 0xffffffff ) {
        console.error("Too large a frame to support in JS");
      } else if ( length > 65535 ) {
        buffer.writeUInt8(127, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8(0, i++);
        buffer.writeUInt8((length >> 24) & 0xff, i++)
        buffer.writeUInt8((length >> 16) & 0xff, i++)
        buffer.writeUInt8((length >> 8) & 0xff, i++)
        buffer.writeUInt8((length & 0xff), i++)
      } else if ( length > 125 ) {
        buffer.writeUInt8(126, i++);
        buffer.writeUInt8((length & 0xff00) >> 8, i++)
        buffer.writeUInt8(length & 0xff, i++)
      } else {
        buffer.writeUInt8(length & 0x7f, i++);
      }

      this.buffer.copy(buffer, i);

      return buffer;
    },

    function frameHeader(byte) {
      this.opcode = byte & 0x0f;
      this.fin = !! ( byte & 0x80 );
      this.rsv1 = !! ( byte & 0x40 );
      this.rsv2  = !! ( byte & 0x20 );
      this.rsv3 = !! ( byte & 0x10 );

      this.headerState = this.maskLength0;
    },

    function maskLength0(byte) {
      this.mask = !! ( byte & 0x80 );
      this.length_ = byte & 0x7f;

      if ( this.length_ == 126 ) {
        this.headerState = this.lengthShort0;
      } else if ( this.length_ === 127 ) {
        this.headerState = this.lengthShort1;
      } else {
        this.headerState = this.maskingKey0;
      }
    },

    function lengthShort0(byte) {
      this.length_ = 0;
      this.length_ += byte << 8;
      this.headerState = this.lengthShort1;
    },

    function lengthShort1(byte) {
      this.length_ += byte;
      this.headerState = this.maskingKey0;
    },

    function lengthLong0(byte) {
      this.length_ = 0;
      if ( byte !== 0 ) this.state = this.tooLarge;
      this.headerState = this.lengthLong1;
    },

    function lengthLong1(byte) {
      if ( byte !== 0 ) this.state = this.tooLarge;
      this.headerState = this.lengthLong2;
    },

    function lengthLong2(byte) {
      if ( byte !== 0 ) this.state = this.tooLarge;
      this.headerState = this.lengthLong3;
    },

    function lengthLong3(byte) {
      if ( byte !== 0 ) this.state = this.tooLarge;
      this.headerState = this.lengthLong4;
    },

    function lengthLong4(byte) {
      this.length_ += byte << 24;
      this.headerState = this.lengthLong5;
    },

    function lengthLong5(byte) {
      this.length_ += byte << 16;
      this.headerState = this.lengthLong6;
    },

    function lengthLong6(byte) {
      this.length_ += byte << 8;
      this.headerState = this.lengthLong7;
    },

    function lengthLong7(byte) {
      this.length_ += byte;
      this.headerState = this.maskingKey0;
    },

    function maskingKey0(byte) {
      this.length = this.length_
      this.buffer = new Buffer(this.length);
      this.bufferPos = 0;
      this.needed = this.length;

      if ( this.mask ) {
        this.masking_key = [];
        this.masking_key.push(byte);
        this.headerState = this.maskingKey1;
      } else {
        this.headerState = this.frameHeader;
        this.state = this.readData;
      }
    },

    function maskingKey1(byte) {
      this.masking_key.push(byte);
      this.headerState = this.maskingKey2;
    },

    function maskingKey2(byte) {
      this.masking_key.push(byte);
      this.headerState = this.maskingKey3;
    },

    function maskingKey3(byte) {
      this.masking_key.push(byte);
      this.headerState = this.frameHeader;
      this.state = this.readData;
    },

    function readHeader(data, i) {
      while ( this.state === this.readHeader &&
              i < data.byteLength ) {
        this.headerState(data.readUInt8(i++));
      }
      return i;
    },

    function readData(data, i) {
      var amount = Math.min(data.length - i, this.needed);
      data.copy(this.buffer, this.bufferPos, i, i + amount);

      if ( this.mask ) {
        for ( var j = this.bufferPos ; j < this.bufferPos + amount; j++ ) {
          this.buffer.writeUInt8(this.buffer.readUInt8(j) ^ this.masking_key[j % 4], j);
        }
      }

      this.bufferPos += amount;
      this.needed -= amount;
      i += amount;

      if ( this.needed == 0 ) {
        this.finished = true;
      }

      return i;
    },

    function tooLarge(data, i) {
      console.error('WebSocket payload too large');
      this.socket.end();
    },

    function onData(data, i) {
      return this.state(data, i);
    }
  ]
});
<|endoftext|>


<commit_before>var App = require('app');

App.Channel = Ember.Object.extend({
  sound: 'kick',
  volume: 1,
  pan: 1,
  steps: Em.A(),
  stepCount: function() {
    return this.get('steps').length;
  }.property('steps.length'),
  addStep: function(data) {
    this.get('steps').pushObject(
      App.Step.create(data)
    );
  },
  addSteps: function(n) {
    for(var i=0; i<n; i++) {
      this.addStep();
    }
  },
  lastUpdated: function() {
    return new Date();
  }.property('sound', 'steps.length', 'steps.@each.lastUpdated'),
  serialize: function() {
    return {
      sound: this.get('sound'),
      volume: this.get('volume'),
      steps: this.get('steps').invoke('serialize')
    };
  }
}).reopenClass({
  sounds: [
    'cowbell',
    'conga_hi',
    'cymbal',
    'conga_mid',
    'conga_low',
    'hihat_open',
    'tom_hi',
    'maracas',
    'tom_mid',
    'hihat_closed',
    'tom_low',
    'clave',
    'clap',
    'snare',
    'rim',
    'kick'
  ],
  previous: function(sound) {
    var index = this.sounds.indexOf(sound);
    if(index === 0) {
      index = this.sounds.length;
    }
    return this.sounds[index - 1];
  },
  next: function(sound) {
    var index = this.sounds.indexOf(sound);
    if(index == this.sounds.length - 1) {
      index = -1;
    }
    return this.sounds[index + 1];
  },
  deserialiseArray: function(data) {
    return Em.A(
      data.map(function(item) {
        return App.Channel.create({
          sound: item.sound,
          volume: item.volume,
          steps: App.Step.deserialiseArray(item.steps)
        });
      })
    );
  }
});
<commit_msg>fixed issue where adding channels would result in the steps being shared between channels<commit_after>var App = require('app');

App.Channel = Ember.Object.extend({
  sound: 'kick',
  volume: 1,
  pan: 1,
  steps: null,
  init: function() {
    this._super();
    if(this.get('steps') === null) {
      this.set('steps', Em.A());
    }
  },
  stepCount: function() {
    return this.get('steps').length;
  }.property('steps.length'),
  addStep: function(data) {
    this.get('steps').pushObject(
      App.Step.create(data)
    );
  },
  addSteps: function(n) {
    for(var i=0; i<n; i++) {
      this.addStep();
    }
  },
  lastUpdated: function() {
    return new Date();
  }.property('sound', 'steps.length', 'steps.@each.lastUpdated'),
  serialize: function() {
    return {
      sound: this.get('sound'),
      volume: this.get('volume'),
      steps: this.get('steps').invoke('serialize')
    };
  }
}).reopenClass({
  sounds: [
    'cowbell',
    'conga_hi',
    'cymbal',
    'conga_mid',
    'conga_low',
    'hihat_open',
    'tom_hi',
    'maracas',
    'tom_mid',
    'hihat_closed',
    'tom_low',
    'clave',
    'clap',
    'snare',
    'rim',
    'kick'
  ],
  previous: function(sound) {
    var index = this.sounds.indexOf(sound);
    if(index === 0) {
      index = this.sounds.length;
    }
    return this.sounds[index - 1];
  },
  next: function(sound) {
    var index = this.sounds.indexOf(sound);
    if(index == this.sounds.length - 1) {
      index = -1;
    }
    return this.sounds[index + 1];
  },
  deserialiseArray: function(data) {
    return Em.A(
      data.map(function(item) {
        return App.Channel.create({
          sound: item.sound,
          volume: item.volume,
          steps: App.Step.deserialiseArray(item.steps)
        });
      })
    );
  }
});
<|endoftext|>


<commit_before>/*
 ************************************************************************
 *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
 **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
 *
 *  (c) 2009.                            (c) 2009.
 *  Government of Canada                 Gouvernement du Canada
 *  National Research Council            Conseil national de recherches
 *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
 *  All rights reserved                  Tous droits réservés
 *
 *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
 *  expressed, implied, or               énoncée, implicite ou légale,
 *  statutory, of any kind with          de quelque nature que ce
 *  respect to the software,             soit, concernant le logiciel,
 *  including without limitation         y compris sans restriction
 *  any warranty of merchantability      toute garantie de valeur
 *  or fitness for a particular          marchande ou de pertinence
 *  purpose. NRC shall not be            pour un usage particulier.
 *  liable in any event for any          Le CNRC ne pourra en aucun cas
 *  damages, whether direct or           être tenu responsable de tout
 *  indirect, special or general,        dommage, direct ou indirect,
 *  consequential or incidental,         particulier ou général,
 *  arising from the use of the          accessoire ou fortuit, résultant
 *  software.  Neither the name          de l'utilisation du logiciel. Ni
 *  of the National Research             le nom du Conseil National de
 *  Council of Canada nor the            Recherches du Canada ni les noms
 *  names of its contributors may        de ses  participants ne peuvent
 *  be used to endorse or promote        être utilisés pour approuver ou
 *  products derived from this           promouvoir les produits dérivés
 *  software without specific prior      de ce logiciel sans autorisation
 *  written permission.                  préalable et particulière
 *                                       par écrit.
 *
 *  This file is part of the             Ce fichier fait partie du projet
 *  OpenCADC project.                    OpenCADC.
 *
 *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
 *  you can redistribute it and/or       vous pouvez le redistribuer ou le
 *  modify it under the terms of         modifier suivant les termes de
 *  the GNU Affero General Public        la “GNU Affero General Public
 *  License as published by the          License” telle que publiée
 *  Free Software Foundation,            par la Free Software Foundation
 *  either version 3 of the              : soit la version 3 de cette
 *  License, or (at your option)         licence, soit (à votre gré)
 *  any later version.                   toute version ultérieure.
 *
 *  OpenCADC is distributed in the       OpenCADC est distribué
 *  hope that it will be useful,         dans l’espoir qu’il vous
 *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
 *  without even the implied             GARANTIE : sans même la garantie
 *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
 *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
 *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
 *  General Public License for           Générale Publique GNU Affero
 *  more details.                        pour plus de détails.
 *
 *  You should have received             Vous devriez avoir reçu une
 *  a copy of the GNU Affero             copie de la Licence Générale
 *  General Public License along         Publique GNU Affero avec
 *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
 *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
 *                                       <http://www.gnu.org/licenses/>.
 *
 *  $Revision: 4 $
 *
 ************************************************************************
 */
(function ($)
{
  // register namespace
  $.extend(true, window, {
    "cadc": {
      "web": {
        "util": {
          "StringUtil": StringUtil,
          "NumberFormat": NumberFormat
        }
      }
    }
  });

  /**
   * Basic String utility class.
   *
   * @param _string
   * @constructor
   */
  function StringUtil(_string)
  {
    var _selfStringUtil = this;

    this.string = function ()
    {
      if (_string === Number(0))
      {
        // want a string with content 0, not the JavaScript boolean default
        return "0";
      }
      else
      {
        return  _string ? _string.toString() : "";
      }
    }();

    function getString()
    {
      return _selfStringUtil.string;
    }

    function hasLength()
    {
      var s = getString();
      return ((s != null) && (s.length > 0));
    }

    function hasText()
    {
      return hasLength() && ($.trim(getString()) != "");
    }

    function format()
    {
      // Create new string.
      var s = "" + getString();
      var args = arguments;

      return s.replace(/{(\d+)}/g, function(match, number)
      {
        return args[number] ? args[number] : match;
      });
    }

    function sanitize()
    {
      var returnValue;

      if (!getString())
      {
        returnValue = "";
      }
      else
      {
        returnValue = getString().toString().replace(/&/g, "&").
            replace(/</g, "<").replace(/>/g, ">");
      }

      return returnValue;
    }

    function matches(_regex)
    {
      return new RegExp(_regex).test(getString());
    }

    $.extend(this,
             {
               "sanitize": sanitize,
               "hasLength": hasLength,
               "hasText": hasText,
               "format": format,
               "matches": matches
             });
  }

  /**
   * Format the given number for output.
   *
   * @param _val          The value to format.
   * @param _sigDigits    The value to use for fixed or precision (Optional).
   * @constructor
   */
  function NumberFormat(_val, _sigDigits)
  {
    var _selfNumberFormat = this;

    this.numberVal = Number(_val);
    this.significantDigits = _sigDigits || 0;

    function getNumberValue()
    {
      return _selfNumberFormat.numberVal;
    }

    function getSignificantDigits()
    {
      return _selfNumberFormat.significantDigits;
    }

    /**
     * Format to fixation, meaning the number of integers after the decimal
     * place.
     * @returns {string}
     */
    function formatFixation()
    {
      return getNumberValue().toFixed(getSignificantDigits());
    }

    /**
     * Format to precision, meaning the number of characters all together.
     * @returns {string}
     */
    function formatPrecision()
    {
      return getNumberValue().toPrecision(getSignificantDigits());
    }

    /**
     * An attempt to reproduce the printf(%.g) format.
     *
     * From the sprintf man page:
     *
     * 'Style e is used if the exponent from its conversion is less than -4 or
     *  greater than or equal to the precision.  Trailing zeros are removed from
     *  the fractional part of the result; a decimal point appears only if it
     *  is followed by at least one digit.'
     *
     * jenkinsd 2013.12.20
     *
     * @returns {string}
     */
    function formatExponentOrFloat()
    {
      var output;
      var num = getNumberValue();
      var exponentialVal = num.toExponential(getSignificantDigits());

      var exponent = num.toExponential().split('+')[1];

      if ((exponent < -4) || (exponent >= getSignificantDigits()))
      {
        output = exponentialVal;
      }
      else
      {
        output = formatFixation();
      }

      return output;
    }

    $.extend(this,
             {
               "format": formatFixation,
               "formatFixation": formatFixation,
               "formatPrecision": formatPrecision,
               "formatExponentOrFloat": formatExponentOrFloat
             });
  }
})(jQuery);
<commit_msg>Story 1814: Added default formatter.<commit_after>/*
 ************************************************************************
 *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
 **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
 *
 *  (c) 2009.                            (c) 2009.
 *  Government of Canada                 Gouvernement du Canada
 *  National Research Council            Conseil national de recherches
 *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
 *  All rights reserved                  Tous droits réservés
 *
 *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
 *  expressed, implied, or               énoncée, implicite ou légale,
 *  statutory, of any kind with          de quelque nature que ce
 *  respect to the software,             soit, concernant le logiciel,
 *  including without limitation         y compris sans restriction
 *  any warranty of merchantability      toute garantie de valeur
 *  or fitness for a particular          marchande ou de pertinence
 *  purpose. NRC shall not be            pour un usage particulier.
 *  liable in any event for any          Le CNRC ne pourra en aucun cas
 *  damages, whether direct or           être tenu responsable de tout
 *  indirect, special or general,        dommage, direct ou indirect,
 *  consequential or incidental,         particulier ou général,
 *  arising from the use of the          accessoire ou fortuit, résultant
 *  software.  Neither the name          de l'utilisation du logiciel. Ni
 *  of the National Research             le nom du Conseil National de
 *  Council of Canada nor the            Recherches du Canada ni les noms
 *  names of its contributors may        de ses  participants ne peuvent
 *  be used to endorse or promote        être utilisés pour approuver ou
 *  products derived from this           promouvoir les produits dérivés
 *  software without specific prior      de ce logiciel sans autorisation
 *  written permission.                  préalable et particulière
 *                                       par écrit.
 *
 *  This file is part of the             Ce fichier fait partie du projet
 *  OpenCADC project.                    OpenCADC.
 *
 *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
 *  you can redistribute it and/or       vous pouvez le redistribuer ou le
 *  modify it under the terms of         modifier suivant les termes de
 *  the GNU Affero General Public        la “GNU Affero General Public
 *  License as published by the          License” telle que publiée
 *  Free Software Foundation,            par la Free Software Foundation
 *  either version 3 of the              : soit la version 3 de cette
 *  License, or (at your option)         licence, soit (à votre gré)
 *  any later version.                   toute version ultérieure.
 *
 *  OpenCADC is distributed in the       OpenCADC est distribué
 *  hope that it will be useful,         dans l’espoir qu’il vous
 *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
 *  without even the implied             GARANTIE : sans même la garantie
 *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
 *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
 *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
 *  General Public License for           Générale Publique GNU Affero
 *  more details.                        pour plus de détails.
 *
 *  You should have received             Vous devriez avoir reçu une
 *  a copy of the GNU Affero             copie de la Licence Générale
 *  General Public License along         Publique GNU Affero avec
 *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
 *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
 *                                       <http://www.gnu.org/licenses/>.
 *
 *  $Revision: 4 $
 *
 ************************************************************************
 */
(function ($)
{
  // register namespace
  $.extend(true, window, {
    "cadc": {
      "web": {
        "util": {
          "StringUtil": StringUtil,
          "NumberFormat": NumberFormat
        }
      }
    }
  });

  /**
   * Basic String utility class.
   *
   * @param _string
   * @constructor
   */
  function StringUtil(_string)
  {
    var _selfStringUtil = this;

    this.string = function ()
    {
      if (_string === Number(0))
      {
        // want a string with content 0, not the JavaScript boolean default
        return "0";
      }
      else
      {
        return  _string ? _string.toString() : "";
      }
    }();

    function getString()
    {
      return _selfStringUtil.string;
    }

    function hasLength()
    {
      var s = getString();
      return ((s != null) && (s.length > 0));
    }

    function hasText()
    {
      return hasLength() && ($.trim(getString()) != "");
    }

    function format()
    {
      // Create new string.
      var s = "" + getString();
      var args = arguments;

      return s.replace(/{(\d+)}/g, function(match, number)
      {
        return args[number] ? args[number] : match;
      });
    }

    function sanitize()
    {
      var returnValue;

      if (!getString())
      {
        returnValue = "";
      }
      else
      {
        returnValue = getString().toString().replace(/&/g, "&").
            replace(/</g, "<").replace(/>/g, ">");
      }

      return returnValue;
    }

    function matches(_regex)
    {
      return new RegExp(_regex).test(getString());
    }

    $.extend(this,
             {
               "sanitize": sanitize,
               "hasLength": hasLength,
               "hasText": hasText,
               "format": format,
               "matches": matches
             });
  }

  /**
   * Format the given number for output.
   *
   * @param _val          The value to format.
   * @param _sigDigits    The value to use for fixed or precision (Optional).
   * @constructor
   */
  function NumberFormat(_val, _sigDigits)
  {
    var _selfNumberFormat = this;

    this.numberVal = Number(_val);
    this.significantDigits = _sigDigits || 0;

    function getNumberValue()
    {
      return _selfNumberFormat.numberVal;
    }

    function getSignificantDigits()
    {
      return _selfNumberFormat.significantDigits;
    }

    /**
     * Format to fixation, meaning the number of integers after the decimal
     * place.
     * @returns {string}
     */
    function formatFixation()
    {
      return getNumberValue().toFixed(getSignificantDigits());
    }

    /**
     * Format to precision, meaning the number of characters all together.
     * @returns {string}
     */
    function formatPrecision()
    {
      return getNumberValue().toPrecision(getSignificantDigits());
    }

    /**
     * An attempt to reproduce the printf(%.g) format.
     *
     * From the sprintf man page:
     *
     * 'Style e is used if the exponent from its conversion is less than -4 or
     *  greater than or equal to the precision.  Trailing zeros are removed from
     *  the fractional part of the result; a decimal point appears only if it
     *  is followed by at least one digit.'
     *
     * jenkinsd 2013.12.20
     *
     * @returns {string}
     */
    function formatExponentOrFloat()
    {
      var output;
      var num = getNumberValue();
      var exponentialVal = num.toExponential(getSignificantDigits());

      var exponent = num.toExponential().split('+')[1];

      if ((exponent < -4) || (exponent >= getSignificantDigits()))
      {
        output = exponentialVal;
      }
      else
      {
        output = formatFixation();
      }

      return output;
    }

    /**
     * Default format function.
     *
     * @return {string}
     */
    function format()
    {
      var formattedValue;

      if (getSignificantDigits())
      {
        formattedValue = formatFixation();
      }
      else
      {
        formattedValue = getNumberValue().toString();
      }

      return formattedValue;
    }

    $.extend(this,
             {
               "format": format,
               "formatFixation": formatFixation,
               "formatPrecision": formatPrecision,
               "formatExponentOrFloat": formatExponentOrFloat
             });
  }
})(jQuery);
<|endoftext|>


<commit_before>(function(){

var GenericActivityItemView = Backbone.Marionette.ItemView.extend({
  tagName:  "div",
  template: "activities/_generic_activity"
});

var CreatedChannelView = GenericActivityItemView.extend({
  template: "activities/_created_channel"
});
var AddedSubchannelView = GenericActivityItemView.extend({
  template: "activities/_added_subchannel"
});

var GenericActivityFactItemView = Backbone.Marionette.CompositeView.extend({
  itemView: FactView,

  initialize: function(opts){
    var fact = new Fact(this.model.get('activity')['fact']);
    this.collection = new Backbone.Collection([fact]);
  },

  appendHtml: function(collectionView, itemView) {
    this.$el.append(itemView.el);
  }

});

var AddedEvidenceView = GenericActivityFactItemView.extend({
  template: "activities/_added_evidence"
});

var AddedOpinionView = GenericActivityFactItemView.extend({
  template: "activities/_added_opinion"
});
var AddedFactToChannelView = GenericActivityFactItemView.extend({
  template: "activities/_added_fact_to_channel"
});

window.ActivityItemView = function(opts) {
  switch (opts.model.get("action")) {
    case "added_supporting_evidence":
    case "added_weakening_evidence":
      return new AddedEvidenceView(opts);

    case "created_channel":
      return new CreatedChannelView(opts);

    case "added_subchannel":
      return new AddedSubchannelView(opts);

    case "believes":
    case "doubts":
    case "disbelieves":
      return new AddedOpinionView(opts);

    case "added_fact_to_channel":
      return new AddedFactToChannelView(opts);

    default:
      return new GenericActivityItemView(opts);
  }
};

}());
<commit_msg>removed own implementation of default behaviour<commit_after>(function(){

var GenericActivityItemView = Backbone.Marionette.ItemView.extend({
  tagName:  "div",
  template: "activities/_generic_activity"
});

var CreatedChannelView = GenericActivityItemView.extend({
  template: "activities/_created_channel"
});
var AddedSubchannelView = GenericActivityItemView.extend({
  template: "activities/_added_subchannel"
});

var GenericActivityFactItemView = Backbone.Marionette.CompositeView.extend({
  itemView: FactView,

  initialize: function(opts){
    var fact = new Fact(this.model.get('activity')['fact']);
    this.collection = new Backbone.Collection([fact]);
  }

});

var AddedEvidenceView = GenericActivityFactItemView.extend({
  template: "activities/_added_evidence"
});

var AddedOpinionView = GenericActivityFactItemView.extend({
  template: "activities/_added_opinion"
});
var AddedFactToChannelView = GenericActivityFactItemView.extend({
  template: "activities/_added_fact_to_channel"
});

window.ActivityItemView = function(opts) {
  switch (opts.model.get("action")) {
    case "added_supporting_evidence":
    case "added_weakening_evidence":
      return new AddedEvidenceView(opts);

    case "created_channel":
      return new CreatedChannelView(opts);

    case "added_subchannel":
      return new AddedSubchannelView(opts);

    case "believes":
    case "doubts":
    case "disbelieves":
      return new AddedOpinionView(opts);

    case "added_fact_to_channel":
      return new AddedFactToChannelView(opts);

    default:
      return new GenericActivityItemView(opts);
  }
};

}());
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import {withRouter, Link} from 'react-router'
import {connect} from 'react-redux'

import {MEMBER_ROLE} from 'src/auth/Authorized'

const getRoleNameByOrgID = (id, roles) => {
  const role = roles.find(r => r.organization === id)
  return (role && role.name) || 'ghost'
}

const Purgatory = ({
  router,
  name,
  provider,
  scheme,
  currentOrganization,
  roles,
  organizations,
}) => {
  const rolesAndOrgs = organizations.map(({id, name: orgName}) => ({
    id,
    organization: orgName,
    role: getRoleNameByOrgID(id, roles),
    currentOrganization: id === currentOrganization.id,
  }))

  const subHeading =
    rolesAndOrgs.length === 1
      ? 'Authenticated in 1 Organization'
      : `Authenticated in ${rolesAndOrgs.length} Organizations`

  const loginLink = () => {
    return router.push('')
  }

  return (

              {name}

              {subHeading}{' '}

                {scheme}/{provider}

            {rolesAndOrgs.length
              ?

                  {rolesAndOrgs.map(rag =>

                          {rag.organization}

                          {rag.role}

                      {rag.role === MEMBER_ROLE
                        ?
                            Contact your Admin
for access

                        : <button
                            className="btn btn-sm btn-primary"
                            onClick={loginLink}
                          >
                            Login
                          </button>}

                  )}

              :
You are a Lost Soul
}
            <Link to={'/logout'} className="btn btn-sm btn-link auth--logout">
              Logout
            </Link>

          Made by InfluxData

  )
}

const {arrayOf, func, shape, string} = PropTypes

Purgatory.propTypes = {
  router: shape({
    push: func.isRequired,
  }).isRequired,
  name: string.isRequired,
  provider: string.isRequired,
  scheme: string.isRequired,
  currentOrganization: shape({
    id: string.isRequired,
    name: string.isRequired,
  }).isRequired,
  roles: arrayOf(
    shape({
      name: string,
      organization: string,
    })
  ).isRequired,
  organizations: arrayOf(
    shape({
      id: string,
      name: string,
    })
  ).isRequired,
}

const mapStateToProps = ({
  auth: {
    me: {name, provider, scheme, currentOrganization, roles, organizations},
  },
}) => ({
  name,
  provider,
  scheme,
  currentOrganization,
  roles,
  organizations,
})

export default connect(mapStateToProps)(withRouter(Purgatory))
<commit_msg>Wire up logout link in Purgatory<commit_after>import React, {PropTypes} from 'react'
import {withRouter} from 'react-router'
import {connect} from 'react-redux'

import {MEMBER_ROLE} from 'src/auth/Authorized'

const getRoleNameByOrgID = (id, roles) => {
  const role = roles.find(r => r.organization === id)
  return (role && role.name) || 'ghost'
}

const Purgatory = ({
  router,
  name,
  provider,
  scheme,
  currentOrganization,
  roles,
  organizations,
  logoutLink,
}) => {
  const rolesAndOrgs = organizations.map(({id, name: orgName}) => ({
    id,
    organization: orgName,
    role: getRoleNameByOrgID(id, roles),
    currentOrganization: id === currentOrganization.id,
  }))

  const subHeading =
    rolesAndOrgs.length === 1
      ? 'Authenticated in 1 Organization'
      : `Authenticated in ${rolesAndOrgs.length} Organizations`

  const loginLink = () => {
    return router.push('')
  }

  return (

              {name}

              {subHeading}{' '}

                {scheme}/{provider}

            {rolesAndOrgs.length
              ?

                  {rolesAndOrgs.map(rag =>

                          {rag.organization}

                          {rag.role}

                      {rag.role === MEMBER_ROLE
                        ?
                            Contact your Admin
for access

                        : <button
                            className="btn btn-sm btn-primary"
                            onClick={loginLink}
                          >
                            Login
                          </button>}

                  )}

              :
You are a Lost Soul
}

              Logout

          Made by InfluxData

  )
}

const {arrayOf, func, shape, string} = PropTypes

Purgatory.propTypes = {
  router: shape({
    push: func.isRequired,
  }).isRequired,
  name: string.isRequired,
  provider: string.isRequired,
  scheme: string.isRequired,
  currentOrganization: shape({
    id: string.isRequired,
    name: string.isRequired,
  }).isRequired,
  roles: arrayOf(
    shape({
      name: string,
      organization: string,
    })
  ).isRequired,
  organizations: arrayOf(
    shape({
      id: string,
      name: string,
    })
  ).isRequired,
  logoutLink: string.isRequired,
}

const mapStateToProps = ({
  auth: {
    me: {name, provider, scheme, currentOrganization, roles, organizations},
    logoutLink,
  },
}) => ({
  name,
  provider,
  scheme,
  currentOrganization,
  roles,
  organizations,
  logoutLink,
})

export default connect(mapStateToProps)(withRouter(Purgatory))
<|endoftext|>