<commit_before>// $Revision$

Ensembl.LayoutManager = new Base();

Ensembl.LayoutManager.extend({
  constructor: null,

  /**
   * Creates events on elements outside of the domain of panels
   */
  initialize: function () {
    this.id = 'LayoutManager';

    Ensembl.EventManager.register('reloadPage',    this, this.reloadPage);
    Ensembl.EventManager.register('validateForms', this, this.validateForms);
    Ensembl.EventManager.register('makeZMenu',     this, this.makeZMenu);
    Ensembl.EventManager.register('relocateTools', this, this.relocateTools);
    Ensembl.EventManager.register('hashChange',    this, this.hashChange);
    Ensembl.EventManager.register('toggleContent', this, this.toggleContent);
    Ensembl.EventManager.register('changeWidth',   this, this.changeWidth);

    $('#page_nav .tool_buttons > p').show();

    $('#header a:not(#tabs a)').addClass('constant');

    if (window.location.hash.match(Ensembl.locationMatch)) {
      $('.ajax_load').val(function (i, val) {
        return Ensembl.urlFromHash(val);
      });

      this.hashChange(Ensembl.urlFromHash(window.location.href, true));
    }

    $(document).on('click', '.modal_link', function () {
      if (Ensembl.EventManager.trigger('modalOpen', this)) {
        return false;
      }
    }).on('click', '.popup', function () {
      if (window.name.match(/^popup_/)) {
        return true;
      }

      window.open(this.href, 'popup_' + window.name, 'width=950,height=500,resizable,scrollbars');
      return false;
    }).on('click', 'a[rel="external"]', function () {
      this.target = '_blank';
    }).on('click', 'a.update_panel', function () {
      var panelId = this.rel;
      var url     = Ensembl.updateURL({ update_panel: 1 }, this.href);

      if (Ensembl.PanelManager.panels[panelId] && this.href.split('?')[0].match(Ensembl.PanelManager.panels[panelId].params.updateURL.split('?')[0])) {
        var params = {};

        if (!$('.update_url', this).each(function () { params[this.name] = this.value; }).length) {
          params = undefined;
        }

        Ensembl.EventManager.triggerSpecific('updatePanel', panelId, url, null, { updateURL: this.href }, params);
      } else {
        $.ajax({
          url: url,
          success: function () {
            Ensembl.EventManager.triggerSpecific('updatePanel', panelId);
          }
        });
      }

      return false;
    }).on({
      'keyup.ensembl': function (event) {
        if (event.keyCode === 27) {
          Ensembl.EventManager.trigger('modalClose', true); // Close modal window if the escape key is pressed
        }
      },
      'mouseup.ensembl': function (e) {
        // only fired on left click
        if (!e.which || e.which === 1) {
          Ensembl.EventManager.trigger('mouseUp', e);
        }
      }
    });

    $('.modal_link').show();

    this.validateForms(document);

    $(window).on({
      'resize.ensembl': function (e) {
        if (window.name.match(/^popup_/)) {
          return false;
        }

        // jquery ui resizable events cause window.resize to fire (all events bubble to window)
        // if target has no tagName it is window or document. Don't resize unless this is the case
        if (!e.target.tagName) {
          var width = Ensembl.width;

          if (Ensembl.dynamicWidth) {
            Ensembl.setWidth(undefined, true);
          }

          Ensembl.EventManager.trigger('windowResize');

          if (Ensembl.dynamicWidth && Ensembl.width !== width) {
            Ensembl.LayoutManager.changeWidth();
            Ensembl.EventManager.trigger('imageResize');
          }
        }
      },
      'hashchange.ensembl': $.proxy(this.popState, this),
      'popstate.ensembl'  : $.proxy(this.popState, this)
    });

    var userMessage = unescape(Ensembl.cookie.get('user_message'));

    if (userMessage) {
      userMessage = userMessage.split('\n');

      $([
        '
',
        '
', userMessage[0], '
',
        '
', userMessage[1], '
',
        '
'
      ].join('')).prependTo('#main').find('h3 img, a').on('click', function () {
        $(this).parents('div.hint').remove();
        Ensembl.cookie.set('user_message', '');
      });
    }

    this.pulseToolButton();
  },

  reloadPage: function (args, url) {
    if (typeof args === 'string') {
      Ensembl.EventManager.triggerSpecific('updatePanel', args);
    } else if (typeof args === 'object') {
      for (var i in args) {
        Ensembl.EventManager.triggerSpecific('updatePanel', i);
      }
    } else {
      return Ensembl.redirect(url);
    }

    $('#messages').hide();
  },

  validateForms: function (context) {
    $('form._check', context).validate().on('submit', function () {
      var form = $(this);

      if (form.parents('#modal_panel').length) {
        var panels = form.parents('.js_panel').map(function () { return this.id; }).toArray();
        var rtn;

        while (panels.length && typeof rtn === 'undefined') {
          rtn = Ensembl.EventManager.triggerSpecific('modalFormSubmit', panels.shift(), form);
        }

        return rtn;
      }
    });
  },

  makeZMenu: function (id, params) {
    if (!$('#' + id).length) {
      $([
        '
',
        ' ',
        '
',
        '
',
        '

',
        '
',
        '
',
        '

',
        '
',
        '

',
        '
',
        '
'
      ].join('')).draggable({ handle: 'thead' }).appendTo('body');
    }

    Ensembl.EventManager.trigger('addPanel', id, 'ZMenu', undefined, undefined, params, 'showExistingZMenu');
  },

  relocateTools: function (tools) {
    var toolButtons = $('#page_nav .tool_buttons');

    tools.each(function () {
      var a        = $(this).find('a');
      var existing = $('.additional .' + a[0].className.replace(' ', '.'), toolButtons);

      if (existing.length) {
        existing.replaceWith(a);
      } else {
        $(this).children().addClass('additional').appendTo(toolButtons).not('.hidden').show();
      }

      a = existing = null;
    }).remove();

    $('a.seq_blast', toolButtons).on('click', function () {
      $('form.seq_blast', toolButtons).submit();
      return false;
    });

    this.pulseToolButton();

    tools = null;
  },

  pulseToolButton: function () {
    $('#page_nav .tool_buttons a.pulse:not(.pulsing)').one('click', function () {
      clearInterval($(this).stop().removeClass('pulse').css({ backgroundColor: '', color: '' }).data('interval'));
    }).addClass('pulsing').each(function () {
      var pulse = $(this).data({
        dark    : false,
        interval: setInterval(function () {
          var data = pulse.data();
          pulse.toggleClass('pulse', !data.dark, 1000);
          data.dark = !data.dark;
        }, 1000)
      });
    });

    els = null;
  },

  popState: function () {
    if (
      Ensembl.historyReady && // stops popState executing on initial page load in Chrome. This value is set to true in Ensembl.updateLocation
      // there is an r param in the hash/search EXCEPT WHEN the browser supports history API, and there is a hash which doesn't have an r param (ajax added content)
      ((window.location[Ensembl.locationURL].match(Ensembl.locationMatch) && !(Ensembl.locationURL === 'search' && window.location.hash && !window.location.hash.match(Ensembl.locationMatch))) ||
      (!window.location.hash && Ensembl.hash.match(Ensembl.locationMatch))) // there is no location.hash, but Ensembl.hash (previous hash value) had an r param (going back from no hash url to hash url)
    ) {
      Ensembl.setCoreParams();
      Ensembl.EventManager.trigger('hashChange', Ensembl.urlFromHash(window.location.href, true));
    }
  },

  hashChange: function (r) {
    if (!r) {
      return;
    }

    r = decodeURIComponent(r);

    var text = r.split(/\W/);
        text = text[0] + ': ' + Ensembl.thousandify(text[1]) + '-' + Ensembl.thousandify(text[2]);

    $('a:not(.constant)').attr('href', function () {
      var r;

      if (this.title === 'UCSC') {
        this.href = this.href.replace(/(&?position=)[^&]+(.?)/, '$1chr' + Ensembl.urlFromHash(this.href, true) + '$2');
      } else if (this.title === 'NCBI') {
        r = Ensembl.urlFromHash(this.href, true).split(/[:\-]/);
        this.href = this.href.replace(/(&?CHR=).+&BEG=.+&END=[^&]+(.?)/, '$1' + r[0] + '&BEG=' + r[1] + '&END=' + r[2] + '$2');
      } else {
        return Ensembl.urlFromHash(this.href);
      }
    });

    $('input[name=r]', 'form:not(#core_params)').val(r);

    $('h1.summary-heading').html(function (i, html) {
      return html.replace(/^(Chromosome ).+/, '$1' + text);
    });

    document.title = document.title.replace(/(Chromosome ).+/, '$1' + text);
  },

  toggleContent: function (rel) {
    if (rel) {
      $('a.toggle[rel="' + rel + '"]').toggleClass('open closed');
    }
  },

  changeWidth: function () {
    var modal = $('#modal_panel');
    $('.navbar, div.info, div.hint, div.warning, div.error').not('.fixed_width').not(function () { return modal.find(this).length; }).width(Ensembl.width);
    modal = null;
  }
});

<commit_msg>removed stuff that can go into specific panels<commit_after>// $Revision$

Ensembl.LayoutManager = new Base();

Ensembl.LayoutManager.extend({
  constructor: null,

  /**
   * Creates events on elements outside of the domain of panels
   */
  initialize: function () {
    this.id = 'LayoutManager';

    Ensembl.EventManager.register('reloadPage',    this, this.reloadPage);
    Ensembl.EventManager.register('validateForms', this, this.validateForms);
    Ensembl.EventManager.register('makeZMenu',     this, this.makeZMenu);
    Ensembl.EventManager.register('hashChange',    this, this.hashChange);
    Ensembl.EventManager.register('toggleContent', this, this.toggleContent);
    Ensembl.EventManager.register('changeWidth',   this, this.changeWidth);

    $('#page_nav .tool_buttons > p').show();

    $('#header a:not(#tabs a)').addClass('constant');

    if (window.location.hash.match(Ensembl.locationMatch)) {
      $('.ajax_load').val(function (i, val) {
        return Ensembl.urlFromHash(val);
      });

      this.hashChange(Ensembl.urlFromHash(window.location.href, true));
    }

    $(document).on('click', '.modal_link', function () {
      if (Ensembl.EventManager.trigger('modalOpen', this)) {
        return false;
      }
    }).on('click', '.popup', function () {
      if (window.name.match(/^popup_/)) {
        return true;
      }

      window.open(this.href, 'popup_' + window.name, 'width=950,height=500,resizable,scrollbars');
      return false;
    }).on('click', 'a[rel="external"]', function () {
      this.target = '_blank';
    }).on('click', 'a.update_panel', function () {
      var panelId = this.rel;
      var url     = Ensembl.updateURL({ update_panel: 1 }, this.href);

      if (Ensembl.PanelManager.panels[panelId] && this.href.split('?')[0].match(Ensembl.PanelManager.panels[panelId].params.updateURL.split('?')[0])) {
        var params = {};

        if (!$('.update_url', this).each(function () { params[this.name] = this.value; }).length) {
          params = undefined;
        }

        Ensembl.EventManager.triggerSpecific('updatePanel', panelId, url, null, { updateURL: this.href }, params);
      } else {
        $.ajax({
          url: url,
          success: function () {
            Ensembl.EventManager.triggerSpecific('updatePanel', panelId);
          }
        });
      }

      return false;
    }).on({
      'keyup.ensembl': function (event) {
        if (event.keyCode === 27) {
          Ensembl.EventManager.trigger('modalClose', true); // Close modal window if the escape key is pressed
        }
      },
      'mouseup.ensembl': function (e) {
        // only fired on left click
        if (!e.which || e.which === 1) {
          Ensembl.EventManager.trigger('mouseUp', e);
        }
      }
    });

    $('.modal_link').show();

    this.validateForms(document);

    $(window).on({
      'resize.ensembl': function (e) {
        if (window.name.match(/^popup_/)) {
          return false;
        }

        // jquery ui resizable events cause window.resize to fire (all events bubble to window)
        // if target has no tagName it is window or document. Don't resize unless this is the case
        if (!e.target.tagName) {
          var width = Ensembl.width;

          if (Ensembl.dynamicWidth) {
            Ensembl.setWidth(undefined, true);
          }

          Ensembl.EventManager.trigger('windowResize');

          if (Ensembl.dynamicWidth && Ensembl.width !== width) {
            Ensembl.LayoutManager.changeWidth();
            Ensembl.EventManager.trigger('imageResize');
          }
        }
      },
      'hashchange.ensembl': $.proxy(this.popState, this),
      'popstate.ensembl'  : $.proxy(this.popState, this)
    });

    var userMessage = unescape(Ensembl.cookie.get('user_message'));

    if (userMessage) {
      userMessage = userMessage.split('\n');

      $([
        '
',
        '
', userMessage[0], '
',
        '
', userMessage[1], '
',
        '
'
      ].join('')).prependTo('#main').find('h3 img, a').on('click', function () {
        $(this).parents('div.hint').remove();
        Ensembl.cookie.set('user_message', '');
      });
    }
  },

  reloadPage: function (args, url) {
    if (typeof args === 'string') {
      Ensembl.EventManager.triggerSpecific('updatePanel', args);
    } else if (typeof args === 'object') {
      for (var i in args) {
        Ensembl.EventManager.triggerSpecific('updatePanel', i);
      }
    } else {
      return Ensembl.redirect(url);
    }

    $('#messages').hide();
  },

  validateForms: function (context) {
    $('form._check', context).validate().on('submit', function () {
      var form = $(this);

      if (form.parents('#modal_panel').length) {
        var panels = form.parents('.js_panel').map(function () { return this.id; }).toArray();
        var rtn;

        while (panels.length && typeof rtn === 'undefined') {
          rtn = Ensembl.EventManager.triggerSpecific('modalFormSubmit', panels.shift(), form);
        }

        return rtn;
      }
    });
  },

  makeZMenu: function (id, params) {
    if (!$('#' + id).length) {
      Ensembl.Panel.ZMenu.template.clone().attr('id', id).appendTo('body');
    }

    Ensembl.EventManager.trigger('addPanel', id, 'ZMenu', undefined, undefined, params, 'showExistingZMenu');
  },

  popState: function () {
    if (
      Ensembl.historyReady && // stops popState executing on initial page load in Chrome. This value is set to true in Ensembl.updateLocation
      // there is an r param in the hash/search EXCEPT WHEN the browser supports history API, and there is a hash which doesn't have an r param (ajax added content)
      ((window.location[Ensembl.locationURL].match(Ensembl.locationMatch) && !(Ensembl.locationURL === 'search' && window.location.hash && !window.location.hash.match(Ensembl.locationMatch))) ||
      (!window.location.hash && Ensembl.hash.match(Ensembl.locationMatch))) // there is no location.hash, but Ensembl.hash (previous hash value) had an r param (going back from no hash url to hash url)
    ) {
      Ensembl.setCoreParams();
      Ensembl.EventManager.trigger('hashChange', Ensembl.urlFromHash(window.location.href, true));
    }
  },

  hashChange: function (r) {
    if (!r) {
      return;
    }

    r = decodeURIComponent(r);

    var text = r.split(/\W/);
        text = text[0] + ': ' + Ensembl.thousandify(text[1]) + '-' + Ensembl.thousandify(text[2]);

    $('a:not(.constant)').attr('href', function () {
      var r;

      if (this.title === 'UCSC') {
        this.href = this.href.replace(/(&?position=)[^&]+(.?)/, '$1chr' + Ensembl.urlFromHash(this.href, true) + '$2');
      } else if (this.title === 'NCBI') {
        r = Ensembl.urlFromHash(this.href, true).split(/[:\-]/);
        this.href = this.href.replace(/(&?CHR=).+&BEG=.+&END=[^&]+(.?)/, '$1' + r[0] + '&BEG=' + r[1] + '&END=' + r[2] + '$2');
      } else {
        return Ensembl.urlFromHash(this.href);
      }
    });

    $('input[name=r]', 'form:not(#core_params)').val(r);

    $('h1.summary-heading').html(function (i, html) {
      return html.replace(/^(Chromosome ).+/, '$1' + text);
    });

    document.title = document.title.replace(/(Chromosome ).+/, '$1' + text);
  },

  toggleContent: function (rel) {
    if (rel) {
      $('a.toggle[rel="' + rel + '"]').toggleClass('open closed');
    }
  },

  changeWidth: function () {
    var modal = $('#modal_panel');
    $('.navbar, div.info, div.hint, div.warning, div.error').not('.fixed_width').not(function () { return modal.find(this).length; }).width(Ensembl.width);
    modal = null;
  }
});

<|endoftext|>


<commit_before>import React from 'react';
import { DropTarget } from 'react-dnd';
import some from 'lodash/some';
import EVENTS from 'api/constants/events';
import first from 'lodash/first';
import find from 'lodash/find';
import flatten from 'lodash/flatten';
import API from 'api';
import Group from 'ui/components/Mocks/Sidebar/Group';
import Mock from 'ui/components/Mocks/Sidebar/Mock';
import ResizeHandle from 'ui/components/common/ResizeHandle';
import ContextMenu from 'ui/components/Mocks/Sidebar/ContextMenu';
import { SidebarContainer, ActionGroup, ActionsContainer, Action } from 'ui/components/Mocks/Sidebar/styled';
import MockState from 'ui/states/MocksState';
import Icon from 'ui/components/common/Icon';
import IconDropdown from 'ui/components/common/IconDropdown';

const noop = () => true;

const groupTarget = {
  drop(props, monitor) {
    if (monitor.didDrop()) {
      return;
    }

    const mockId = monitor.getItem().id;
    const mock   = API.getMock(mockId);

    if (mock) {
      API.updateMock(mockId, { ...mock, groupId: null });
    }
  }
};

function collect(connector, monitor) {
  return {
    connectDropTarget: connector.dropTarget(),
    isHovered: monitor.isOver({ shallow: true })
  };
}

class MocksSidebar extends React.PureComponent {

  componentDidMount() {
    API.on(EVENTS.UPDATE_MOCK, this.reRender);
    API.on(EVENTS.UPDATE_GROUP, this.reRender);
  }

  componentWillUnmount() {
    API.off(EVENTS.UPDATE_MOCK, this.reRender);
    API.off(EVENTS.UPDATE_GROUP, this.reRender);
  }

  reRender = () => {
    setTimeout(() => this.forceUpdate(), 0);
  };

  select = (item, isRightClick) => (event) => {
    if (isRightClick && this.props.hasMultipleSelection) {
      return;
    }

    if (!isRightClick) {
      event.stopPropagation();
    }

    if (event.shiftKey && this.props.selectedItems.length > 0) {
      const groups = flatten(API.groups.map((group) => [group, ...group.mocks]));
      const mocks  = API.mocks.filter((mock) => !mock.groupId);

      const list          = [...groups, ...mocks];
      const selectedItems = list.filter((listItem) => this.props.selectedItems.indexOf(listItem) !== -1);
      const firstIndex    = list.indexOf(first(selectedItems));
      const currentIndex  = list.indexOf(item);
      const itemsBetween  = currentIndex > firstIndex
        ? list.slice(firstIndex, currentIndex + 1)
        : list.slice(currentIndex, firstIndex);

      return itemsBetween
        .filter((itemInRange) => this.props.selectedItems.indexOf(itemInRange) === -1)
        .forEach((itemInRange) => this.props.select(itemInRange, true));
    }

    const multiple = event.metaKey || event.ctrlKey;

    this.props.select(item, multiple);
  };

  toggleMock = (mock) => (event) => {
    event.stopPropagation();

    API.toggleMock(mock.id);
  };

  toggleGroup = (group) => (event) => {
    event.stopPropagation();

    API.toggleGroup(group.id);
  };

  getMocks() {
    return API.mocks
      .filter((mock) => !mock.groupId)
      .filter((mock) => mock.url.includes(this.props.searchTerm))
      .filter(this.props.customFilter || noop)
      .map((mock) => (
          <Mock key={ mock.id }
                mock={ mock }
                searchTerm={ this.props.searchTerm }
                isSelected={ !!find(this.props.selectedMocks, { id: mock.id }) }
                toggleMock={ this.toggleMock(mock) }
                onClick={ this.select(mock) }
                onContextMenu={ this.select(mock, true) }
                renamedItemId={ this.props.renamedItemId }
                editItemName={ this.props.editItemName }/>
        )
      );
  }

  getGroups() {
    return API.groups
      .filter((group) => {
        if (!this.props.searchTerm) {
          return true;
        }

        if (group.name.includes(this.props.searchTerm)) {
          return true;
        }

        return some(group.mocks, (mock) => mock.url.includes(this.props.searchTerm))
      })
      .map((group) => {
        const mocks = group.mocks.filter(this.props.customFilter || noop);

        return (
          <Group
            key={ group.id }
            id={ group.id }
            name={ group.name }
            active={ group.active }
            isSelected={ !!find(this.props.selectedGroups, { id: group.id }) }
            mocks={ mocks }
            searchTerm={ this.props.searchTerm }
            toggleGroup={ this.toggleGroup(group) }
            toggleMock={ this.toggleMock }
            onSelect={ this.select }
            onClick={ this.select(group) }
            onContextMenu={ this.select(group, true) }
            groups={ this.props.groups }
            setGroups={ this.props.setGroups }
            selectedMocks={ this.props.selectedMocks }
            editItemName={ this.props.editItemName }
            addGroup={ this.props.addGroup }
            renamedItemId={ this.props.renamedItemId }/>
        )
      });
  }

  openContextMenu = (event) => {
    event.preventDefault();

    this.props.openMenu(event.clientX, event.clientY);
  };

  addNew = (type) => {
    if (type === 'mock') {
      const newMock = API.addMock();

      MocksState.selectItems([newMock]);
    }

    if (type === 'group') {
      const newGroup = API.addGroup({ name: 'New Group' });

      MockState.addGroup({
        id: newGroup.id,
        isOpen: false,
        lastState: null
      });
      MockState.selectItems([newGroup]);
    }
  };

  toggleRecording = () => {
    if (API.isRecording) {
      API.stopRecording();
    } else {
      API.startRecording();
    }
  };

  render() {
    return this.props.connectDropTarget(

        <ResizeHandle value="mocksSidebarWidth"/>

        <SidebarContainer
          isHovered={ this.props.isHovered }
          style={{ width: this.props.sidebarWidth }}
          onContextMenu={ this.openContextMenu }>
          <ActionsContainer>
            <ActionGroup marginRight="40">
              <Action>
                <IconDropdown
                  icon="add"
                  options={ [ { label: 'Mock', value: 'mock' }, { label: 'Group', value: 'group' }]}
                  onChange={ this.addNew }/>
              </Action>
              <Action onClick={ this.toggleRecording } margin="2">
                { API.isRecording ? <Icon src="recording"/> : <Icon src="record"/> }
              </Action>
            </ActionGroup>

            <ActionGroup autosize>
              <Action onClick={ MockState.collapseAllGroups }>
                <Icon src="collapseAll"/>
              </Action>
              <Action onClick={ MockState.expandAllGroups }>
                <Icon src="expandAll"/>
              </Action>
            </ActionGroup>

            <ActionGroup>
              <Action>
                <Icon src="undo"/>
              </Action>
              <Action onClick={ MockState.deleteSelected }>
                <Icon src="remove"/>
              </Action>
            </ActionGroup>
          </ActionsContainer>
          { this.getGroups() }
          { this.getMocks() }

          <ContextMenu
            closeMenu={ this.props.closeMenu }
            selectedMocks={ this.props.selectedMocks }
            selectedGroups={ this.props.selectedGroups }
            expandAllGroups={ this.props.expandAllGroups }
            collapseAllGroups={ this.props.collapseAllGroups }
            selectedItems={ this.props.selectedItems }
            selectFirstMock={ this.props.selectFirstMock }
            renameItemId={ this.props.renamedItemId }
            recaptureMocks={ this.props.recaptureMocks }
            recaptureRequestIds={ this.props.recaptureRequestIds }
            updateQuery={ this.props.updateQuery }
            clipboardAction={ this.props.clipboardAction }
            clipboard={ this.props.clipboard }
            contextMenu={ this.props.contextMenu }
            hasSelection={ this.props.hasSelection }
            hasMultipleSelection={ this.props.hasMultipleSelection }
            groups={ this.props.groups }
            canPaste={ this.props.canPaste }/>
        </SidebarContainer>

    );
  }
}

MocksSidebar.propTypes = {
  hasSelection: React.PropTypes.bool.isRequired,
  selectedItems: React.PropTypes.array.isRequired,
  selectedMocks: React.PropTypes.array.isRequired,
  selectedGroups: React.PropTypes.array.isRequired,
  openMenu: React.PropTypes.func.isRequired,
  sidebarWidth: React.PropTypes.number.isRequired,
  groups: React.PropTypes.array.isRequired,
  setGroups: React.PropTypes.func.isRequired,
  renamedItemId: React.PropTypes.string,
  editItemName: React.PropTypes.func.isRequired,
  closeMenu: React.PropTypes.func.isRequired,
  expandAllGroups: React.PropTypes.func.isRequired,
  collapseAllGroups: React.PropTypes.func.isRequired,
  selectFirstMock: React.PropTypes.func.isRequired,
  renameItemId: React.PropTypes.string,
  recaptureMocks: React.PropTypes.func.isRequired,
  recaptureRequestIds: React.PropTypes.array.isRequired,
  updateQuery: React.PropTypes.func.isRequired,
  clipboardAction: React.PropTypes.func.isRequired,
  clipboard: React.PropTypes.shape({
    command: React.PropTypes.string,
    items: React.PropTypes.array.isRequired
  }),
  contextMenu: React.PropTypes.shape({
    visible: React.PropTypes.bool.isRequired,
    x: React.PropTypes.number.isRequired,
    y: React.PropTypes.number.isRequired
  }),
  addGroup: React.PropTypes.func.isRequired,
  canPaste: React.PropTypes.func.isRequired
};

export default DropTarget('mock', groupTarget, collect)(MocksSidebar);
<commit_msg>add glowing for recording icon<commit_after>import React from 'react';
import { DropTarget } from 'react-dnd';
import some from 'lodash/some';
import EVENTS from 'api/constants/events';
import first from 'lodash/first';
import find from 'lodash/find';
import flatten from 'lodash/flatten';
import API from 'api';
import Group from 'ui/components/Mocks/Sidebar/Group';
import Mock from 'ui/components/Mocks/Sidebar/Mock';
import ResizeHandle from 'ui/components/common/ResizeHandle';
import ContextMenu from 'ui/components/Mocks/Sidebar/ContextMenu';
import { SidebarContainer, ActionGroup, ActionsContainer, Action } from 'ui/components/Mocks/Sidebar/styled';
import MockState from 'ui/states/MocksState';
import Icon from 'ui/components/common/Icon';
import IconDropdown from 'ui/components/common/IconDropdown';

const noop = () => true;

const groupTarget = {
  drop(props, monitor) {
    if (monitor.didDrop()) {
      return;
    }

    const mockId = monitor.getItem().id;
    const mock   = API.getMock(mockId);

    if (mock) {
      API.updateMock(mockId, { ...mock, groupId: null });
    }
  }
};

function collect(connector, monitor) {
  return {
    connectDropTarget: connector.dropTarget(),
    isHovered: monitor.isOver({ shallow: true })
  };
}

class MocksSidebar extends React.PureComponent {

  componentDidMount() {
    API.on(EVENTS.UPDATE_MOCK, this.reRender);
    API.on(EVENTS.UPDATE_GROUP, this.reRender);
  }

  componentWillUnmount() {
    API.off(EVENTS.UPDATE_MOCK, this.reRender);
    API.off(EVENTS.UPDATE_GROUP, this.reRender);
  }

  reRender = () => {
    setTimeout(() => this.forceUpdate(), 0);
  };

  select = (item, isRightClick) => (event) => {
    if (isRightClick && this.props.hasMultipleSelection) {
      return;
    }

    if (!isRightClick) {
      event.stopPropagation();
    }

    if (event.shiftKey && this.props.selectedItems.length > 0) {
      const groups = flatten(API.groups.map((group) => [group, ...group.mocks]));
      const mocks  = API.mocks.filter((mock) => !mock.groupId);

      const list          = [...groups, ...mocks];
      const selectedItems = list.filter((listItem) => this.props.selectedItems.indexOf(listItem) !== -1);
      const firstIndex    = list.indexOf(first(selectedItems));
      const currentIndex  = list.indexOf(item);
      const itemsBetween  = currentIndex > firstIndex
        ? list.slice(firstIndex, currentIndex + 1)
        : list.slice(currentIndex, firstIndex);

      return itemsBetween
        .filter((itemInRange) => this.props.selectedItems.indexOf(itemInRange) === -1)
        .forEach((itemInRange) => this.props.select(itemInRange, true));
    }

    const multiple = event.metaKey || event.ctrlKey;

    this.props.select(item, multiple);
  };

  toggleMock = (mock) => (event) => {
    event.stopPropagation();

    API.toggleMock(mock.id);
  };

  toggleGroup = (group) => (event) => {
    event.stopPropagation();

    API.toggleGroup(group.id);
  };

  getMocks() {
    return API.mocks
      .filter((mock) => !mock.groupId)
      .filter((mock) => mock.url.includes(this.props.searchTerm))
      .filter(this.props.customFilter || noop)
      .map((mock) => (
          <Mock key={ mock.id }
                mock={ mock }
                searchTerm={ this.props.searchTerm }
                isSelected={ !!find(this.props.selectedMocks, { id: mock.id }) }
                toggleMock={ this.toggleMock(mock) }
                onClick={ this.select(mock) }
                onContextMenu={ this.select(mock, true) }
                renamedItemId={ this.props.renamedItemId }
                editItemName={ this.props.editItemName }/>
        )
      );
  }

  getGroups() {
    return API.groups
      .filter((group) => {
        if (!this.props.searchTerm) {
          return true;
        }

        if (group.name.includes(this.props.searchTerm)) {
          return true;
        }

        return some(group.mocks, (mock) => mock.url.includes(this.props.searchTerm))
      })
      .map((group) => {
        const mocks = group.mocks.filter(this.props.customFilter || noop);

        return (
          <Group
            key={ group.id }
            id={ group.id }
            name={ group.name }
            active={ group.active }
            isSelected={ !!find(this.props.selectedGroups, { id: group.id }) }
            mocks={ mocks }
            searchTerm={ this.props.searchTerm }
            toggleGroup={ this.toggleGroup(group) }
            toggleMock={ this.toggleMock }
            onSelect={ this.select }
            onClick={ this.select(group) }
            onContextMenu={ this.select(group, true) }
            groups={ this.props.groups }
            setGroups={ this.props.setGroups }
            selectedMocks={ this.props.selectedMocks }
            editItemName={ this.props.editItemName }
            addGroup={ this.props.addGroup }
            renamedItemId={ this.props.renamedItemId }/>
        )
      });
  }

  openContextMenu = (event) => {
    event.preventDefault();

    this.props.openMenu(event.clientX, event.clientY);
  };

  addNew = (type) => {
    if (type === 'mock') {
      const newMock = API.addMock();

      MocksState.selectItems([newMock]);
    }

    if (type === 'group') {
      const newGroup = API.addGroup({ name: 'New Group' });

      MockState.addGroup({
        id: newGroup.id,
        isOpen: false,
        lastState: null
      });
      MockState.selectItems([newGroup]);
    }
  };

  toggleRecording = () => {
    if (API.isRecording) {
      API.stopRecording();
    } else {
      API.startRecording();
    }
  };

  render() {
    return this.props.connectDropTarget(

        <ResizeHandle value="mocksSidebarWidth"/>

        <SidebarContainer
          isHovered={ this.props.isHovered }
          style={{ width: this.props.sidebarWidth }}
          onContextMenu={ this.openContextMenu }>
          <ActionsContainer>
            <ActionGroup marginRight="40">
              <Action>
                <IconDropdown
                  icon="add"
                  options={ [ { label: 'Mock', value: 'mock' }, { label: 'Group', value: 'group' }]}
                  onChange={ this.addNew }/>
              </Action>
              <Action onClick={ this.toggleRecording } margin="2">
                { API.isRecording ? <Icon src="recording" style={{ fill: '#c44a13' }}/> : <Icon src="record"/> }
              </Action>
            </ActionGroup>

            <ActionGroup autosize>
              <Action onClick={ MockState.collapseAllGroups }>
                <Icon src="collapseAll"/>
              </Action>
              <Action onClick={ MockState.expandAllGroups }>
                <Icon src="expandAll"/>
              </Action>
            </ActionGroup>

            <ActionGroup>
              <Action>
                <Icon src="undo"/>
              </Action>
              <Action onClick={ MockState.deleteSelected }>
                <Icon src="remove"/>
              </Action>
            </ActionGroup>
          </ActionsContainer>
          { this.getGroups() }
          { this.getMocks() }

          <ContextMenu
            closeMenu={ this.props.closeMenu }
            selectedMocks={ this.props.selectedMocks }
            selectedGroups={ this.props.selectedGroups }
            expandAllGroups={ this.props.expandAllGroups }
            collapseAllGroups={ this.props.collapseAllGroups }
            selectedItems={ this.props.selectedItems }
            selectFirstMock={ this.props.selectFirstMock }
            renameItemId={ this.props.renamedItemId }
            recaptureMocks={ this.props.recaptureMocks }
            recaptureRequestIds={ this.props.recaptureRequestIds }
            updateQuery={ this.props.updateQuery }
            clipboardAction={ this.props.clipboardAction }
            clipboard={ this.props.clipboard }
            contextMenu={ this.props.contextMenu }
            hasSelection={ this.props.hasSelection }
            hasMultipleSelection={ this.props.hasMultipleSelection }
            groups={ this.props.groups }
            canPaste={ this.props.canPaste }/>
        </SidebarContainer>

    );
  }
}

MocksSidebar.propTypes = {
  hasSelection: React.PropTypes.bool.isRequired,
  selectedItems: React.PropTypes.array.isRequired,
  selectedMocks: React.PropTypes.array.isRequired,
  selectedGroups: React.PropTypes.array.isRequired,
  openMenu: React.PropTypes.func.isRequired,
  sidebarWidth: React.PropTypes.number.isRequired,
  groups: React.PropTypes.array.isRequired,
  setGroups: React.PropTypes.func.isRequired,
  renamedItemId: React.PropTypes.string,
  editItemName: React.PropTypes.func.isRequired,
  closeMenu: React.PropTypes.func.isRequired,
  expandAllGroups: React.PropTypes.func.isRequired,
  collapseAllGroups: React.PropTypes.func.isRequired,
  selectFirstMock: React.PropTypes.func.isRequired,
  renameItemId: React.PropTypes.string,
  recaptureMocks: React.PropTypes.func.isRequired,
  recaptureRequestIds: React.PropTypes.array.isRequired,
  updateQuery: React.PropTypes.func.isRequired,
  clipboardAction: React.PropTypes.func.isRequired,
  clipboard: React.PropTypes.shape({
    command: React.PropTypes.string,
    items: React.PropTypes.array.isRequired
  }),
  contextMenu: React.PropTypes.shape({
    visible: React.PropTypes.bool.isRequired,
    x: React.PropTypes.number.isRequired,
    y: React.PropTypes.number.isRequired
  }),
  addGroup: React.PropTypes.func.isRequired,
  canPaste: React.PropTypes.func.isRequired
};

export default DropTarget('mock', groupTarget, collect)(MocksSidebar);
<|endoftext|>


<commit_before>module('users.timfelgentreff.experimental.assert').requires('users.timfelgentreff.babelsberg.constraintinterpreter').toRun(function() {

	JSLoader.loadJs(module('users.timfelgentreff.csp.underscore-min').uri());

	/***************************************************************
	 * Continuous asserts
	 ***************************************************************/

	// TODO: improve syntax of bbb.assert
	// TODO: multiple asserts and multiple variable interconnected
	// TODO: enable and disable AssertConstraints
	// TODO: integration with other solvers

	Error.subclass("ContinuousAssertError", {
		initialize: function ContinuousAssertError(msg) {
			if (Error.captureStackTrace) {
				Error.captureStackTrace(this, ContinuousAssertError);
			} else {
				this.stack = (new Error).stack || '';
			}
			this.message = msg;
		},
		name: "ContinuousAssertError"
	});

	Object.subclass("AssertSolver", {
		initialize: function(message) {
			this.message = message;
		},
	    isConstraintObject: function() { return true; },
	    always: function(opts, func) {
	    	//console.log("AssertSolver.always");
	        var ctx = opts.ctx;
	        func.varMapping = ctx;
	        var cobj = new Constraint(func, this);
			cobj.allowFailing = true;
	        cobj.addPrimitiveConstraint(new AssertSolver.Constraint(this, cobj, func));
			try {
				if(!opts.postponeEnabling) { cobj.enable(); }
			} catch(e) {
				if(e instanceof ContinuousAssertError) {
					cobj.disable();
				}
				throw e;
			}
	        return cobj;
	    },
	    constraintVariableFor: function(value, ivarname, bbbCVar) {
	    	//console.log("AssertSolver.constraintVariableFor", value, ivarname, bbbCVar);
	    	return new AssertSolver.Variable(this, value, ivarname, bbbCVar);
	    },
	    solve: function() {
	    	//console.log("AssertSolver.solve");
	    	this.check();
	    },
	    check: function() {
	    	//console.log("AssertSolver.check");
	    	if(this.constraint && this.constraint.enabled && typeof this.constraint.predicate === "function")
	    		if(!this.constraint.predicate())
	    			throw new ContinuousAssertError(this.message);
	    },
	    weight: 10
	});

	Object.subclass("AssertSolver.Variable", {
		initialize: function(solver, value, ivarname, bbbCVar) {
	    	//console.log("AssertSolver.Variable.initialize");
			this.solver = solver;
			this.__val__ = value;
		},
	    isConstraintObject: function() { return true; },
	    toFulfill: function(assertPredicate) {
	    	//console.log("AssertSolver.Variable.toFulfill", assertPredicate);
	    	this.assertPredicate = assertPredicate;
	    },
	    suggestValue: function(value) {
	    	//console.log("AssertSolver.Variable.suggestValue", value);
			var prev = this.__val__;
	    	this.__val__ = value;
			try {
				this.solver.solve();
			} catch(e) {
				// revert value in case of a violated assertion
				if(e instanceof ContinuousAssertError) {
					this.__val__ = prev;
				}
				throw e;
			}
	    	return this.__val__;
	    },
	    value: function() {
	    	return this.__val__;
	    },
	    setReadonly: function(bool) {
	    	//console.log("AssertSolver.Variable.setReadonly", bool);
	    	this.readonly = bool;
	    },
	    isReadonly: function() {
	    	//console.log("AssertSolver.Variable.isReadonly");
	        return this.readonly;
	    },
	    cnEquals: function() {
	    	//console.log("AssertSolver.Variable.cnEquals");
	        return new AssertSolver.PrimitiveConstraint(this, arguments);
	    }
	});

	Object.subclass("AssertSolver.PrimitiveConstraint", {
	    isConstraintObject: function() { return true; },
	    initialize: function(variable, args) {
	    	//console.log("AssertSolver.PrimitiveConstraint.initialize", variable, args);
			this.enabled = false;
	    	this.solver = variable.solver;
	    },
	    enable: function() {
	    	//console.log("AssertSolver.PrimitiveConstraint.enable");
			this.enabled = true;
	    },
	    disable: function() {
	    	//console.log("AssertSolver.PrimitiveConstraint.disable");
			this.enabled = false;
	    }
	});

	Object.subclass("AssertSolver.Constraint", {
	    isConstraintObject: function() { return true; },
	    initialize: function(solver, bbbConstraint, func) {
	    	//console.log("AssertSolver.Constraint.initialize");
			this.enabled = false;
	    	this.solver = solver;
	    	this.solver.constraint = this;

	    	// extract predicate from bbbConstraint
	    	this.predicate = func;/*_.chain(bbbConstraint.constraintvariables)
	    		.map(function(variable) {
	    			return variable.externalVariables(this.solver);
	    		}, this)
	    		.find(function(externalVariable) {
	    			return typeof externalVariable.assertPredicate === "function";
	    		})
	    		.value()
	    		//.assertPredicate;*/
	    },
	    enable: function() {
	    	//console.log("AssertSolver.Constraint.enable");
			this.enabled = true;
	    },
	    disable: function() {
	    	//console.log("AssertSolver.Constraint.disable");
			this.enabled = false;
	    }
	});

	Object.extend(Babelsberg.prototype, {
		assert: function(opts, func) {
			opts.solver = new AssertSolver(opts.message);
			opts.allowUnsolvableOperations = true;
			opts.allowTests = true;
			//opts.debugging = true;
	        return this.always(opts, func);
		}
	});

	/***************************************************************
	 * Triggering
	 ***************************************************************/
	// TODO: queue triggered actions and process list after assignments are done

	AssertSolver.subclass("TriggerSolver", {
		initialize: function(callback) {
			this.callback = callback;
			this.triggeredOnce = false;
		},
	    always: function(opts, func) {
	    	//console.log("TriggerSolver.always");
	        var ctx = opts.ctx;
	        func.varMapping = ctx;
	        var cobj = new Constraint(func, this);
			cobj.allowFailing = true;
	        cobj.addPrimitiveConstraint(new AssertSolver.Constraint(this, cobj, func));
			if(!opts.postponeEnabling) { cobj.enable(); }
	        return cobj;
	    },
	    solve: function() {
	    	//console.log("TriggerSolver.solve", this.constraint.predicate());
	    	if(this.constraint &&
				this.constraint.enabled &&
				typeof this.constraint.predicate === "function" &&
				!this.triggeredOnce
			) {
	    		if(this.constraint.predicate()) {
					this.triggeredOnce = true;
					this.constraint.disable();
	    			this.callback();
				} else {
					// resetOnFalse?
				}
			}
	    },
	    weight: 10
	});

	Object.extend(Babelsberg.prototype, {
		trigger: function(opts, func) {
			opts.solver = new TriggerSolver(opts.callback);
			opts.allowUnsolvableOperations = true;
			opts.allowTests = true;
			//opts.debugging = true;
	        return this.always(opts, func);
		}
	});

	/***************************************************************
	 * Layer activation
	 ***************************************************************/
	AssertSolver.subclass("LayerActivationSolver", {
		initialize: function(layer) {
			this.layer = layer;
		},
	    always: function(opts, func) {
	    	//console.log("LayerActivationSolver.always");
	        var ctx = opts.ctx;
	        func.varMapping = ctx;
	        var cobj = new Constraint(func, this);
			cobj.allowFailing = true;
	        cobj.addPrimitiveConstraint(new AssertSolver.Constraint(this, cobj, func));
			cobj.enable();
	        return cobj;
	    },
	    solve: function() {
	    	//console.log("LayerActivationSolver.solve", this.constraint.predicate());
	    	if(this.constraint &&
				this.constraint.enabled &&
				typeof this.constraint.predicate === "function"
			) {
				var predicateFulfilled = this.constraint.predicate();
	    		if(predicateFulfilled && !this.layer.isGlobal()) {
	    			this.layer.beGlobal();
				} else  if(!predicateFulfilled && this.layer.isGlobal()) {
	    			this.layer.beNotGlobal();
				}
			}
	    },
	    weight: 10
	});

	// hotfix
	// instead of cop.GlobalLayers.removeAt(idx); just use standard conform splice method
	Object.extend(cop, {
		disableLayer: function(layer) {
			var idx = cop.GlobalLayers.indexOf(layer)
			if (idx < 0) return;
			cop.GlobalLayers.splice(idx, 1);
			cop.invalidateLayerComposition();
		},
	});

	Object.extend(Layer.prototype, {
		activeOn: function(opts, func) {
			opts.solver = new LayerActivationSolver(this);
			opts.allowUnsolvableOperations = true;
			opts.allowTests = true;
			//opts.debugging = true;
	        bbb.always(opts, func);

			return this;
		}
	});

	/***************************************************************
	 * Scoped constraints
	 ***************************************************************/
	Object.extend(Layer.prototype, {
		always: function(opts, func) {
			opts.postponeEnabling = !this.isGlobal();
			var cobj = bbb.always(opts, func);

			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.push(cobj);
		},
		assert: function(opts, func) {
			opts.postponeEnabling = !this.isGlobal();
			var cobj = bbb.assert(opts, func);

			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.push(cobj);
		},
		trigger: function(opts, func) {
			opts.postponeEnabling = !this.isGlobal();
			var cobj = bbb.trigger(opts, func);

			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.push(cobj);
		},
		_activate: function() {
			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.forEach(function(cobj) {
				cobj.enable();
			});
		},
		_deactivate: function() {
			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.forEach(function(cobj) {
				cobj.disable();
			});
		}
	});

	var copWithLayers = cop.withLayers;
	var copWithoutLayers = cop.withoutLayers;
	var copEnableLayer = cop.enableLayer;
	var copDisableLayer = cop.disableLayer;
	Object.extend(cop, {
		/* Layer Activation */
		withLayers: function withLayers(layers, func) {
			layers.forEach(function(layer) { layer._activate(); });

			try {
				return copWithLayers.apply(this, arguments);
			} finally {
				layers.forEach(function(layer) { layer._deactivate(); });
			}
		},

		withoutLayers: function withoutLayers(layers, func) {
			layers.forEach(function(layer) { layer._deactivate(); });

			try {
				return copWithoutLayers.apply(this, arguments);
			} finally {
				layers.forEach(function(layer) { layer._activate(); });
			}
		},

		/* Global Layer Activation */
		enableLayer: function(layer) {
			layer._activate();
			return copEnableLayer.apply(this, arguments);
		},

		disableLayer: function(layer) {
			layer._deactivate();
			return copDisableLayer.apply(this, arguments);
		}
	});

});
<commit_msg>reset trigger on false<commit_after>module('users.timfelgentreff.experimental.assert').requires('users.timfelgentreff.babelsberg.constraintinterpreter').toRun(function() {

	JSLoader.loadJs(module('users.timfelgentreff.csp.underscore-min').uri());

	/***************************************************************
	 * Continuous asserts
	 ***************************************************************/

	// TODO: improve syntax of bbb.assert
	// TODO: multiple asserts and multiple variable interconnected
	// TODO: enable and disable AssertConstraints
	// TODO: integration with other solvers

	Error.subclass("ContinuousAssertError", {
		initialize: function ContinuousAssertError(msg) {
			if (Error.captureStackTrace) {
				Error.captureStackTrace(this, ContinuousAssertError);
			} else {
				this.stack = (new Error).stack || '';
			}
			this.message = msg;
		},
		name: "ContinuousAssertError"
	});

	Object.subclass("AssertSolver", {
		initialize: function(message) {
			this.message = message;
		},
	    isConstraintObject: function() { return true; },
	    always: function(opts, func) {
	    	//console.log("AssertSolver.always");
	        var ctx = opts.ctx;
	        func.varMapping = ctx;
	        var cobj = new Constraint(func, this);
			cobj.allowFailing = true;
	        cobj.addPrimitiveConstraint(new AssertSolver.Constraint(this, cobj, func));
			try {
				if(!opts.postponeEnabling) { cobj.enable(); }
			} catch(e) {
				if(e instanceof ContinuousAssertError) {
					cobj.disable();
				}
				throw e;
			}
	        return cobj;
	    },
	    constraintVariableFor: function(value, ivarname, bbbCVar) {
	    	//console.log("AssertSolver.constraintVariableFor", value, ivarname, bbbCVar);
	    	return new AssertSolver.Variable(this, value, ivarname, bbbCVar);
	    },
	    solve: function() {
	    	//console.log("AssertSolver.solve");
	    	this.check();
	    },
	    check: function() {
	    	//console.log("AssertSolver.check");
	    	if(this.constraint && this.constraint.enabled && typeof this.constraint.predicate === "function")
	    		if(!this.constraint.predicate())
	    			throw new ContinuousAssertError(this.message);
	    },
	    weight: 10
	});

	Object.subclass("AssertSolver.Variable", {
		initialize: function(solver, value, ivarname, bbbCVar) {
	    	//console.log("AssertSolver.Variable.initialize");
			this.solver = solver;
			this.__val__ = value;
		},
	    isConstraintObject: function() { return true; },
	    toFulfill: function(assertPredicate) {
	    	//console.log("AssertSolver.Variable.toFulfill", assertPredicate);
	    	this.assertPredicate = assertPredicate;
	    },
	    suggestValue: function(value) {
	    	//console.log("AssertSolver.Variable.suggestValue", value);
			var prev = this.__val__;
	    	this.__val__ = value;
			try {
				this.solver.solve();
			} catch(e) {
				// revert value in case of a violated assertion
				if(e instanceof ContinuousAssertError) {
					this.__val__ = prev;
				}
				throw e;
			}
	    	return this.__val__;
	    },
	    value: function() {
	    	return this.__val__;
	    },
	    setReadonly: function(bool) {
	    	//console.log("AssertSolver.Variable.setReadonly", bool);
	    	this.readonly = bool;
	    },
	    isReadonly: function() {
	    	//console.log("AssertSolver.Variable.isReadonly");
	        return this.readonly;
	    },
	    cnEquals: function() {
	    	//console.log("AssertSolver.Variable.cnEquals");
	        return new AssertSolver.PrimitiveConstraint(this, arguments);
	    }
	});

	Object.subclass("AssertSolver.PrimitiveConstraint", {
	    isConstraintObject: function() { return true; },
	    initialize: function(variable, args) {
	    	//console.log("AssertSolver.PrimitiveConstraint.initialize", variable, args);
			this.enabled = false;
	    	this.solver = variable.solver;
	    },
	    enable: function() {
	    	//console.log("AssertSolver.PrimitiveConstraint.enable");
			this.enabled = true;
	    },
	    disable: function() {
	    	//console.log("AssertSolver.PrimitiveConstraint.disable");
			this.enabled = false;
	    }
	});

	Object.subclass("AssertSolver.Constraint", {
	    isConstraintObject: function() { return true; },
	    initialize: function(solver, bbbConstraint, func) {
	    	//console.log("AssertSolver.Constraint.initialize");
			this.enabled = false;
	    	this.solver = solver;
	    	this.solver.constraint = this;
			this.bbbConstraint = bbbConstraint;

	    	// extract predicate from bbbConstraint
	    	this.predicate = func;/*_.chain(bbbConstraint.constraintvariables)
	    		.map(function(variable) {
	    			return variable.externalVariables(this.solver);
	    		}, this)
	    		.find(function(externalVariable) {
	    			return typeof externalVariable.assertPredicate === "function";
	    		})
	    		.value()
	    		//.assertPredicate;*/
	    },
	    enable: function() {
	    	//console.log("AssertSolver.Constraint.enable");
			this.enabled = true;
	    },
	    disable: function() {
	    	//console.log("AssertSolver.Constraint.disable");
			this.enabled = false;
	    }
	});

	Object.extend(Babelsberg.prototype, {
		assert: function(opts, func) {
			opts.solver = new AssertSolver(opts.message);
			opts.allowUnsolvableOperations = true;
			opts.allowTests = true;
			//opts.debugging = true;
	        return this.always(opts, func);
		}
	});

	/***************************************************************
	 * Triggering
	 ***************************************************************/
	AssertSolver.subclass("TriggerSolver", {
		initialize: function(callback) {
			this.callback = callback;
			this.triggeredOnce = false;
		},
	    always: function(opts, func) {
	    	//console.log("TriggerSolver.always");
	        var ctx = opts.ctx;
	        func.varMapping = ctx;
	        var cobj = new Constraint(func, this);
			cobj.allowFailing = true;
	        cobj.addPrimitiveConstraint(new AssertSolver.Constraint(this, cobj, func));
			if(!opts.postponeEnabling) { cobj.enable(); }
	        return cobj;
	    },
	    solve: function() {
	    	//console.log("TriggerSolver.solve", this.constraint.predicate());
	    	if(this.constraint &&
				this.constraint.enabled &&
				typeof this.constraint.predicate === "function"
			) {
	    		if(this.constraint.predicate()) {
					if(!this.triggeredOnce) {
						this.triggeredOnce = true;
						this.callback.call(this.constraint.bbbConstraint);
					}
				} else {
					this.triggeredOnce = false;
				}
			}
	    },
	    weight: 10
	});

	Object.extend(Babelsberg.prototype, {
		trigger: function(opts, func) {
			opts.solver = new TriggerSolver(opts.callback);
			opts.allowUnsolvableOperations = true;
			opts.allowTests = true;
			//opts.debugging = true;
	        return this.always(opts, func);
		}
	});

	/***************************************************************
	 * Layer activation
	 ***************************************************************/
	AssertSolver.subclass("LayerActivationSolver", {
		initialize: function(layer) {
			this.layer = layer;
		},
	    always: function(opts, func) {
	    	//console.log("LayerActivationSolver.always");
	        var ctx = opts.ctx;
	        func.varMapping = ctx;
	        var cobj = new Constraint(func, this);
			cobj.allowFailing = true;
	        cobj.addPrimitiveConstraint(new AssertSolver.Constraint(this, cobj, func));
			cobj.enable();
	        return cobj;
	    },
	    solve: function() {
	    	//console.log("LayerActivationSolver.solve", this.constraint.predicate());
	    	if(this.constraint &&
				this.constraint.enabled &&
				typeof this.constraint.predicate === "function"
			) {
				var predicateFulfilled = this.constraint.predicate();
	    		if(predicateFulfilled && !this.layer.isGlobal()) {
	    			this.layer.beGlobal();
				} else  if(!predicateFulfilled && this.layer.isGlobal()) {
	    			this.layer.beNotGlobal();
				}
			}
	    },
	    weight: 10
	});

	// hotfix
	// instead of cop.GlobalLayers.removeAt(idx); just use standard conform splice method
	Object.extend(cop, {
		disableLayer: function(layer) {
			var idx = cop.GlobalLayers.indexOf(layer)
			if (idx < 0) return;
			cop.GlobalLayers.splice(idx, 1);
			cop.invalidateLayerComposition();
		},
	});

	Object.extend(Layer.prototype, {
		activeOn: function(opts, func) {
			opts.solver = new LayerActivationSolver(this);
			opts.allowUnsolvableOperations = true;
			opts.allowTests = true;
			//opts.debugging = true;
	        bbb.always(opts, func);

			return this;
		}
	});

	/***************************************************************
	 * Scoped constraints
	 ***************************************************************/
	Object.extend(Layer.prototype, {
		always: function(opts, func) {
			opts.postponeEnabling = !this.isGlobal();
			var cobj = bbb.always(opts, func);

			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.push(cobj);
		},
		assert: function(opts, func) {
			opts.postponeEnabling = !this.isGlobal();
			var cobj = bbb.assert(opts, func);

			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.push(cobj);
		},
		trigger: function(opts, func) {
			opts.postponeEnabling = !this.isGlobal();
			var cobj = bbb.trigger(opts, func);

			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.push(cobj);
		},
		_activate: function() {
			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.forEach(function(cobj) {
				cobj.enable();
			});
		},
		_deactivate: function() {
			this.constraintObjects = this.constraintObjects || [];
			this.constraintObjects.forEach(function(cobj) {
				cobj.disable();
			});
		}
	});

	var copWithLayers = cop.withLayers;
	var copWithoutLayers = cop.withoutLayers;
	var copEnableLayer = cop.enableLayer;
	var copDisableLayer = cop.disableLayer;
	Object.extend(cop, {
		/* Layer Activation */
		withLayers: function withLayers(layers, func) {
			layers.forEach(function(layer) { layer._activate(); });

			try {
				return copWithLayers.apply(this, arguments);
			} finally {
				layers.forEach(function(layer) { layer._deactivate(); });
			}
		},

		withoutLayers: function withoutLayers(layers, func) {
			layers.forEach(function(layer) { layer._deactivate(); });

			try {
				return copWithoutLayers.apply(this, arguments);
			} finally {
				layers.forEach(function(layer) { layer._activate(); });
			}
		},

		/* Global Layer Activation */
		enableLayer: function(layer) {
			layer._activate();
			return copEnableLayer.apply(this, arguments);
		},

		disableLayer: function(layer) {
			layer._deactivate();
			return copDisableLayer.apply(this, arguments);
		}
	});

});
<|endoftext|>


<commit_before>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

/*
 global $, Config, jQuery, sakai, sdata, fluid
 */

require(["jquery", "sakai/sakai.api.core", "myb/myb.api.core", "/devwidgets/mylinks/javascript/default-links.js"], function($, sakai, myb, defaultLinks) {

    sakai_global.mylinks = function (tuid) {

        // page elements
        var widgetContainer = $("#" + tuid);
        var linkTitleInput = $("#link-title", widgetContainer);
        var linkUrlInput = $("#link-url", widgetContainer);
        var linkList = $(".link_list", widgetContainer);
        var addEditPanel = $(".addedit_link_panel", widgetContainer);
        var saveLinkButton = $("#savelink-button", widgetContainer);
        var addLinkButton = $("#addlink-button", widgetContainer);
        var addEditPanelTitle = $(".addedit_link_panel_title", widgetContainer);
        var accordionContainer = $("#accordion", widgetContainer);

        // index of the link currently being edited
        var currentLinkIndex = null;

        // data files and paths
        var linksDataPath = "/~" + sakai.data.me.user.userid + "/private/my_links";

        // the user's own links ("My Links")
        var userLinkData = defaultLinks.sections[defaultLinks.userSectionIndex];

        var validator = $("#mylinks-form").validate({
                    rules : {
                        "link-title" : {
                            required: true
                        },
                        "link-url" : {
                            required : true,
                            url : true
                        }
                    }
                });

        var renderLinkList = function(data) {
            accordionContainer.html(sakai.api.Util.TemplateRenderer("accordion_template", data));
            setupAccordion();
            setupEditIcons();
        };

        /*
         Load user's list if there is one, and merge it with the default links. If no user's list, just use
         the default ones.
         */
        var loadUserList = function() {
            sakai.api.Server.loadJSON(linksDataPath + ".2.json", function (success, data) {
                if (success) {
                    // merge the user's links with the default links
                    userLinkData = data;
                    defaultLinks.sections[defaultLinks.userSectionIndex] = userLinkData;
                } else {
                    userLinkData.activeSection = 0;
                }
                defaultLinks.activeSection = userLinkData.activeSection;
                renderLinkList(defaultLinks);
            });
        };

        var setAddEditLinkTitle = function (title) {
            addEditPanelTitle.text(title);
        };

        var cancelEditMode = function() {
            currentLinkIndex = null;
            //linkList.show();
            addEditPanel.hide();
            linkTitleInput.attr("value", "").removeClass("error");
            linkUrlInput.attr("value", "").removeClass("error");
            $("label.error").hide();
        };

        var enterAddMode = function() {
            showPane($(".accordion_pane:last"));
            setAddEditLinkTitle("Add link");
            currentLinkIndex = null;
            addEditPanel.show();
            addLinkButton.show();
            saveLinkButton.hide();
            linkTitleInput[0].focus();
        };

        var enterEditMode = function(index) {
            var link = userLinkData.links[index];
            setAddEditLinkTitle("Edit link");
            linkTitleInput[0].value = link.name;
            linkUrlInput[0].value = link.url;
            currentLinkIndex = index;
            addEditPanel.show();
            addLinkButton.hide();
            saveLinkButton.show();
            linkTitleInput[0].focus();
        };

        var saveLink = function() {
            var isValid = validator.form();
            if (isValid) {
                var index = currentLinkIndex;
                if (index === null) {
                    index = userLinkData.links.length;
                }
                userLinkData.links[index] = {
                    "name" : linkTitleInput[0].value,
                    "url" : linkUrlInput[0].value,
                    "popup_description": linkTitleInput[0].value
                };
                defaultLinks.sections[defaultLinks.userSectionIndex] = userLinkData;

                sakai.api.Server.saveJSON(linksDataPath, userLinkData, function(success) {
                    if (success) {
                        cancelEditMode();
                        renderLinkList(defaultLinks);
                    } else {
                        sakai.api.Util.notification.show("", "A server error occurred while trying to save your link.",
                                sakai.api.Util.notification.type.ERROR, false);
                    }
                });
            }
        };

        var setupEventHandlers = function() {
            $("#add-link-mode", widgetContainer).click(function() {
                enterAddMode();
            });
            $("#cancel-button", widgetContainer).click(function() {
                cancelEditMode();
            });
            addLinkButton.click(function() {
                saveLink();
            });
            saveLinkButton.click(function() {
                saveLink();
            });
            linkTitleInput.keydown(function(event) {
                if (event.keyCode === 13) {
                    event.preventDefault();
                    saveLink();
                }
            });
            linkUrlInput.keydown(function(event) {
                if (event.keyCode === 13) {
                    event.preventDefault();
                    saveLink();
                }
            });
        };

        var setupEditIcons = function() {
            for (var i = 0; i < userLinkData.links.length; i++) {

                // edit button
                var editIcon = $("#mylinks_edit_" + i, widgetContainer);
                editIcon.click(function() {
                    var idx = this.id.replace("mylinks_edit_", "");
                    enterEditMode(idx);
                });

                // delete button
                var deleteIcon = $("#mylinks_delete_" + i, widgetContainer);
                deleteIcon.click(function() {
                    var idx = this.id.replace("mylinks_delete_", "");
                    userLinkData.links.splice(idx, 1);
                    defaultLinks.sections[defaultLinks.userSectionIndex] = userLinkData;
                    sakai.api.Server.saveJSON(linksDataPath, userLinkData, function(success) {
                        if (success) {
                            renderLinkList(defaultLinks);
                        } else {
                            sakai.api.Util.notification.show("", "A server error occurred while trying to delete your link.",
                                    sakai.api.Util.notification.type.ERROR, false);
                        }
                    }, true);
                });
            }
        };

        var showPane = function (pane) {

            if (!pane.hasClass("accordion_open")) {
                closePanes();
                pane.addClass("accordion_open");
            }
            pane.children(".accordion_content").slideDown(300, function() {
                pane.children(".accordion_content").css("overflow", "auto");
            });

            var previousActiveSection = userLinkData.activeSection;
            if ( typeof pane[0] != 'undefined') {
                userLinkData.activeSection = pane[0].id.replace("mylinks_section_", "");
            } else {
                userLinkData.activeSection = 0;
            }
            defaultLinks.activeSection = userLinkData.activeSection;
            if (previousActiveSection != userLinkData.activeSection) {
                // save active section only if it's different
                sakai.api.Server.saveJSON(linksDataPath, userLinkData);
            }
        };

        var hidePane = function (pane) {
            pane.removeClass("accordion_open");
            pane.children(".accordion_content").slideUp(300);
        };

        var closePanes = function () {
            $(".accordion_pane", accordionContainer).each(function () {
                hidePane($(this));
            });
        };

        var setupAccordion = function() {

            $(".section_label", accordionContainer).click(function() {
                showPane($(this).parent());
            });

            // Add Google Analytics outbound links tracking
            $("li.link a", accordionContainer).click(function () {
                myb.api.google.recordOutboundLink(this, 'Outbound Links', $(this).attr('href'));
                return false;
            });

            if (accordionContainer.width() > 250) {
                accordionContainer.addClass("link_grid")
            }

            showPane($(".accordion_open", accordionContainer));

        };

        var doInit = function() {
            loadUserList();
            setupEventHandlers();
        };

        doInit();

    };

    sakai.api.Widgets.widgetLoader.informOnLoad("mylinks");

});
<commit_msg>NOJIRA Remove unused and commented-out code<commit_after>/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

/*
 global $, Config, jQuery, sakai, sdata, fluid
 */

require(["jquery", "sakai/sakai.api.core", "myb/myb.api.core", "/devwidgets/mylinks/javascript/default-links.js"], function($, sakai, myb, defaultLinks) {

    sakai_global.mylinks = function (tuid) {

        // page elements
        var widgetContainer = $("#" + tuid);
        var linkTitleInput = $("#link-title", widgetContainer);
        var linkUrlInput = $("#link-url", widgetContainer);
        var addEditPanel = $(".addedit_link_panel", widgetContainer);
        var saveLinkButton = $("#savelink-button", widgetContainer);
        var addLinkButton = $("#addlink-button", widgetContainer);
        var addEditPanelTitle = $(".addedit_link_panel_title", widgetContainer);
        var accordionContainer = $("#accordion", widgetContainer);

        // index of the link currently being edited
        var currentLinkIndex = null;

        // data files and paths
        var linksDataPath = "/~" + sakai.data.me.user.userid + "/private/my_links";

        // the user's own links ("My Links")
        var userLinkData = defaultLinks.sections[defaultLinks.userSectionIndex];

        var validator = $("#mylinks-form").validate({
                    rules : {
                        "link-title" : {
                            required: true
                        },
                        "link-url" : {
                            required : true,
                            url : true
                        }
                    }
                });

        var renderLinkList = function(data) {
            accordionContainer.html(sakai.api.Util.TemplateRenderer("accordion_template", data));
            setupAccordion();
            setupEditIcons();
        };

        /*
         Load user's list if there is one, and merge it with the default links. If no user's list, just use
         the default ones.
         */
        var loadUserList = function() {
            sakai.api.Server.loadJSON(linksDataPath + ".2.json", function (success, data) {
                if (success) {
                    // merge the user's links with the default links
                    userLinkData = data;
                    defaultLinks.sections[defaultLinks.userSectionIndex] = userLinkData;
                } else {
                    userLinkData.activeSection = 0;
                }
                defaultLinks.activeSection = userLinkData.activeSection;
                renderLinkList(defaultLinks);
            });
        };

        var setAddEditLinkTitle = function (title) {
            addEditPanelTitle.text(title);
        };

        var cancelEditMode = function() {
            currentLinkIndex = null;
            addEditPanel.hide();
            linkTitleInput.attr("value", "").removeClass("error");
            linkUrlInput.attr("value", "").removeClass("error");
            $("label.error").hide();
        };

        var enterAddMode = function() {
            showPane($(".accordion_pane:last"));
            setAddEditLinkTitle("Add link");
            currentLinkIndex = null;
            addEditPanel.show();
            addLinkButton.show();
            saveLinkButton.hide();
            linkTitleInput[0].focus();
        };

        var enterEditMode = function(index) {
            var link = userLinkData.links[index];
            setAddEditLinkTitle("Edit link");
            linkTitleInput[0].value = link.name;
            linkUrlInput[0].value = link.url;
            currentLinkIndex = index;
            addEditPanel.show();
            addLinkButton.hide();
            saveLinkButton.show();
            linkTitleInput[0].focus();
        };

        var saveLink = function() {
            var isValid = validator.form();
            if (isValid) {
                var index = currentLinkIndex;
                if (index === null) {
                    index = userLinkData.links.length;
                }
                userLinkData.links[index] = {
                    "name" : linkTitleInput[0].value,
                    "url" : linkUrlInput[0].value,
                    "popup_description": linkTitleInput[0].value
                };
                defaultLinks.sections[defaultLinks.userSectionIndex] = userLinkData;

                sakai.api.Server.saveJSON(linksDataPath, userLinkData, function(success) {
                    if (success) {
                        cancelEditMode();
                        renderLinkList(defaultLinks);
                    } else {
                        sakai.api.Util.notification.show("", "A server error occurred while trying to save your link.",
                                sakai.api.Util.notification.type.ERROR, false);
                    }
                });
            }
        };

        var setupEventHandlers = function() {
            $("#add-link-mode", widgetContainer).click(function() {
                enterAddMode();
            });
            $("#cancel-button", widgetContainer).click(function() {
                cancelEditMode();
            });
            addLinkButton.click(function() {
                saveLink();
            });
            saveLinkButton.click(function() {
                saveLink();
            });
            linkTitleInput.keydown(function(event) {
                if (event.keyCode === 13) {
                    event.preventDefault();
                    saveLink();
                }
            });
            linkUrlInput.keydown(function(event) {
                if (event.keyCode === 13) {
                    event.preventDefault();
                    saveLink();
                }
            });
        };

        var setupEditIcons = function() {
            for (var i = 0; i < userLinkData.links.length; i++) {

                // edit button
                var editIcon = $("#mylinks_edit_" + i, widgetContainer);
                editIcon.click(function() {
                    var idx = this.id.replace("mylinks_edit_", "");
                    enterEditMode(idx);
                });

                // delete button
                var deleteIcon = $("#mylinks_delete_" + i, widgetContainer);
                deleteIcon.click(function() {
                    var idx = this.id.replace("mylinks_delete_", "");
                    userLinkData.links.splice(idx, 1);
                    defaultLinks.sections[defaultLinks.userSectionIndex] = userLinkData;
                    sakai.api.Server.saveJSON(linksDataPath, userLinkData, function(success) {
                        if (success) {
                            renderLinkList(defaultLinks);
                        } else {
                            sakai.api.Util.notification.show("", "A server error occurred while trying to delete your link.",
                                    sakai.api.Util.notification.type.ERROR, false);
                        }
                    }, true);
                });
            }
        };

        var showPane = function (pane) {
            if (!pane.hasClass("accordion_open")) {
                closePanes();
                pane.addClass("accordion_open");
            }
            pane.children(".accordion_content").slideDown(300, function() {
                pane.children(".accordion_content").css("overflow", "auto");
            });

            var previousActiveSection = userLinkData.activeSection;
            if ( typeof pane[0] != 'undefined') {
                userLinkData.activeSection = pane[0].id.replace("mylinks_section_", "");
            } else {
                userLinkData.activeSection = 0;
            }
            defaultLinks.activeSection = userLinkData.activeSection;
            if (previousActiveSection != userLinkData.activeSection) {
                // save active section only if it's different
                sakai.api.Server.saveJSON(linksDataPath, userLinkData);
            }
        };

        var hidePane = function (pane) {
            pane.removeClass("accordion_open");
            pane.children(".accordion_content").slideUp(300);
        };

        var closePanes = function () {
            $(".accordion_pane", accordionContainer).each(function () {
                hidePane($(this));
            });
        };

        var setupAccordion = function() {

            $(".section_label", accordionContainer).click(function() {
                showPane($(this).parent());
            });

            // Add Google Analytics outbound links tracking
            $("li.link a", accordionContainer).click(function () {
                myb.api.google.recordOutboundLink(this, 'Outbound Links', $(this).attr('href'));
                return false;
            });

            if (accordionContainer.width() > 250) {
                accordionContainer.addClass("link_grid")
            }

            showPane($(".accordion_open", accordionContainer));

        };

        var doInit = function() {
            loadUserList();
            setupEventHandlers();
        };

        doInit();

    };

    sakai.api.Widgets.widgetLoader.informOnLoad("mylinks");

});
<|endoftext|>