<commit_before>"use strict";

(function (document, window, Orn, Utils) {

  var Tray = {

    selectors: {
      buttons: "data-tray-button",
      closeButtons: "data-tray-close",
      scrollPusher: "data-tray-scroll-positioner",
      overlay: "data-tray-overlay",
    },

    classes: {
      open: "overlay__open",
      transitioning: "overlay__transitioning"
    },

    // Time to wait until the transition class is removed
    transition: 200,

    // Internal flags
    isOpen: false,
    transitionTimer: null,
    scrollOffset: 0,
    focusTrap: {},

    // pixels, should match $tray-breakpoint
    breakTabLockAt: false,

    // Simple helper to loop over all toggle buttons
    forEachButton: function(callback){
      if(Tray.$buttons.length) {
        for(var i = 0; i < Tray.$buttons.length; i++) {
          callback(Tray.$buttons[i]);
        }
      }
    },

    // Get all focusable elements in the overlay so that we can
    // lock the user in to only keyboard tabbing through the
    // overlay and not on to the actual page
    getFocusableElementsInOverlay: function(){
      var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]';
      var focusableElements = Tray.$overlay.querySelectorAll(focusableElementsString);
      // Convert NodeList to Array
      focusableElements = Array.prototype.slice.call(focusableElements);
      // Assign our tab-lock sentinals
      Tray.focusTrap.first = focusableElements[0];
      Tray.focusTrap.last = focusableElements[focusableElements.length - 1];
    },

    // Simple helper to update the aria label of each toggle button
    // based on the open state of the tray
    setAria: function(){
      var label = Tray.isOpen ? "Close menu" : "Open menu";
      Tray.forEachButton(function($button){
        $button.setAttribute("aria-label", label);
      });
    },

    isMobile: function(){
      if(Tray.breakTabLockAt) {
        return window.matchMedia("(max-width: " + Tray.breakTabLockAt + "px)").matches;
      } else {
        return true;
      }
    },

    // Open the tray
    openTray: function(){
      if(Tray.beforeOpen) {
        Tray.beforeOpen();
      }

      Tray.isOpen = true;
      Tray.setAria();
      Tray.focusTrap.opener = document.activeElement;

      // Clear timer if already going
      if(Tray.transitionTimer) {
        clearTimeout(Tray.transitionTimer);
      }

      // Offset the pusher element by the amount scrolled (in the case of sticky header)
      if(Tray.$scrollPusher) {
        Tray.scrollOffset = window.scrollY;
        Tray.$scrollPusher.style.top = (Tray.scrollOffset * -1) + "px";
      }

      // Add the transition and opening classes
      Tray.$classTarget.classList.add(Tray.classes.transitioning);
      Tray.$classTarget.classList.add(Tray.classes.open);

      // Wait until transition is completed and remove the transitioning class
      Tray.transitionTimer = setTimeout(function(){
        Tray.$classTarget.classList.remove(Tray.classes.transitioning);

        // Find first element to focus on after opening
        if(Tray.focusTrap.firstAfterOpen) {
          Tray.focusTrap.firstAfterOpen.focus();
        } else if(Tray.focusTrap.first) {
          Tray.focusTrap.first.focus();
        }

        // Callback
        if(Tray.afterOpen) {
          Tray.afterOpen();
        }
      }, Tray.transition);
    },

    // Close the tray
    closeTray: function(){
      if(Tray.beforeClose) {
        Tray.beforeClose();
      }

      Tray.isOpen = false;
      Tray.setAria();

      // Clear timer if already going
      if(Tray.transitionTimer) {
        clearTimeout(Tray.transitionTimer);
      }

      // Remove open class and add transitioning class
      Tray.$classTarget.classList.remove(Tray.classes.open);
      Tray.$classTarget.classList.add(Tray.classes.transitioning);

      // Offset the pusher element by the amount scrolled (in the case of sticky header)
      if(Tray.$scrollPusher) {
        Tray.$scrollPusher.style.top = "";
        window.scrollTo(0, Tray.scrollOffset);
      }

      // Wait until transition is completed and remove the transitioning class
      Tray.transitionTimer = setTimeout(function(){
        Tray.$classTarget.classList.remove(Tray.classes.transitioning);

        // Re-focus on opener element
        if(Tray.focusTrap.opener) {
          Tray.focusTrap.opener.focus();
        }

        // Callback
        if(Tray.afterClose) {
          Tray.afterClose();
        }
      }, Tray.transition);
    },

    // Open or close the tray depending on the current state
    toggleTray: function(){
      if(Tray.isOpen) {
        Tray.closeTray();
      } else {
        Tray.openTray();
      }
    },

    // Bind a button to toggle the tray when clicked
    bindToggleButton: function($button) {
      Ornament.U.bindOnce($button, "click", Tray.toggleTray);
    },

    // Bind a button to close the tray when clicked
    bindCloseButton: function($button) {
      Ornament.U.bindOnce($button, "click", Tray.closeTray);
    },

    bindShortcuts: function(){
      // Populate Tray.focusTrap with our element list
      Tray.getFocusableElementsInOverlay();

      document.addEventListener("keydown", function(event) {
        // Keyboard shortcuts only when menu is open
        if(Tray.isOpen && Tray.isMobile()) {

          // Trap tab and shift+tab keys to lock user in to keyboard
          // navigating only the menu
          // On <tab> press
          if(event.keyCode === 9) {

            // On <shift + tab>
            if(event.shiftKey) {
              if(document.activeElement === Tray.focusTrap.first) {
                // Stop shift+tab default action
                event.preventDefault();
                // Focus on last element (loop focus back around)
                Tray.focusTrap.last.focus();
              }
            } else {

              if(document.activeElement === Tray.focusTrap.last) {
                // Stop default tab action
                event.preventDefault();
                // Focus on first element (loop back around)
                Tray.focusTrap.first.focus();
              }
            }
          }

          // Close sidebar when user presses "esc"
          if(event.keyCode === 27) {
            Tray.closeTray();
          }
        }

      });
    },

    init: function(){
      Tray.$buttons = document.querySelectorAll("[" + Tray.selectors.buttons + "]");
      Tray.$closeButtons = document.querySelectorAll("[" + Tray.selectors.closeButtons + "]");
      Tray.$scrollPusher = document.querySelector("[" + Tray.selectors.scrollPusher + "]");
      Tray.$classTarget = document.body;
      Tray.$overlay = document.querySelector("[" + Tray.selectors.overlay + "]");

      // Bind Toggle Buttons
      Tray.forEachButton(function($button) {
        Tray.bindToggleButton($button);
      });

      // Bind Close Buttons
      if(Tray.$closeButtons.length) {
        for(var i = 0; i < Tray.$closeButtons.length; i++) {
          Tray.bindCloseButton(Tray.$closeButtons[i]);
        }
      }

      // Set aria labels
      Tray.setAria();

      // Bind keyboard shortcuts
      Tray.bindShortcuts();

      // Clicking away from the tray should close it
      Ornament.U.bindOnce(document, "mousedown", function(event){
        if(Tray.isOpen) {
          var target = event.target;
          var inOverlay = Tray.$overlay && Tray.$overlay.contains(target);
          var inAnchors = false;
          for(var i = 0; i < Tray.$buttons.length; i++) {
            if(Tray.$buttons[i].contains(target)) {
              inAnchors = true;
            }
          }
          if(!inOverlay && !inAnchors) {
            Tray.closeTray();
          }
        }
      });
    }
  }

  Orn.registerComponent("OverlayTray", Tray);

}(document, window, Ornament, Ornament.Utilities));<commit_msg>Fixed tray overlay complaining when no overlay is present<commit_after>"use strict";

(function (document, window, Orn, Utils) {

  var Tray = {

    selectors: {
      buttons: "data-tray-button",
      closeButtons: "data-tray-close",
      scrollPusher: "data-tray-scroll-positioner",
      overlay: "data-tray-overlay",
    },

    classes: {
      open: "overlay__open",
      transitioning: "overlay__transitioning"
    },

    // Time to wait until the transition class is removed
    transition: 200,

    // Internal flags
    isOpen: false,
    transitionTimer: null,
    scrollOffset: 0,
    focusTrap: {},

    // pixels, should match $tray-breakpoint
    breakTabLockAt: false,

    // Simple helper to loop over all toggle buttons
    forEachButton: function(callback){
      if(Tray.$buttons.length) {
        for(var i = 0; i < Tray.$buttons.length; i++) {
          callback(Tray.$buttons[i]);
        }
      }
    },

    // Get all focusable elements in the overlay so that we can
    // lock the user in to only keyboard tabbing through the
    // overlay and not on to the actual page
    getFocusableElementsInOverlay: function(){
      if(!Tray.$overlay) {
        return;
      }
      var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]';
      var focusableElements = Tray.$overlay.querySelectorAll(focusableElementsString);
      // Convert NodeList to Array
      focusableElements = Array.prototype.slice.call(focusableElements);
      // Assign our tab-lock sentinals
      Tray.focusTrap.first = focusableElements[0];
      Tray.focusTrap.last = focusableElements[focusableElements.length - 1];
    },

    // Simple helper to update the aria label of each toggle button
    // based on the open state of the tray
    setAria: function(){
      var label = Tray.isOpen ? "Close menu" : "Open menu";
      Tray.forEachButton(function($button){
        $button.setAttribute("aria-label", label);
      });
    },

    isMobile: function(){
      if(Tray.breakTabLockAt) {
        return window.matchMedia("(max-width: " + Tray.breakTabLockAt + "px)").matches;
      } else {
        return true;
      }
    },

    // Open the tray
    openTray: function(){
      if(Tray.beforeOpen) {
        Tray.beforeOpen();
      }

      Tray.isOpen = true;
      Tray.setAria();
      Tray.focusTrap.opener = document.activeElement;

      // Clear timer if already going
      if(Tray.transitionTimer) {
        clearTimeout(Tray.transitionTimer);
      }

      // Offset the pusher element by the amount scrolled (in the case of sticky header)
      if(Tray.$scrollPusher) {
        Tray.scrollOffset = window.scrollY;
        Tray.$scrollPusher.style.top = (Tray.scrollOffset * -1) + "px";
      }

      // Add the transition and opening classes
      Tray.$classTarget.classList.add(Tray.classes.transitioning);
      Tray.$classTarget.classList.add(Tray.classes.open);

      // Wait until transition is completed and remove the transitioning class
      Tray.transitionTimer = setTimeout(function(){
        Tray.$classTarget.classList.remove(Tray.classes.transitioning);

        // Find first element to focus on after opening
        if(Tray.focusTrap.firstAfterOpen) {
          Tray.focusTrap.firstAfterOpen.focus();
        } else if(Tray.focusTrap.first) {
          Tray.focusTrap.first.focus();
        }

        // Callback
        if(Tray.afterOpen) {
          Tray.afterOpen();
        }
      }, Tray.transition);
    },

    // Close the tray
    closeTray: function(){
      if(Tray.beforeClose) {
        Tray.beforeClose();
      }

      Tray.isOpen = false;
      Tray.setAria();

      // Clear timer if already going
      if(Tray.transitionTimer) {
        clearTimeout(Tray.transitionTimer);
      }

      // Remove open class and add transitioning class
      Tray.$classTarget.classList.remove(Tray.classes.open);
      Tray.$classTarget.classList.add(Tray.classes.transitioning);

      // Offset the pusher element by the amount scrolled (in the case of sticky header)
      if(Tray.$scrollPusher) {
        Tray.$scrollPusher.style.top = "";
        window.scrollTo(0, Tray.scrollOffset);
      }

      // Wait until transition is completed and remove the transitioning class
      Tray.transitionTimer = setTimeout(function(){
        Tray.$classTarget.classList.remove(Tray.classes.transitioning);

        // Re-focus on opener element
        if(Tray.focusTrap.opener) {
          Tray.focusTrap.opener.focus();
        }

        // Callback
        if(Tray.afterClose) {
          Tray.afterClose();
        }
      }, Tray.transition);
    },

    // Open or close the tray depending on the current state
    toggleTray: function(){
      if(Tray.isOpen) {
        Tray.closeTray();
      } else {
        Tray.openTray();
      }
    },

    // Bind a button to toggle the tray when clicked
    bindToggleButton: function($button) {
      Ornament.U.bindOnce($button, "click", Tray.toggleTray);
    },

    // Bind a button to close the tray when clicked
    bindCloseButton: function($button) {
      Ornament.U.bindOnce($button, "click", Tray.closeTray);
    },

    bindShortcuts: function(){
      // Populate Tray.focusTrap with our element list
      Tray.getFocusableElementsInOverlay();

      document.addEventListener("keydown", function(event) {
        // Keyboard shortcuts only when menu is open
        if(Tray.isOpen && Tray.isMobile()) {

          // Trap tab and shift+tab keys to lock user in to keyboard
          // navigating only the menu
          // On <tab> press
          if(event.keyCode === 9) {

            // On <shift + tab>
            if(event.shiftKey) {
              if(document.activeElement === Tray.focusTrap.first) {
                // Stop shift+tab default action
                event.preventDefault();
                // Focus on last element (loop focus back around)
                Tray.focusTrap.last.focus();
              }
            } else {

              if(document.activeElement === Tray.focusTrap.last) {
                // Stop default tab action
                event.preventDefault();
                // Focus on first element (loop back around)
                Tray.focusTrap.first.focus();
              }
            }
          }

          // Close sidebar when user presses "esc"
          if(event.keyCode === 27) {
            Tray.closeTray();
          }
        }

      });
    },

    init: function(){
      Tray.$buttons = document.querySelectorAll("[" + Tray.selectors.buttons + "]");
      Tray.$closeButtons = document.querySelectorAll("[" + Tray.selectors.closeButtons + "]");
      Tray.$scrollPusher = document.querySelector("[" + Tray.selectors.scrollPusher + "]");
      Tray.$classTarget = document.body;
      Tray.$overlay = document.querySelector("[" + Tray.selectors.overlay + "]");

      // Bind Toggle Buttons
      Tray.forEachButton(function($button) {
        Tray.bindToggleButton($button);
      });

      // Bind Close Buttons
      if(Tray.$closeButtons.length) {
        for(var i = 0; i < Tray.$closeButtons.length; i++) {
          Tray.bindCloseButton(Tray.$closeButtons[i]);
        }
      }

      // Set aria labels
      Tray.setAria();

      // Bind keyboard shortcuts
      Tray.bindShortcuts();

      // Clicking away from the tray should close it
      Ornament.U.bindOnce(document, "mousedown", function(event){
        if(Tray.isOpen) {
          var target = event.target;
          var inOverlay = Tray.$overlay && Tray.$overlay.contains(target);
          var inAnchors = false;
          for(var i = 0; i < Tray.$buttons.length; i++) {
            if(Tray.$buttons[i].contains(target)) {
              inAnchors = true;
            }
          }
          if(!inOverlay && !inAnchors) {
            Tray.closeTray();
          }
        }
      });
    }
  }

  Orn.registerComponent("OverlayTray", Tray);

}(document, window, Ornament, Ornament.Utilities));<|endoftext|>


<commit_before>import React from 'react';
import { interpolate } from 'd3-interpolate';
import * as Easing from 'd3-ease';

class Motion extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.move = this.move.bind(this);
        this.state = {items: []};
    }
    componentDidMount() {
        this.moveId = requestAnimationFrame(this.move)
    }
    componentWillUnmount() {
        cancelAnimationFrame(this.moveId);
    }
    move() {
        this.setState(({items: prevItems, update}) => {
            var {data, enter, leave} = this.props;
            var dataByKey = data.reduce((acc, item) => ({...acc, [item.key]: item}), {});
            var itemsByKey = items.reduce((acc, item) => ({...acc, [item.key]: item}), {});
            var tick = performance.now();
            var items = prevItems
                .map(item => {
                    var end = !dataByKey[item.key] ? leave(item.data) : update(item.data);
                    const equal = areEqual(item.end, end);
                    var progress = equal ? Math.max(Math.min((tick - item.tick) / 500, 1), 0) : 0;
                    var interpolators = equal ? item.interpolators : this.getInterpolators(item.style, end);
                    var style = this.interpolateStyle(interpolators, end, progress);
                    return {...item, style, end, interpolators, progress, tick};
                })
                .concat(data
                    .filter(item => !itemsByKey[item.key])
                    .map(item => {
                        var style = enter(item);
                        var end = update(item);
                        var interpolators = this.getInterpolators(style, end);
                        return {...item, style, end, interpolators, progress: 0, tick};
                    })
                );
            this.moveId = requestAnimationFrame(this.move);
            return {items};
        })
    }
    areEqual(from, to) {
        if (Object.keys(from).length !== Object.keys(to).length)
            return false;
        for(var key in from) {
            if (from[key] !== to[key])
                return false;
        }
        return true;
    }
    getInterpolators(start, end) {
        var interpolators = {};
        for(var key in start) {
            interpolators[key] = interpolate(start[key], end[key]);
        }
        return interpolators;
    }
    interpolateStyle(interpolators, end, progress) {
        var style = {};
        for(var key in end) {
            style[key] = interpolators[key](Easing.easeLinear(progress))
        }
        return style;
    }
}

export default Motion;
<commit_msg>Move key from data into getKey to match move<commit_after>import React from 'react';
import { interpolate } from 'd3-interpolate';
import * as Easing from 'd3-ease';

class Motion extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.move = this.move.bind(this);
        this.state = {items: []};
    }
    componentDidMount() {
        this.moveId = requestAnimationFrame(this.move)
    }
    componentWillUnmount() {
        cancelAnimationFrame(this.moveId);
    }
    move() {
        this.setState(({items: prevItems, update}) => {
            var {data, enter, leave, getKey} = this.props;
            var dataByKey = data.reduce((acc, item) => ({...acc, [getKey(item)]: item}), {});
            var itemsByKey = items.reduce((acc, item) => ({...acc, [item.key]: item}), {});
            var tick = performance.now();
            var items = prevItems
                .map(item => {
                    var end = !dataByKey[item.key] ? leave(item.data) : update(item.data);
                    const equal = areEqual(item.end, end);
                    var progress = equal ? Math.max(Math.min((tick - item.tick) / 500, 1), 0) : 0;
                    var interpolators = equal ? item.interpolators : this.getInterpolators(item.style, end);
                    var style = this.interpolateStyle(interpolators, end, progress);
                    return {key: item.key, data: item.data, style, end, interpolators, progress, tick};
                })
                .concat(data
                    .filter(item => !itemsByKey[getKey(item)])
                    .map(item => {
                        var style = enter(item);
                        var end = update(item);
                        var interpolators = this.getInterpolators(style, end);
                        return {key: getKey(data), data: item, style, end, interpolators, progress: 0, tick};
                    })
                );
            this.moveId = requestAnimationFrame(this.move);
            return {items};
        })
    }
    areEqual(from, to) {
        if (Object.keys(from).length !== Object.keys(to).length)
            return false;
        for(var key in from) {
            if (from[key] !== to[key])
                return false;
        }
        return true;
    }
    getInterpolators(start, end) {
        var interpolators = {};
        for(var key in start) {
            interpolators[key] = interpolate(start[key], end[key]);
        }
        return interpolators;
    }
    interpolateStyle(interpolators, end, progress) {
        var style = {};
        for(var key in end) {
            style[key] = interpolators[key](Easing.easeLinear(progress))
        }
        return style;
    }
}

export default Motion;
<|endoftext|>


<commit_before>(function (win) {

    if (!(win.history && win.history.pushState))
        return;

    var submitData = {};
    win.document.addEventListener('click', function (e) {
        var element = e.target;
        var anchor = getAjaxTarget(element, 'A');
        if (!e.ctrlKey && !e.shiftKey && anchor) {
            e.preventDefault();
            refreshAjax(anchor.getAttribute('href'), true, anchor);
        }
        if (element.tagName === 'INPUT' && element.name) {
            if (element.type === 'submit')
                submitData[element.name] = element.value;
            if (element.type === 'image') {
                submitData[element.name + '.x'] = win.Math.max(0, e.offsetX);
                submitData[element.name + '.y'] = win.Math.max(0, e.offsetY);
            }
        }
    });

    win.document.addEventListener('submit', function (e) {
        var form = getAjaxTarget(e.target, 'FORM');
        if (form) {
            var req = new win.XMLHttpRequest();
            req.onreadystatechange = onReady(req, true);
            e.preventDefault();
            req.open('post', getAjaxLink(form.getAttribute('action'), form));
            req.setRequestHeader("Content-Type", "application/json");
            req.send(win.JSON.stringify(getFormData(form)));
        }
    });

    var tagNames = /^INPUT|TEXTAREA|SELECT$/;
    var ignoreTypes = /^button|image|submit|reset|file$/;
    var checkTypes = /^checkbox|radio$/;
    function getFormData(form) {
        var elements = form.elements;
        var data = {};
        for (var i = 0; i < elements.length; i++) {
            var element = elements[i];
            if (tagNames.test(element.tagName) && element.name
                && !ignoreTypes.test(element.type) && !element.disabled
                && (!checkTypes.test(element.type) || element.checked))
                data[element.name] = element.value;
        }
        for (var key in submitData)
            data[key] = submitData[key];
        submitData = {};
        return data;
    }

    function getAjaxTarget(element, tagName) {
        var target = null;
        while (element) {
            if (element.getAttribute) {
                var navigation = element.getAttribute('data-navigation');
                if (!target && element.tagName === tagName && navigation === 'refresh')
                    target = element;
                if (navigation === 'noajax')
                    return null;
            }
            element = element.parentNode;
        }
        return target;
    }

    var link = win.location.pathname + win.location.search;
    function refreshAjax(newLink, addHistory, target, title) {
        var req = new win.XMLHttpRequest();
        req.onreadystatechange = onReady(req, addHistory, title);
        req.open('get', getAjaxLink(newLink, target));
        req.send();
    }

    function getAjaxLink(baseLink, target) {
        baseLink += baseLink.indexOf('?') > 0 ? '&' : '?';
        baseLink += 'refreshajax=' + win.encodeURIComponent(link);
        baseLink += '&navigation=' + (target ? 'refresh' : 'history');
        if (target) {
            baseLink = setLinkData(target, baseLink, 'include-current');
            baseLink = setLinkData(target, baseLink, 'current-keys');
            baseLink = setLinkData(target, baseLink, 'to-keys');
        }
        return baseLink;
    }

    function setLinkData(target, link, name) {
        var value = target.getAttribute('data-' + name);
        if (value)
            link += '&' + name.replace('-','') + '=' + win.encodeURIComponent(value);
        return link;
    }

    var cache = {};
    function onReady(req, addHistory, title) {
        var oldLink = link;
        return function () {
            if (req.readyState === 4 && req.status === 200) {
                var resp = win.JSON.parse(req.responseText);
                if (!resp.Title)
                    resp.Title = title ? title : win.document.title;
                handleRespone(resp, addHistory, oldLink);
            }
        };
    }

    var neighbourhood = {};
    neighbourhood[link] = [];
    var links = [link];
    function handleRespone(resp, addHistory, oldLink) {
        if (resp.RedirectLink) {
            win.location.href = resp.RedirectLink;
            return;
        }
        if (link !== oldLink)
            return;
        var backResp = {};
        backResp.Link = link;
        backResp.Title = win.document.title;
        backResp.Panels = {};
        for (var id in resp.Panels) {
            var panel = win.document.getElementById(id);
            backResp.Panels[id] = panel.innerHTML;
            panel.innerHTML = resp.Panels[id];
            var evt;
            if (typeof win.Event === 'function')
                evt = new win.Event('refreshajax');
            else {
                evt = win.document.createEvent('Event');
                evt.initEvent('refreshajax', false, false);
            }
            panel.dispatchEvent(evt);
        }
        var newLink = resp.Link;
        if (link !== newLink) {
            cacheResponse(resp, backResp);
            if (addHistory)
                win.history.pushState(resp.Title, resp.Title, newLink);
        }
        win.document.title = resp.Title;
        link = newLink;
    }

    function cacheResponse(resp, backResp) {
        var newLink = resp.Link;
        cache[link + '&' + newLink] = resp;
        cache[newLink + '&' + link] = backResp;
        if (links.indexOf(newLink) === -1) {
            neighbourhood[newLink] = [];
            links.push(newLink);
        }
        if (neighbourhood[link].indexOf(newLink) === -1) {
            neighbourhood[link].push(newLink);
            neighbourhood[newLink].push(link);
        }
    }

    win.addEventListener('popstate', function (e) {
        var newLink = win.location.pathname + win.location.search;
        if (link !== newLink) {
            var path = getShortestPath(link, newLink);
            if (path) {
                for (var i = 0; i < path.length - 1; i++) {
                    var resp = cache[path[i] + '&' + path[i + 1]];
                    handleRespone(resp, false, path[i]);
                }
            } else
                refreshAjax(newLink, false, null, e.state);
        }
    });

    function getShortestPath(fromLink, toLink) {
        var previous = {};
        var distances = {};
        var unlinks = [];
        var max = links.length;
        for (var i = 0; i < links.length; i++) {
            unlinks.push(links[i]);
            distances[links[i]] = max;
        }
        var distance = distances[fromLink] = 0;
        while (unlinks.length !== 0) {
            distance++;
            var link = unlinks.sort(function (x, y) {
                return distances[x] - distances[y]
            })[0];
            if (link === toLink) {
                var path = [];
                while (toLink) {
                    path.push(toLink);
                    toLink = previous[toLink];
                }
                return path.reverse();
            }
            var neighbours = neighbourhood[link];
            for (var i = 0; i < neighbours.length; i++) {
                var neighbour = neighbours[i];
                if (distances[neighbour] === max) {
                    distances[neighbour] = distance;
                    previous[neighbour] = link;
                }
            }
            unlinks.shift();
        }
        return null;
    }
})(window);<commit_msg>Start of refactor to expose client side api for refresh ajax functionality<commit_after>(function (win) {

    if (!(win.history && win.history.pushState))
        return;

    var submitData = {};
    win.document.addEventListener('click', function (e) {
        var element = e.target;
        var anchor = getAjaxTarget(element, 'A');
        if (!e.ctrlKey && !e.shiftKey && anchor) {
            e.preventDefault();
            refreshAjax(anchor.getAttribute('href'), null, true, anchor);
        }
        if (element.tagName === 'INPUT' && element.name) {
            if (element.type === 'submit')
                submitData[element.name] = element.value;
            if (element.type === 'image') {
                submitData[element.name + '.x'] = win.Math.max(0, e.offsetX);
                submitData[element.name + '.y'] = win.Math.max(0, e.offsetY);
            }
        }
    });

    win.document.addEventListener('submit', function (e) {
        var form = getAjaxTarget(e.target, 'FORM');
        if (form) {
            e.preventDefault();
            refreshAjax(form.getAttribute('action'), getFormData(form), true, form);
        }
    });

    var tagNames = /^INPUT|TEXTAREA|SELECT$/;
    var ignoreTypes = /^button|image|submit|reset|file$/;
    var checkTypes = /^checkbox|radio$/;
    function getFormData(form) {
        var elements = form.elements;
        var data = {};
        for (var i = 0; i < elements.length; i++) {
            var element = elements[i];
            if (tagNames.test(element.tagName) && element.name
                && !ignoreTypes.test(element.type) && !element.disabled
                && (!checkTypes.test(element.type) || element.checked))
                data[element.name] = element.value;
        }
        for (var key in submitData)
            data[key] = submitData[key];
        submitData = {};
        return data;
    }

    function getAjaxTarget(element, tagName) {
        var target = null;
        while (element) {
            if (element.getAttribute) {
                var navigation = element.getAttribute('data-navigation');
                if (!target && element.tagName === tagName && navigation === 'refresh')
                    target = element;
                if (navigation === 'noajax')
                    return null;
            }
            element = element.parentNode;
        }
        return target;
    }

    var link = win.location.pathname + win.location.search;
    function navigate(data) {
        refreshAjax(link, data, true);
    }

    function refreshAjax(newLink, data, addHistory, target, title) {
        var req = new win.XMLHttpRequest();
        req.onreadystatechange = onReady(req, addHistory, title);
        req.open(data ? 'post' : 'get', getAjaxLink(newLink, target));
        if (data)
            req.setRequestHeader("Content-Type", "application/json");
        req.send(win.JSON.stringify(data));
    }

    function getAjaxLink(baseLink, target) {
        baseLink += baseLink.indexOf('?') > 0 ? '&' : '?';
        baseLink += 'refreshajax=' + win.encodeURIComponent(link);
        baseLink += '&navigation=' + (target ? 'refresh' : 'history');
        if (target) {
            baseLink = setLinkData(target, baseLink, 'include-current');
            baseLink = setLinkData(target, baseLink, 'current-keys');
            baseLink = setLinkData(target, baseLink, 'to-keys');
        }
        return baseLink;
    }

    function setLinkData(target, link, name) {
        var value = target.getAttribute('data-' + name);
        if (value)
            link += '&' + name.replace('-','') + '=' + win.encodeURIComponent(value);
        return link;
    }

    var cache = {};
    function onReady(req, addHistory, title) {
        var oldLink = link;
        return function () {
            if (req.readyState === 4 && req.status === 200) {
                var resp = win.JSON.parse(req.responseText);
                if (!resp.Title)
                    resp.Title = title ? title : win.document.title;
                handleRespone(resp, addHistory, oldLink);
            }
        };
    }

    var neighbourhood = {};
    neighbourhood[link] = [];
    var links = [link];
    function handleRespone(resp, addHistory, oldLink) {
        if (resp.RedirectLink) {
            win.location.href = resp.RedirectLink;
            return;
        }
        if (link !== oldLink)
            return;
        var backResp = {};
        backResp.Link = link;
        backResp.Title = win.document.title;
        backResp.Panels = {};
        for (var id in resp.Panels) {
            var panel = win.document.getElementById(id);
            backResp.Panels[id] = panel.innerHTML;
            panel.innerHTML = resp.Panels[id];
            var evt;
            if (typeof win.Event === 'function')
                evt = new win.Event('refreshajax');
            else {
                evt = win.document.createEvent('Event');
                evt.initEvent('refreshajax', false, false);
            }
            panel.dispatchEvent(evt);
        }
        var newLink = resp.Link;
        if (link !== newLink) {
            cacheResponse(resp, backResp);
            if (addHistory)
                win.history.pushState(resp.Title, resp.Title, newLink);
        }
        win.document.title = resp.Title;
        link = newLink;
    }

    function cacheResponse(resp, backResp) {
        var newLink = resp.Link;
        cache[link + '&' + newLink] = resp;
        cache[newLink + '&' + link] = backResp;
        if (links.indexOf(newLink) === -1) {
            neighbourhood[newLink] = [];
            links.push(newLink);
        }
        if (neighbourhood[link].indexOf(newLink) === -1) {
            neighbourhood[link].push(newLink);
            neighbourhood[newLink].push(link);
        }
    }

    win.addEventListener('popstate', function (e) {
        var newLink = win.location.pathname + win.location.search;
        if (link !== newLink) {
            var path = getShortestPath(link, newLink);
            if (path) {
                for (var i = 0; i < path.length - 1; i++) {
                    var resp = cache[path[i] + '&' + path[i + 1]];
                    handleRespone(resp, false, path[i]);
                }
            } else
                refreshAjax(newLink, null, false, null, e.state);
        }
    });

    function getShortestPath(fromLink, toLink) {
        var previous = {};
        var distances = {};
        var unlinks = [];
        var max = links.length;
        for (var i = 0; i < links.length; i++) {
            unlinks.push(links[i]);
            distances[links[i]] = max;
        }
        var distance = distances[fromLink] = 0;
        while (unlinks.length !== 0) {
            distance++;
            var link = unlinks.sort(function (x, y) {
                return distances[x] - distances[y]
            })[0];
            if (link === toLink) {
                var path = [];
                while (toLink) {
                    path.push(toLink);
                    toLink = previous[toLink];
                }
                return path.reverse();
            }
            var neighbours = neighbourhood[link];
            for (var i = 0; i < neighbours.length; i++) {
                var neighbour = neighbours[i];
                if (distances[neighbour] === max) {
                    distances[neighbour] = distance;
                    previous[neighbour] = link;
                }
            }
            unlinks.shift();
        }
        return null;
    }

    win.refreshAjax = {
        navigate: navigate
    };
})(window);<|endoftext|>


<commit_before>(function (win) {

    if (!(win.history && win.history.pushState))
        return;

    win.document.addEventListener('click', function (e) {
        if (e.target.tagName && e.target.tagName === 'A'
            && !e.ctrlKey && !e.shiftKey && ajaxOn(e.target)) {
            e.preventDefault();
            refreshAjax(e.target.getAttribute('href'), true);
        }
    });

    win.document.addEventListener('submit', function (e) {
        if (ajaxOn(e.target)) {
            var els = e.target.elements;
            var req = new win.XMLHttpRequest();
            req.onreadystatechange = onReady(req, true, null);
            var data = {};
            for (var i = 0; i < els.length; i++) {
                data[els[0].name] = els[0].value;
            }
            e.preventDefault();
            req.open('post', getLink(e.target.action));
            req.setRequestHeader("Content-Type", "application/json");
            req.send(win.JSON.stringify(data));
        }
    });

    function ajaxOn(element) {
        if (element.getAttribute('data-navigation') === 'refresh') {
            var ajax = true;
            while (element != null && ajax) {
                if (element.getAttribute)
                    ajax = element.getAttribute('data-navigation') !== 'noajax';
                element = element.parentNode;
            }
            return ajax;
        }
        return false;
    }

    win.addEventListener('popstate', function (e) {
        refreshAjax(win.location.pathname + win.location.search, false)
    });

    var link = win.location.pathname + win.location.search;
    function refreshAjax(newLink, addHistory) {
        var req = new win.XMLHttpRequest();
        req.onreadystatechange = onReady(req, addHistory, newLink);
        req.open('get', getLink(newLink));
        req.send();
    }

    function getLink(baseLink) {
        baseLink += baseLink.indexOf('?') > 0 ? '&' : '?';
        baseLink += 'refreshajax=' + win.encodeURIComponent(link);
        return baseLink;
    }

    function onReady(req, addHistory, newLink) {
        return function () {
            if (req.readyState === 4 && req.status === 200) {
                var resp = win.JSON.parse(req.responseText);
                for (var id in resp.Panels) {
                    var panel = win.document.getElementById(id);
                    panel.innerHTML = resp.Panels[id];
                    var evt;
                    if (typeof Event === 'function')
                        evt = new win.Event('refreshajax');
                    else {
                        evt = win.document.createEvent('Event');
                        evt.initEvent('refreshajax', false, false);
                    }
                    panel.dispatchEvent(evt);
                }
                if (!newLink)
                    newLink = resp.Link;
                if (addHistory)
                    win.history.pushState(newLink, win.document.title, newLink);
                link = newLink;
            }
        };
    }
})(window);<commit_msg>Renamed variable for consistency<commit_after>(function (win) {

    if (!(win.history && win.history.pushState))
        return;

    win.document.addEventListener('click', function (e) {
        if (e.target.tagName && e.target.tagName === 'A'
            && !e.ctrlKey && !e.shiftKey && ajaxOn(e.target)) {
            e.preventDefault();
            refreshAjax(e.target.getAttribute('href'), true);
        }
    });

    win.document.addEventListener('submit', function (e) {
        if (ajaxOn(e.target)) {
            var elements = e.target.elements;
            var req = new win.XMLHttpRequest();
            req.onreadystatechange = onReady(req, true, null);
            var data = {};
            for (var i = 0; i < elements.length; i++) {
                data[elements[0].name] = elements[0].value;
            }
            e.preventDefault();
            req.open('post', getLink(e.target.action));
            req.setRequestHeader("Content-Type", "application/json");
            req.send(win.JSON.stringify(data));
        }
    });

    function ajaxOn(element) {
        if (element.getAttribute('data-navigation') === 'refresh') {
            var ajax = true;
            while (element != null && ajax) {
                if (element.getAttribute)
                    ajax = element.getAttribute('data-navigation') !== 'noajax';
                element = element.parentNode;
            }
            return ajax;
        }
        return false;
    }

    win.addEventListener('popstate', function (e) {
        refreshAjax(win.location.pathname + win.location.search, false)
    });

    var link = win.location.pathname + win.location.search;
    function refreshAjax(newLink, addHistory) {
        var req = new win.XMLHttpRequest();
        req.onreadystatechange = onReady(req, addHistory, newLink);
        req.open('get', getLink(newLink));
        req.send();
    }

    function getLink(baseLink) {
        baseLink += baseLink.indexOf('?') > 0 ? '&' : '?';
        baseLink += 'refreshajax=' + win.encodeURIComponent(link);
        return baseLink;
    }

    function onReady(req, addHistory, newLink) {
        return function () {
            if (req.readyState === 4 && req.status === 200) {
                var resp = win.JSON.parse(req.responseText);
                for (var id in resp.Panels) {
                    var panel = win.document.getElementById(id);
                    panel.innerHTML = resp.Panels[id];
                    var evt;
                    if (typeof Event === 'function')
                        evt = new win.Event('refreshajax');
                    else {
                        evt = win.document.createEvent('Event');
                        evt.initEvent('refreshajax', false, false);
                    }
                    panel.dispatchEvent(evt);
                }
                if (!newLink)
                    newLink = resp.Link;
                if (addHistory)
                    win.history.pushState(newLink, win.document.title, newLink);
                link = newLink;
            }
        };
    }
})(window);<|endoftext|>


<commit_before>$(function(){
  $.each($('form[validate=true]'), function(i, form){
    $(form).submit(validate);
  });
});

function validate(e){
  var form = $(e.target);

  if(form.data('validated') == true){
    return true;
  }

  $.post(form.attr('action') + '/validate', $(e.target).serialize(), function(data){
    // clear all error messages
    $('.error-message').text('');

    if($.isEmptyObject(data)){
      form.data('validated', true);
      form.submit();
    } else {
      $.each(data, function(key, value){
        $('#error-' + key).text(value);
      });
    }
  }, 'json');
  return false;
}
<commit_msg>Fix validation error message cleaning.<commit_after>$(function(){
  $.each($('form[validate=true]'), function(i, form){
    $(form).submit(validate);
  });
});

function validate(e){
  var form = $(e.target);

  if(form.data('validated') == true){
    return true;
  }

  $.post(form.attr('action') + '/validate', $(e.target).serialize(), function(data){
    // clear all error messages
    $('.error').text('');

    if($.isEmptyObject(data)){
      form.data('validated', true);
      form.submit();
    } else {
      $.each(data, function(key, value){
        $('#error-' + key).text(value);
      });
    }
  }, 'json');
  return false;
}
<|endoftext|>


<commit_before>import './editable_node_selector.html';

import {Blaze} from 'meteor/blaze';
import {Template} from 'meteor/templating';

import './x_editable_node_selector.js';
import './x_editable_node_selector.css';

/**
 * Template Helpers
 */
Template.EditableNodeSelector.helpers({});

/**
 * Template Event Handlers
 */
Template.EditableNodeSelector.events({});

/**
 * Template Created
 */
Template.EditableNodeSelector.created = function () {

};

/**
 * Template Rendered
 */
Template.EditableNodeSelector.rendered = function () {
  var instance = Template.instance();

  instance.$(".editable").editable({
    type: "nodeSelector",
    mode: instance.data.mode || "popup",
    placement: instance.data.placement || "auto",
    value: instance.data.value,
    projectVersionId: instance.data.projectVersionId,
    parentInstance: instance,
    highlight: false,
    display() {},
    success(response, newValue) {
      console.log("EditableNodeSelector edited: ", newValue);
      var editedElement = this;
      $(editedElement).trigger("edited", [newValue]);
      setTimeout(function () {
        $(editedElement).removeClass('editable-unsaved');
      }, 10);
    }
  });

  // this event listener needs to be registered directly
  instance.$(".editable").on("hidden", function() {
    if(instance.searchView){
      setTimeout(function () {
        Blaze.remove(instance.searchView);
      }, 100);
    }
  });

  instance.autorun(function () {
    var data = Template.currentData();
    instance.$(".editable").editable("setValue", data.value);
  });
};

/**
 * Template Destroyed
 */
Template.EditableNodeSelector.destroyed = function () {

};
<commit_msg>Code Cleanup<commit_after>import './editable_node_selector.html';
import { Blaze } from 'meteor/blaze';
import { Template } from 'meteor/templating';
import './x_editable_node_selector.js';
import './x_editable_node_selector.css';

/**
 * Template Helpers
 */
Template.EditableNodeSelector.helpers({});

/**
 * Template Event Handlers
 */
Template.EditableNodeSelector.events({});

/**
 * Template Created
 */
Template.EditableNodeSelector.created = function () {

};

/**
 * Template Rendered
 */
Template.EditableNodeSelector.rendered = function () {
  var instance = Template.instance();

  instance.$(".editable").editable({
    type            : "nodeSelector",
    mode            : instance.data.mode || "popup",
    placement       : instance.data.placement || "auto",
    value           : instance.data.value,
    projectVersionId: instance.data.projectVersionId,
    parentInstance  : instance,
    highlight       : false,
    display() {
    },
    success(response, newValue) {
      console.log("EditableNodeSelector edited: ", newValue);
      var editedElement = this;
      $(editedElement).trigger("edited", [ newValue ]);
      setTimeout(function () {
        $(editedElement).removeClass('editable-unsaved');
      }, 10);
    }
  });

  // this event listener needs to be registered directly
  instance.$(".editable").on("hidden", function () {
    if (instance.searchView) {
      setTimeout(function () {
        Blaze.remove(instance.searchView);
      }, 100);
    }
  });

  instance.autorun(function () {
    var data = Template.currentData();
    instance.$(".editable").editable("setValue", data.value);
  });
};

/**
 * Template Destroyed
 */
Template.EditableNodeSelector.destroyed = function () {

};
<|endoftext|>


<commit_before>import { createSelector } from 'reselect'
import { getMapOverlays, getMapZoom } from '../map/selectors'

export const FETCH_PANEL_ITEMS_REQUEST = 'FETCH_PANEL_ITEMS_REQUEST'
export const FETCH_PANEL_ITEMS_SUCCESS = 'FETCH_PANEL_ITEMS_SUCCESS'
export const FETCH_PANEL_ITEMS_FAILURE = 'FETCH_PANEL_ITEMS_FAILURE'

const initialState = {
  items: [],
  isLoading: false,
  error: null,
}

export const getActiveMapLayers = ({ map, mapLayers, user }) =>
  map.overlays
    .filter((overlay) => overlay.isVisible)
    .map((overlay) => {
      const overlayId = overlay.id.split('-')[1] || overlay.id // The active mapLayer must be matched to the original mapLayer independent of the collection it's in
      const mapLayer = mapLayers.layers.items.find((layer) => layer.id === overlayId) || {}

      return {
        ...mapLayer,
        id: overlay.id, // We want to preserve the ID of the overlay
      }
    })
    .filter((layer) => {
      return (
        layer.detailUrl &&
        !layer.noDetail &&
        (!layer.authScope || user.scopes.includes(layer.authScope))
      )
    })

export const getPanelLayers = (state) => state.mapLayers.panelLayers?.items || []

// Selector to get the mapLayers from the collections in the redux state
export const getMapPanelLayers = createSelector(getPanelLayers, (panelLayers) =>
  panelLayers.length > 0
    ? panelLayers.reduce((acc, cur) => {
        if (cur.mapLayers) {
          return [...acc, ...cur.mapLayers]
        }
        return cur
      }, [])
    : [],
)

export const selectActivePanelLayers = createSelector(
  [getMapPanelLayers, getMapOverlays],
  (panelLayers, overlays) => {
    const mapLayerIds = overlays.map((mapLayer) => mapLayer.id)

    return panelLayers
      .filter((mapLayer) =>
        [mapLayer.id, ...(mapLayer.legendItems?.map((legendItem) => legendItem.id) || [])]
          .filter((mapLayerId) => Boolean(mapLayerId))
          .some((mapLayerId) => mapLayerIds.includes(mapLayerId)),
      )
      .sort((a, b) => {
        const aId = a.id || a.legendItems[0].id
        const bId = b.id || b.legendItems[0].id
        return mapLayerIds.indexOf(bId) - mapLayerIds.indexOf(aId)
      })
  },
)

export const getActiveMapLayersWithinZoom = createSelector(
  [getMapZoom, selectActivePanelLayers],
  (zoomLevel, activePanelLayers) =>
    activePanelLayers.filter(
      (mapLayer) => zoomLevel >= mapLayer.minZoom && zoomLevel <= mapLayer.maxZoom,
    ),
)

export const selectNotClickableVisibleMapLayers = createSelector(
  [getActiveMapLayersWithinZoom, getMapOverlays],
  (activePanelLayers, overlays) =>
    activePanelLayers
      .map((mapLayer) => [mapLayer, ...mapLayer.legendItems])
      .reduce((accumulator, legendItems) => accumulator.concat(legendItems), [])
      .filter((legendItem) => legendItem.noDetail)
      .filter((legendItem) =>
        overlays.some((overlay) => overlay.id === legendItem.id && overlay.isVisible),
      ),
)

export const getLayers = createSelector(
  [getMapPanelLayers, getActiveMapLayers, getMapZoom],
  (panelLayers, activeMapLayers, zoom) =>
    activeMapLayers.filter((layer) => {
      const matchingPanelLayer = panelLayers.find(
        (panelLayer) =>
          panelLayer.id === layer.id ||
          panelLayer?.legendItems?.some((legendItem) => legendItem.id === layer.id),
      )
      return (
        matchingPanelLayer &&
        zoom <= matchingPanelLayer.maxZoom &&
        zoom >= matchingPanelLayer.minZoom
      )
    }),
)

export default function PanelLayersReducer(state = initialState, action) {
  switch (action.type) {
    case FETCH_PANEL_ITEMS_REQUEST:
      return { ...state, isLoading: true, error: null }

    case FETCH_PANEL_ITEMS_SUCCESS:
      return { ...state, isLoading: false, items: action.panelLayers }

    case FETCH_PANEL_ITEMS_FAILURE:
      return { ...state, isLoading: false, error: action.error }

    default:
      return state
  }
}

export const fetchPanelLayers = (panelLayers) => ({
  type: FETCH_PANEL_ITEMS_REQUEST,
  panelLayers,
})
<commit_msg>Fixed issue where legendItems need to be spread but can be undefined<commit_after>import { createSelector } from 'reselect'
import { getMapOverlays, getMapZoom } from '../map/selectors'

export const FETCH_PANEL_ITEMS_REQUEST = 'FETCH_PANEL_ITEMS_REQUEST'
export const FETCH_PANEL_ITEMS_SUCCESS = 'FETCH_PANEL_ITEMS_SUCCESS'
export const FETCH_PANEL_ITEMS_FAILURE = 'FETCH_PANEL_ITEMS_FAILURE'

const initialState = {
  items: [],
  isLoading: false,
  error: null,
}

export const getActiveMapLayers = ({ map, mapLayers, user }) =>
  map.overlays
    .filter((overlay) => overlay.isVisible)
    .map((overlay) => {
      const overlayId = overlay.id.split('-')[1] || overlay.id // The active mapLayer must be matched to the original mapLayer independent of the collection it's in
      const mapLayer = mapLayers.layers.items.find((layer) => layer.id === overlayId) || {}

      return {
        ...mapLayer,
        id: overlay.id, // We want to preserve the ID of the overlay
      }
    })
    .filter((layer) => {
      return (
        layer.detailUrl &&
        !layer.noDetail &&
        (!layer.authScope || user.scopes.includes(layer.authScope))
      )
    })

export const getPanelLayers = (state) => state.mapLayers.panelLayers?.items || []

// Selector to get the mapLayers from the collections in the redux state
export const getMapPanelLayers = createSelector(getPanelLayers, (panelLayers) =>
  panelLayers.length > 0
    ? panelLayers.reduce((acc, cur) => {
        if (cur.mapLayers) {
          return [...acc, ...cur.mapLayers]
        }
        return cur
      }, [])
    : [],
)

export const selectActivePanelLayers = createSelector(
  [getMapPanelLayers, getMapOverlays],
  (panelLayers, overlays) => {
    const mapLayerIds = overlays.map((mapLayer) => mapLayer.id)

    return panelLayers
      .filter((mapLayer) =>
        [mapLayer.id, ...(mapLayer.legendItems?.map((legendItem) => legendItem.id) || [])]
          .filter((mapLayerId) => Boolean(mapLayerId))
          .some((mapLayerId) => mapLayerIds.includes(mapLayerId)),
      )
      .sort((a, b) => {
        const aId = a.id || a.legendItems[0].id
        const bId = b.id || b.legendItems[0].id
        return mapLayerIds.indexOf(bId) - mapLayerIds.indexOf(aId)
      })
  },
)

export const getActiveMapLayersWithinZoom = createSelector(
  [getMapZoom, selectActivePanelLayers],
  (zoomLevel, activePanelLayers) =>
    activePanelLayers.filter(
      (mapLayer) => zoomLevel >= mapLayer.minZoom && zoomLevel <= mapLayer.maxZoom,
    ),
)

export const selectNotClickableVisibleMapLayers = createSelector(
  [getActiveMapLayersWithinZoom, getMapOverlays],
  (activePanelLayers, overlays) =>
    activePanelLayers
      .map((mapLayer) => [mapLayer, ...(mapLayer.legendItems || [])])
      .reduce((accumulator, legendItems) => accumulator.concat(legendItems), [])
      .filter((legendItem) => legendItem.noDetail)
      .filter((legendItem) =>
        overlays.some((overlay) => overlay.id === legendItem.id && overlay.isVisible),
      ),
)

export const getLayers = createSelector(
  [getMapPanelLayers, getActiveMapLayers, getMapZoom],
  (panelLayers, activeMapLayers, zoom) =>
    activeMapLayers.filter((layer) => {
      const matchingPanelLayer = panelLayers.find(
        (panelLayer) =>
          panelLayer.id === layer.id ||
          panelLayer?.legendItems?.some((legendItem) => legendItem.id === layer.id),
      )
      return (
        matchingPanelLayer &&
        zoom <= matchingPanelLayer.maxZoom &&
        zoom >= matchingPanelLayer.minZoom
      )
    }),
)

export default function PanelLayersReducer(state = initialState, action) {
  switch (action.type) {
    case FETCH_PANEL_ITEMS_REQUEST:
      return { ...state, isLoading: true, error: null }

    case FETCH_PANEL_ITEMS_SUCCESS:
      return { ...state, isLoading: false, items: action.panelLayers }

    case FETCH_PANEL_ITEMS_FAILURE:
      return { ...state, isLoading: false, error: action.error }

    default:
      return state
  }
}

export const fetchPanelLayers = (panelLayers) => ({
  type: FETCH_PANEL_ITEMS_REQUEST,
  panelLayers,
})
<|endoftext|>


<commit_before>/* global Inputmask formatJS */
/* eslint-disable no-unused-vars */

var DateTimeHelper = (function() {
  function isValidTimeStr(timeStr) {
    return /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/.test(timeStr);
  }

  function isValidDate(date) {
    return (date instanceof Date) && !isNaN(date.getTime());
  }

  function addLeadingZero(value) {
    return ('0' + value).slice(-2);
  }

  function recalcTimestamp(date, timeStr) {
    if (!isValidTimeStr(timeStr)) {
      date.setHours(0);
      date.setMinutes(0);
      return date;
    }

    date.setHours(timeStr.split(':')[0]);
    date.setMinutes(timeStr.split(':')[1]);
    return date;
  }

  function stringDateTimeFormat(date, format) {
    let y = date.getFullYear();
    let m = addLeadingZero(date.getMonth() + 1);
    let d = addLeadingZero(date.getDate());
    let hours = addLeadingZero(date.getHours());
    let mins = addLeadingZero(date.getMinutes());

    if (format === 'dateonly') {
      return `${y}/${m}/${d}`;
    }
    return `${y}/${m}/${d} ${hours}:${mins}`;
  }

  function insertHiddenField($container) {
    let formId = $container.data('form-id');
    let columnId = $container.data('column-id');
    let dateStr = $container.find('input.date-part').data('selected-date');
    let timeStr = $container.find('input.time-part').val();
    let columnType = $container.data('type');
    let date = new Date(dateStr);
    let value;
    let hiddenField;

    if (isValidDate(date) && isValidTimeStr(timeStr)) {
      value = stringDateTimeFormat(recalcTimestamp(date, timeStr), 'full');
    } else {
      value = '';
    }

    hiddenField = `
      <input class="repository-cell-value"
             type="hidden"
             form="${formId}"
             name="repository_cells[${columnId}]"
             value="${value}"
             data-type="${columnType}"/>`;

    $container.find('input.repository-cell-value').remove('input.repository-cell-value');
    $container.prepend(hiddenField);
  }

  function insertRangeHiddenField($container) {
    let formId = $container.data('form-id');
    let columnId = $container.data('column-id');
    let columnType = $container.data('type');
    let $startContainer = $container.find('.start-time');
    let $endContainer = $container.find('.end-time');
    let startDate = new Date($startContainer.find('input.date-part').data('selected-date'));
    let startTimeStr = $startContainer.find('input.time-part').val();
    let endDate = new Date($endContainer.find('input.date-part').data('selected-date'));
    let endTimeStr = $endContainer.find('input.time-part').val();
    let hiddenField;
    let value = '';

    if (isValidDate(startDate)
      && isValidTimeStr(startTimeStr)
      && isValidDate(endDate)
      && isValidTimeStr(endTimeStr)) {
      let start = stringDateTimeFormat(recalcTimestamp(startDate, startTimeStr), 'full');
      let end = stringDateTimeFormat(recalcTimestamp(endDate, endTimeStr), 'full');
      value = JSON.stringify({ start_time: start, end_time: end });
    }

    hiddenField = `
      <input class="repository-cell-value"
             type="hidden"
             form="${formId}"
             name="repository_cells[${columnId}]"
             value='${value}'
             data-type="${columnType}"/>`;

    $container.find('input.repository-cell-value').remove('input.repository-cell-value');
    $container.prepend(hiddenField);
  }

  function initChangeEvents($cell) {
    $cell.find('input.time-part').on('change', function() {
      let $input = $(this);
      let $container = $input.closest('.datetime-container');

      if ($container.hasClass('range-type')) {
        insertRangeHiddenField($container);
      } else {
        insertHiddenField($container);
      }
    });

    $cell.find('input.date-part').on('dp.change', function(e) {
      let $input = $(this);
      let date = e.date._d;
      let $container = $input.closest('.datetime-container');

      if (date !== undefined) {
        $input.data('selected-date', stringDateTimeFormat(date, 'dateonly'));
      } else {
        $input.data('selected-date', '');
      }

      if ($container.hasClass('range-type')) {
        insertRangeHiddenField($container);
      } else {
        insertHiddenField($container);
      }
    });
  }

  function dateInputField(value, dateDataValue) {
    return `
       <input class="form-control editing calendar-input date-part"
              type="datetime"
              data-datetime-part="date"
              data-selected-date="${dateDataValue}"
              value='${value}'/>
    `;
  }

  function timeInputField(value) {
    return `
      <input class="form-control editing time-part"
             type="text"
             data-mask-type="time"
             value='${value}'
             placeholder="HH:mm"/>
    `;
  }

  function getDateOrDefault($span, mode) {
    let dateStr = $span.data('date');
    let date;
    if (mode === 'timeonly') {
      // Set default date if no data in span
      date = new Date(dateStr);
      if (isValidDate(date)) {
        dateStr = stringDateTimeFormat(new Date(date), 'dateonly');
      } else {
        dateStr = stringDateTimeFormat(new Date(), 'dateonly');
      }
    }
    return dateStr;
  }

  function getTimeOrDefault($span, mode) {
    let timeStr = $span.data('time');

    if ((mode === 'dateonly') && (!isValidTimeStr(timeStr))) {
      timeStr = '00:00';
    }
    return timeStr;
  }

  function initDateTimeEditMode(formId, columnId, $cell, mode, columnType) {
    let $span = $cell.find('span').first();
    let date = $span.data('date');
    let dateDataValue = getDateOrDefault($span, mode);
    let time = getTimeOrDefault($span, mode);
    let datetime = $span.data('datetime');
    let inputFields = `

      ${dateInputField(date, dateDataValue)}
      ${timeInputField(time)}

  `;

    $cell.html(inputFields);

    Inputmask('datetime', {
      inputFormat: 'HH:MM',
      placeholder: 'HH:mm',
      clearIncomplete: true,
      showMaskOnHover: true,
      hourFormat: 24
    }).mask($cell.find('input[data-mask-type="time"]'));

    $cell.find('.calendar-input').datetimepicker({ ignoreReadonly: true, locale: 'en', format: formatJS });
    initChangeEvents($cell);
  }

  function initDateTimeRangeEditMode(formId, columnId, $cell, mode, columnType) {
    let $startSpan = $cell.find('span').first();
    let startDate = $startSpan.data('date');
    let startTime = getTimeOrDefault($startSpan, mode);
    let startDatetime = $startSpan.data('datetime');
    let startDateDataValue = getDateOrDefault($startSpan, mode);
    let $endSpan = $cell.find('span').last();
    let endDate = $endSpan.data('date');
    let endTime = getTimeOrDefault($endSpan, mode);
    let endDatetime = $endSpan.data('datetime');
    let endDateDataValue = getDateOrDefault($endSpan, mode);

    let inputFields = `

        ${dateInputField(startDate, startDateDataValue)}
        ${timeInputField(startTime)}

        ${dateInputField(endDate, endDateDataValue)}
        ${timeInputField(endTime)}

  `;

    $cell.html(inputFields);

    Inputmask('datetime', {
      inputFormat: 'HH:MM',
      placeholder: 'HH:mm',
      clearIncomplete: true,
      showMaskOnHover: true,
      hourFormat: 24
    }).mask($cell.find('input[data-mask-type="time"]'));

    let $cal1 = $cell.find('.calendar-input').first().datetimepicker({ ignoreReadonly: true, locale: 'en', format: formatJS });
    let $cal2 = $cell.find('.calendar-input').last().datetimepicker({ ignoreReadonly: true, locale: 'en', format: formatJS });

    $cal1.on('dp.change', function(e) {
      $cal2.data('DateTimePicker').minDate(e.date);
    });
    $cal2.on('dp.change', function(e) {
      $cal1.data('DateTimePicker').maxDate(e.date);
    });

    initChangeEvents($cell);
  }

  return {
    initDateTimeEditMode: initDateTimeEditMode,
    initDateTimeRangeEditMode: initDateTimeRangeEditMode
  };
}());
<commit_msg>JS fix<commit_after>/* global Inputmask formatJS */
/* eslint-disable no-unused-vars */

var DateTimeHelper = (function() {
  function isValidTimeStr(timeStr) {
    return /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/.test(timeStr);
  }

  function isValidDate(date) {
    return (date instanceof Date) && !isNaN(date.getTime());
  }

  function addLeadingZero(value) {
    return ('0' + value).slice(-2);
  }

  function recalcTimestamp(date, timeStr) {
    if (!isValidTimeStr(timeStr)) {
      date.setHours(0);
      date.setMinutes(0);
      return date;
    }

    date.setHours(timeStr.split(':')[0]);
    date.setMinutes(timeStr.split(':')[1]);
    return date;
  }

  function stringDateTimeFormat(date, format) {
    let y = date.getFullYear();
    let m = addLeadingZero(date.getMonth() + 1);
    let d = addLeadingZero(date.getDate());
    let hours = addLeadingZero(date.getHours());
    let mins = addLeadingZero(date.getMinutes());

    if (format === 'dateonly') {
      return `${y}/${m}/${d}`;
    }
    return `${y}/${m}/${d} ${hours}:${mins}`;
  }

  function insertHiddenField($container) {
    let formId = $container.data('form-id');
    let columnId = $container.data('column-id');
    let dateStr = $container.find('input.date-part').data('selected-date');
    let timeStr = $container.find('input.time-part').val();
    let columnType = $container.data('type');
    let date = new Date(dateStr);
    let value = '';
    let hiddenField;

    if (isValidDate(date) && isValidTimeStr(timeStr)) {
      value = stringDateTimeFormat(recalcTimestamp(date, timeStr), 'full');
    }

    hiddenField = `
      <input class="repository-cell-value"
             type="hidden"
             form="${formId}"
             name="repository_cells[${columnId}]"
             value="${value}"
             data-type="${columnType}"/>`;

    $container.find('input.repository-cell-value').remove();
    $container.prepend(hiddenField);
  }

  function insertRangeHiddenField($container) {
    let formId = $container.data('form-id');
    let columnId = $container.data('column-id');
    let columnType = $container.data('type');
    let $startContainer = $container.find('.start-time');
    let $endContainer = $container.find('.end-time');
    let startDate = new Date($startContainer.find('input.date-part').data('selected-date'));
    let startTimeStr = $startContainer.find('input.time-part').val();
    let endDate = new Date($endContainer.find('input.date-part').data('selected-date'));
    let endTimeStr = $endContainer.find('input.time-part').val();
    let hiddenField;
    let value = '';

    if (isValidDate(startDate)
      && isValidTimeStr(startTimeStr)
      && isValidDate(endDate)
      && isValidTimeStr(endTimeStr)) {
      let start = stringDateTimeFormat(recalcTimestamp(startDate, startTimeStr), 'full');
      let end = stringDateTimeFormat(recalcTimestamp(endDate, endTimeStr), 'full');
      value = JSON.stringify({ start_time: start, end_time: end });
    }

    hiddenField = `
      <input class="repository-cell-value"
             type="hidden"
             form="${formId}"
             name="repository_cells[${columnId}]"
             value='${value}'
             data-type="${columnType}"/>`;

    $container.find('input.repository-cell-value').remove();
    $container.prepend(hiddenField);
  }

  function initChangeEvents($cell) {
    $cell.find('input.time-part').on('change', function() {
      let $input = $(this);
      let $container = $input.closest('.datetime-container');

      if ($container.hasClass('range-type')) {
        insertRangeHiddenField($container);
      } else {
        insertHiddenField($container);
      }
    });

    $cell.find('input.date-part').on('dp.change', function(e) {
      let $input = $(this);
      let date = e.date._d;
      let $container = $input.closest('.datetime-container');

      if (date !== undefined) {
        $input.data('selected-date', stringDateTimeFormat(date, 'dateonly'));
      } else {
        $input.data('selected-date', '');
      }

      if ($container.hasClass('range-type')) {
        insertRangeHiddenField($container);
      } else {
        insertHiddenField($container);
      }
    });
  }

  function dateInputField(value, dateDataValue) {
    return `
       <input class="form-control editing calendar-input date-part"
              type="datetime"
              data-datetime-part="date"
              data-selected-date="${dateDataValue}"
              value='${value}'/>
    `;
  }

  function timeInputField(value) {
    return `
      <input class="form-control editing time-part"
             type="text"
             data-mask-type="time"
             value='${value}'
             placeholder="HH:mm"/>
    `;
  }

  function getDateOrDefault($span, mode) {
    let dateStr = $span.data('date');
    let date;
    if (mode === 'timeonly') {
      // Set default date if no data in span
      date = new Date(dateStr);
      if (isValidDate(date)) {
        dateStr = stringDateTimeFormat(new Date(date), 'dateonly');
      } else {
        dateStr = stringDateTimeFormat(new Date(), 'dateonly');
      }
    }
    return dateStr;
  }

  function getTimeOrDefault($span, mode) {
    let timeStr = $span.data('time');

    if ((mode === 'dateonly') && (!isValidTimeStr(timeStr))) {
      timeStr = '00:00';
    }
    return timeStr;
  }

  function initDateTimeEditMode(formId, columnId, $cell, mode, columnType) {
    let $span = $cell.find('span').first();
    let date = $span.data('date');
    let dateDataValue = getDateOrDefault($span, mode);
    let time = getTimeOrDefault($span, mode);
    let datetime = $span.data('datetime');
    let inputFields = `

      ${dateInputField(date, dateDataValue)}
      ${timeInputField(time)}

  `;

    $cell.html(inputFields);

    Inputmask('datetime', {
      inputFormat: 'HH:MM',
      placeholder: 'HH:mm',
      clearIncomplete: true,
      showMaskOnHover: true,
      hourFormat: 24
    }).mask($cell.find('input[data-mask-type="time"]'));

    $cell.find('.calendar-input').datetimepicker({ ignoreReadonly: true, locale: 'en', format: formatJS });
    initChangeEvents($cell);
  }

  function initDateTimeRangeEditMode(formId, columnId, $cell, mode, columnType) {
    let $startSpan = $cell.find('span').first();
    let startDate = $startSpan.data('date');
    let startTime = getTimeOrDefault($startSpan, mode);
    let startDatetime = $startSpan.data('datetime');
    let startDateDataValue = getDateOrDefault($startSpan, mode);
    let $endSpan = $cell.find('span').last();
    let endDate = $endSpan.data('date');
    let endTime = getTimeOrDefault($endSpan, mode);
    let endDatetime = $endSpan.data('datetime');
    let endDateDataValue = getDateOrDefault($endSpan, mode);

    let inputFields = `

        ${dateInputField(startDate, startDateDataValue)}
        ${timeInputField(startTime)}

        ${dateInputField(endDate, endDateDataValue)}
        ${timeInputField(endTime)}

  `;

    $cell.html(inputFields);

    Inputmask('datetime', {
      inputFormat: 'HH:MM',
      placeholder: 'HH:mm',
      clearIncomplete: true,
      showMaskOnHover: true,
      hourFormat: 24
    }).mask($cell.find('input[data-mask-type="time"]'));

    let $cal1 = $cell.find('.calendar-input').first().datetimepicker({ ignoreReadonly: true, locale: 'en', format: formatJS });
    let $cal2 = $cell.find('.calendar-input').last().datetimepicker({ ignoreReadonly: true, locale: 'en', format: formatJS });

    $cal1.on('dp.change', function(e) {
      $cal2.data('DateTimePicker').minDate(e.date);
    });
    $cal2.on('dp.change', function(e) {
      $cal1.data('DateTimePicker').maxDate(e.date);
    });

    initChangeEvents($cell);
  }

  return {
    initDateTimeEditMode: initDateTimeEditMode,
    initDateTimeRangeEditMode: initDateTimeRangeEditMode
  };
}());
<|endoftext|>


<commit_before>'use strict';

let angular = require('angular');

module.exports = angular.module('spinnaker.core.pipeline.stage.pipelineStage', [])
  .config(function(pipelineConfigProvider) {
    pipelineConfigProvider.registerStage({
      label: 'Pipeline',
      description: 'Runs a pipeline',
      key: 'pipeline',
      controller: 'pipelineStageCtrl',
      controllerAs: 'pipelineStageCtrl',
      templateUrl: require('./pipelineStage.html'),
      executionDetailsUrl: require('./pipelineExecutionDetails.html'),
      validators: [
        { type: 'requiredField', fieldName: 'pipeline', },
      ],
    });
  }).controller('pipelineStageCtrl', function($scope, stage, pipelineConfigService, applicationReader, _) {

    $scope.stage = stage;

    if (!$scope.stage.application) {
      $scope.stage.application = $scope.application.name;
    }

    $scope.viewState = {
      mastersLoaded: false,
      mastersRefreshing: false,
      mastersLastRefreshed: null,
      pipelinesLoaded : false,
      jobsRefreshing: false,
      jobsLastRefreshed: null,
      infiniteScroll: {
        numToAdd: 20,
        currentItems: 20,
      },
    };

    this.addMoreItems = function() {
      $scope.viewState.infiniteScroll.currentItems += $scope.viewState.infiniteScroll.numToAdd;
    };

    applicationReader.listApplications().then(function(applications) {
      $scope.applications = _.pluck(applications, 'name').sort();
      initializeMasters();
    });

    function initializeMasters() {
      if ($scope.stage.application) {
        pipelineConfigService.getPipelinesForApplication($scope.stage.application).then(function (pipelines) {
          $scope.pipelines = _.filter( pipelines, function(pipeline){ return pipeline.id !== $scope.pipeline.id; } );
          if (!_.find( pipelines, function(pipeline) { return pipeline.id === $scope.stage.pipeline; })) {
            $scope.stage.pipeline = null;
          }
          $scope.viewState.pipelinesLoaded = true;
          updatePipelineConfig();
        });
      }
    }

    function updatePipelineConfig() {
      if ($scope.stage && $scope.stage.application && $scope.stage.pipeline) {
        var config = _.find( $scope.pipelines, function(pipeline){ return pipeline.id === $scope.stage.pipeline; } );
        if(config && config.parameterConfig) {
          if (!$scope.stage.pipelineParameters) {
            $scope.stage.pipelineParameters = {};
          }
          $scope.pipelineParameters = config.parameterConfig;
          $scope.userSuppliedParameters = $scope.stage.pipelineParameters;
          $scope.useDefaultParameters = {};
          _.each($scope.pipelineParameters, function (property) {
            if (!(property.name in $scope.stage.pipelineParameters) && (property.default !== null)) {
              $scope.useDefaultParameters[property.name] = true;
            }
          });
        } else {
          clearParams();
        }
      } else {
        clearParams();
      }
    }

    function clearParams() {
      $scope.pipelineParameters = {};
      $scope.useDefaultParameters = {};
      $scope.userSuppliedParameters = {};
    }

    $scope.useDefaultParameters = {};
    $scope.userSuppliedParameters = {};

    this.updateParam = function(parameter){
      if($scope.useDefaultParameters[parameter] === true){
        delete $scope.userSuppliedParameters[parameter];
        delete $scope.stage.parameters[parameter];
      } else if($scope.userSuppliedParameters[parameter]){
        $scope.stage.pipelineParameters[parameter] = $scope.userSuppliedParameters[parameter];
      }
    };

    $scope.$watch('stage.application', initializeMasters);
    $scope.$watch('stage.pipeline', updatePipelineConfig);

  });

<commit_msg>fix copy/paste bug<commit_after>'use strict';

let angular = require('angular');

module.exports = angular.module('spinnaker.core.pipeline.stage.pipelineStage', [])
  .config(function(pipelineConfigProvider) {
    pipelineConfigProvider.registerStage({
      label: 'Pipeline',
      description: 'Runs a pipeline',
      key: 'pipeline',
      controller: 'pipelineStageCtrl',
      controllerAs: 'pipelineStageCtrl',
      templateUrl: require('./pipelineStage.html'),
      executionDetailsUrl: require('./pipelineExecutionDetails.html'),
      validators: [
        { type: 'requiredField', fieldName: 'pipeline', },
      ],
    });
  }).controller('pipelineStageCtrl', function($scope, stage, pipelineConfigService, applicationReader, _) {

    $scope.stage = stage;

    if (!$scope.stage.application) {
      $scope.stage.application = $scope.application.name;
    }

    $scope.viewState = {
      mastersLoaded: false,
      mastersRefreshing: false,
      mastersLastRefreshed: null,
      pipelinesLoaded : false,
      jobsRefreshing: false,
      jobsLastRefreshed: null,
      infiniteScroll: {
        numToAdd: 20,
        currentItems: 20,
      },
    };

    this.addMoreItems = function() {
      $scope.viewState.infiniteScroll.currentItems += $scope.viewState.infiniteScroll.numToAdd;
    };

    applicationReader.listApplications().then(function(applications) {
      $scope.applications = _.pluck(applications, 'name').sort();
      initializeMasters();
    });

    function initializeMasters() {
      if ($scope.stage.application) {
        pipelineConfigService.getPipelinesForApplication($scope.stage.application).then(function (pipelines) {
          $scope.pipelines = _.filter( pipelines, function(pipeline){ return pipeline.id !== $scope.pipeline.id; } );
          if (!_.find( pipelines, function(pipeline) { return pipeline.id === $scope.stage.pipeline; })) {
            $scope.stage.pipeline = null;
          }
          $scope.viewState.pipelinesLoaded = true;
          updatePipelineConfig();
        });
      }
    }

    function updatePipelineConfig() {
      if ($scope.stage && $scope.stage.application && $scope.stage.pipeline) {
        var config = _.find( $scope.pipelines, function(pipeline){ return pipeline.id === $scope.stage.pipeline; } );
        if(config && config.parameterConfig) {
          if (!$scope.stage.pipelineParameters) {
            $scope.stage.pipelineParameters = {};
          }
          $scope.pipelineParameters = config.parameterConfig;
          $scope.userSuppliedParameters = $scope.stage.pipelineParameters;
          $scope.useDefaultParameters = {};
          _.each($scope.pipelineParameters, function (property) {
            if (!(property.name in $scope.stage.pipelineParameters) && (property.default !== null)) {
              $scope.useDefaultParameters[property.name] = true;
            }
          });
        } else {
          clearParams();
        }
      } else {
        clearParams();
      }
    }

    function clearParams() {
      $scope.pipelineParameters = {};
      $scope.useDefaultParameters = {};
      $scope.userSuppliedParameters = {};
    }

    $scope.useDefaultParameters = {};
    $scope.userSuppliedParameters = {};

    this.updateParam = function(parameter){
      if($scope.useDefaultParameters[parameter] === true){
        delete $scope.userSuppliedParameters[parameter];
        delete $scope.stage.pipelineParameters[parameter];
      } else if($scope.userSuppliedParameters[parameter]){
        $scope.stage.pipelineParameters[parameter] = $scope.userSuppliedParameters[parameter];
      }
    };

    $scope.$watch('stage.application', initializeMasters);
    $scope.$watch('stage.pipeline', updatePipelineConfig);

  });

<|endoftext|>


<commit_before>import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
import Session from 'simple-auth/session';
import EphemeralStore from 'simple-auth/stores/ephemeral';
import Configuration from 'simple-auth/configuration';

var TestRoute = Ember.Route.extend(ApplicationRouteMixin);

describe('ApplicationRouteMixin', function() {
  beforeEach(function() {
    this.session = Session.create();
    this.session.setProperties({ store: EphemeralStore.create() });
    this.route = Ember.Route.extend(ApplicationRouteMixin, {
      send: function() {},
      transitionTo: function() {}
    }).create({ session: this.session });
  });

  describe('#beforeModel', function() {
    beforeEach(function() {
      this.transition = { send: function() {} };
      sinon.spy(this.route, 'send');
    });

    context('when there is no active route', function() {
      beforeEach(function() {
        sinon.spy(this.transition, 'send');
        this.route.beforeModel(this.transition);
      });

      it('sends the action to the transition', function (done) {
        this.session.trigger('authorizationFailed');

        Ember.run.next(this, function () {
          expect(this.transition.send).to.have.been.calledWith('authorizationFailed');
          done();
        });
      });
    });

    context('when there is an active route', function() {
      beforeEach(function() {
        this.route.beforeModel(this.transition);
        this.route.activate();
      });

      it("translates the session's 'sessionAuthenticationSucceeded' event into an action invocation", function(done) {
        expect(this.route.get('_authRouteEntryComplete')).to.be.true;
        this.session.trigger('sessionAuthenticationSucceeded');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionAuthenticationSucceeded');
          done();
        });
      });

      it("sends the action to the transition on a new route instance", function(done) {
        route2 = Ember.Route.extend(ApplicationRouteMixin, {
          transitionTo: function() {}
        }).create({ session: this.session });
        var transition = { send: function() {} };
        sinon.spy(transition, 'send');
        sinon.spy(route2, 'send');
        route2.beforeModel(transition);
        this.session.trigger('sessionAuthenticationSucceeded');
        Ember.run.next(this, function() {
          expect(route2.get('_authRouteEntryComplete')).to.not.be.true;
          expect(transition.send).to.have.been.calledWith('sessionAuthenticationSucceeded');
          expect(route2.send).to.not.have.been.called;
          done();
        });
      });

      it("translates the session's 'sessionAuthenticationFailed' event into an action invocation", function(done) {
        this.session.trigger('sessionAuthenticationFailed', 'error');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionAuthenticationFailed', 'error');
          done();
        });
      });

      it("translates the session's 'sessionInvalidationSucceeded' event into an action invocation", function(done) {
        this.session.trigger('sessionInvalidationSucceeded');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionInvalidationSucceeded');
          done();
        });
      });

      it("translates the session's 'sessionInvalidationFailed' event into an action invocation", function(done) {
        this.session.trigger('sessionInvalidationFailed', 'error');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionInvalidationFailed', 'error');
          done();
        });
      });

      it("translates the session's 'authorizationFailed' event into an action invocation", function(done) {
        this.session.trigger('authorizationFailed');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('authorizationFailed');
          done();
        });
      });

      it('does not attach the event listeners twice', function(done) {
        this.route.beforeModel(this.transition);
        this.session.trigger('sessionAuthenticationSucceeded');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledOnce;
          done();
        });
      });
    });
  });

  describe('the "sessionRequiresAuthentication" action', function() {
    beforeEach(function() {
      sinon.spy(this.route, 'transitionTo');
    });

    it('transitions to "Configuration.authenticationRoute"', function() {
      this.route._actions.sessionRequiresAuthentication.apply(this.route);

      expect(this.route.transitionTo).to.have.been.calledWith(Configuration.authenticationRoute);
    });
  });

  describe('the "authenticateSession" action', function() {
    beforeEach(function() {
      var route = this.route;
      sinon.spy(route, 'transitionTo');
      route.send = function(name) {
        if (name === 'sessionRequiresAuthentication') {
          route._actions.sessionRequiresAuthentication.apply(route);
        }
      };
    });

    it('transitions to "Configuration.authenticationRoute"', function() {
      this.route._actions.authenticateSession.apply(this.route);

      expect(this.route.transitionTo).to.have.been.calledWith(Configuration.authenticationRoute);
    });
  });

  describe('the "sessionAuthenticationSucceeded" action', function() {
    beforeEach(function() {
      sinon.spy(this.route, 'transitionTo');
    });

    context('when an attempted transition is stored in the session', function() {
      beforeEach(function() {
        this.attemptedTransition = { retry: function() {} };
        this.session.set('attemptedTransition', this.attemptedTransition);
      });

      it('retries that transition', function() {
        sinon.spy(this.attemptedTransition, 'retry');

        this.route._actions.sessionAuthenticationSucceeded.apply(this.route);

        expect(this.attemptedTransition.retry).to.have.been.calledOnce;
      });

      it('removes it from the session', function() {
        this.route._actions.sessionAuthenticationSucceeded.apply(this.route);

        expect(this.session.get('attemptedTransition')).to.be.null;
      });
    });

    context('when no attempted transition is stored in the session', function() {
      it('transitions to "Configuration.routeAfterAuthentication"', function() {
        this.route._actions.sessionAuthenticationSucceeded.apply(this.route);

        expect(this.route.transitionTo).to.have.been.calledWith(Configuration.routeAfterAuthentication);
      });
    });
  });

  describe('the "invalidateSession" action', function() {
    it('invalidates the session', function() {
      sinon.stub(this.session, 'invalidate').returns(Ember.RSVP.resolve());
      this.route._actions.invalidateSession.apply(this.route);

      expect(this.session.invalidate).to.have.been.calledOnce;
    });
  });

  describe('the "authorizationFailed" action', function() {
    context('when the session is authenticated', function() {
      beforeEach(function() {
        this.session.set('isAuthenticated', true);
      });

      it('invalidates the session', function() {
        sinon.stub(this.session, 'invalidate').returns(Ember.RSVP.resolve());
        this.route._actions.authorizationFailed.apply(this.route);

        expect(this.session.invalidate).to.have.been.calledOnce;
      });
    });

    context('when the session is not authenticated', function() {
      it('does not try to invalidate the session', function() {
        sinon.stub(this.session, 'invalidate').returns(Ember.RSVP.resolve());
        this.route._actions.authorizationFailed.apply(this.route);

        expect(this.session.invalidate).to.not.have.been.calledOnce;
      });
    });
  });
});
<commit_msg>make separate test for _authRouteEntryComplete flag; remove unnecessary assertion<commit_after>import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
import Session from 'simple-auth/session';
import EphemeralStore from 'simple-auth/stores/ephemeral';
import Configuration from 'simple-auth/configuration';

var TestRoute = Ember.Route.extend(ApplicationRouteMixin);

describe('ApplicationRouteMixin', function() {
  beforeEach(function() {
    this.session = Session.create();
    this.session.setProperties({ store: EphemeralStore.create() });
    this.route = Ember.Route.extend(ApplicationRouteMixin, {
      send: function() {},
      transitionTo: function() {}
    }).create({ session: this.session });
  });

  describe('#beforeModel', function() {
    beforeEach(function() {
      this.transition = { send: function() {} };
      sinon.spy(this.route, 'send');
    });

    context('when there is no active route', function() {
      beforeEach(function() {
        sinon.spy(this.transition, 'send');
        this.route.beforeModel(this.transition);
      });

      it('sends the action to the transition', function (done) {
        this.session.trigger('authorizationFailed');

        Ember.run.next(this, function () {
          expect(this.transition.send).to.have.been.calledWith('authorizationFailed');
          done();
        });
      });
    });

    context('when there is an active route', function() {
      beforeEach(function() {
        this.route.beforeModel(this.transition);
        this.route.activate();
      });

      it("translates the session's 'sessionAuthenticationSucceeded' event into an action invocation", function(done) {
        this.session.trigger('sessionAuthenticationSucceeded');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionAuthenticationSucceeded');
          done();
        });
      });

      it("sets _authRouteEntryComplete so we know actions can be sent to the route", function(done) {
        expect(this.route.get('_authRouteEntryComplete')).to.be.true;
        done();
      });

      it("sends the action to the transition on a new route instance", function(done) {
        route2 = Ember.Route.extend(ApplicationRouteMixin, {
          transitionTo: function() {}
        }).create({ session: this.session });
        var transition = { send: function() {} };
        sinon.spy(transition, 'send');
        sinon.spy(route2, 'send');
        route2.beforeModel(transition);
        this.session.trigger('sessionAuthenticationSucceeded');
        Ember.run.next(this, function() {
          expect(transition.send).to.have.been.calledWith('sessionAuthenticationSucceeded');
          expect(route2.send).to.not.have.been.called;
          done();
        });
      });

      it("translates the session's 'sessionAuthenticationFailed' event into an action invocation", function(done) {
        this.session.trigger('sessionAuthenticationFailed', 'error');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionAuthenticationFailed', 'error');
          done();
        });
      });

      it("translates the session's 'sessionInvalidationSucceeded' event into an action invocation", function(done) {
        this.session.trigger('sessionInvalidationSucceeded');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionInvalidationSucceeded');
          done();
        });
      });

      it("translates the session's 'sessionInvalidationFailed' event into an action invocation", function(done) {
        this.session.trigger('sessionInvalidationFailed', 'error');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('sessionInvalidationFailed', 'error');
          done();
        });
      });

      it("translates the session's 'authorizationFailed' event into an action invocation", function(done) {
        this.session.trigger('authorizationFailed');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledWith('authorizationFailed');
          done();
        });
      });

      it('does not attach the event listeners twice', function(done) {
        this.route.beforeModel(this.transition);
        this.session.trigger('sessionAuthenticationSucceeded');

        Ember.run.next(this, function() {
          expect(this.route.send).to.have.been.calledOnce;
          done();
        });
      });
    });
  });

  describe('the "sessionRequiresAuthentication" action', function() {
    beforeEach(function() {
      sinon.spy(this.route, 'transitionTo');
    });

    it('transitions to "Configuration.authenticationRoute"', function() {
      this.route._actions.sessionRequiresAuthentication.apply(this.route);

      expect(this.route.transitionTo).to.have.been.calledWith(Configuration.authenticationRoute);
    });
  });

  describe('the "authenticateSession" action', function() {
    beforeEach(function() {
      var route = this.route;
      sinon.spy(route, 'transitionTo');
      route.send = function(name) {
        if (name === 'sessionRequiresAuthentication') {
          route._actions.sessionRequiresAuthentication.apply(route);
        }
      };
    });

    it('transitions to "Configuration.authenticationRoute"', function() {
      this.route._actions.authenticateSession.apply(this.route);

      expect(this.route.transitionTo).to.have.been.calledWith(Configuration.authenticationRoute);
    });
  });

  describe('the "sessionAuthenticationSucceeded" action', function() {
    beforeEach(function() {
      sinon.spy(this.route, 'transitionTo');
    });

    context('when an attempted transition is stored in the session', function() {
      beforeEach(function() {
        this.attemptedTransition = { retry: function() {} };
        this.session.set('attemptedTransition', this.attemptedTransition);
      });

      it('retries that transition', function() {
        sinon.spy(this.attemptedTransition, 'retry');

        this.route._actions.sessionAuthenticationSucceeded.apply(this.route);

        expect(this.attemptedTransition.retry).to.have.been.calledOnce;
      });

      it('removes it from the session', function() {
        this.route._actions.sessionAuthenticationSucceeded.apply(this.route);

        expect(this.session.get('attemptedTransition')).to.be.null;
      });
    });

    context('when no attempted transition is stored in the session', function() {
      it('transitions to "Configuration.routeAfterAuthentication"', function() {
        this.route._actions.sessionAuthenticationSucceeded.apply(this.route);

        expect(this.route.transitionTo).to.have.been.calledWith(Configuration.routeAfterAuthentication);
      });
    });
  });

  describe('the "invalidateSession" action', function() {
    it('invalidates the session', function() {
      sinon.stub(this.session, 'invalidate').returns(Ember.RSVP.resolve());
      this.route._actions.invalidateSession.apply(this.route);

      expect(this.session.invalidate).to.have.been.calledOnce;
    });
  });

  describe('the "authorizationFailed" action', function() {
    context('when the session is authenticated', function() {
      beforeEach(function() {
        this.session.set('isAuthenticated', true);
      });

      it('invalidates the session', function() {
        sinon.stub(this.session, 'invalidate').returns(Ember.RSVP.resolve());
        this.route._actions.authorizationFailed.apply(this.route);

        expect(this.session.invalidate).to.have.been.calledOnce;
      });
    });

    context('when the session is not authenticated', function() {
      it('does not try to invalidate the session', function() {
        sinon.stub(this.session, 'invalidate').returns(Ember.RSVP.resolve());
        this.route._actions.authorizationFailed.apply(this.route);

        expect(this.session.invalidate).to.not.have.been.calledOnce;
      });
    });
  });
});
<|endoftext|>