<commit_before>//import React, { Component } from 'react';
import React from 'react';
import './App.css';
import Game from './Game';

class App extends React.Component {
  constructor(props){
    super(props);
    //set the games state object
    this.state = {
      isOn: true, //temporarily set to true for testing
      isStrict: false,
      activePanel: ''
    };

    this.isGameRunning = false;
    this.isPlayerTurn = false;
    this.isSuccess = true;
    this.turn = 1; //turn one means 1 panel played, turn 2 means 2, etc.
    this.aiPanelSequence = [];
    this.playerPanelSequence = [];

    //bind button  handlers to app's context
    this.handleOnButtonClick = this.handleOnButtonClick.bind(this);
    this.handleStartButtonClick = this.handleStartButtonClick.bind(this);
    this.handleStrictButtonClick = this.handleStrictButtonClick.bind(this);
    this.handlePanelClick = this.handlePanelClick.bind(this);
    this.handlePanelUnClick = this.handlePanelUnClick.bind(this);

    //create new sound object for panels
    this.panelSound = new Audio();

    //set some defaults for configuring the game
    this.panelPlaySpeed = 1000; //speed in miliseconds (so 1000 = 1 second)
    this.debug = true;  //set to true to turn on debug mode
  }
  render() {
    return <Game panelClicked={this.state.activePanel}
            onPanelClick={this.handlePanelClick}
            onPanelUnClick={ this.handlePanelUnClick }
            onStartClick={ this.handleStartButtonClick }
            />;
  }
  /********************************************************************/
  /*Wrapper methods for control clicks                                */
  /********************************************************************/
  handlePanelClick(panelClicked, clickEvent){
    /*takes a reference 'panel' to panel object*/
    this.activatePanel(panelClicked.props.id);
    return true;
  }
  handlePanelUnClick(panelClicked, clickEvent) {
    this.deactivatePanel(panelClicked.props.id);
    this.updatePlayerTurn(panelClicked.props.id);
    return true;
  }
  handleOnButtonClick(clickEvent){
    /*Toggles on state*/
    return true;
  }
  handleStartButtonClick(clickEvent){
    /* toggles isGameRunning and calls takeTurn */

    //button only works if the console turned on and game not currently running.
    if(this.state.isOn && !this.isGameRunning){
      console.log('start button clicked!');
      //toggles isGameRunning
      this.isGameRunning = true;
      //call takeTurn
      this.updateAiTurn();
      return true;
    }else if(this.debug){
      this.isGameRunning = true;
      this.updateAiTurn();
      return true;
    }
    return false;

  }
  handleStrictButtonClick(clickEvent){
    /* toggles strict mode */
    return true;
  }
  /********************************************************************/
  /*Game Engine                                                       */
  /********************************************************************/
  updatePlayerTurn(panelId){
    //1. check to see if panelId matches next item in sequence
    //2. if it does, then remove current item from sequence
    // 2a. check to see if sequence is over with. if it is, then switch to ai player
    //     and generate new sequence
    //3. if it does not match, signal an error and trigger ai to play sequence again
    return true;
  }
  updateAiTurn(panelId, aiCurrentTurn){
    /* computer player updates sequence to run with next move
    and then runs sequence  */
    //deactivate panel if it is active
    let newPanelId;

    if(!aiCurrentTurn){
      aiCurrentTurn = 0;
    }
    if(panelId){
      this.deactivatePanel(panelId);
      //check to see if ai  has played full sequence, and if it has, update turn
      if(aiCurrentTurn === this.turn){
        this.turn = ++this.turn;
        return true;
      }
    }
    //NEED TO WRITE: newPanelId should be  either item in sequence[aiCurrentTurn-1] or a new random sequence if at end of array
    //if it is a new random sequence it should be pushed on to sequence array.

    //get random panel id using generateRandomPanelSequence(random) and then push onto sequence
    newPanelId = this.generateRandomPanelSequence();
    this.aiPanelSequence.push(newPanelId);
    //activate current panel with newPanelId
    this.activatePanel(newPanelId);
    //call this function again after pnaelPlaySpeed amount of time
    window.setTimeout(this.updateAiTurn.bind(this, newPanelId, ++aiCurrentTurn), this.panelPlaySpeed);
    return false;

    //let sequence = this.aiPanelSequence.slice();
  }
  activatePanel(panelId){
    this.setState((prevState, props) => ({
      activePanel: panelId
    }));
    this.playPanelSound(panelId);

  }
  deactivatePanel(panelId){
    this.setState((prevSate, props) => ({
      activePanel: ''
    }));
    this.stopPanelSound(panelId);
  }
  playPanelSound(panelId){
    const sound1 = "media/simonSound1.mp3",
        sound2 = "media/simonSound2.mp3",
        sound3 = "media/simonSound3.mp3",
        sound4 = "media/simonSound4.mp3";
    let currentSound = "";

      switch(panelId){
            case "green":
                currentSound = sound1;
                break;
            case "red":
                currentSound = sound2;
                break;
            case "blue":
                currentSound = sound3;
                break;
            case "yellow":
                currentSound = sound4;
                break;
            default:
                console.log("WRONG ID FOR PANEL! CAN'T PLAY SOUND!");
        }
        this.panelSound.src = currentSound;

      this.panelSound.play();
      console.log('panel ' + panelId + ' turned on!');
  }
  stopPanelSound(panelId){
    console.log('panel ' + panelId + ' turned off!');
  }
  generateRandomPanelSequence(random){
  /* helper function for takeTurn*/
  if(!random){
    random = this.getRandomIntInclusive(1, 4); //1 to 4 panels
  }
  switch(random){
    case 1:
      return 'green';
    case 2:
      return 'red';
    case 3:
      return 'blue';
    case 4:
      return 'yellow';
    default:
      console.log('ERROR!');
      return 'undefined';
    }
  }
  getRandomIntInclusive(min, max){
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

App.defaultProps = {
};

export default App;
<commit_msg>Add ability to play same sequence with additional step<commit_after>//import React, { Component } from 'react';
import React from 'react';
import './App.css';
import Game from './Game';

class App extends React.Component {
  constructor(props){
    super(props);
    //set the games state object
    this.state = {
      isOn: true, //temporarily set to true for testing
      isStrict: false,
      activePanel: ''
    };

    this.isGameRunning = false;
    this.isPlayerTurn = false;
    this.isSuccess = true;
    this.turn = 1; //turn one means 1 panel played, turn 2 means 2, etc.
    this.aiPanelSequence = [];
    this.playerPanelSequence = [];

    //bind button  handlers to app's context
    this.handleOnButtonClick = this.handleOnButtonClick.bind(this);
    this.handleStartButtonClick = this.handleStartButtonClick.bind(this);
    this.handleStrictButtonClick = this.handleStrictButtonClick.bind(this);
    this.handlePanelClick = this.handlePanelClick.bind(this);
    this.handlePanelUnClick = this.handlePanelUnClick.bind(this);

    //create new sound object for panels
    this.panelSound = new Audio();

    //set some defaults for configuring the game
    this.panelPlaySpeed = 1000; //speed in miliseconds (so 1000 = 1 second)
    this.debug = true;  //set to true to turn on debug mode
  }
  render() {
    return <Game panelClicked={this.state.activePanel}
            onPanelClick={this.handlePanelClick}
            onPanelUnClick={ this.handlePanelUnClick }
            onStartClick={ this.handleStartButtonClick }
            />;
  }
  /********************************************************************/
  /*Wrapper methods for control clicks                                */
  /********************************************************************/
  handlePanelClick(panelClicked, clickEvent){
    /*takes a reference 'panel' to panel object*/
    this.activatePanel(panelClicked.props.id);
    return true;
  }
  handlePanelUnClick(panelClicked, clickEvent) {
    this.deactivatePanel(panelClicked.props.id);
    this.updatePlayerTurn(panelClicked.props.id);
    return true;
  }
  handleOnButtonClick(clickEvent){
    /*Toggles on state*/
    return true;
  }
  handleStartButtonClick(clickEvent){
    /* toggles isGameRunning and calls takeTurn */

    //button only works if the console turned on and game not currently running.
    if(this.state.isOn && !this.isGameRunning){
      console.log('start button clicked!');
      //toggles isGameRunning
      this.isGameRunning = true;
      //call takeTurn
      this.updateAiTurn();
      return true;
    }else if(this.debug){
      this.isGameRunning = true;
      this.updateAiTurn();
      return true;
    }
    return false;

  }
  handleStrictButtonClick(clickEvent){
    /* toggles strict mode */
    return true;
  }
  /********************************************************************/
  /*Game Engine                                                       */
  /********************************************************************/
  updatePlayerTurn(panelId){
    //1. check to see if panelId matches next item in sequence
    //2. if it does, then remove current item from sequence
    // 2a. check to see if sequence is over with. if it is, then switch to ai player
    //     and generate new sequence
    //3. if it does not match, signal an error and trigger ai to play sequence again
    return true;
  }
  updateAiTurn(panelId, aiCurrentTurn){
    /* computer player updates sequence to run with next move
    and then runs sequence  */
    //deactivate panel if it is active
    let newPanelId;

    if(!aiCurrentTurn){
      aiCurrentTurn = 0;
    }
    if(panelId){
      this.deactivatePanel(panelId);
      //check to see if ai  has played full sequence, and if it has, update turn
      if(aiCurrentTurn === this.turn){
        this.turn = ++this.turn;
        return true;
      }
    }
    aiCurrentTurn += 1;
    //NEED TO WRITE: newPanelId should be  either item in sequence[aiCurrentTurn-1] or a new random sequence if at end of array
    //if it is a new random sequence it should be pushed on to sequence array.
    if(this.aiPanelSequence.length >= aiCurrentTurn){
      newPanelId = this.aiPanelSequence[aiCurrentTurn-1];
    } else {
      //get random panel id using generateRandomPanelSequence(random) and then push onto sequence
      newPanelId = this.generateRandomPanelSequence();
      this.aiPanelSequence.push(newPanelId);
    }

    //activate current panel with newPanelId
    this.activatePanel(newPanelId);
    //call this function again after pnaelPlaySpeed amount of time
    window.setTimeout(this.updateAiTurn.bind(this, newPanelId, aiCurrentTurn), this.panelPlaySpeed);
    return false;

    //let sequence = this.aiPanelSequence.slice();
  }
  activatePanel(panelId){
    this.setState((prevState, props) => ({
      activePanel: panelId
    }));
    this.playPanelSound(panelId);

  }
  deactivatePanel(panelId){
    this.setState((prevSate, props) => ({
      activePanel: ''
    }));
    this.stopPanelSound(panelId);
  }
  playPanelSound(panelId){
    const sound1 = "media/simonSound1.mp3",
        sound2 = "media/simonSound2.mp3",
        sound3 = "media/simonSound3.mp3",
        sound4 = "media/simonSound4.mp3";
    let currentSound = "";

      switch(panelId){
            case "green":
                currentSound = sound1;
                break;
            case "red":
                currentSound = sound2;
                break;
            case "blue":
                currentSound = sound3;
                break;
            case "yellow":
                currentSound = sound4;
                break;
            default:
                console.log("WRONG ID FOR PANEL! CAN'T PLAY SOUND!");
        }
        this.panelSound.src = currentSound;

      this.panelSound.play();
      console.log('panel ' + panelId + ' turned on!');
  }
  stopPanelSound(panelId){
    console.log('panel ' + panelId + ' turned off!');
  }
  generateRandomPanelSequence(random){
  /* helper function for takeTurn*/
  if(!random){
    random = this.getRandomIntInclusive(1, 4); //1 to 4 panels
  }
  switch(random){
    case 1:
      return 'green';
    case 2:
      return 'red';
    case 3:
      return 'blue';
    case 4:
      return 'yellow';
    default:
      console.log('ERROR!');
      return 'undefined';
    }
  }
  getRandomIntInclusive(min, max){
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

App.defaultProps = {
};

export default App;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      questions: [
        { text: "I have found a meaningful career", answer: 1 },
        { text: "I view my work as contributing to my personal growth.", answer: 1 },
        { text: "My work really makes no difference to the world.", answer: 1 },
        { text: "I understand how my work contributes to my life's meaning.", answer: 1 },
        { text: "I have a good sense of what makes my job meaningful.", answer: 1 },
        { text: "I know my work makes a positive difference in the world.", answer: 1 },
        { text: "My work helps me better understand myself.", answer: 1 },
        { text: "I have discovered work that has a satisfying purpose.", answer: 1 },
        { text: "My work helps me make sense of the world around me.", answer: 1 },
        { text: "The work I do serves a greater purpose.", answer: 1 },
      ],
      results: {
        positiveMeaning: {
          name: "Positive Meaning",
          text: "The Positive Meaning scale reflects the degree to which people find their work to hold personal meaning, significance, or purpose.",
          result: 0,
          max: 20
        },
        meaningMakingThroughWork: {
          name: "Meaning-Making through Work",
          text: "The Meaning-Making through Work score reflects the fact that work is often a source of broader meaning in life for people, helping them to make sense of their live experience.",
          result: 0,
          max: 15
        },
        greaterGoodMotivations: {
          name: "Greater Good Motivations",
          text: "The Greater Good Motivations score reflects the degree to which people see that their effort at work makes a positive contribution and benefits others or society.",
          result: 0,
          max: 15
        },
        meaningfulWork: {
          name: "Meaningful Work",
          text: "The Meaningful Work score reflects the depth to which people experience their work as meaningful, as something they are personally invested in, and which is a source of flourishing in their lives.",
          result: 0,
          max: 50
        }
      },
    };

    this.updateAnswer = this.updateAnswer.bind(this);
  }

  updateAnswer(event) {
    var newValue = Number.parseInt(event.target.value, 10);

    // Extract the question index from the identifier (created in render)
    var answerIndex = event.target.id.slice(6);

    var newState = this.state;
    newState.questions[answerIndex].answer = newValue;

    // Calculations from http://www.michaelfsteger.com/wp-content/uploads/2012/08/WAMI.pdf
    // Answers array is 0-indexed so "item 1" from the PDF is index 0 in the questions array
    newState.results.positiveMeaning.result = newState.questions[0].answer + newState.questions[3].answer + newState.questions[4].answer + newState.questions[7].answer;
    newState.results.meaningMakingThroughWork.result = newState.questions[1].answer + newState.questions[6].answer + newState.questions[8].answer;
    newState.results.greaterGoodMotivations.result = 6 - newState.questions[2].answer + newState.questions[5].answer + newState.questions[9].answer;
    newState.results.meaningfulWork.result = newState.results.positiveMeaning.result + newState.results.meaningMakingThroughWork.result + newState.results.greaterGoodMotivations.result;

    this.setState(newState);
  }

  render() {
    return (

The Work and Meaning Inventory

          The Work and Meaning Inventory (WAMI) estimates your feelings of meaning in work. It was created by Dr. Steger. The original PDF may be found here and the research summary may be found here.

Questions

        {
          this.state.questions.map((question, index) =>

              {question.text}
              <input id={"answer" + index} type="range" min="1" max="5" step="1" defaultValue="1" onChange={this.updateAnswer} />
              {question.answer}

          )
        }

Results

            {
              [ "positiveMeaning", "meaningMakingThroughWork", "greaterGoodMotivations", "meaningfulWork" ].map((name) =>

{this.state.results[name].name}: {this.state.results[name].result} out of {this.state.results[name].max} ({((this.state.results[name].result / this.state.results[name].max) * 100).toFixed(2)}%)

              )
            }

Source code at https://github.com/kevgrig/wami-react

    );
  }
}

export default App;
<commit_msg>Add email link<commit_after>import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);

    // Initialize state
    this.state = {
      questions: [
        { text: "I have found a meaningful career", answer: 1 },
        { text: "I view my work as contributing to my personal growth", answer: 1 },
        { text: "My work really makes no difference to the world", answer: 1 },
        { text: "I understand how my work contributes to my life's meaning", answer: 1 },
        { text: "I have a good sense of what makes my job meaningful", answer: 1 },
        { text: "I know my work makes a positive difference in the world", answer: 1 },
        { text: "My work helps me better understand myself", answer: 1 },
        { text: "I have discovered work that has a satisfying purpose", answer: 1 },
        { text: "My work helps me make sense of the world around me", answer: 1 },
        { text: "The work I do serves a greater purpose", answer: 1 },
      ],
      results: {
        positiveMeaning: {
          name: "Positive Meaning",
          text: "The Positive Meaning scale reflects the degree to which people find their work to hold personal meaning, significance, or purpose.",
          result: 0,
          max: 20
        },
        meaningMakingThroughWork: {
          name: "Meaning-Making through Work",
          text: "The Meaning-Making through Work score reflects the fact that work is often a source of broader meaning in life for people, helping them to make sense of their live experience.",
          result: 0,
          max: 15
        },
        greaterGoodMotivations: {
          name: "Greater Good Motivations",
          text: "The Greater Good Motivations score reflects the degree to which people see that their effort at work makes a positive contribution and benefits others or society.",
          result: 0,
          max: 15
        },
        meaningfulWork: {
          name: "Meaningful Work",
          text: "The Meaningful Work score reflects the depth to which people experience their work as meaningful, as something they are personally invested in, and which is a source of flourishing in their lives.",
          result: 0,
          max: 50
        }
      },
    };

    this.updateAnswer = this.updateAnswer.bind(this);
  }

  updateAnswer(event) {
    var newValue = Number.parseInt(event.target.value, 10);

    // Extract the question index from the identifier (created in render)
    var answerIndex = event.target.id.slice(6);

    var newState = this.state;
    newState.questions[answerIndex].answer = newValue;

    // Calculations from http://www.michaelfsteger.com/wp-content/uploads/2012/08/WAMI.pdf
    // Answers array is 0-indexed so "item 1" from the PDF is index 0 in the questions array
    newState.results.positiveMeaning.result = newState.questions[0].answer + newState.questions[3].answer + newState.questions[4].answer + newState.questions[7].answer;
    newState.results.meaningMakingThroughWork.result = newState.questions[1].answer + newState.questions[6].answer + newState.questions[8].answer;
    newState.results.greaterGoodMotivations.result = 6 - newState.questions[2].answer + newState.questions[5].answer + newState.questions[9].answer;
    newState.results.meaningfulWork.result = newState.results.positiveMeaning.result + newState.results.meaningMakingThroughWork.result + newState.results.greaterGoodMotivations.result;

    this.setState(newState);
  }

  render() {
    return (

The Work and Meaning Inventory

          The Work and Meaning Inventory (WAMI) estimates your feelings of meaning in work. It was created by Dr. Steger. The original PDF may be found here and the research summary may be found here.

Questions

        {
          this.state.questions.map((question, index) =>

              {question.text}
              <input id={"answer" + index} type="range" min="1" max="5" step="1" defaultValue="1" onChange={this.updateAnswer} />
              {question.answer}

          )
        }

Results

            {
              [ "positiveMeaning", "meaningMakingThroughWork", "greaterGoodMotivations", "meaningfulWork" ].map((name) =>

{this.state.results[name].name}: {this.state.results[name].result} out of {this.state.results[name].max} ({((this.state.results[name].result / this.state.results[name].max) * 100).toFixed(2)}%)

              )
            }

          { this.state.results.meaningfulWork.result > 0 &&

                  this.state.results[name].name + ": " + this.state.results[name].result + " out of " + this.state.results[name].max + " (" + (((this.state.results[name].result / this.state.results[name].max) * 100).toFixed(2)) + "%)"
                ).join("\n")
              ) + "\n\nAnswers:\n\n" + this.state.questions.map((question, index) => question.text + ": " + this.state.questions[index].answer ).join("\n"))}>Email these results.

          }

Source code at https://github.com/kevgrig/wami-react

    );
  }
}

export default App;
<|endoftext|>


<commit_before>
import HttpService from "../http/HttpService";
import * as GraphUtils from "../components/graph/GraphUtils";
import NodeUtils from "../components/graph/NodeUtils";
import * as SubprocessSchemaAligner from "../components/graph/SubprocessSchemaAligner";
import _ from "lodash";
import * as UndoRedoActions from "./undoRedoActions";
import * as VisualizationUrl from '../common/VisualizationUrl';
import {dateFormat} from "../config";
import history from '../history'

export function fetchProcessToDisplay(processId, versionId, businessView) {
  return (dispatch) => {
    dispatch({
      type: "PROCESS_LOADING"
    })
    return HttpService.fetchProcessDetails(processId, versionId, businessView).then((response) => {
        displayTestCapabilites(response.data.json, response.data.processingType)(dispatch)
        return dispatch(displayProcess(response.data))
      })
  }
}

export function fetchProcessDefinition(processingType, isSubprocess, subprocessVersions) {
  return (dispatch) => {
    return HttpService.fetchProcessDefinitionData(processingType, isSubprocess, subprocessVersions).then((response) => {
        dispatch({type: "PROCESS_DEFINITION_DATA", processDefinitionData: response.data})
      }
    )
  }
}

export function fetchAvailableQueryStates() {
  return (dispatch) => {
    return HttpService.availableQueryableStates().then((response) =>
      dispatch({type: "AVAILABLE_QUERY_STATES", availableQueryableStates: response.data})
    )
  }
}

export function displayCurrentProcessVersion(processId) {
  return fetchProcessToDisplay(processId)
}

export function addComment(processId, processVersionId, comment) {
  return (dispatch) => {
    return HttpService.addComment(processId, processVersionId, comment).then(() => {
      return dispatch(displayProcessActivity(processId))
    })
  }
}

export function deleteComment(processId, commentId) {
  return (dispatch) => {
    return HttpService.deleteComment(processId, commentId).then(() => {
      return dispatch(displayProcessActivity(processId))
    })
  }
}

export function addAttachment(processId, processVersionId, comment) {
  return (dispatch) => {
    return HttpService.addAttachment(processId, processVersionId, comment).then(() => {
      return dispatch(displayProcessActivity(processId))
    })
  }
}

export function displayProcessActivity(processId) {
  return (dispatch) => {
    return HttpService.fetchProcessActivity(processId).then((response) => {
      return dispatch({
        type: "DISPLAY_PROCESS_ACTIVITY",
        comments: response.data.comments,
        attachments: response.data.attachments
      })
    })
  }
}

function displayTestCapabilites(processDetails) {
  return (dispatch) => {
    HttpService.getTestCapabilities(processDetails).then((response) => dispatch({
      type: "UPDATE_TEST_CAPABILITIES",
      capabilities: response.data
    }))
  }
}

function displayProcess(processDetails) {
  return {
    type: "DISPLAY_PROCESS",
    fetchedProcessDetails: processDetails
  };
}

export function saveProcess(processId, processJson, comment) {
  return (dispatch) => {
    return HttpService.saveProcess(processId, processJson, comment)
      .then((response) => dispatch(displayCurrentProcessVersion(processId)))
      .then((response) => dispatch(displayProcessActivity(processId)))
      .then((response) => dispatch(UndoRedoActions.clear()))
  }
}

export function importProcess(processId, file) {
  return (dispatch) => {
    dispatch({
      type: "PROCESS_LOADING"
    })

    return HttpService.importProcess(processId, file)
      .then((process) => dispatch(updateImportedProcess(process)))
      .catch((error) => dispatch({type: "LOADING_FAILED"}))
  }
}

export function updateImportedProcess(processJson) {
  return {
    type: "UPDATE_IMPORTED_PROCESS",
    processJson: processJson
  };
}

export function clearProcess() {
  return (dispatch) => {
    dispatch(UndoRedoActions.clear())
    return dispatch({
      type: "CLEAR_PROCESS"
    })
  }
}

export function displayModalNodeDetails(node, readonly) {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      nodeId: node.id,
      edgeId: null
    })
  })

  return {
    type: "DISPLAY_MODAL_NODE_DETAILS",
    nodeToDisplay: node,
    nodeToDisplayReadonly: readonly
  };
}

export function displayModalEdgeDetails(edge) {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      nodeId: null,
      edgeId: NodeUtils.edgeId(edge)
    })
  })

  return {
    type: "DISPLAY_MODAL_EDGE_DETAILS",
    edgeToDisplay: edge
  };
}

export function displayNodeDetails(node) {
  return {
    type: "DISPLAY_NODE_DETAILS",
    nodeToDisplay: node
  };
}

export function closeModals() {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      edgeId: null,
      nodeId: null
    })
  })

  return {
    type: "CLOSE_MODALS"
  };
}

export function deleteNodes(ids) {
  return runSyncActionsThenValidate(state => [{
    type: "DELETE_NODES",
    ids: ids
  }])
}

export function startGrouping() {
  return { type: "START_GROUPING"}
}

export function cancelGrouping() {
  return { type: "CANCEL_GROUPING"}
}

export function finishGrouping() {
  return { type: "FINISH_GROUPING"}
}

export function addToGroup(nodeId) {
  return { type: "ADD_NODE_TO_GROUP", nodeId: nodeId}
}

export function ungroup(node) {
  return { type: "UNGROUP", groupToRemove: node.id}
}

export function expandSelection(nodeId) {
  return { type: "EXPAND_SELECTION", nodeId }
}

export function resetSelection(nodeId) {
  return { type: "RESET_SELECTION", nodeId }
}

//TODO: is it ok how we process validations here? first we *simulate* reducer on
//current process (which may be outdated...) and after validation we invoke reducer
//this is error prone... :|

export function editEdge(process, before, after) {
  return (dispatch) => {
    const changedProcess = GraphUtils.mapProcessWithNewEdge(process, before, after)
    return HttpService.validateProcess(changedProcess).then((response) => {
      dispatch({
        type: "EDIT_EDGE",
        before: before,
        after: after,
        validationResult: response.data
      })
    })
  }
}

export function editNode(process, before, after) {
  return (dispatch) => {
    const processAfterChange = calculateProcessAfterChange(process, before, after, dispatch)
    return processAfterChange.then((process) => {
      return HttpService.validateProcess(process).then((response) => {
        dispatch({
          type: "EDIT_NODE",
          before: before,
          after: after,
          validationResult: response.data,
          processAfterChange: process
        })
      })
    })
  }
}

function calculateProcessAfterChange(process, before, after, dispatch) {
  if (NodeUtils.nodeIsProperties(after)) {
    const subprocessVersions = after.subprocessVersions || process.properties.subprocessVersions
    return dispatch(
      fetchProcessDefinition(process.processingType, process.properties.isSubprocess, subprocessVersions)
    ).then((processDef) => {
      const processWithNewSubprocessSchema = alignSubprocessesWithSchema(process, processDef.processDefinitionData)
      return GraphUtils.mapProcessWithNewNode(processWithNewSubprocessSchema, before, after)
    })
  } else {
    return Promise.resolve(GraphUtils.mapProcessWithNewNode(process, before, after))
  }
}

function alignSubprocessesWithSchema(process, processDefinitionData) {
  const nodesWithNewSubprocessSchema = _.map(process.nodes, (node) => {
    if (node.type === "SubprocessInput") {
      return SubprocessSchemaAligner.alignSubprocessWithSchema(processDefinitionData, node)
    } else {
      return node
    }
  })
  return {...process, nodes: nodesWithNewSubprocessSchema};
}

export function editGroup(process, oldGroupId, newGroup) {
  return (dispatch) => {
    const newProcess = NodeUtils.editGroup(process, oldGroupId, newGroup)
    return HttpService.validateProcess(newProcess).then((response) => {
      dispatch({
        type: "EDIT_GROUP",
        oldGroupId: oldGroupId,
        newGroup: newGroup,
        validationResult: response.data
      })
    })
  }

}

export function nodesConnected(fromNode, toNode) {
  return runSyncActionsThenValidate(state => [
    {
        type: "NODES_CONNECTED",
        fromNode: fromNode,
        toNode: toNode,
        processDefinitionData: state.settings.processDefinitionData
    }
  ]);
}

export function nodesDisconnected(from, to) {
  return runSyncActionsThenValidate(state => [{
      type: "NODES_DISCONNECTED",
      from: from,
      to: to
    }]);
}

export function injectNode(from, middle, to, edgeType) {
  return runSyncActionsThenValidate(state => [
    {
        type: "NODES_DISCONNECTED",
        from: from.id,
        to: to.id
    },
    {
        type: "NODES_CONNECTED",
        fromNode: from,
        toNode: middle,
        processDefinitionData: state.settings.processDefinitionData,
        edgeType: edgeType
    },
    {
        type: "NODES_CONNECTED",
        fromNode: middle,
        toNode: to,
        processDefinitionData: state.settings.processDefinitionData
    }
  ])
}

//this WON'T work for async actions - have to handle promises separately
function runSyncActionsThenValidate(syncActions) {
  return (dispatch, getState) => {
    syncActions(getState()).forEach(action => dispatch(action))
    return HttpService.validateProcess(getState().graphReducer.processToDisplay).then(
      (response) => dispatch({type: "VALIDATION_RESULT", validationResult: response.data})
    )
  }
}

export function nodeAdded(node, position) {
  return {
    type: "NODE_ADDED",
    node: node,
    position: position
  }
}

export function nodesWithEdgesAdded(nodesWithPositions, edges) {
  return (dispatch, getState) => dispatch({
    type: "NODES_WITH_EDGES_ADDED",
    nodesWithPositions,
    edges,
    processDefinitionData: getState().settings.processDefinitionData
  })
}

export function layoutChanged(layout) {
  return {
    type: "LAYOUT_CHANGED",
    layout: layout
  }
}

export function toggleLeftPanel() {
  return {
    type: "TOGGLE_LEFT_PANEL",
  }
}

export function toggleRightPanel() {
  return {
    type: "TOGGLE_RIGHT_PANEL",
  }
}

export function enableToolTipsHighlight() {
  return {
    type: "SWITCH_TOOL_TIPS_HIGHLIGHT",
    isHighlighted: true
  }
}

export function disableToolTipsHighlight() {
  return {
    type: "SWITCH_TOOL_TIPS_HIGHLIGHT",
    isHighlighted: false
  }
}

export function toggleConfirmDialog(isOpen, text, action) {
  return {
    type: "TOGGLE_CONFIRM_DIALOG",
    isOpen: isOpen,
    text: text,
    onConfirmCallback: action
  }
}

export function toggleProcessActionDialog(message, action, displayWarnings) {
  return {
    type: "TOGGLE_PROCESS_ACTION_MODAL",
    message: message,
    action: action,
    displayWarnings: displayWarnings
  }
}

export function toggleModalDialog(openDialog) {
  return {
    type: "TOGGLE_MODAL_DIALOG",
    openDialog: openDialog
  }
}

export function toggleInfoModal(openDialog, text) {
  return {
    type: "TOGGLE_INFO_MODAL",
    openDialog: openDialog,
    text: text
  }
}

export function testProcessFromFile(id, testDataFile, process) {
  return (dispatch) => {
    dispatch({
      type: "PROCESS_LOADING"
    })

    HttpService.testProcess(id, testDataFile, process)
      .then(response => dispatch(displayTestResults(response.data)))
      .catch(error => dispatch({type: "LOADING_FAILED"}))
  }
}

function displayTestResults(testResults) {
  return (dispatch) => {
    dispatch({
      type: "DISPLAY_TEST_RESULTS_DETAILS",
      testResults: testResults.results
    })
    dispatch({
        type: "DISPLAY_PROCESS_COUNTS",
        processCounts: testResults.counts
      }
    )
  }
}

export function displayProcessCounts(processCounts) {
  return {
    type: "DISPLAY_PROCESS_COUNTS",
    processCounts: processCounts
  }
}

export function urlChange(location) {

  return (dispatch) => {
    dispatch(handleHTTPError(null))

    dispatch({
      type: 'URL_CHANGED',
      location: location
    })
  }
}

export function fetchAndDisplayProcessCounts(processName, from, to) {
  return (dispatch) =>
    HttpService.fetchProcessCounts(
      processName,
      from ? from.format(dateFormat): null,
      to ? to.format(dateFormat) : null
    ).then((response) => dispatch(displayProcessCounts(response.data)))
}

export function hideRunProcessDetails() {
  return {type: "HIDE_RUN_PROCESS_DETAILS"}
}

export function expandGroup(id) {
  return {type: "EXPAND_GROUP", id: id}
}

export function collapseGroup(id) {
  return {type: "COLLAPSE_GROUP", id: id}
}

export function businessViewChanged(value) {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      businessView: value
    })
  })

  return {
    type: "BUSINESS_VIEW_CHANGED",
    businessView: value
  }
}

//Developers can handle error on two ways:
//1. Catching it at HttpService and show error information modal - application still works normally
//2. Catching it at Containers / etc.. and show ErrorPage by run action handleHTTPError - application stop works
export function handleHTTPError(error) {
  return {
    type: "HANDLE_HTTP_ERROR",
    error: error
  }
}
<commit_msg>Fix unable to save process properties<commit_after>
import HttpService from "../http/HttpService";
import * as GraphUtils from "../components/graph/GraphUtils";
import NodeUtils from "../components/graph/NodeUtils";
import * as SubprocessSchemaAligner from "../components/graph/SubprocessSchemaAligner";
import _ from "lodash";
import * as UndoRedoActions from "./undoRedoActions";
import * as VisualizationUrl from '../common/VisualizationUrl';
import {dateFormat} from "../config";
import history from '../history'

export function fetchProcessToDisplay(processId, versionId, businessView) {
  return (dispatch) => {
    dispatch({
      type: "PROCESS_LOADING"
    })
    return HttpService.fetchProcessDetails(processId, versionId, businessView).then((response) => {
        displayTestCapabilites(response.data.json, response.data.processingType)(dispatch)
        return dispatch(displayProcess(response.data))
      })
  }
}

export function fetchProcessDefinition(processingType, isSubprocess, subprocessVersions) {
  return (dispatch) => {
    return HttpService.fetchProcessDefinitionData(processingType, isSubprocess, subprocessVersions).then((response) => (
        dispatch({type: "PROCESS_DEFINITION_DATA", processDefinitionData: response.data})
      )
    )
  }
}

export function fetchAvailableQueryStates() {
  return (dispatch) => {
    return HttpService.availableQueryableStates().then((response) =>
      dispatch({type: "AVAILABLE_QUERY_STATES", availableQueryableStates: response.data})
    )
  }
}

export function displayCurrentProcessVersion(processId) {
  return fetchProcessToDisplay(processId)
}

export function addComment(processId, processVersionId, comment) {
  return (dispatch) => {
    return HttpService.addComment(processId, processVersionId, comment).then(() => {
      return dispatch(displayProcessActivity(processId))
    })
  }
}

export function deleteComment(processId, commentId) {
  return (dispatch) => {
    return HttpService.deleteComment(processId, commentId).then(() => {
      return dispatch(displayProcessActivity(processId))
    })
  }
}

export function addAttachment(processId, processVersionId, comment) {
  return (dispatch) => {
    return HttpService.addAttachment(processId, processVersionId, comment).then(() => {
      return dispatch(displayProcessActivity(processId))
    })
  }
}

export function displayProcessActivity(processId) {
  return (dispatch) => {
    return HttpService.fetchProcessActivity(processId).then((response) => {
      return dispatch({
        type: "DISPLAY_PROCESS_ACTIVITY",
        comments: response.data.comments,
        attachments: response.data.attachments
      })
    })
  }
}

function displayTestCapabilites(processDetails) {
  return (dispatch) => {
    HttpService.getTestCapabilities(processDetails).then((response) => dispatch({
      type: "UPDATE_TEST_CAPABILITIES",
      capabilities: response.data
    }))
  }
}

function displayProcess(processDetails) {
  return {
    type: "DISPLAY_PROCESS",
    fetchedProcessDetails: processDetails
  };
}

export function saveProcess(processId, processJson, comment) {
  return (dispatch) => {
    return HttpService.saveProcess(processId, processJson, comment)
      .then((response) => dispatch(displayCurrentProcessVersion(processId)))
      .then((response) => dispatch(displayProcessActivity(processId)))
      .then((response) => dispatch(UndoRedoActions.clear()))
  }
}

export function importProcess(processId, file) {
  return (dispatch) => {
    dispatch({
      type: "PROCESS_LOADING"
    })

    return HttpService.importProcess(processId, file)
      .then((process) => dispatch(updateImportedProcess(process)))
      .catch((error) => dispatch({type: "LOADING_FAILED"}))
  }
}

export function updateImportedProcess(processJson) {
  return {
    type: "UPDATE_IMPORTED_PROCESS",
    processJson: processJson
  };
}

export function clearProcess() {
  return (dispatch) => {
    dispatch(UndoRedoActions.clear())
    return dispatch({
      type: "CLEAR_PROCESS"
    })
  }
}

export function displayModalNodeDetails(node, readonly) {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      nodeId: node.id,
      edgeId: null
    })
  })

  return {
    type: "DISPLAY_MODAL_NODE_DETAILS",
    nodeToDisplay: node,
    nodeToDisplayReadonly: readonly
  };
}

export function displayModalEdgeDetails(edge) {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      nodeId: null,
      edgeId: NodeUtils.edgeId(edge)
    })
  })

  return {
    type: "DISPLAY_MODAL_EDGE_DETAILS",
    edgeToDisplay: edge
  };
}

export function displayNodeDetails(node) {
  return {
    type: "DISPLAY_NODE_DETAILS",
    nodeToDisplay: node
  };
}

export function closeModals() {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      edgeId: null,
      nodeId: null
    })
  })

  return {
    type: "CLOSE_MODALS"
  };
}

export function deleteNodes(ids) {
  return runSyncActionsThenValidate(state => [{
    type: "DELETE_NODES",
    ids: ids
  }])
}

export function startGrouping() {
  return { type: "START_GROUPING"}
}

export function cancelGrouping() {
  return { type: "CANCEL_GROUPING"}
}

export function finishGrouping() {
  return { type: "FINISH_GROUPING"}
}

export function addToGroup(nodeId) {
  return { type: "ADD_NODE_TO_GROUP", nodeId: nodeId}
}

export function ungroup(node) {
  return { type: "UNGROUP", groupToRemove: node.id}
}

export function expandSelection(nodeId) {
  return { type: "EXPAND_SELECTION", nodeId }
}

export function resetSelection(nodeId) {
  return { type: "RESET_SELECTION", nodeId }
}

//TODO: is it ok how we process validations here? first we *simulate* reducer on
//current process (which may be outdated...) and after validation we invoke reducer
//this is error prone... :|

export function editEdge(process, before, after) {
  return (dispatch) => {
    const changedProcess = GraphUtils.mapProcessWithNewEdge(process, before, after)
    return HttpService.validateProcess(changedProcess).then((response) => {
      dispatch({
        type: "EDIT_EDGE",
        before: before,
        after: after,
        validationResult: response.data
      })
    })
  }
}

export function editNode(process, before, after) {
  return (dispatch) => {
    const processAfterChange = calculateProcessAfterChange(process, before, after, dispatch)
    return processAfterChange.then((process) => {
      return HttpService.validateProcess(process).then((response) => {
        dispatch({
          type: "EDIT_NODE",
          before: before,
          after: after,
          validationResult: response.data,
          processAfterChange: process
        })
      })
    })
  }
}

function calculateProcessAfterChange(process, before, after, dispatch) {
  if (NodeUtils.nodeIsProperties(after)) {
    const subprocessVersions = after.subprocessVersions || process.properties.subprocessVersions
    return dispatch(
      fetchProcessDefinition(process.processingType, process.properties.isSubprocess, subprocessVersions)
    ).then((processDef) => {
      const processWithNewSubprocessSchema = alignSubprocessesWithSchema(process, processDef.processDefinitionData)
      return GraphUtils.mapProcessWithNewNode(processWithNewSubprocessSchema, before, after)
    })
  } else {
    return Promise.resolve(GraphUtils.mapProcessWithNewNode(process, before, after))
  }
}

function alignSubprocessesWithSchema(process, processDefinitionData) {
  const nodesWithNewSubprocessSchema = _.map(process.nodes, (node) => {
    if (node.type === "SubprocessInput") {
      return SubprocessSchemaAligner.alignSubprocessWithSchema(processDefinitionData, node)
    } else {
      return node
    }
  })
  return {...process, nodes: nodesWithNewSubprocessSchema};
}

export function editGroup(process, oldGroupId, newGroup) {
  return (dispatch) => {
    const newProcess = NodeUtils.editGroup(process, oldGroupId, newGroup)
    return HttpService.validateProcess(newProcess).then((response) => {
      dispatch({
        type: "EDIT_GROUP",
        oldGroupId: oldGroupId,
        newGroup: newGroup,
        validationResult: response.data
      })
    })
  }

}

export function nodesConnected(fromNode, toNode) {
  return runSyncActionsThenValidate(state => [
    {
        type: "NODES_CONNECTED",
        fromNode: fromNode,
        toNode: toNode,
        processDefinitionData: state.settings.processDefinitionData
    }
  ]);
}

export function nodesDisconnected(from, to) {
  return runSyncActionsThenValidate(state => [{
      type: "NODES_DISCONNECTED",
      from: from,
      to: to
    }]);
}

export function injectNode(from, middle, to, edgeType) {
  return runSyncActionsThenValidate(state => [
    {
        type: "NODES_DISCONNECTED",
        from: from.id,
        to: to.id
    },
    {
        type: "NODES_CONNECTED",
        fromNode: from,
        toNode: middle,
        processDefinitionData: state.settings.processDefinitionData,
        edgeType: edgeType
    },
    {
        type: "NODES_CONNECTED",
        fromNode: middle,
        toNode: to,
        processDefinitionData: state.settings.processDefinitionData
    }
  ])
}

//this WON'T work for async actions - have to handle promises separately
function runSyncActionsThenValidate(syncActions) {
  return (dispatch, getState) => {
    syncActions(getState()).forEach(action => dispatch(action))
    return HttpService.validateProcess(getState().graphReducer.processToDisplay).then(
      (response) => dispatch({type: "VALIDATION_RESULT", validationResult: response.data})
    )
  }
}

export function nodeAdded(node, position) {
  return {
    type: "NODE_ADDED",
    node: node,
    position: position
  }
}

export function nodesWithEdgesAdded(nodesWithPositions, edges) {
  return (dispatch, getState) => dispatch({
    type: "NODES_WITH_EDGES_ADDED",
    nodesWithPositions,
    edges,
    processDefinitionData: getState().settings.processDefinitionData
  })
}

export function layoutChanged(layout) {
  return {
    type: "LAYOUT_CHANGED",
    layout: layout
  }
}

export function toggleLeftPanel() {
  return {
    type: "TOGGLE_LEFT_PANEL",
  }
}

export function toggleRightPanel() {
  return {
    type: "TOGGLE_RIGHT_PANEL",
  }
}

export function enableToolTipsHighlight() {
  return {
    type: "SWITCH_TOOL_TIPS_HIGHLIGHT",
    isHighlighted: true
  }
}

export function disableToolTipsHighlight() {
  return {
    type: "SWITCH_TOOL_TIPS_HIGHLIGHT",
    isHighlighted: false
  }
}

export function toggleConfirmDialog(isOpen, text, action) {
  return {
    type: "TOGGLE_CONFIRM_DIALOG",
    isOpen: isOpen,
    text: text,
    onConfirmCallback: action
  }
}

export function toggleProcessActionDialog(message, action, displayWarnings) {
  return {
    type: "TOGGLE_PROCESS_ACTION_MODAL",
    message: message,
    action: action,
    displayWarnings: displayWarnings
  }
}

export function toggleModalDialog(openDialog) {
  return {
    type: "TOGGLE_MODAL_DIALOG",
    openDialog: openDialog
  }
}

export function toggleInfoModal(openDialog, text) {
  return {
    type: "TOGGLE_INFO_MODAL",
    openDialog: openDialog,
    text: text
  }
}

export function testProcessFromFile(id, testDataFile, process) {
  return (dispatch) => {
    dispatch({
      type: "PROCESS_LOADING"
    })

    HttpService.testProcess(id, testDataFile, process)
      .then(response => dispatch(displayTestResults(response.data)))
      .catch(error => dispatch({type: "LOADING_FAILED"}))
  }
}

function displayTestResults(testResults) {
  return (dispatch) => {
    dispatch({
      type: "DISPLAY_TEST_RESULTS_DETAILS",
      testResults: testResults.results
    })
    dispatch({
        type: "DISPLAY_PROCESS_COUNTS",
        processCounts: testResults.counts
      }
    )
  }
}

export function displayProcessCounts(processCounts) {
  return {
    type: "DISPLAY_PROCESS_COUNTS",
    processCounts: processCounts
  }
}

export function urlChange(location) {

  return (dispatch) => {
    dispatch(handleHTTPError(null))

    dispatch({
      type: 'URL_CHANGED',
      location: location
    })
  }
}

export function fetchAndDisplayProcessCounts(processName, from, to) {
  return (dispatch) =>
    HttpService.fetchProcessCounts(
      processName,
      from ? from.format(dateFormat): null,
      to ? to.format(dateFormat) : null
    ).then((response) => dispatch(displayProcessCounts(response.data)))
}

export function hideRunProcessDetails() {
  return {type: "HIDE_RUN_PROCESS_DETAILS"}
}

export function expandGroup(id) {
  return {type: "EXPAND_GROUP", id: id}
}

export function collapseGroup(id) {
  return {type: "COLLAPSE_GROUP", id: id}
}

export function businessViewChanged(value) {
  history.replace({
    pathname: window.location.pathname,
    search: VisualizationUrl.setAndPreserveLocationParams({
      businessView: value
    })
  })

  return {
    type: "BUSINESS_VIEW_CHANGED",
    businessView: value
  }
}

//Developers can handle error on two ways:
//1. Catching it at HttpService and show error information modal - application still works normally
//2. Catching it at Containers / etc.. and show ErrorPage by run action handleHTTPError - application stop works
export function handleHTTPError(error) {
  return {
    type: "HANDLE_HTTP_ERROR",
    error: error
  }
}
<|endoftext|>


<commit_before>/*
 * jQuery UI Autocomplete @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Autocomplete
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function( $, undefined ) {

// used to prevent race conditions with remote data sources
var requestIndex = 0;

$.widget( "ui.autocomplete", {
	version: "@VERSION",
	defaultElement: "<input>",
	options: {
		appendTo: "body",
		autoFocus: false,
		delay: 300,
		minLength: 1,
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		source: null,

		// callbacks
		change: null,
		close: null,
		focus: null,
		open: null,
		response: null,
		search: null,
		select: null
	},

	pending: 0,

	_create: function() {
		var self = this,
			doc = this.element[ 0 ].ownerDocument,
			suppressKeyPress,
			suppressInput;

		this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ];

		this.element
			.addClass( "ui-autocomplete-input" )
			.attr( "autocomplete", "off" )
			// TODO verify these actually work as intended
			.attr({
				role: "textbox",
				"aria-autocomplete": "list",
				"aria-haspopup": "true"
			})
			.bind( "keydown.autocomplete", function( event ) {
				if ( self.options.disabled || self.element.prop( "readOnly" ) ) {
					suppressKeyPress = true;
					suppressInput = true;
					return;
				}

				suppressKeyPress = false;
				suppressInput = false;
				var keyCode = $.ui.keyCode;
				switch( event.keyCode ) {
				case keyCode.PAGE_UP:
					suppressKeyPress = true;
					self._move( "previousPage", event );
					break;
				case keyCode.PAGE_DOWN:
					suppressKeyPress = true;
					self._move( "nextPage", event );
					break;
				case keyCode.UP:
					suppressKeyPress = true;
					self._move( "previous", event );
					// prevent moving cursor to beginning of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.DOWN:
					suppressKeyPress = true;
					self._move( "next", event );
					// prevent moving cursor to end of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.ENTER:
				case keyCode.NUMPAD_ENTER:
					// when menu is open and has focus
					if ( self.menu.active ) {
						// #6055 - Opera still allows the keypress to occur
						// which causes forms to submit
						suppressKeyPress = true;
						event.preventDefault();
					}
					//passthrough - ENTER and TAB both select the current element
				case keyCode.TAB:
					if ( !self.menu.active ) {
						return;
					}
					self.menu.select( event );
					break;
				case keyCode.ESCAPE:
					self._value( self.term );
					self.close( event );
					break;
				default:
					// search timeout should be triggered before the input value is changed
					self._searchTimeout( event );
					break;
				}
			})
			.bind( "keypress.autocomplete", function( event ) {
				if ( suppressKeyPress ) {
					suppressKeyPress = false;
					event.preventDefault();
					return;
				}

				// replicate some key handlers to allow them to repeat in Firefox and Opera
				var keyCode = $.ui.keyCode;
				switch( event.keyCode ) {
				case keyCode.PAGE_UP:
					self._move( "previousPage", event );
					break;
				case keyCode.PAGE_DOWN:
					self._move( "nextPage", event );
					break;
				case keyCode.UP:
					self._move( "previous", event );
					// prevent moving cursor to beginning of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.DOWN:
					self._move( "next", event );
					// prevent moving cursor to end of text field in some browsers
					event.preventDefault();
					break;
				}
			})
			.bind( "input.autocomplete", function(event) {
				if ( suppressInput ) {
					suppressInput = false;
					event.preventDefault();
					return;
				}
				self._searchTimeout( event );
			})
			.bind( "focus.autocomplete", function() {
				if ( self.options.disabled ) {
					return;
				}

				self.selectedItem = null;
				self.previous = self._value();
			})
			.bind( "blur.autocomplete", function( event ) {
				if ( self.options.disabled ) {
					return;
				}

				clearTimeout( self.searching );
				// clicks on the menu (or a button to trigger a search) will cause a blur event
				self.closing = setTimeout(function() {
					self.close( event );
					self._change( event );
				}, 150 );
			});
		this._initSource();
		this.response = function() {
			return self._response.apply( self, arguments );
		};
		this.menu = $( "

" )
			.addClass( "ui-autocomplete" )
			.appendTo( $( this.options.appendTo || "body", doc )[0] )
			// prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
			.mousedown(function( event ) {
				// clicking on the scrollbar causes focus to shift to the body
				// but we can't detect a mouseup or a click immediately afterward
				// so we have to track the next mousedown and close the menu if
				// the user clicks somewhere outside of the autocomplete
				var menuElement = self.menu.element[ 0 ];
				if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
					setTimeout(function() {
						$( document ).one( 'mousedown', function( event ) {
							if ( event.target !== self.element[ 0 ] &&
								event.target !== menuElement &&
								!$.contains( menuElement, event.target ) ) {
								self.close();
							}
						});
					}, 1 );
				}

				// use another timeout to make sure the blur-event-handler on the input was already triggered
				setTimeout(function() {
					clearTimeout( self.closing );
				}, 13);
			})
			.menu({
				// custom key handling for now
				input: $(),
				focus: function( event, ui ) {
					var item = ui.item.data( "item.autocomplete" );
					if ( false !== self._trigger( "focus", event, { item: item } ) ) {
						// use value to match what will end up in the input, if it was a key event
						if ( /^key/.test(event.originalEvent.type) ) {
							self._value( item.value );
						}
					}
				},
				select: function( event, ui ) {
					var item = ui.item.data( "item.autocomplete" ),
						previous = self.previous;

					// only trigger when focus was lost (click on menu)
					if ( self.element[0] !== doc.activeElement ) {
						self.element.focus();
						self.previous = previous;
						// #6109 - IE triggers two focus events and the second
						// is asynchronous, so we need to reset the previous
						// term synchronously and asynchronously :-(
						setTimeout(function() {
							self.previous = previous;
							self.selectedItem = item;
						}, 1);
					}

					if ( false !== self._trigger( "select", event, { item: item } ) ) {
						self._value( item.value );
					}
					// reset the term after the select event
					// this allows custom select handling to work properly
					self.term = self._value();

					self.close( event );
					self.selectedItem = item;
				}
			})
			.zIndex( this.element.zIndex() + 1 )
			.hide()
			.data( "menu" );
		if ( $.fn.bgiframe ) {
			 this.menu.element.bgiframe();
		}
	},

	_destroy: function() {
		clearTimeout( this.searching );
		this.element
			.removeClass( "ui-autocomplete-input" )
			.removeAttr( "autocomplete" )
			.removeAttr( "role" )
			.removeAttr( "aria-autocomplete" )
			.removeAttr( "aria-haspopup" );
		this.menu.element.remove();
	},

	_setOption: function( key, value ) {
		this._super( "_setOption", key, value );
		if ( key === "source" ) {
			this._initSource();
		}
		if ( key === "appendTo" ) {
			this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
		}
		if ( key === "disabled" && value && this.xhr ) {
			this.xhr.abort();
		}
	},

	_initSource: function() {
		var self = this,
			array,
			url;
		if ( $.isArray(this.options.source) ) {
			array = this.options.source;
			this.source = function( request, response ) {
				response( $.ui.autocomplete.filter(array, request.term) );
			};
		} else if ( typeof this.options.source === "string" ) {
			url = this.options.source;
			this.source = function( request, response ) {
				if ( self.xhr ) {
					self.xhr.abort();
				}
				self.xhr = $.ajax({
					url: url,
					data: request,
					dataType: "json",
					autocompleteRequest: ++requestIndex,
					success: function( data, status ) {
						if ( this.autocompleteRequest === requestIndex ) {
							response( data );
						}
					},
					error: function() {
						if ( this.autocompleteRequest === requestIndex ) {
							response( [] );
						}
					}
				});
			};
		} else {
			this.source = this.options.source;
		}
	},

	_searchTimeout: function( event ) {
		var self = this;
		clearTimeout( self.searching );
		self.searching = setTimeout(function() {
			// only search if the value has changed
			if ( self.term !== self._value() ) {
				self.selectedItem = null;
				self.search( null, event );
			}
		}, self.options.delay );
	},

	search: function( value, event ) {
		value = value != null ? value : this._value();

		// always save the actual value, not the one passed as an argument
		this.term = this._value();

		if ( value.length < this.options.minLength ) {
			return this.close( event );
		}

		clearTimeout( this.closing );
		if ( this._trigger( "search", event ) === false ) {
			return;
		}

		return this._search( value );
	},

	_search: function( value ) {
		this.pending++;
		this.element.addClass( "ui-autocomplete-loading" );

		this.source( { term: value }, this.response );
	},

	_response: function( content ) {
		if ( content ) {
			content = this._normalize( content );
		}
		this._trigger( "response", null, { content: content } );
		if ( !this.options.disabled && content && content.length ) {
			this._suggest( content );
			this._trigger( "open" );
		} else {
			this.close();
		}
		this.pending--;
		if ( !this.pending ) {
			this.element.removeClass( "ui-autocomplete-loading" );
		}
	},

	close: function( event ) {
		clearTimeout( this.closing );
		if ( this.menu.element.is(":visible") ) {
			this.menu.element.hide();
			this.menu.blur();
			this._trigger( "close", event );
		}
	},

	_change: function( event ) {
		if ( this.previous !== this._value() ) {
			this._trigger( "change", event, { item: this.selectedItem } );
		}
	},

	_normalize: function( items ) {
		// assume all items have the right format when the first item is complete
		if ( items.length && items[0].label && items[0].value ) {
			return items;
		}
		return $.map( items, function(item) {
			if ( typeof item === "string" ) {
				return {
					label: item,
					value: item
				};
			}
			return $.extend({
				label: item.label || item.value,
				value: item.value || item.label
			}, item );
		});
	},

	_suggest: function( items ) {
		var ul = this.menu.element
			.empty()
			.zIndex( this.element.zIndex() + 1 );
		this._renderMenu( ul, items );
		// TODO refresh should check if the active item is still in the dom, removing the need for a manual blur
		this.menu.blur();
		this.menu.refresh();

		// size and position menu
		ul.show();
		this._resizeMenu();
		ul.position( $.extend({
			of: this.element
		}, this.options.position ));

		if ( this.options.autoFocus ) {
			this.menu.next( new $.Event("mouseover") );
		}
	},

	_resizeMenu: function() {
		var ul = this.menu.element;
		ul.outerWidth( Math.max(
			ul.width( "" ).outerWidth(),
			this.element.outerWidth()
		) );
	},

	_renderMenu: function( ul, items ) {
		var self = this;
		$.each( items, function( index, item ) {
			self._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item) {
		return $( "

" )
			.data( "item.autocomplete", item )
			.append( $( "" ).text( item.label ) )
			.appendTo( ul );
	},

	_move: function( direction, event ) {
		if ( !this.menu.element.is(":visible") ) {
			this.search( null, event );
			return;
		}
		if ( this.menu.first() && /^previous/.test(direction) ||
				this.menu.last() && /^next/.test(direction) ) {
			this._value( this.term );
			this.menu.blur();
			return;
		}
		this.menu[ direction ]( event );
	},

	widget: function() {
		return this.menu.element;
	},

	_value: function( value ) {
		return this.valueMethod.apply( this.element, arguments );
	}
});

$.extend( $.ui.autocomplete, {
	escapeRegex: function( value ) {
		return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
	},
	filter: function(array, term) {
		var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
		return $.grep( array, function(value) {
			return matcher.test( value.label || value.value || value );
		});
	}
});

}( jQuery ));
<commit_msg>Autocomplete: Added missing semicolon.<commit_after>/*
 * jQuery UI Autocomplete @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Autocomplete
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function( $, undefined ) {

// used to prevent race conditions with remote data sources
var requestIndex = 0;

$.widget( "ui.autocomplete", {
	version: "@VERSION",
	defaultElement: "<input>",
	options: {
		appendTo: "body",
		autoFocus: false,
		delay: 300,
		minLength: 1,
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		source: null,

		// callbacks
		change: null,
		close: null,
		focus: null,
		open: null,
		response: null,
		search: null,
		select: null
	},

	pending: 0,

	_create: function() {
		var self = this,
			doc = this.element[ 0 ].ownerDocument,
			suppressKeyPress,
			suppressInput;

		this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ];

		this.element
			.addClass( "ui-autocomplete-input" )
			.attr( "autocomplete", "off" )
			// TODO verify these actually work as intended
			.attr({
				role: "textbox",
				"aria-autocomplete": "list",
				"aria-haspopup": "true"
			})
			.bind( "keydown.autocomplete", function( event ) {
				if ( self.options.disabled || self.element.prop( "readOnly" ) ) {
					suppressKeyPress = true;
					suppressInput = true;
					return;
				}

				suppressKeyPress = false;
				suppressInput = false;
				var keyCode = $.ui.keyCode;
				switch( event.keyCode ) {
				case keyCode.PAGE_UP:
					suppressKeyPress = true;
					self._move( "previousPage", event );
					break;
				case keyCode.PAGE_DOWN:
					suppressKeyPress = true;
					self._move( "nextPage", event );
					break;
				case keyCode.UP:
					suppressKeyPress = true;
					self._move( "previous", event );
					// prevent moving cursor to beginning of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.DOWN:
					suppressKeyPress = true;
					self._move( "next", event );
					// prevent moving cursor to end of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.ENTER:
				case keyCode.NUMPAD_ENTER:
					// when menu is open and has focus
					if ( self.menu.active ) {
						// #6055 - Opera still allows the keypress to occur
						// which causes forms to submit
						suppressKeyPress = true;
						event.preventDefault();
					}
					//passthrough - ENTER and TAB both select the current element
				case keyCode.TAB:
					if ( !self.menu.active ) {
						return;
					}
					self.menu.select( event );
					break;
				case keyCode.ESCAPE:
					self._value( self.term );
					self.close( event );
					break;
				default:
					// search timeout should be triggered before the input value is changed
					self._searchTimeout( event );
					break;
				}
			})
			.bind( "keypress.autocomplete", function( event ) {
				if ( suppressKeyPress ) {
					suppressKeyPress = false;
					event.preventDefault();
					return;
				}

				// replicate some key handlers to allow them to repeat in Firefox and Opera
				var keyCode = $.ui.keyCode;
				switch( event.keyCode ) {
				case keyCode.PAGE_UP:
					self._move( "previousPage", event );
					break;
				case keyCode.PAGE_DOWN:
					self._move( "nextPage", event );
					break;
				case keyCode.UP:
					self._move( "previous", event );
					// prevent moving cursor to beginning of text field in some browsers
					event.preventDefault();
					break;
				case keyCode.DOWN:
					self._move( "next", event );
					// prevent moving cursor to end of text field in some browsers
					event.preventDefault();
					break;
				}
			})
			.bind( "input.autocomplete", function(event) {
				if ( suppressInput ) {
					suppressInput = false;
					event.preventDefault();
					return;
				}
				self._searchTimeout( event );
			})
			.bind( "focus.autocomplete", function() {
				if ( self.options.disabled ) {
					return;
				}

				self.selectedItem = null;
				self.previous = self._value();
			})
			.bind( "blur.autocomplete", function( event ) {
				if ( self.options.disabled ) {
					return;
				}

				clearTimeout( self.searching );
				// clicks on the menu (or a button to trigger a search) will cause a blur event
				self.closing = setTimeout(function() {
					self.close( event );
					self._change( event );
				}, 150 );
			});
		this._initSource();
		this.response = function() {
			return self._response.apply( self, arguments );
		};
		this.menu = $( "

" )
			.addClass( "ui-autocomplete" )
			.appendTo( $( this.options.appendTo || "body", doc )[0] )
			// prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
			.mousedown(function( event ) {
				// clicking on the scrollbar causes focus to shift to the body
				// but we can't detect a mouseup or a click immediately afterward
				// so we have to track the next mousedown and close the menu if
				// the user clicks somewhere outside of the autocomplete
				var menuElement = self.menu.element[ 0 ];
				if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
					setTimeout(function() {
						$( document ).one( 'mousedown', function( event ) {
							if ( event.target !== self.element[ 0 ] &&
								event.target !== menuElement &&
								!$.contains( menuElement, event.target ) ) {
								self.close();
							}
						});
					}, 1 );
				}

				// use another timeout to make sure the blur-event-handler on the input was already triggered
				setTimeout(function() {
					clearTimeout( self.closing );
				}, 13);
			})
			.menu({
				// custom key handling for now
				input: $(),
				focus: function( event, ui ) {
					var item = ui.item.data( "item.autocomplete" );
					if ( false !== self._trigger( "focus", event, { item: item } ) ) {
						// use value to match what will end up in the input, if it was a key event
						if ( /^key/.test(event.originalEvent.type) ) {
							self._value( item.value );
						}
					}
				},
				select: function( event, ui ) {
					var item = ui.item.data( "item.autocomplete" ),
						previous = self.previous;

					// only trigger when focus was lost (click on menu)
					if ( self.element[0] !== doc.activeElement ) {
						self.element.focus();
						self.previous = previous;
						// #6109 - IE triggers two focus events and the second
						// is asynchronous, so we need to reset the previous
						// term synchronously and asynchronously :-(
						setTimeout(function() {
							self.previous = previous;
							self.selectedItem = item;
						}, 1);
					}

					if ( false !== self._trigger( "select", event, { item: item } ) ) {
						self._value( item.value );
					}
					// reset the term after the select event
					// this allows custom select handling to work properly
					self.term = self._value();

					self.close( event );
					self.selectedItem = item;
				}
			})
			.zIndex( this.element.zIndex() + 1 )
			.hide()
			.data( "menu" );
		if ( $.fn.bgiframe ) {
			 this.menu.element.bgiframe();
		}
	},

	_destroy: function() {
		clearTimeout( this.searching );
		this.element
			.removeClass( "ui-autocomplete-input" )
			.removeAttr( "autocomplete" )
			.removeAttr( "role" )
			.removeAttr( "aria-autocomplete" )
			.removeAttr( "aria-haspopup" );
		this.menu.element.remove();
	},

	_setOption: function( key, value ) {
		this._super( "_setOption", key, value );
		if ( key === "source" ) {
			this._initSource();
		}
		if ( key === "appendTo" ) {
			this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] );
		}
		if ( key === "disabled" && value && this.xhr ) {
			this.xhr.abort();
		}
	},

	_initSource: function() {
		var self = this,
			array,
			url;
		if ( $.isArray(this.options.source) ) {
			array = this.options.source;
			this.source = function( request, response ) {
				response( $.ui.autocomplete.filter(array, request.term) );
			};
		} else if ( typeof this.options.source === "string" ) {
			url = this.options.source;
			this.source = function( request, response ) {
				if ( self.xhr ) {
					self.xhr.abort();
				}
				self.xhr = $.ajax({
					url: url,
					data: request,
					dataType: "json",
					autocompleteRequest: ++requestIndex,
					success: function( data, status ) {
						if ( this.autocompleteRequest === requestIndex ) {
							response( data );
						}
					},
					error: function() {
						if ( this.autocompleteRequest === requestIndex ) {
							response( [] );
						}
					}
				});
			};
		} else {
			this.source = this.options.source;
		}
	},

	_searchTimeout: function( event ) {
		var self = this;
		clearTimeout( self.searching );
		self.searching = setTimeout(function() {
			// only search if the value has changed
			if ( self.term !== self._value() ) {
				self.selectedItem = null;
				self.search( null, event );
			}
		}, self.options.delay );
	},

	search: function( value, event ) {
		value = value != null ? value : this._value();

		// always save the actual value, not the one passed as an argument
		this.term = this._value();

		if ( value.length < this.options.minLength ) {
			return this.close( event );
		}

		clearTimeout( this.closing );
		if ( this._trigger( "search", event ) === false ) {
			return;
		}

		return this._search( value );
	},

	_search: function( value ) {
		this.pending++;
		this.element.addClass( "ui-autocomplete-loading" );

		this.source( { term: value }, this.response );
	},

	_response: function( content ) {
		if ( content ) {
			content = this._normalize( content );
		}
		this._trigger( "response", null, { content: content } );
		if ( !this.options.disabled && content && content.length ) {
			this._suggest( content );
			this._trigger( "open" );
		} else {
			this.close();
		}
		this.pending--;
		if ( !this.pending ) {
			this.element.removeClass( "ui-autocomplete-loading" );
		}
	},

	close: function( event ) {
		clearTimeout( this.closing );
		if ( this.menu.element.is(":visible") ) {
			this.menu.element.hide();
			this.menu.blur();
			this._trigger( "close", event );
		}
	},

	_change: function( event ) {
		if ( this.previous !== this._value() ) {
			this._trigger( "change", event, { item: this.selectedItem } );
		}
	},

	_normalize: function( items ) {
		// assume all items have the right format when the first item is complete
		if ( items.length && items[0].label && items[0].value ) {
			return items;
		}
		return $.map( items, function(item) {
			if ( typeof item === "string" ) {
				return {
					label: item,
					value: item
				};
			}
			return $.extend({
				label: item.label || item.value,
				value: item.value || item.label
			}, item );
		});
	},

	_suggest: function( items ) {
		var ul = this.menu.element
			.empty()
			.zIndex( this.element.zIndex() + 1 );
		this._renderMenu( ul, items );
		// TODO refresh should check if the active item is still in the dom, removing the need for a manual blur
		this.menu.blur();
		this.menu.refresh();

		// size and position menu
		ul.show();
		this._resizeMenu();
		ul.position( $.extend({
			of: this.element
		}, this.options.position ));

		if ( this.options.autoFocus ) {
			this.menu.next( new $.Event("mouseover") );
		}
	},

	_resizeMenu: function() {
		var ul = this.menu.element;
		ul.outerWidth( Math.max(
			ul.width( "" ).outerWidth(),
			this.element.outerWidth()
		) );
	},

	_renderMenu: function( ul, items ) {
		var self = this;
		$.each( items, function( index, item ) {
			self._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item) {
		return $( "

" )
			.data( "item.autocomplete", item )
			.append( $( "" ).text( item.label ) )
			.appendTo( ul );
	},

	_move: function( direction, event ) {
		if ( !this.menu.element.is(":visible") ) {
			this.search( null, event );
			return;
		}
		if ( this.menu.first() && /^previous/.test(direction) ||
				this.menu.last() && /^next/.test(direction) ) {
			this._value( this.term );
			this.menu.blur();
			return;
		}
		this.menu[ direction ]( event );
	},

	widget: function() {
		return this.menu.element;
	},

	_value: function( value ) {
		return this.valueMethod.apply( this.element, arguments );
	}
});

$.extend( $.ui.autocomplete, {
	escapeRegex: function( value ) {
		return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
	},
	filter: function(array, term) {
		var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
		return $.grep( array, function(value) {
			return matcher.test( value.label || value.value || value );
		});
	}
});

}( jQuery ));
<|endoftext|>


<commit_before>
zeit.cms.declare_namespace('zeit.content.cp');

zeit.content.cp.BlockHover = gocept.Class.extend({

    construct: function() {
        var self = this;
        MochiKit.Signal.connect('cp-content', 'onmouseover', self, self.over);
        MochiKit.Signal.connect('cp-content', 'onmouseout', self, self.out);
    },

    over: function(event) {
        var self = this;
        var block = self.get_block(event.target());
        if (!isNull(block)) {
            MochiKit.DOM.addElementClass(block, 'hover');
        }
    },

    out: function(event) {
        var self = this;
        var block = self.get_block(event.target());
        if (!isNull(block)) {
            MochiKit.DOM.removeElementClass(block, 'hover');
        }
    },

    get_block: function(element) {
        var class = 'block';
        if (MochiKit.DOM.hasElementClass(element, class)) {
            return element
        }
        return MochiKit.DOM.getFirstParentByTagAndClassName(
            element, null, class);
    },
});

MochiKit.Signal.connect(window, 'cp-editor-initialized', function() {
    zeit.content.cp.block_hover = new zeit.content.cp.BlockHover();
});

zeit.content.cp.TeaserBarContentsSorter = gocept.Class.extend({

    __name__: 'zeit.content.cp.TeaserBarSorter',

    construct: function() {
        var self = this;
        new zeit.edit.context.Editor(self);
        self.sorters = [];
    },

    connect: function() {
        var self = this;
        forEach($$('.block.type-teaser-bar > .block-inner'), function(bar) {
            if (!bar.id) {
                bar.id = bar.parentNode.id + '-inner';
            }
            var url = bar.parentNode.getAttribute(
                'cms:url');
            var sorter = new zeit.edit.sortable.BlockSorter(
                bar.id, {
                constraint: 'horizontal',
                overlap: 'horizontal',
                update_url: url + '/@@updateOrder',
                reload_id: bar.parentNode.id,
                reload_url: url + '/@@contents',
            });
            self.sorters.push(sorter);
        });
    },

    disconnect: function() {
        var self = this;
        while (self.sorters.length) {
            var sorter = self.sorters.pop()
            log("Destroying sorter " + sorter.container);
            sorter.__context__.deactivate();
            sorter.__context__.destroy();
        }
    },

});

MochiKit.Signal.connect(window, 'cp-editor-initialized', function() {
    zeit.content.cp.lead_sorter = new zeit.edit.sortable.BlockSorter(
        'lead');
    zeit.content.cp.informatives_sorter = new zeit.edit.sortable.BlockSorter(
        'informatives');
    zeit.content.cp.teaser_bar_sorter = new zeit.edit.sortable.BlockSorter(
        'teaser-mosaic');
    zeit.content.cp.teaser_bar_contents_sorter =
        new zeit.content.cp.TeaserBarContentsSorter();
});

(function() {

    var added = function(id) {
        log('added');
        log('added ' + id);
        if (isNull($(id))) {
            log("Added but not found: " + id);
            return
        }
        log('Added ' + id + $(id));
        MochiKit.Style.setOpacity($(id), 0.0);
        MochiKit.Visual.appear(id);
        MochiKit.Async.callLater(2, function() {
            $(id).style['opacity'] = 1;
        });
    };

    var deleted  = function(id) {
        log('Deleted ' + id + $(id));
        MochiKit.Visual.fade(id, {duration: 0.5,
                                  to: 0.01});
    };

    var ident = MochiKit.Signal.connect(
        window, 'cp-editor-initialized',
        function() {
            MochiKit.Signal.disconnect(ident);
            MochiKit.Signal.connect(
                zeit.edit.editor, 'added', added);
            MochiKit.Signal.connect(
                zeit.edit.editor, 'deleted', deleted);
    });
})();

zeit.content.cp.makeBoxesEquallyHigh = function(container) {
    // check for unloaded images:

    log("fixing box heights.", container.id);
    var images = MochiKit.DOM.getElementsByTagAndClassName(
        'img', null, container);
    var exit = false;
    forEach(images, function(image) {
        if (!image.height) {
            exit = true;
            throw MochiKit.Iter.StopIteration;
        }
    });
    if (exit) {
        MochiKit.Async.callLater(
            0.25, zeit.content.cp.makeBoxesEquallyHigh, container);
        return
    }

    var max_height = 0;
    var blocks = [];
    forEach($(container).childNodes, function(block) {
        if (block.nodeType != block.ELEMENT_NODE)
            return
        if (!MochiKit.DOM.hasElementClass(block, 'block'))
            return
        var block = MochiKit.DOM.getFirstElementByTagAndClassName(
            'div', 'block-inner', block);
        if (isNull(block))
            return
        blocks.push(block);
        var height = MochiKit.Style.getStyle(block, 'height');
        MochiKit.Style.setStyle(block, {'height': 'auto'});
        var dim = MochiKit.Style.getElementDimensions(block, true);
        MochiKit.Style.setStyle(block, {'height': height});
        max_height = Math.max(max_height, dim.h);
    });

    forEach(blocks, function(block) {
        var new_dim = new MochiKit.Style.Dimensions(null, max_height)
        MochiKit.Style.setElementDimensions(block, new_dim);
    });
};

(function() {

    var fix_box_heights = function() {
        log('fixing box heights');
        forEach($$('#teaser-mosaic > .block.type-teaser-bar > .block-inner'),
            function(bar) {
                log('fixing box heights for', bar.id);
                try {
                    zeit.content.cp.makeBoxesEquallyHigh(bar);
                } catch (e) {
                    log("Error", e)
                }
        });
    }

    var ident = MochiKit.Signal.connect(
        window, 'cp-editor-initialized',
        function() {
            MochiKit.Signal.disconnect(ident);
            MochiKit.Signal.connect(
                zeit.edit.editor, 'after-reload', fix_box_heights);
        });
})();

<commit_msg>linted<commit_after>
zeit.cms.declare_namespace('zeit.content.cp');

zeit.content.cp.BlockHover = gocept.Class.extend({

    construct: function() {
        var self = this;
        MochiKit.Signal.connect('cp-content', 'onmouseover', self, self.over);
        MochiKit.Signal.connect('cp-content', 'onmouseout', self, self.out);
    },

    over: function(event) {
        var self = this;
        var block = self.get_block(event.target());
        if (!isNull(block)) {
            MochiKit.DOM.addElementClass(block, 'hover');
        }
    },

    out: function(event) {
        var self = this;
        var block = self.get_block(event.target());
        if (!isNull(block)) {
            MochiKit.DOM.removeElementClass(block, 'hover');
        }
    },

    get_block: function(element) {
        var css_class = 'block';
        if (MochiKit.DOM.hasElementClass(element, css_class)) {
            return element;
        }
        return MochiKit.DOM.getFirstParentByTagAndClassName(
            element, null, css_class);
    }
});

MochiKit.Signal.connect(window, 'cp-editor-initialized', function() {
    zeit.content.cp.block_hover = new zeit.content.cp.BlockHover();
});

zeit.content.cp.TeaserBarContentsSorter = gocept.Class.extend({

    __name__: 'zeit.content.cp.TeaserBarSorter',

    construct: function() {
        var self = this;
        new zeit.edit.context.Editor(self);
        self.sorters = [];
    },

    connect: function() {
        var self = this;
        forEach($$('.block.type-teaser-bar > .block-inner'), function(bar) {
            if (!bar.id) {
                bar.id = bar.parentNode.id + '-inner';
            }
            var url = bar.parentNode.getAttribute(
                'cms:url');
            var sorter = new zeit.edit.sortable.BlockSorter(
                bar.id, {
                constraint: 'horizontal',
                overlap: 'horizontal',
                update_url: url + '/@@updateOrder',
                reload_id: bar.parentNode.id,
                reload_url: url + '/@@contents'
            });
            self.sorters.push(sorter);
        });
    },

    disconnect: function() {
        var self = this;
        while (self.sorters.length) {
            var sorter = self.sorters.pop();
            log("Destroying sorter " + sorter.container);
            sorter.__context__.deactivate();
            sorter.__context__.destroy();
        }
    }

});

MochiKit.Signal.connect(window, 'cp-editor-initialized', function() {
    zeit.content.cp.lead_sorter = new zeit.edit.sortable.BlockSorter(
        'lead');
    zeit.content.cp.informatives_sorter = new zeit.edit.sortable.BlockSorter(
        'informatives');
    zeit.content.cp.teaser_bar_sorter = new zeit.edit.sortable.BlockSorter(
        'teaser-mosaic');
    zeit.content.cp.teaser_bar_contents_sorter =
        new zeit.content.cp.TeaserBarContentsSorter();
});

(function() {

    var added = function(id) {
        log('added');
        log('added ' + id);
        if (isNull($(id))) {
            log("Added but not found: " + id);
            return;
        }
        log('Added ' + id + $(id));
        MochiKit.Style.setOpacity($(id), 0.0);
        MochiKit.Visual.appear(id);
        MochiKit.Async.callLater(2, function() {
            $(id).style['opacity'] = 1;
        });
    };

    var deleted  = function(id) {
        log('Deleted ' + id + $(id));
        MochiKit.Visual.fade(id, {duration: 0.5,
                                  to: 0.01});
    };

    var ident = MochiKit.Signal.connect(
        window, 'cp-editor-initialized',
        function() {
            MochiKit.Signal.disconnect(ident);
            MochiKit.Signal.connect(
                zeit.edit.editor, 'added', added);
            MochiKit.Signal.connect(
                zeit.edit.editor, 'deleted', deleted);
    });
})();

zeit.content.cp.makeBoxesEquallyHigh = function(container) {
    // check for unloaded images:

    log("fixing box heights.", container.id);
    var images = MochiKit.DOM.getElementsByTagAndClassName(
        'img', null, container);
    var exit = false;
    forEach(images, function(image) {
        if (!image.height) {
            exit = true;
            throw MochiKit.Iter.StopIteration;
        }
    });
    if (exit) {
        MochiKit.Async.callLater(
            0.25, zeit.content.cp.makeBoxesEquallyHigh, container);
        return;
    }

    var max_height = 0;
    var blocks = [];
    forEach($(container).childNodes, function(block) {
        if (block.nodeType != block.ELEMENT_NODE) {
            return;
        }
        if (!MochiKit.DOM.hasElementClass(block, 'block')) {
            return;
        }
        var block_inner = MochiKit.DOM.getFirstElementByTagAndClassName(
            'div', 'block-inner', block);
        if (isNull(block)) {
            return;
        }
        blocks.push(block_inner);
        var height = MochiKit.Style.getStyle(block_inner, 'height');
        MochiKit.Style.setStyle(block_inner, {'height': 'auto'});
        var dim = MochiKit.Style.getElementDimensions(block_inner, true);
        MochiKit.Style.setStyle(block_inner, {'height': height});
        max_height = Math.max(max_height, dim.h);
    });

    forEach(blocks, function(block) {
        var new_dim = new MochiKit.Style.Dimensions(null, max_height);
        MochiKit.Style.setElementDimensions(block, new_dim);
    });
};

(function() {

    var fix_box_heights = function() {
        log('fixing box heights');
        forEach($$('#teaser-mosaic > .block.type-teaser-bar > .block-inner'),
            function(bar) {
                log('fixing box heights for', bar.id);
                try {
                    zeit.content.cp.makeBoxesEquallyHigh(bar);
                } catch (e) {
                    log("Error", e);
                }
        });
    };

    var ident = MochiKit.Signal.connect(
        window, 'cp-editor-initialized',
        function() {
            MochiKit.Signal.disconnect(ident);
            MochiKit.Signal.connect(
                zeit.edit.editor, 'after-reload', fix_box_heights);
        });
})();

<|endoftext|>