<commit_before>/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.
 */
$(function() {

    'use strict';

    if (typeof(Isis) === 'object' && typeof(Isis.openInNewTab) === 'function') {
        return;
    }

    window.Isis = {
        Topic: {
            OPEN_IN_NEW_TAB: 'openInNewTab',
            OPEN_SELECT2: 'openSelect2',
            CLOSE_SELECT2: 'closeSelect2',
            FOCUS_FIRST_PARAMETER: 'focusFirstParameter',
            FOCUS_FIRST_PROPERTY: 'focusFirstProperty'
        },
        copyModalShown: false
    };

    /**
     * taken from http://www.minimit.com/articles/solutions-tutorials/vertical-center-bootstrap-3-modals
     */
    function centerModals() {
        $('.modal').each(function() {
            var $clone = $(this).clone().css('display', 'block').appendTo('body');
            var top = Math.round(($clone.height() - $clone.find('.modal-content').height()) / 2);
            top = top > 0 ? top : 0;
            $clone.remove();
            $(this).find('.modal-content').css("margin-top", top);
        });
    }
    $(document, '.modal').on('show.bs.modal', centerModals);
    $(window).on('resize', centerModals);

    var isisVeilTimeoutId;

    var isisShowVeil = function() {
        if(isisVeilTimeoutId) {
            clearTimeout(isisVeilTimeoutId);
            isisVeilTimeoutId = null;
        }
        $("#veil").show();
    };

    var isisFadeInVeil = function(attributes, jqxhr, settings) {
        // use timeouts because JQuery's delay(...) cannot be stopped.
        if(isisVeilTimeoutId) {
            // already queued a fade-in
            return;
        }
        isisVeilTimeoutId = setTimeout(function() {
            $("#veil").fadeIn(750);
        }, 250);

    };

    var isisHideVeil = function() {
        if(isisVeilTimeoutId) {
            clearTimeout(isisVeilTimeoutId);
            isisVeilTimeoutId = null;
        }
        $("#veil").stop().hide();
    };

    Wicket.Event.subscribe(Isis.Topic.OPEN_IN_NEW_TAB, function(jqEvent, url) {
        var win=window.open(url, '_blank');
        if(win) { win.focus(); }
    });

    Wicket.Event.subscribe(Isis.Topic.OPEN_SELECT2, function(jqEvent, panelId) {
        setTimeout(function() {
            var $panel = $('#'+panelId);
             console.log($panel);
            $($panel).find('select').select2('open');
//            $($panel).find('select').filter(':visible:first').focus();
        }, 0);
    });

    Wicket.Event.subscribe(Isis.Topic.CLOSE_SELECT2, function(jqEvent, panelId) {
        setTimeout(function() {
            var $panel = $('#'+panelId);
             console.log($panel);
            $($panel).find('select').select2('close');
            $($panel).find('select').filter(':visible:first').focus();
        }, 0);
    });

    Wicket.Event.subscribe(Isis.Topic.FOCUS_FIRST_PARAMETER, function(jqEvent, elementId) {
        setTimeout(function() {
            let el = $('#'+elementId).find('.inputFormTable.parameters').find('input,textarea,div.cbx,select').filter(':visible:first');
            let x = $(el).prop('nodeName').toLowerCase()
            if(x === 'input') {
                el.focus();
            } else {
                el.select2('open').select2('close');
            }
        }, 0);
    });

    Wicket.Event.subscribe(Isis.Topic.FOCUS_FIRST_PROPERTY, function(jqEvent, elementId) {
        setTimeout(function() {
            if(elementId) {
                $("#" + elementId).find('a.scalarValueInlinePromptLink').filter(':visible:first').focus();
            } else {
                $(document).find('a.scalarValueInlinePromptLink').filter(':visible:first').focus();
            }
        }, 0);
    });

    /* for modal dialogs */
    Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_BEFORE_SEND, function(jqEvent, attributes, jqXHR, settings) {
        if (attributes.c !== window && !$('#'+attributes.c).hasClass('noVeil')) {
            isisFadeInVeil(attributes, jqXHR, settings);
        }
    });

    Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_COMPLETE, function(jqEvent, attributes, jqXHR, status) {
        isisHideVeil(/*attributes, jqXHR, status*/);
    });

    /* only seem to work in non-modal situation */
    $('.buttons .okButton:not(.noVeil)').click(isisFadeInVeil);
    $('.buttons .ok:not(.noVeil)').click(isisFadeInVeil);
    $('.cssSubMenuItemsPanel .cssSubMenuItem a:not(.noVeil)').click(isisFadeInVeil);

    $('div.collectionContentsAsAjaxTablePanel > table.contents > tbody > tr.reloaded-after-concurrency-exception')
        .livequery(function(){
            var x = $(this);
            $(this).animate({ "backgroundColor": "#FFF" }, 1000, "linear", function() {
                $(x).css('background-color','').removeClass("reloaded-after-concurrency-exception");
            });
        });

    /**
     * Show/hide the CopyLink modal window with alt+]
     */
    $('body').keydown(function(e) {
        if (e.which === 221 && e.altKey) {
            if (Isis.copyModalShown) {
                $('.copyModal').modal('hide');
                $('.modal-backdrop').remove();
                Isis.copyModalShown = false;
            }
            else {
                Isis.copyModalShown = true;
                $('.copyLink').click();
            }
        }
    });

    /**
     * Show/Hide the copy link only when hovering the entity icon and title
     */
    $('.entityIconTitleAndCopylink').hover(
        function() {
            $('.copyLink').css({visibility: 'visible'});
        },
        function() {
            $('.copyLink').css({visibility: 'hidden'});
        }
    );

    $('#menu-toggle').click(function(e) {
        e.preventDefault();
        $('#wrapper').toggleClass('toggled');
    });

    $('#sidebar-wrapper').keyup(function(e) {
        if (e.keyCode == 27) {
           $('#wrapper').addClass('toggled');
        }
    });

	/* force hide popover on button click */

	let hidePopover = function (e) {
	    $(e.target).closest('[rel="popover"]').each(function () {
	   		$(this).popover('hide');
	    });
	}

	$(document, '.isis-component-with-tooltip').on('click', hidePopover);

/*
    $('.editing .editable').parent().hover(function () {
        var inputEl = $(this).find("> .editable")
        $(inputEl).css({cursor: 'pointer'})
    },function () {
        var inputEl = $(this).find("> .editable")
        $(inputEl).css({cursor: 'auto'})
    });
*/

    /*
    Adapted from https://bootstrap-menu.com/detail-basic-hover.html
    Ignoring mouseleave events if hovering over a popover that belongs to a menuitem.
    The magic number in the window width predicate corresponds to the nav-bar collaps behavior
    as used in Footer/HeaderPanel.html templates; see Bootstrap 4 ref. ...
        navbar-expand = never collapses vertically (remains horizontal)
        navbar-expand-sm = collapses below sm widths <576px
        navbar-expand-md = collapses below md widths <768px
        navbar-expand-lg = collapses below lg widths <992px
        navbar-expand-xl = collapses below xl widths <1200px
	*/
	document.querySelectorAll('.navbar .nav-item, div.additionalLinkList').forEach(function(everyitem){
		/*
		   disabled for additional-action-links,
           as it currently does not work consistently eg. with AJAX requests

		   let autoShowSelector = 'a[data-toggle], button[data-toggle]';
		*/
		let autoShowSelector = 'a[data-toggle]';

		everyitem.addEventListener('mouseover', function(e){
			if(window.innerWidth<768){
				return; // when collapsed is a no-op
			}
			this.querySelectorAll(autoShowSelector)
			.forEach(function(el_link){
				let nextEl = el_link.nextElementSibling;
				el_link.classList.add('show');
				nextEl.classList.add('show');
			})
		});

		everyitem.addEventListener('mouseleave', function(e){
			if(window.innerWidth<768){
				return; // when collapsed is a no-op
			}
			// do not hide the dropdown if hovering over a popover (tooltip) attached to the dropdown item
			// The MouseEvent.relatedTarget read-only property is the secondary target for the mouse event,
			// if there is one: That is, the EventTarget the pointing device entered to.
			let relatedTarget = $(e.relatedTarget);
			if(relatedTarget.hasClass('popover')
					|| relatedTarget.hasClass('popover-body') // not strictly required, just an optimization
					|| relatedTarget.parents('.popover').length>0) {
				e.preventDefault();
				return;
			}
			this.querySelectorAll(autoShowSelector)
			.forEach(function(el_link){
				let nextEl = el_link.nextElementSibling;
				el_link.classList.remove('show');
				nextEl.classList.remove('show');
			})
		});
	});

});

/**
 * enables 'maxlength' to work as an attribute on 'textarea'
 *
 * as per: see http://stackoverflow.com/questions/4459610/set-maxlength-in-html-textarea
 */
$(function() {
    $("textarea[maxlength]").bind('input propertychange', function() {
        var maxLength = $(this).attr('maxlength');
        if ($(this).val().length > maxLength) {
            $(this).val($(this).val().substring(0, maxLength));
        }
    })
});

<commit_msg>ISIS-2657: removes spurious console warning if there is no element to focus on.<commit_after>/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.
 */
$(function() {

    'use strict';

    if (typeof(Isis) === 'object' && typeof(Isis.openInNewTab) === 'function') {
        return;
    }

    window.Isis = {
        Topic: {
            OPEN_IN_NEW_TAB: 'openInNewTab',
            OPEN_SELECT2: 'openSelect2',
            CLOSE_SELECT2: 'closeSelect2',
            FOCUS_FIRST_PARAMETER: 'focusFirstParameter',
            FOCUS_FIRST_PROPERTY: 'focusFirstProperty'
        },
        copyModalShown: false
    };

    /**
     * taken from http://www.minimit.com/articles/solutions-tutorials/vertical-center-bootstrap-3-modals
     */
    function centerModals() {
        $('.modal').each(function() {
            var $clone = $(this).clone().css('display', 'block').appendTo('body');
            var top = Math.round(($clone.height() - $clone.find('.modal-content').height()) / 2);
            top = top > 0 ? top : 0;
            $clone.remove();
            $(this).find('.modal-content').css("margin-top", top);
        });
    }
    $(document, '.modal').on('show.bs.modal', centerModals);
    $(window).on('resize', centerModals);

    var isisVeilTimeoutId;

    var isisShowVeil = function() {
        if(isisVeilTimeoutId) {
            clearTimeout(isisVeilTimeoutId);
            isisVeilTimeoutId = null;
        }
        $("#veil").show();
    };

    var isisFadeInVeil = function(attributes, jqxhr, settings) {
        // use timeouts because JQuery's delay(...) cannot be stopped.
        if(isisVeilTimeoutId) {
            // already queued a fade-in
            return;
        }
        isisVeilTimeoutId = setTimeout(function() {
            $("#veil").fadeIn(750);
        }, 250);

    };

    var isisHideVeil = function() {
        if(isisVeilTimeoutId) {
            clearTimeout(isisVeilTimeoutId);
            isisVeilTimeoutId = null;
        }
        $("#veil").stop().hide();
    };

    Wicket.Event.subscribe(Isis.Topic.OPEN_IN_NEW_TAB, function(jqEvent, url) {
        var win=window.open(url, '_blank');
        if(win) { win.focus(); }
    });

    Wicket.Event.subscribe(Isis.Topic.OPEN_SELECT2, function(jqEvent, panelId) {
        setTimeout(function() {
            var $panel = $('#'+panelId);
             console.log($panel);
            $($panel).find('select').select2('open');
//            $($panel).find('select').filter(':visible:first').focus();
        }, 0);
    });

    Wicket.Event.subscribe(Isis.Topic.CLOSE_SELECT2, function(jqEvent, panelId) {
        setTimeout(function() {
            var $panel = $('#'+panelId);
             console.log($panel);
            $($panel).find('select').select2('close');
            $($panel).find('select').filter(':visible:first').focus();
        }, 0);
    });

    Wicket.Event.subscribe(Isis.Topic.FOCUS_FIRST_PARAMETER, function(jqEvent, elementId) {
        setTimeout(function() {
            let el = $('#'+elementId).find('.inputFormTable.parameters').find('input,textarea,div.cbx,select').filter(':visible:first');
            if(el) {
                let elNodeName = $(el).prop('nodeName')
                if (elNodeName) {
                    let x = elNodeName.toLowerCase()
                    if(x === 'input') {
                        el.focus();
                    } else {
                        el.select2('open').select2('close');
                    }
                } else {
                    // fallback
                    el.focus();
                }
            }
        }, 0);
    });

    Wicket.Event.subscribe(Isis.Topic.FOCUS_FIRST_PROPERTY, function(jqEvent, elementId) {
        setTimeout(function() {
            if(elementId) {
                $("#" + elementId).find('a.scalarValueInlinePromptLink').filter(':visible:first').focus();
            } else {
                $(document).find('a.scalarValueInlinePromptLink').filter(':visible:first').focus();
            }
        }, 0);
    });

    /* for modal dialogs */
    Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_BEFORE_SEND, function(jqEvent, attributes, jqXHR, settings) {
        if (attributes.c !== window && !$('#'+attributes.c).hasClass('noVeil')) {
            isisFadeInVeil(attributes, jqXHR, settings);
        }
    });

    Wicket.Event.subscribe(Wicket.Event.Topic.AJAX_CALL_COMPLETE, function(jqEvent, attributes, jqXHR, status) {
        isisHideVeil(/*attributes, jqXHR, status*/);
    });

    /* only seem to work in non-modal situation */
    $('.buttons .okButton:not(.noVeil)').click(isisFadeInVeil);
    $('.buttons .ok:not(.noVeil)').click(isisFadeInVeil);
    $('.cssSubMenuItemsPanel .cssSubMenuItem a:not(.noVeil)').click(isisFadeInVeil);

    $('div.collectionContentsAsAjaxTablePanel > table.contents > tbody > tr.reloaded-after-concurrency-exception')
        .livequery(function(){
            var x = $(this);
            $(this).animate({ "backgroundColor": "#FFF" }, 1000, "linear", function() {
                $(x).css('background-color','').removeClass("reloaded-after-concurrency-exception");
            });
        });

    /**
     * Show/hide the CopyLink modal window with alt+]
     */
    $('body').keydown(function(e) {
        if (e.which === 221 && e.altKey) {
            if (Isis.copyModalShown) {
                $('.copyModal').modal('hide');
                $('.modal-backdrop').remove();
                Isis.copyModalShown = false;
            }
            else {
                Isis.copyModalShown = true;
                $('.copyLink').click();
            }
        }
    });

    /**
     * Show/Hide the copy link only when hovering the entity icon and title
     */
    $('.entityIconTitleAndCopylink').hover(
        function() {
            $('.copyLink').css({visibility: 'visible'});
        },
        function() {
            $('.copyLink').css({visibility: 'hidden'});
        }
    );

    $('#menu-toggle').click(function(e) {
        e.preventDefault();
        $('#wrapper').toggleClass('toggled');
    });

    $('#sidebar-wrapper').keyup(function(e) {
        if (e.keyCode == 27) {
           $('#wrapper').addClass('toggled');
        }
    });

	/* force hide popover on button click */

	let hidePopover = function (e) {
	    $(e.target).closest('[rel="popover"]').each(function () {
	   		$(this).popover('hide');
	    });
	}

	$(document, '.isis-component-with-tooltip').on('click', hidePopover);

/*
    $('.editing .editable').parent().hover(function () {
        var inputEl = $(this).find("> .editable")
        $(inputEl).css({cursor: 'pointer'})
    },function () {
        var inputEl = $(this).find("> .editable")
        $(inputEl).css({cursor: 'auto'})
    });
*/

    /*
    Adapted from https://bootstrap-menu.com/detail-basic-hover.html
    Ignoring mouseleave events if hovering over a popover that belongs to a menuitem.
    The magic number in the window width predicate corresponds to the nav-bar collaps behavior
    as used in Footer/HeaderPanel.html templates; see Bootstrap 4 ref. ...
        navbar-expand = never collapses vertically (remains horizontal)
        navbar-expand-sm = collapses below sm widths <576px
        navbar-expand-md = collapses below md widths <768px
        navbar-expand-lg = collapses below lg widths <992px
        navbar-expand-xl = collapses below xl widths <1200px
	*/
	document.querySelectorAll('.navbar .nav-item, div.additionalLinkList').forEach(function(everyitem){
		/*
		   disabled for additional-action-links,
           as it currently does not work consistently eg. with AJAX requests

		   let autoShowSelector = 'a[data-toggle], button[data-toggle]';
		*/
		let autoShowSelector = 'a[data-toggle]';

		everyitem.addEventListener('mouseover', function(e){
			if(window.innerWidth<768){
				return; // when collapsed is a no-op
			}
			this.querySelectorAll(autoShowSelector)
			.forEach(function(el_link){
				let nextEl = el_link.nextElementSibling;
				el_link.classList.add('show');
				nextEl.classList.add('show');
			})
		});

		everyitem.addEventListener('mouseleave', function(e){
			if(window.innerWidth<768){
				return; // when collapsed is a no-op
			}
			// do not hide the dropdown if hovering over a popover (tooltip) attached to the dropdown item
			// The MouseEvent.relatedTarget read-only property is the secondary target for the mouse event,
			// if there is one: That is, the EventTarget the pointing device entered to.
			let relatedTarget = $(e.relatedTarget);
			if(relatedTarget.hasClass('popover')
					|| relatedTarget.hasClass('popover-body') // not strictly required, just an optimization
					|| relatedTarget.parents('.popover').length>0) {
				e.preventDefault();
				return;
			}
			this.querySelectorAll(autoShowSelector)
			.forEach(function(el_link){
				let nextEl = el_link.nextElementSibling;
				el_link.classList.remove('show');
				nextEl.classList.remove('show');
			})
		});
	});

});

/**
 * enables 'maxlength' to work as an attribute on 'textarea'
 *
 * as per: see http://stackoverflow.com/questions/4459610/set-maxlength-in-html-textarea
 */
$(function() {
    $("textarea[maxlength]").bind('input propertychange', function() {
        var maxLength = $(this).attr('maxlength');
        if ($(this).val().length > maxLength) {
            $(this).val($(this).val().substring(0, maxLength));
        }
    })
});

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * 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.
 */
({

	/**
	 * Test the top row. Check if tooltip is being displayed correctly in all directions
	 */

	// Top portion of tooltip going out of viewport. Uncomment assertion ~line 71 when fixed
	testTop: {
		test: function(component) {
			var tooltips = ['topLeftWest', 'topLeftNorth', 'topLeftSouth', 'topLeftEast',
			                'topCenterWest', 'topCenterNorth', 'topCenterSouth', 'topCenterEast',
			                'topRightWest', 'topRightNorth', 'topRightSouth', 'topRightEast'];

			var triggers = ['topLeftWestlabel', 'topLeftNorthlabel', 'topLeftSouthlabel', 'topLeftEastlabel',
			                'topCenterWestlabel', 'topCenterNorthlabel', 'topCenterSouthlabel', 'topCenterEastlabel',
			                'topRightWestlabel', 'topRightNorthlabel', 'topRightSouthlabel', 'topRightEastlabel']

			for(var i = 0; i < tooltips.length; i++) {

				function topRow(ttLabel, triggerLabel) {
					var trigger = component.find(triggerLabel).getElement();
					var tt = component.find(ttLabel);
					$A.test.assertFalse($A.util.hasClass(tt._tooltip,"visible"), "Tooltip visible should not be visible at this point for tooltip with aura:id = "+ ttLabel);
					$A.test.fireDomEvent(trigger, "mouseover");
					$A.test.addWaitForWithFailureMessage(true, function(){

						if($A.util.hasClass(tt._tooltip,"visible") == true) {
							/* verticalEpsilon is different from epsilon since for north and south,
							   the boundaries of the pointer and tooltip body are not perfectly aligned */
							var verticalEpsilon = 4;
							var epsilon = 0.001;
							var direction = tt.get('v.direction');
							var wrapper = tt._tooltip.querySelector('div.tooltip');
							var body = tt._tooltip.querySelector('div.tooltip-body');
							var bodyBoundingRect = body.getBoundingClientRect();
							var pointer = tt._tooltip.querySelector('div.pointer');
							var pointerBoundingRect = pointer.getBoundingClientRect();

							// Check if correct styling has been applied
							// and make sure pointer is not disjoint from the tooltip body

							/* For tooltip triggers along the top edge of the viewport,
							   the tooltip is displayed in the 'south' direction only */
							if(direction == "south") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"south"), 'the class south is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom + epsilon) >= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom - verticalEpsilon) <= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}

							// Check that the tooltip body is within the viewport
							$A.test.assertTrue(bodyBoundingRect.right < window.innerWidth, 'Right edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.left >= 0, 'Left edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.bottom < window.innerHeight, 'Bottom edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.top >= 0, 'Top edge leaving the view port for tooltip ' + ttLabel);

							return true;
						}

						return false;

					}, "'visible' class not attached to tooltip " + ttLabel);
				}

				topRow(tooltips[i], triggers[i]);
			}
		}
	},

	/**
	 * Test the center row. Check if tooltip is being displayed correctly in all directions
	 */
	testCenter: {
		test: function(component) {
			var tooltips = ['leftCenterWest', 'leftCenterNorth', 'leftCenterSouth', 'leftCenterEast',
			                'centerCenterWest', 'centerCenterNorth', 'centerCenterSouth', 'centerCenterEast',
			                'rightCenterWest', 'rightCenterNorth', 'rightCenterSouth', 'rightCenterEast'];

			var triggers = ['leftCenterWestlabel', 'leftCenterNorthlabel', 'leftCenterSouthlabel', 'leftCenterEastlabel',
			                'centerCenterWestlabel', 'centerCenterNorthlabel', 'centerCenterSouthlabel', 'centerCenterEastlabel',
			                'rightCenterWestlabel', 'rightCenterNorthlabel', 'rightCenterSouthlabel', 'rightCenterEastlabel']

			for(var i = 0; i < tooltips.length; i++) {

				function centerRow(ttLabel, triggerLabel) {
					var trigger = component.find(triggerLabel).getElement();
					var tt = component.find(ttLabel);
					$A.test.assertFalse($A.util.hasClass(tt._tooltip,"visible"), "Tooltip visible should not be visible at this point for tooltip with aura:id = "+ ttLabel);
					$A.test.fireDomEvent(trigger, "mouseover");
					$A.test.addWaitForWithFailureMessage(true, function(){

						if($A.util.hasClass(tt._tooltip,"visible") == true) {

							/* verticalEpsilon is different from epsilon since for north and south,
							   the boundaries of the pointer and tooltip body are not perfectly aligned */
							var epsilon = 0.001;
							var verticalEpsilon = 4;
							var direction = tt.get('v.direction');
							var wrapper = tt._tooltip.querySelector('div.tooltip');
							var body = tt._tooltip.querySelector('div.tooltip-body');
							var bodyBoundingRect = body.getBoundingClientRect();
							var pointer = tt._tooltip.querySelector('div.pointer');
							var pointerBoundingRect = pointer.getBoundingClientRect();

							// Check if correct styling has been applied
							// and make sure pointer is not disjoint from the tooltip body
							if(direction == "west") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"west"), 'the class west is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.left <= (bodyBoundingRect.right + epsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.left >= (bodyBoundingRect.right - epsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}
							else if(direction == "north") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"north"), 'the class north is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top <= (bodyBoundingRect.bottom + verticalEpsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top >= (bodyBoundingRect.bottom - verticalEpsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}
							else if(direction == "south") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"south"), 'the class south is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom + verticalEpsilon) >= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom - verticalEpsilon) <= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}
							else if(direction == "east") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"east"), 'the class east is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.right + epsilon) >= bodyBoundingRect.left, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.right - epsilon) <= bodyBoundingRect.left, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}

							// Check that the tooltip body is within the viewport
							$A.test.assertTrue(bodyBoundingRect.right < window.innerWidth, 'Right edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.left >= 0, 'Left edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.bottom < window.innerHeight, 'Bottom edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.top >= 0, 'Top edge leaving the view port for tooltip ' + ttLabel);

							return true;
						}

						return false;

					}, "'visible' class not attached to tooltip " + ttLabel);
				}
				centerRow(tooltips[i], triggers[i]);
			}
		}
	},

	/**
	 * Test the bottom row. Check if tooltip is being displayed correctly in all directions
	 */
	// Bottom portion of tooltip going out of viewport. Uncomment assertion ~line 191 when fixed
	testBottom: {
		test: function(component) {
			var tooltips = ['bottomLeftWest', 'bottomLeftNorth', 'bottomLeftSouth', 'bottomLeftEast',
			                'bottomCenterWest', 'bottomCenterNorth', 'bottomCenterSouth', 'bottomCenterEast',
			                'bottomRightWest', 'bottomRightNorth', 'bottomRightSouth', 'bottomRightEast'];

			var triggers = ['bottomLeftWestlabel', 'bottomLeftNorthlabel', 'bottomLeftSouthlabel', 'bottomLeftEastlabel',
			                'bottomCenterWestlabel', 'bottomCenterNorthlabel', 'bottomCenterSouthlabel', 'bottomCenterEastlabel',
			                'bottomRightWestlabel', 'bottomRightNorthlabel', 'bottomRightSouthlabel', 'bottomRightEastlabel'];
			for(var i = 0; i < tooltips.length; i++) {

				function bottomRow(ttLabel, triggerLabel) {
					var trigger = component.find(triggerLabel).getElement();
					var tt = component.find(ttLabel);
					$A.test.assertFalse($A.util.hasClass(tt._tooltip,"visible"), "Tooltip visible should not be visible at this point for tooltip with aura:id = "+ ttLabel);
					$A.test.fireDomEvent(trigger, "mouseover");
					$A.test.addWaitForWithFailureMessage(true, function(){

						if($A.util.hasClass(tt._tooltip,"visible") == true) {

							/* verticalEpsilon is different from epsilon since for north and south,
							   the boundaries of the pointer and tooltip body are not perfectly aligned */
							var epsilon = 0.001;
							var verticalEpsilon = 4;
							var direction = tt.get('v.direction');
							var wrapper = tt._tooltip.querySelector('div.tooltip');
							var body = tt._tooltip.querySelector('div.tooltip-body');
							var bodyBoundingRect = body.getBoundingClientRect();
							var pointer = tt._tooltip.querySelector('div.pointer');
							var pointerBoundingRect = pointer.getBoundingClientRect();

							// Check if correct styling has been applied
							// and make sure pointer is not disjoint from the tooltip body

							/* For tooltip triggers along the bottom edge of the viewport,
							   the tooltip is displayed in the 'north' direction only */
							if(direction == "north") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"north"), 'the class north is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top <= (bodyBoundingRect.bottom + epsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top >= (bodyBoundingRect.bottom - verticalEpsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}

							// Check that the tooltip body is within the viewport
							$A.test.assertTrue(bodyBoundingRect.right < window.innerWidth, 'Right edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.left >= 0, 'Left edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.bottom < window.innerHeight, 'Bottom edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.top >= 0, 'Top edge leaving the view port for tooltip ' + ttLabel);

							return true;
						}

						return false;

					}, "'visible' class not attached to tooltip " + ttLabel);
				}

				bottomRow(tooltips[i], triggers[i]);
			}
		}
	}

});<commit_msg>Move check to beginning to avoid timing conflicts @rev ctatlah@ @bug W-2731353@<commit_after>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * 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.
 */
({

	/**
	 * Test the top row. Check if tooltip is being displayed correctly in all directions
	 */

	// Top portion of tooltip going out of viewport. Uncomment assertion ~line 71 when fixed
	testTop: {
		test: function(component) {
			var tooltips = ['topLeftWest', 'topLeftNorth', 'topLeftSouth', 'topLeftEast',
			                'topCenterWest', 'topCenterNorth', 'topCenterSouth', 'topCenterEast',
			                'topRightWest', 'topRightNorth', 'topRightSouth', 'topRightEast'];

			var triggers = ['topLeftWestlabel', 'topLeftNorthlabel', 'topLeftSouthlabel', 'topLeftEastlabel',
			                'topCenterWestlabel', 'topCenterNorthlabel', 'topCenterSouthlabel', 'topCenterEastlabel',
			                'topRightWestlabel', 'topRightNorthlabel', 'topRightSouthlabel', 'topRightEastlabel'];

			for(var i = 0; i < tooltips.length; i++) {
				var tt = component.find(tooltips[i]);
				$A.test.assertFalse($A.util.hasClass(tt._tooltip,"visible"), "Tooltip visible should not be visible at this point for tooltip with aura:id = "+ tooltips[i]);
			}

			for(var i = 0; i < tooltips.length; i++) {

				function topRow(ttLabel, triggerLabel) {
					var trigger = component.find(triggerLabel).getElement();
					var tt = component.find(ttLabel);
					$A.test.fireDomEvent(trigger, "mouseover");
					$A.test.addWaitForWithFailureMessage(true, function(){

						if($A.util.hasClass(tt._tooltip,"visible") == true) {
							/* verticalEpsilon is different from epsilon since for north and south,
							   the boundaries of the pointer and tooltip body are not perfectly aligned */
							var verticalEpsilon = 4;
							var epsilon = 0.001;
							var direction = tt.get('v.direction');
							var wrapper = tt._tooltip.querySelector('div.tooltip');
							var body = tt._tooltip.querySelector('div.tooltip-body');
							var bodyBoundingRect = body.getBoundingClientRect();
							var pointer = tt._tooltip.querySelector('div.pointer');
							var pointerBoundingRect = pointer.getBoundingClientRect();

							// Check if correct styling has been applied
							// and make sure pointer is not disjoint from the tooltip body

							/* For tooltip triggers along the top edge of the viewport,
							   the tooltip is displayed in the 'south' direction only */
							if(direction == "south") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"south"), 'the class south is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom + epsilon) >= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom - verticalEpsilon) <= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}

							// Check that the tooltip body is within the viewport
							$A.test.assertTrue(bodyBoundingRect.right < window.innerWidth, 'Right edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.left >= 0, 'Left edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.bottom < window.innerHeight, 'Bottom edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.top >= 0, 'Top edge leaving the view port for tooltip ' + ttLabel);

							return true;
						}

						return false;

					}, "'visible' class not attached to tooltip " + ttLabel);
				}

				topRow(tooltips[i], triggers[i]);
			}
		}
	},

	/**
	 * Test the center row. Check if tooltip is being displayed correctly in all directions
	 */
	testCenter: {
		test: function(component) {
			var tooltips = ['leftCenterWest', 'leftCenterNorth', 'leftCenterSouth', 'leftCenterEast',
			                'centerCenterWest', 'centerCenterNorth', 'centerCenterSouth', 'centerCenterEast',
			                'rightCenterWest', 'rightCenterNorth', 'rightCenterSouth', 'rightCenterEast'];

			var triggers = ['leftCenterWestlabel', 'leftCenterNorthlabel', 'leftCenterSouthlabel', 'leftCenterEastlabel',
			                'centerCenterWestlabel', 'centerCenterNorthlabel', 'centerCenterSouthlabel', 'centerCenterEastlabel',
			                'rightCenterWestlabel', 'rightCenterNorthlabel', 'rightCenterSouthlabel', 'rightCenterEastlabel'];

			for(var i = 0; i < tooltips.length; i++) {
				var tt = component.find(tooltips[i]);
				$A.test.assertFalse($A.util.hasClass(tt._tooltip,"visible"), "Tooltip visible should not be visible at this point for tooltip with aura:id = "+ tooltips[i]);
			}

			for(var i = 0; i < tooltips.length; i++) {

				function centerRow(ttLabel, triggerLabel) {
					var trigger = component.find(triggerLabel).getElement();
					var tt = component.find(ttLabel);
					$A.test.fireDomEvent(trigger, "mouseover");
					$A.test.addWaitForWithFailureMessage(true, function(){

						if($A.util.hasClass(tt._tooltip,"visible") == true) {

							/* verticalEpsilon is different from epsilon since for north and south,
							   the boundaries of the pointer and tooltip body are not perfectly aligned */
							var epsilon = 0.001;
							var verticalEpsilon = 4;
							var direction = tt.get('v.direction');
							var wrapper = tt._tooltip.querySelector('div.tooltip');
							var body = tt._tooltip.querySelector('div.tooltip-body');
							var bodyBoundingRect = body.getBoundingClientRect();
							var pointer = tt._tooltip.querySelector('div.pointer');
							var pointerBoundingRect = pointer.getBoundingClientRect();

							// Check if correct styling has been applied
							// and make sure pointer is not disjoint from the tooltip body
							if(direction == "west") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"west"), 'the class west is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.left <= (bodyBoundingRect.right + epsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.left >= (bodyBoundingRect.right - epsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}
							else if(direction == "north") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"north"), 'the class north is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top <= (bodyBoundingRect.bottom + verticalEpsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top >= (bodyBoundingRect.bottom - verticalEpsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}
							else if(direction == "south") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"south"), 'the class south is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom + verticalEpsilon) >= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.bottom - verticalEpsilon) <= bodyBoundingRect.top, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}
							else if(direction == "east") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"east"), 'the class east is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.right + epsilon) >= bodyBoundingRect.left, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue((pointerBoundingRect.right - epsilon) <= bodyBoundingRect.left, 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}

							// Check that the tooltip body is within the viewport
							$A.test.assertTrue(bodyBoundingRect.right < window.innerWidth, 'Right edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.left >= 0, 'Left edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.bottom < window.innerHeight, 'Bottom edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.top >= 0, 'Top edge leaving the view port for tooltip ' + ttLabel);

							return true;
						}

						return false;

					}, "'visible' class not attached to tooltip " + ttLabel);
				}
				centerRow(tooltips[i], triggers[i]);
			}
		}
	},

	/**
	 * Test the bottom row. Check if tooltip is being displayed correctly in all directions
	 */
	// Bottom portion of tooltip going out of viewport. Uncomment assertion ~line 191 when fixed
	testBottom: {
		test: function(component) {
			var tooltips = ['bottomLeftWest', 'bottomLeftNorth', 'bottomLeftSouth', 'bottomLeftEast',
			                'bottomCenterWest', 'bottomCenterNorth', 'bottomCenterSouth', 'bottomCenterEast',
			                'bottomRightWest', 'bottomRightNorth', 'bottomRightSouth', 'bottomRightEast'];

			var triggers = ['bottomLeftWestlabel', 'bottomLeftNorthlabel', 'bottomLeftSouthlabel', 'bottomLeftEastlabel',
			                'bottomCenterWestlabel', 'bottomCenterNorthlabel', 'bottomCenterSouthlabel', 'bottomCenterEastlabel',
			                'bottomRightWestlabel', 'bottomRightNorthlabel', 'bottomRightSouthlabel', 'bottomRightEastlabel'];

			for(var i = 0; i < tooltips.length; i++) {
				var tt = component.find(tooltips[i]);
				$A.test.assertFalse($A.util.hasClass(tt._tooltip,"visible"), "Tooltip visible should not be visible at this point for tooltip with aura:id = "+ tooltips[i]);
			}

			for(var i = 0; i < tooltips.length; i++) {

				function bottomRow(ttLabel, triggerLabel) {
					var trigger = component.find(triggerLabel).getElement();
					var tt = component.find(ttLabel);
					$A.test.fireDomEvent(trigger, "mouseover");
					$A.test.addWaitForWithFailureMessage(true, function(){

						if($A.util.hasClass(tt._tooltip,"visible") == true) {

							/* verticalEpsilon is different from epsilon since for north and south,
							   the boundaries of the pointer and tooltip body are not perfectly aligned */
							var epsilon = 0.001;
							var verticalEpsilon = 4;
							var direction = tt.get('v.direction');
							var wrapper = tt._tooltip.querySelector('div.tooltip');
							var body = tt._tooltip.querySelector('div.tooltip-body');
							var bodyBoundingRect = body.getBoundingClientRect();
							var pointer = tt._tooltip.querySelector('div.pointer');
							var pointerBoundingRect = pointer.getBoundingClientRect();

							// Check if correct styling has been applied
							// and make sure pointer is not disjoint from the tooltip body

							/* For tooltip triggers along the bottom edge of the viewport,
							   the tooltip is displayed in the 'north' direction only */
							if(direction == "north") {
								$A.test.assertTrue($A.util.hasClass(wrapper,"north"), 'the class north is not attached to tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top <= (bodyBoundingRect.bottom + epsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
								$A.test.assertTrue(pointerBoundingRect.top >= (bodyBoundingRect.bottom - verticalEpsilon), 'Pointer is disjoint from tooltip body for tooltip: ' + ttLabel);
							}

							// Check that the tooltip body is within the viewport
							$A.test.assertTrue(bodyBoundingRect.right < window.innerWidth, 'Right edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.left >= 0, 'Left edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.bottom < window.innerHeight, 'Bottom edge leaving the view port for tooltip ' + ttLabel);
							$A.test.assertTrue(bodyBoundingRect.top >= 0, 'Top edge leaving the view port for tooltip ' + ttLabel);

							return true;
						}

						return false;

					}, "'visible' class not attached to tooltip " + ttLabel);
				}

				bottomRow(tooltips[i], triggers[i]);
			}
		}
	}

});<|endoftext|>


<commit_before>/**
 * @author Gerard van Helden <gerard@zicht.nl>
 */
var ZichtQuicklistAutocomplete = (function($) {
    'use strict';

    function initTextControl($hidden, $text, service_url, callback) {
        callback = callback || $.noop;

        var language = $text.data('language') || false,
            params = {};

        if ($text.data('language')) {
            params = {language: language};
        }

        $text
            .focus(function(){$text.select();})
            .autocomplete({
                source: function(req, resp) {
                    params.pattern = req.term;
                    $.get(
                        service_url,
                        params,
                        resp
                    );
                },
                select: function(e, ui) {
                    $hidden.val(ui.item.id);
                    $text.val(ui.item.label);
                    callback(ui.item);
                }
            })
        ;
        return $text;
    }

    function initCheckboxControl($hidden, $text, $checkbox) {
        function updateCheckboxStatus() {
            if ($checkbox.prop('checked')) {
                $text.val('');
                $hidden.val('');
                $text.prop('readonly', true);
            } else {
                $text.prop('readonly', false);
            }
        }
        updateCheckboxStatus();
        $checkbox.on('change', updateCheckboxStatus);
        $text.on({
            'click': function() {
                if ($checkbox.prop('checked')) {
                    $checkbox.prop('checked', false);
                    $checkbox.trigger('change');
                }
            }
        });
        $hidden.on('change', function() {
            $checkbox.prop('checked', !$hidden.val());
            $checkbox.trigger('change');
        });
    }

    function initRemoveControl($control) {
        $control.on('click', function(e) {
            $control.parents('li:first').remove();
            e.preventDefault();
        });
    }

    function initSingleAutocomplete($hidden, service_url, callback) {
        var $text = $hidden.siblings('input[type="text"]');
        initTextControl($hidden, $text, service_url, callback);
        initCheckboxControl($hidden, $text, $hidden.siblings('input[type="checkbox"]'));
    }

    function initListItem($li, service_url, callback) {
        initRemoveControl($li.find('.remove-control'));
        initSingleAutocomplete($li.find('input[type="hidden"]'), service_url, callback);
    }

    function initMultipleAutocomplete($ul, service_url) {
        var $add        = $ul.find('.add-control');
        var $items      = $ul.find('>li');
        var $template   = $ul.find('script#' + $ul.attr('data-template'));

        $items.each(function(i, li) {
            var $li = $(li);

            if ($li.find('.add-control')) {
                initListItem($li, service_url, function() {
                    addSelectionToList($(li));
                });
            } else {
                initListItem($li, service_url);
            }
        });

        function addSelectionToList($li) {
            var $added = $($template.text());

            // copy values and reset the 'add' control.
            $.each(['input[type="hidden"]', 'input[type="text"]'], function (i, ptn) {
                $added.find(ptn).val($li.find(ptn).val());
                $li.find(ptn).val('');
            });

            initListItem($added, service_url);
            $added.insertBefore($li);
            $li.find('input[type="text"]').focus();
        }

        $add.on('click', function(e) {
            e.preventDefault();
            var $li = $add.parents('li:first');
            // if no value set, ignore click
            if (!$li.find('input[type="hidden"]').val()) {
                return;
            }
            addSelectionToList($li);
        });
    }

    return {
        'init': function($control, service_url, multiple) {
            if (multiple) {
                initMultipleAutocomplete($control, service_url);
            } else {
                initSingleAutocomplete($control, service_url);
            }
        }
    };
})(jQuery);<commit_msg>Add submit check to make sure the hidden-field is emptied when the text-field is submitted with an empty value<commit_after>/**
 * @author Gerard van Helden <gerard@zicht.nl>
 */
var ZichtQuicklistAutocomplete = (function($) {
    'use strict';

    function initTextControl($hidden, $text, service_url, callback) {
        callback = callback || $.noop;

        var language = $text.data('language') || false,
            params = {};

        if ($text.data('language')) {
            params = {language: language};
        }

        $text
            .focus(function(){$text.select();})
            .autocomplete({
                source: function(req, resp) {
                    params.pattern = req.term;
                    $.get(
                        service_url,
                        params,
                        resp
                    );
                },
                select: function(e, ui) {
                    $hidden.val(ui.item.id);
                    $text.val(ui.item.label);
                    callback(ui.item);
                }
            })
        ;
        return $text;
    }

    function initCheckboxControl($hidden, $text, $checkbox) {
        function updateCheckboxStatus() {
            if ($checkbox.prop('checked')) {
                $text.val('');
                $hidden.val('');
                $text.prop('readonly', true);
            } else {
                $text.prop('readonly', false);
            }
        }
        updateCheckboxStatus();
        $checkbox.on('change', updateCheckboxStatus);
        $text.on({
            'click': function() {
                if ($checkbox.prop('checked')) {
                    $checkbox.prop('checked', false);
                    $checkbox.trigger('change');
                }
            }
        });
        $hidden.on('change', function() {
            $checkbox.prop('checked', !$hidden.val());
            $checkbox.trigger('change');
        });
    }

    function initRemoveControl($control) {
        $control.on('click', function(e) {
            $control.parents('li:first').remove();
            e.preventDefault();
        });
    }

    /**
     * Initialize the pre-submit hook
     *
     * @param {jQuery} $hidden
     * @param {jQuery} $text
     * @param {jQuery} $form
     */
    function initPreSubmitCheck($hidden, $text, $form) {
        $form.on('submit', () => {
            if ($text.val() === '') {
                $hidden.val('');
            }
        });
    }

    function initSingleAutocomplete($hidden, service_url, callback) {
        var $text = $hidden.siblings('input[type="text"]');
        initTextControl($hidden, $text, service_url, callback);
        initCheckboxControl($hidden, $text, $hidden.siblings('input[type="checkbox"]'));
        initPreSubmitCheck($hidden, $text, $hidden.closest('form'));
    }

    function initListItem($li, service_url, callback) {
        initRemoveControl($li.find('.remove-control'));
        initSingleAutocomplete($li.find('input[type="hidden"]'), service_url, callback);
    }

    function initMultipleAutocomplete($ul, service_url) {
        var $add        = $ul.find('.add-control');
        var $items      = $ul.find('>li');
        var $template   = $ul.find('script#' + $ul.attr('data-template'));

        $items.each(function(i, li) {
            var $li = $(li);

            if ($li.find('.add-control')) {
                initListItem($li, service_url, function() {
                    addSelectionToList($(li));
                });
            } else {
                initListItem($li, service_url);
            }
        });

        function addSelectionToList($li) {
            var $added = $($template.text());

            // copy values and reset the 'add' control.
            $.each(['input[type="hidden"]', 'input[type="text"]'], function (i, ptn) {
                $added.find(ptn).val($li.find(ptn).val());
                $li.find(ptn).val('');
            });

            initListItem($added, service_url);
            $added.insertBefore($li);
            $li.find('input[type="text"]').focus();
        }

        $add.on('click', function(e) {
            e.preventDefault();
            var $li = $add.parents('li:first');
            // if no value set, ignore click
            if (!$li.find('input[type="hidden"]').val()) {
                return;
            }
            addSelectionToList($li);
        });
    }

    return {
        'init': function($control, service_url, multiple) {
            if (multiple) {
                initMultipleAutocomplete($control, service_url);
            } else {
                initSingleAutocomplete($control, service_url);
            }
        }
    };
})(jQuery);<|endoftext|>


<commit_before>
 function showTagsScopesMin(htmlId){
        htmlId=".scope-min-header";
        //if(typeof actionOnSetGlobalScope != "undefined" && actionOnSetGlobalScope=="save"){
        scopeHtml='<button class="pull-left btn btn-link bg-white text-red tooltips item-globalscope-checker start-new-communexion" '+
                        'data-toggle="tooltip" data-placement="top" title="Communecter avec '+$.cookie('communexionName')+'" '+
                        'data-scope-value="'+$.cookie('communexionValue')+'" '+
                        'data-scope-name="'+$.cookie('communexionName')+'" '+
                        'data-scope-level="'+$.cookie('communexionLevel')+'" '+
                        'data-scope-type="'+$.cookie('communexionType')+'" '+
                        'id="btn-my-co">'+
                        ''+
                    '</button>'+
                    '
'+
                        '<button class="btn btn-default main-btn-scopes text-white tooltips margin-bottom-5 margin-left-10 margin-right-10" '+
                            'data-target="#modalScopes" data-toggle="modal" '+
                            'data-toggle="tooltip" data-placement="top" '+
                            'title="Sélectionner des lieux de recherche">'+
                            ''+
                        '</button>'+
                        'recherche ciblée '+
                    '
'+
                    '
'+
                    '
';
        $("#container-scope-filter").html(scopeHtml);
        //}
        /************** SCOPES **************/
        var iconSelectScope = "";
        var scopeSelected = false;

        html = "
";

        var numberOfScope = 0;
        if(typeof myMultiScopes != "undefined")
        $.each(myMultiScopes, function(key, value){
            numberOfScope++;
            var disabled = value.active == false ? "disabled" : "";
            if(typeof value.name == "undefined") value.name = key;
            html +=     "" +
                            " " + value.name +
                        " ";
        });
        // if (numberOfScope == 0) {
        //     html +=     ''+
        //                     ' Ajouter des filtres géographiques ?'+
        //                 '';
        // }
        //html +=     "";
        html += "
";
        $(htmlId).html(html);
        if(actionOnSetGlobalScope=="save"){
            scopeHtml='<a class="pull-left btn btn-link bg-white text-red tooltips item-globalscope-checker start-new-communexion" '+
                            'data-toggle="tooltip" data-placement="top" title="Communecter avec '+$.cookie('communexionName')+'" '+
                            'data-scope-value="'+$.cookie('communexionValue')+'" '+
                            'data-scope-name="'+$.cookie('communexionName')+'" '+
                            'data-scope-level="'+$.cookie('communexionLevel')+'" '+
                            'data-scope-type="'+$.cookie('communexionType')+'" '+
                            'id="btn-my-co">'+
                            ''+
                        ''+
                        '
'+
                            ''+
                                ''+
                            ''+
                            'Selectionner les endroits de publications '+
                        '
'+
                        '
'+
                            html+
                        '
';
            $("#scopeListContainerForm").html(scopeHtml);
        }
        multiTagScopeLbl();
        bindCommunexionScopeEvents();

        $(".toggle-scope-dropdown").click(function(){ //mylog.log("toogle");
            if(!$("#dropdown-content-multi-scope").hasClass('open'))
            setTimeout(function(){ $("#dropdown-content-multi-scope").addClass('open'); }, 300);
        });

        if(scopeSelected){ $(".btnShowAllScope").hide(); $(".btnHideAllScope").show(); }
        else             { $(".btnShowAllScope").show(); $(".btnHideAllScope").hide(); }

        checkScopeMax();
        rebuildSearchScopeInput();

    }

var currentTypeSearchSend = "search";
function multiTagScopeLbl(type){
	if(!notEmpty(type)) type = currentTypeSearchSend;
	if(type=="search"){
		$("#lbl-my-scopes").html("Rechercher par lieux  ");
		$("#lbl-my-tags").html("Rechercher par tags  ");
		$("br.visible-in-form").hide();
	}else if(type=="send"){
		$("#lbl-my-scopes").html(" Sélectionnez les lieux de destination");
		$("#lbl-my-tags").html(" Sélectionner des tags pour définir le contenu de votre message");
		$("br.visible-in-form").show();
	}
	currentTypeSearchSend = type;
}

function showEmptyMsg(){
	var c=0;
	if(typeof myMultiScopes != "undefined")
		$.each(myMultiScopes, function(key, value){ c++; });
	console.log("showEmptyMsg", c);
	if(c==0) $("#modalScopes .visible-empty").show(); else $("#modalScopes .visible-empty").hide();
	if(c==0) $("#modalScopes .hidden-empty").hide(); else $("#modalScopes .hidden-empty").show();

	//c=0; $.each(myMultiTags, function(key, value){ c++; });
	//if(c==0) $("#dropdown-multi-tag .visible-empty").show(); else $("#dropdown-multi-tag .visible-empty").hide();

}

function slidupScopetagsMin(show){ //mylog.log("slidupScopetagsMin", show);
	if($("#list_filters").hasClass("hidden")){
	    $("#list_filters").removeClass("hidden");
	    $("#btn-slidup-scopetags").html("");
	}
	else{
	    $("#list_filters").addClass("hidden"); //mylog.log("hidden slidupScopetagsMin", show);
	    $("#btn-slidup-scopetags").html("");
	}

	if(show==true){
	    $("#list_filters").removeClass("hidden"); //mylog.log("removeClass hidden slidupScopetagsMin", show);
	    $("#btn-slidup-scopetags").html("");
	}
	else if(show==false){
	    $("#list_filters").addClass("hidden");
	    $("#btn-slidup-scopetags").html("");
	}
}

<commit_msg><commit_after>
 function showTagsScopesMin(htmlId){
        htmlId=".scope-min-header";
        var numberOfScope = 0;
        if(typeof myMultiScopes != "undefined"){
            $.each(myMultiScopes, function(key, value){
                numberOfScope++;
            })  ;
        }
        //if(typeof actionOnSetGlobalScope != "undefined" && actionOnSetGlobalScope=="save"){
        scopeHtml='<button class="pull-left btn btn-link bg-white text-red tooltips item-globalscope-checker start-new-communexion" '+
                        'data-toggle="tooltip" data-placement="top" title="Communecter avec '+$.cookie('communexionName')+'" '+
                        'data-scope-value="'+$.cookie('communexionValue')+'" '+
                        'data-scope-name="'+$.cookie('communexionName')+'" '+
                        'data-scope-level="'+$.cookie('communexionLevel')+'" '+
                        'data-scope-type="'+$.cookie('communexionType')+'" '+
                        'id="btn-my-co">'+
                        ''+
                    '</button>'+
                    '
'+
                        '<button class="btn btn-default main-btn-scopes text-white tooltips margin-bottom-5 margin-left-10 margin-right-10" '+
                            'data-target="#modalScopes" data-toggle="modal" '+
                            'data-toggle="tooltip" data-placement="top" '+
                            'title="Sélectionner des lieux de recherche">'+
                            ''+
                        '</button>';
                        if(numberOfScope > 0){
                            scopeHtml+= 'recherche ciblée ';
                        } else{
                            scopeHtml+= ''+
                                           ''+
                                                ' Ajouter des filtres géographiques ?'+
                                            ''+
                                        '';
                        }
        scopeHtml+= '
'+
                    '
'+
                    '
';
        $("#container-scope-filter").html(scopeHtml);
        //}
        /************** SCOPES **************/
        var iconSelectScope = "";
        var scopeSelected = false;

        html = "
";
        if(numberOfScope > 0){
            $.each(myMultiScopes, function(key, value){
                numberOfScope++;
                var disabled = value.active == false ? "disabled" : "";
                if(typeof value.name == "undefined") value.name = key;
                html +=     "" +
                                " " + value.name +
                            " ";
            });
        }
        html += "
";
        $(htmlId).html(html);
        if(actionOnSetGlobalScope=="save"){
            scopeHtml='<a class="pull-left btn btn-link bg-white text-red tooltips item-globalscope-checker start-new-communexion" '+
                            'data-toggle="tooltip" data-placement="top" title="Communecter avec '+$.cookie('communexionName')+'" '+
                            'data-scope-value="'+$.cookie('communexionValue')+'" '+
                            'data-scope-name="'+$.cookie('communexionName')+'" '+
                            'data-scope-level="'+$.cookie('communexionLevel')+'" '+
                            'data-scope-type="'+$.cookie('communexionType')+'" '+
                            'id="btn-my-co">'+
                            ''+
                        ''+
                        '
'+
                            ''+
                                ''+
                            ''+
                            'Selectionner les endroits de publications '+
                        '
'+
                        '
'+
                            html+
                        '
';
            $("#scopeListContainerForm").html(scopeHtml);
        }
        multiTagScopeLbl();
        bindCommunexionScopeEvents();

        $(".toggle-scope-dropdown").click(function(){ //mylog.log("toogle");
            if(!$("#dropdown-content-multi-scope").hasClass('open'))
            setTimeout(function(){ $("#dropdown-content-multi-scope").addClass('open'); }, 300);
        });

        if(scopeSelected){ $(".btnShowAllScope").hide(); $(".btnHideAllScope").show(); }
        else             { $(".btnShowAllScope").show(); $(".btnHideAllScope").hide(); }

        checkScopeMax();
        rebuildSearchScopeInput();

    }

var currentTypeSearchSend = "search";
function multiTagScopeLbl(type){
	if(!notEmpty(type)) type = currentTypeSearchSend;
	if(type=="search"){
		$("#lbl-my-scopes").html("Rechercher par lieux  ");
		$("#lbl-my-tags").html("Rechercher par tags  ");
		$("br.visible-in-form").hide();
	}else if(type=="send"){
		$("#lbl-my-scopes").html(" Sélectionnez les lieux de destination");
		$("#lbl-my-tags").html(" Sélectionner des tags pour définir le contenu de votre message");
		$("br.visible-in-form").show();
	}
	currentTypeSearchSend = type;
}

function showEmptyMsg(){
	var c=0;
	if(typeof myMultiScopes != "undefined")
		$.each(myMultiScopes, function(key, value){ c++; });
	console.log("showEmptyMsg", c);
	if(c==0) $("#modalScopes .visible-empty").show(); else $("#modalScopes .visible-empty").hide();
	if(c==0) $("#modalScopes .hidden-empty").hide(); else $("#modalScopes .hidden-empty").show();

	//c=0; $.each(myMultiTags, function(key, value){ c++; });
	//if(c==0) $("#dropdown-multi-tag .visible-empty").show(); else $("#dropdown-multi-tag .visible-empty").hide();

}

function slidupScopetagsMin(show){ //mylog.log("slidupScopetagsMin", show);
	if($("#list_filters").hasClass("hidden")){
	    $("#list_filters").removeClass("hidden");
	    $("#btn-slidup-scopetags").html("");
	}
	else{
	    $("#list_filters").addClass("hidden"); //mylog.log("hidden slidupScopetagsMin", show);
	    $("#btn-slidup-scopetags").html("");
	}

	if(show==true){
	    $("#list_filters").removeClass("hidden"); //mylog.log("removeClass hidden slidupScopetagsMin", show);
	    $("#btn-slidup-scopetags").html("");
	}
	else if(show==false){
	    $("#list_filters").addClass("hidden");
	    $("#btn-slidup-scopetags").html("");
	}
}

<|endoftext|>


<commit_before>/*************************************
    ES RETRIEVAL
*************************************/
    // Contains all group specific options for aggregating release readiness score
    var aggregateOptions = {};

    // Called after ESQueryRunner.js has finished importing scripts
    // Loop through every group, and for each query in the group
    //      Execute the Qb query against ElasticSearch one at a time
    //      With every returned data set, format for compatibility with Rickshaw
    //      Then attempt to plot and log the number on view for that group
    //      Note: The attempt fails if group does not have all data sets
    //            The attempt will succeed on retrieval of the last data set.
    function startLoading() {
        $.each( coreData.groups, function( group_id, group_value ) {
            $.each( group_value.queries, function( query_id, query_value ) {
                if ($.parseJSON( query_value.qb_query )['talos'] == 1) {
                    coreData.groups[group_id].queries[query_id]['es_data'] = [{x:1391500800,y:2},{x:1391587200,y:0}];
                    executeAll( group_id );

                } else if ( query_value.es_data !== '' ) {

                    // We have some es_data sent in from the server.
                    // Use that instead of loading fresh data from ElasticSearch

                    coreData.groups[group_id].queries[query_id]['es_data'] = $.parseJSON( query_value.es_data );
                    // Checks for complete es_data through this group.
                    var dataInvalid = false;
                    $.each( coreData.groups[group_id].queries, function( key, value ) {
                        if( typeof value.es_data !== 'object' ) { dataInvalid = true; }
                    });

                    if ( dataInvalid === false ) {
                        executeAll( group_id );

                        //sets function parameter toSave = false
                        var score = aggregateScores(false);
                        $('.rrscore').css('color', score);

                    } else {
                        console.log("Not all data is ready for "+group_value.title+".");
                    }

                } else {

                    // The es_data field appears to be blank.
                    // Server did not give us anything to load up
                    // Go to the ElasticSearch cluster and pull fresh data
                    ESQueryRunner(
                        $.parseJSON( query_value.qb_query ),
                        function( response ){ // Executes after data is returned from ES.
                            // Format the returned ElasticSearch data for Rickshaw compatibility
                            var tempStore = new Array();
                            $.each( response.cube, function( key, value ) {
                                // Put the data we have in an array for plotting {date, count}
                                var d = response.edges[0]['domain'].partitions[key].value.getTime() / 1000;
                                tempStore.push( { x: d , y: value } );
                            });
                            coreData.groups[group_id].queries[query_id]['es_data'] = tempStore;
                            console.log(tempStore);
                            // End of formatting the returned ElasticSearch data for Rickshaw compatibility

                            // Checks for complete es_data through this group.
                            var dataMissing = false;
                            $.each( coreData.groups[group_id].queries, function( key, value ) {
                                if( value.es_data == '' ) { dataMissing = true; }
                            });

                            if ( dataMissing === false ) {
                                // OK all data present in group. Let's roll!
                                executeAll( group_id );

                                // Attempt to aggregate the score
                                // Only actually runs when all groups with rules are complete
                                var score = aggregateScores();
                                $('.rrscore').css('color', score);

                            } else {
                                // Do nothing, probably still retrieving data
                                console.log("Not all data is ready for "+group_value.title+".");
                            }

                            // Store this in the cache for future use!
                            cacheESData( query_id, coreData.groups[group_id].queries[query_id]['es_data'] );
                        }
                    );
                }
            });
        });
    }

/************************
    EXECUTES PLOT AND NUMBER PRINTING
************************/
    function executeAll( group_id ) {
        // If this group is a plot, plot it in the box
        if ( coreData.groups[group_id].is_plot ) { executePlot( group_id ); }

        // If this group is a number, log it in the box
        if ( coreData.groups[group_id].is_number ) { executeNumber( group_id ); }

        // If this group has a rule, apply it
        if ( coreData.groups[group_id].has_rule ) { applyStatus( group_id ); }

        // Component breakdown is available only after main plot is done loading
        $('.btn#get-component-breakdowns[data-group-id="'+group_id+'"]').show('slow');

        // Remove the Bugzilla chomping GIF icon
        removeLoader( 'g' + group_id );
    }

    function executePlot( group_id ) {
        // View graphing documentation here
        // https://github.com/shutterstock/rickshaw

        // Building up an array for each line that goes into the plot
        var rickshawData = new Array() ;
        var palette = new Rickshaw.Color.Palette();
        $.each( coreData.groups[group_id].queries, function( key, value ) {
            var plot_colour;
            if ( value.colour ) {
                plot_colour = value.colour;
            } else {
                plot_colour = palette.color();
            }

            var plot_data = [];
            if (value.is_reference) {
                // Reference plot only, use corresponding dates from main plot
                $.each( coreData.groups[group_id].queries[value.ref_query].es_data , function( esIndex, esValue ){
                    if ( value['es_data'][esIndex] ){
                        plot_data[esIndex] = { x: esValue.x , y: value['es_data'][esIndex].y } ;
                    }
                });
            } else {
                var today = todayIndex( value.es_data );
                for (var i = 0; i < value.es_data.length; i++) {
                    if ( i < today + 1 ) {
                        plot_data[i] = { x: value.es_data[i].x, y: value.es_data[i].y };
                    } else {
                        plot_data[i] = { x: value.es_data[i].x, y: undefined };
                    }
                };
            }

            rickshawData.push({
                name: value['title'],
                data: plot_data,
                color: plot_colour
            });
        });

        // Start the plot
        var graph = new Rickshaw.Graph({
            element: document.querySelector('.plot#g'+group_id),
            width: $('.group#g' + group_id).width() * 0.90,
            height: $('.group#g' + group_id).width() * 0.40,
            renderer: 'line',
            series: rickshawData
        });

        var time = new Rickshaw.Fixtures.Time().unit('month');
        var x_axis = new Rickshaw.Graph.Axis.Time({
            graph       : graph,
            timeUnit    : time
        });

        var y_axis = new Rickshaw.Graph.Axis.Y({
            graph       : graph,
            orientation : 'right',
            tickFormat  : Rickshaw.Fixtures.Number.formatKMBT,
            element     : document.querySelector('.y-axis#g'+group_id)
        });

        var hoverDetail = new Rickshaw.Graph.HoverDetail({
            graph: graph,
            xFormatter: function(x){
                return new Date( x * 1000 ).toDateString();
            }
        });

        /*
        graph.onUpdate(function(){
            $('.plot#g'+group_id+' svg').click(function(){
                var dated = $('.rickshaw_graph#g'+group_id+' .detail .x_label').html();
                alert(dated);
            });
        });
        */

        graph.render();
        // End of graphing
    }

    function executeNumber( group_id ) {
        $.each( coreData.groups[group_id].queries, function( key, value ) {
            var font_colour = '#000000';
            if ( value.colour ) {
                font_colour = value.colour;
            }

            var logNumber = value.es_data[value.es_data.length - 1].y;
            if (value.is_reference) {
                // Reference number only, use corresponding dates from main plot
                // Finding the index of the data set that corresponds to current time
                var i = todayIndex( coreData.groups[group_id].queries[value.ref_query].es_data );
                if ( value.es_data[i] ) {
                    logNumber = value.es_data[i].y;
                }
            }

            $('.group-number #q'+key).html(
                '
' +
                    logNumber +
                '
' );
        });
    }

    function applyStatus( group_id ) {
        var ruled = eval( 'rule_' + group_id + '()' );
        var status_colour = ruled;

        if ( ruled == 'green' ) {
            status_colour = 'lightgreen';
        } else if ( ruled == 'yellow' ) {
            status_colour = 'orange';
        } else if ( ruled == 'red' ) {
            status_colour = 'red';
        }

        $('.group-title#g'+group_id).css('background', status_colour);

        coreData.groups[group_id].status = ruled;
    }

    // Checks for whether or not all groups with rules have finished loading
    // If all groups with rules are done, execute aggregation and return the score
    // Accepts bool parameter (defaults to true) that specifies whether or not to save score
    var aggregatedBefore = false;
    function aggregateScores( toSave ){
        if( typeof(toSave) === 'undefined' ) toSave = true;

        if ( !aggregatedBefore ) {
            var isComplete = true;
            $.each( coreData.groups, function( group_id, group ){
                if ( group.has_rule ){
                    if ( group.status == undefined ){
                        // group has rule but no status yet - incomplete.
                        isComplete = false;
                    }
                } // else group has no rule, skip
            });

            if ( isComplete ) {
                aggregatedBefore = true;

                // Extract groups with rules into smaller variable that is easier to work with
                var ruledGroups = {};
                $.each( coreData.groups, function( group_id, group ){
                    if ( group.has_rule ){
                        ruledGroups[group_id] = group ;
                    }
                });

                var greenCount  = 0;
                var yellowCount = 0;
                var redCount    = 0;

                $.each( ruledGroups, function( group_id, group ){
                    if ( aggregateOptions[group_id].isShipwrecker && group.status == 'red' ) {
                        redCount = redCount + 9999999 ; //maxout the redcount

                    } else if ( aggregateOptions[group_id].isSignificant ) {

                        if ( group.status == 'green' ){
                            greenCount++;

                        } else if ( group.status == 'yellow' ){
                            yellowCount++;

                        } else if ( group.status == 'red' ){
                            redCount++;

                        }

                    } else {
                        // do nothing. group is not shipwrecker and not significant
                    }
                });

                var score = '';
                if ( greenCount != 0 || yellowCount != 0 || redCount != 0 ) {
                    var highScore = Math.max(redCount, yellowCount, greenCount);
                    if ( highScore == redCount ){
                        score = 'red';
                    } else if ( highScore == yellowCount ) {
                        score = 'orange';
                    } else {
                        score = 'lightgreen';
                    }
                }

                // Did we specify that we didn't want to save when applying this score?
                if ( toSave ) {
                    saveAggregate(score)
                }

                return score;
            } // Not all rules have been executed yet. Do nothing
        }
    }

/*****************************
    MAKE LIFE AWESOME FUNCTIONS
*****************************/

    function saveAggregate( score ) {
        $.ajax({
            url: '/api/scores',
            type: 'POST',
            data: {
                version_id    : coreData['id'],
                version_score : score
            },
            success: function(response) {
                console.log(response);
            },
            error: function(response) {
                alert('Fail: API could not be reached.');
                console.log(response);
            }
        });
    }

    function cacheESData( query_id, es_data ) {
        es_data = JSON.stringify( es_data );
        $.ajax({
            url: '/api/caches/es_data',
            type: 'POST',
            data: {
                query_id      : query_id,
                version_id    : coreData['id'],
                query_es_data : es_data
            },
            success: function(response) {
                console.log(response);
            },
            error: function(response) {
                alert('Fail: API could not be reached.');
                console.log(response);
            }
        });
    }

    function removeLoader( group_key ) {
        $('.group-title#'+group_key+' img.load-status').remove();
    }

    function todayIndex( rickshawArray ) {
        var i = 0;
        var temp = rickshawArray[i] ;
        var current = new Date().getTime() / 1000 ;

        while( temp.x < current ){
            i++;
            if ( rickshawArray[i] ) {
                temp = rickshawArray[i];

            } else {
                // At the end of alpha
                break;
            }
        }

        i--;

        return i;
    }
<commit_msg>can add talos via disgusting hack<commit_after>/*************************************
    ES RETRIEVAL
*************************************/
    // Contains all group specific options for aggregating release readiness score
    var aggregateOptions = {};

    // Called after ESQueryRunner.js has finished importing scripts
    // Loop through every group, and for each query in the group
    //      Execute the Qb query against ElasticSearch one at a time
    //      With every returned data set, format for compatibility with Rickshaw
    //      Then attempt to plot and log the number on view for that group
    //      Note: The attempt fails if group does not have all data sets
    //            The attempt will succeed on retrieval of the last data set.
    function startLoading() {
        $.each( coreData.groups, function( group_id, group_value ) {
            $.each( group_value.queries, function( query_id, query_value ) {
                if ($.parseJSON( query_value.qb_query )['talos'] == 1) {
                    $.ajax({
                        type: "POST",
                        url: "http://127.0.0.1:5000/_get_data",
                        data: {'api':'talos'},
                        success: function(data) {
                           coreData.groups[group_id].queries[query_id]['es_data'] = data['series_data'];
                           executeAll( group_id );
                        }
                    });

                } else if ( query_value.es_data !== '' ) {

                    // We have some es_data sent in from the server.
                    // Use that instead of loading fresh data from ElasticSearch

                    coreData.groups[group_id].queries[query_id]['es_data'] = $.parseJSON( query_value.es_data );
                    // Checks for complete es_data through this group.
                    var dataInvalid = false;
                    $.each( coreData.groups[group_id].queries, function( key, value ) {
                        if( typeof value.es_data !== 'object' ) { dataInvalid = true; }
                    });

                    if ( dataInvalid === false ) {
                        executeAll( group_id );

                        //sets function parameter toSave = false
                        var score = aggregateScores(false);
                        $('.rrscore').css('color', score);

                    } else {
                        console.log("Not all data is ready for "+group_value.title+".");
                    }

                } else {

                    // The es_data field appears to be blank.
                    // Server did not give us anything to load up
                    // Go to the ElasticSearch cluster and pull fresh data
                    ESQueryRunner(
                        $.parseJSON( query_value.qb_query ),
                        function( response ){ // Executes after data is returned from ES.
                            // Format the returned ElasticSearch data for Rickshaw compatibility
                            var tempStore = new Array();
                            $.each( response.cube, function( key, value ) {
                                // Put the data we have in an array for plotting {date, count}
                                var d = response.edges[0]['domain'].partitions[key].value.getTime() / 1000;
                                tempStore.push( { x: d , y: value } );
                            });
                            coreData.groups[group_id].queries[query_id]['es_data'] = tempStore;
                            console.log(tempStore);
                            // End of formatting the returned ElasticSearch data for Rickshaw compatibility

                            // Checks for complete es_data through this group.
                            var dataMissing = false;
                            $.each( coreData.groups[group_id].queries, function( key, value ) {
                                if( value.es_data == '' ) { dataMissing = true; }
                            });

                            if ( dataMissing === false ) {
                                // OK all data present in group. Let's roll!
                                executeAll( group_id );

                                // Attempt to aggregate the score
                                // Only actually runs when all groups with rules are complete
                                var score = aggregateScores();
                                $('.rrscore').css('color', score);

                            } else {
                                // Do nothing, probably still retrieving data
                                console.log("Not all data is ready for "+group_value.title+".");
                            }

                            // Store this in the cache for future use!
                            cacheESData( query_id, coreData.groups[group_id].queries[query_id]['es_data'] );
                        }
                    );
                }
            });
        });
    }

/************************
    EXECUTES PLOT AND NUMBER PRINTING
************************/
    function executeAll( group_id ) {
        // If this group is a plot, plot it in the box
        if ( coreData.groups[group_id].is_plot ) { executePlot( group_id ); }

        // If this group is a number, log it in the box
        if ( coreData.groups[group_id].is_number ) { executeNumber( group_id ); }

        // If this group has a rule, apply it
        if ( coreData.groups[group_id].has_rule ) { applyStatus( group_id ); }

        // Component breakdown is available only after main plot is done loading
        $('.btn#get-component-breakdowns[data-group-id="'+group_id+'"]').show('slow');

        // Remove the Bugzilla chomping GIF icon
        removeLoader( 'g' + group_id );
    }

    function executePlot( group_id ) {
        // View graphing documentation here
        // https://github.com/shutterstock/rickshaw

        // Building up an array for each line that goes into the plot
        var rickshawData = new Array() ;
        var palette = new Rickshaw.Color.Palette();
        $.each( coreData.groups[group_id].queries, function( key, value ) {
            var plot_colour;
            if ( value.colour ) {
                plot_colour = value.colour;
            } else {
                plot_colour = palette.color();
            }

            var plot_data = [];
            if (value.is_reference) {
                // Reference plot only, use corresponding dates from main plot
                $.each( coreData.groups[group_id].queries[value.ref_query].es_data , function( esIndex, esValue ){
                    if ( value['es_data'][esIndex] ){
                        plot_data[esIndex] = { x: esValue.x , y: value['es_data'][esIndex].y } ;
                    }
                });
            } else {
                var today = todayIndex( value.es_data );
                for (var i = 0; i < value.es_data.length; i++) {
                    if ( i < today + 1 ) {
                        plot_data[i] = { x: value.es_data[i].x, y: value.es_data[i].y };
                    } else {
                        plot_data[i] = { x: value.es_data[i].x, y: undefined };
                    }
                };
            }

            rickshawData.push({
                name: value['title'],
                data: plot_data,
                color: plot_colour
            });
        });

        // Start the plot
        var graph = new Rickshaw.Graph({
            element: document.querySelector('.plot#g'+group_id),
            width: $('.group#g' + group_id).width() * 0.90,
            height: $('.group#g' + group_id).width() * 0.40,
            renderer: 'line',
            series: rickshawData
        });

        var time = new Rickshaw.Fixtures.Time().unit('month');
        var x_axis = new Rickshaw.Graph.Axis.Time({
            graph       : graph,
            timeUnit    : time
        });

        var y_axis = new Rickshaw.Graph.Axis.Y({
            graph       : graph,
            orientation : 'right',
            tickFormat  : Rickshaw.Fixtures.Number.formatKMBT,
            element     : document.querySelector('.y-axis#g'+group_id)
        });

        var hoverDetail = new Rickshaw.Graph.HoverDetail({
            graph: graph,
            xFormatter: function(x){
                return new Date( x * 1000 ).toDateString();
            }
        });

        /*
        graph.onUpdate(function(){
            $('.plot#g'+group_id+' svg').click(function(){
                var dated = $('.rickshaw_graph#g'+group_id+' .detail .x_label').html();
                alert(dated);
            });
        });
        */

        graph.render();
        // End of graphing
    }

    function executeNumber( group_id ) {
        $.each( coreData.groups[group_id].queries, function( key, value ) {
            var font_colour = '#000000';
            if ( value.colour ) {
                font_colour = value.colour;
            }

            var logNumber = value.es_data[value.es_data.length - 1].y;
            if (value.is_reference) {
                // Reference number only, use corresponding dates from main plot
                // Finding the index of the data set that corresponds to current time
                var i = todayIndex( coreData.groups[group_id].queries[value.ref_query].es_data );
                if ( value.es_data[i] ) {
                    logNumber = value.es_data[i].y;
                }
            }

            $('.group-number #q'+key).html(
                '
' +
                    logNumber +
                '
' );
        });
    }

    function applyStatus( group_id ) {
        var ruled = eval( 'rule_' + group_id + '()' );
        var status_colour = ruled;

        if ( ruled == 'green' ) {
            status_colour = 'lightgreen';
        } else if ( ruled == 'yellow' ) {
            status_colour = 'orange';
        } else if ( ruled == 'red' ) {
            status_colour = 'red';
        }

        $('.group-title#g'+group_id).css('background', status_colour);

        coreData.groups[group_id].status = ruled;
    }

    // Checks for whether or not all groups with rules have finished loading
    // If all groups with rules are done, execute aggregation and return the score
    // Accepts bool parameter (defaults to true) that specifies whether or not to save score
    var aggregatedBefore = false;
    function aggregateScores( toSave ){
        if( typeof(toSave) === 'undefined' ) toSave = true;

        if ( !aggregatedBefore ) {
            var isComplete = true;
            $.each( coreData.groups, function( group_id, group ){
                if ( group.has_rule ){
                    if ( group.status == undefined ){
                        // group has rule but no status yet - incomplete.
                        isComplete = false;
                    }
                } // else group has no rule, skip
            });

            if ( isComplete ) {
                aggregatedBefore = true;

                // Extract groups with rules into smaller variable that is easier to work with
                var ruledGroups = {};
                $.each( coreData.groups, function( group_id, group ){
                    if ( group.has_rule ){
                        ruledGroups[group_id] = group ;
                    }
                });

                var greenCount  = 0;
                var yellowCount = 0;
                var redCount    = 0;

                $.each( ruledGroups, function( group_id, group ){
                    if ( aggregateOptions[group_id].isShipwrecker && group.status == 'red' ) {
                        redCount = redCount + 9999999 ; //maxout the redcount

                    } else if ( aggregateOptions[group_id].isSignificant ) {

                        if ( group.status == 'green' ){
                            greenCount++;

                        } else if ( group.status == 'yellow' ){
                            yellowCount++;

                        } else if ( group.status == 'red' ){
                            redCount++;

                        }

                    } else {
                        // do nothing. group is not shipwrecker and not significant
                    }
                });

                var score = '';
                if ( greenCount != 0 || yellowCount != 0 || redCount != 0 ) {
                    var highScore = Math.max(redCount, yellowCount, greenCount);
                    if ( highScore == redCount ){
                        score = 'red';
                    } else if ( highScore == yellowCount ) {
                        score = 'orange';
                    } else {
                        score = 'lightgreen';
                    }
                }

                // Did we specify that we didn't want to save when applying this score?
                if ( toSave ) {
                    saveAggregate(score)
                }

                return score;
            } // Not all rules have been executed yet. Do nothing
        }
    }

/*****************************
    MAKE LIFE AWESOME FUNCTIONS
*****************************/

    function saveAggregate( score ) {
        $.ajax({
            url: '/api/scores',
            type: 'POST',
            data: {
                version_id    : coreData['id'],
                version_score : score
            },
            success: function(response) {
                console.log(response);
            },
            error: function(response) {
                alert('Fail: API could not be reached.');
                console.log(response);
            }
        });
    }

    function cacheESData( query_id, es_data ) {
        es_data = JSON.stringify( es_data );
        $.ajax({
            url: '/api/caches/es_data',
            type: 'POST',
            data: {
                query_id      : query_id,
                version_id    : coreData['id'],
                query_es_data : es_data
            },
            success: function(response) {
                console.log(response);
            },
            error: function(response) {
                alert('Fail: API could not be reached.');
                console.log(response);
            }
        });
    }

    function removeLoader( group_key ) {
        $('.group-title#'+group_key+' img.load-status').remove();
    }

    function todayIndex( rickshawArray ) {
        var i = 0;
        var temp = rickshawArray[i] ;
        var current = new Date().getTime() / 1000 ;

        while( temp.x < current ){
            i++;
            if ( rickshawArray[i] ) {
                temp = rickshawArray[i];

            } else {
                // At the end of alpha
                break;
            }
        }

        i--;

        return i;
    }
<|endoftext|>