<commit_before>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';

import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native';

import { COMPONENT_HEIGHT } from '../../globalStyles';

/**
 * Generic choice list for use within a ModalContainer.
 *
 * On selecting a choice, returns the object which was selected, the index/row
 * within the data array and the field which was displayed to the user.
 * @prop  {array}   data                Array of objects from which a choice must be made from
 * @prop  {string}  keyToDisplay        The object property which should be displayed to the user
 * @prop  {func}    onPress             Function to call on selection returns {item, index, field}
 * @prop  {int}     highlightIndex      Index in `data` for which object should be 'highlighted'
 * @prop  {string}  highlightValue      Alternate to highlightIndex, using the value over index.
 * @prop  {func}    renderLeftComponent Renders a component to the left of the text in a row.
 *
 * NOTE: If there are multiple, equal values used in highlightValue within the data array,
 * multiple values will be highlighted.
 */
export const GenericChoiceList = React.memo(
  ({ keyToDisplay, onPress, data, highlightIndex, highlightValue, renderLeftComponent }) => {
    const keyExtractor = React.useCallback(
      (item, index) => {
        const content = keyToDisplay && item ? item[keyToDisplay] : item;
        return `${content}${index}`;
      },
      [keyToDisplay]
    );

    const renderRow = React.useCallback(
      ({ item, index }) => {
        const { row, text } = localStyles;

        let shouldHighlight = false;
        if (keyToDisplay && highlightValue) shouldHighlight = item[keyToDisplay] === highlightValue;
        else if (!highlightValue) shouldHighlight = index === highlightIndex;

        const rowStyle = shouldHighlight ? { ...row, backgroundColor: '#E95C30' } : row;
        const textStyle = shouldHighlight ? { ...text, color: '#FFF' } : text;

        return (
          <TouchableOpacity onPress={() => onPress({ item, index, keyToDisplay })}>
            <View style={rowStyle}>
              {renderLeftComponent ? renderLeftComponent(item) : null}
              <Text style={textStyle}>{keyToDisplay ? item[keyToDisplay] : item}</Text>
            </View>
          </TouchableOpacity>
        );
      },
      [keyToDisplay, highlightValue, highlightIndex]
    );

    if (!data) return null;
    return (
      <FlatList
        data={data}
        renderItem={renderRow}
        style={localStyles.list}
        keyExtractor={keyExtractor}
      />
    );
  }
);

const localStyles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    paddingLeft: 30,
    justifyContent: 'flex-start',
    alignItems: 'center',
    backgroundColor: '#FFFFFF',
    height: COMPONENT_HEIGHT,
  },
  list: {
    marginTop: 10,
    minWidth: '70%',
  },
  text: {
    fontSize: 20,
    marginLeft: 20,
  },
});

GenericChoiceList.defaultProps = {
  highlightIndex: 0,
  highlightValue: null,
  renderLeftComponent: null,
};

GenericChoiceList.propTypes = {
  keyToDisplay: PropTypes.string.isRequired,
  onPress: PropTypes.func.isRequired,
  data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
  highlightIndex: PropTypes.number,
  highlightValue: PropTypes.string,
  renderLeftComponent: PropTypes.func,
};
<commit_msg>Add updated GenericChoiceList component<commit_after>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React from 'react';
import PropTypes from 'prop-types';

import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native';

import { textStyles, WHITE, COMPONENT_HEIGHT } from '../../globalStyles';

/**
 * Generic choice list for use within a ModalContainer.
 *
 * On selecting a choice, returns the object which was selected, the index/row
 * within the data array and the field which was displayed to the user.
 * @prop  {array}   data                Array of objects from which a choice must be made from
 * @prop  {string}  keyToDisplay        The object property which should be displayed to the user
 * @prop  {func}    onPress             Function to call on selection returns {item, index, field}
 * @prop  {int}     highlightIndex      Index in `data` for which object should be 'highlighted'
 * @prop  {string}  highlightValue      Alternate to highlightIndex, using the value over index.
 * @prop  {func}    renderLeftComponent Renders a component to the left of the text in a row.
 * @prop  {string}  placeholderText     Text used when there are no items to render in the list.
 *
 * NOTE: If there are multiple, equal values used in highlightValue within the data array,
 * multiple values will be highlighted.
 */
export const GenericChoiceList = React.memo(
  ({
    keyToDisplay,
    onPress,
    data,
    highlightIndex,
    highlightValue,
    renderLeftComponent,
    placeholderText,
  }) => {
    const keyExtractor = React.useCallback(
      (item, index) => {
        const content = keyToDisplay && item ? item[keyToDisplay] : item;
        return `${content}${index}`;
      },
      [keyToDisplay]
    );

    const renderRow = React.useCallback(
      ({ item, index }) => {
        const { row, text } = localStyles;

        let shouldHighlight = false;
        if (keyToDisplay && highlightValue) shouldHighlight = item[keyToDisplay] === highlightValue;
        else if (!highlightValue) shouldHighlight = index === highlightIndex;

        const rowStyle = shouldHighlight ? { ...row, backgroundColor: '#E95C30' } : row;
        const textStyle = shouldHighlight ? { ...text, color: '#FFF' } : text;

        return (
          <TouchableOpacity onPress={() => onPress({ item, index, keyToDisplay })}>
            <View style={rowStyle}>
              {renderLeftComponent ? renderLeftComponent(item) : null}
              <Text style={textStyle}>{keyToDisplay ? item[keyToDisplay] : item}</Text>
            </View>
          </TouchableOpacity>
        );
      },
      [keyToDisplay, highlightValue, highlightIndex]
    );

    if (!data) return null;
    return (
      <FlatList
        data={data}
        renderItem={renderRow}
        style={localStyles.list}
        keyExtractor={keyExtractor}
        ListEmptyComponent={<Text style={localStyles.placeholderText}>{placeholderText}</Text>}
      />
    );
  }
);

const localStyles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    paddingLeft: 30,
    justifyContent: 'flex-start',
    alignItems: 'center',
    backgroundColor: '#FFFFFF',
    height: COMPONENT_HEIGHT,
  },
  list: {
    marginTop: 10,
    minWidth: '70%',
    flex: 1,
  },
  text: {
    fontSize: 20,
    marginLeft: 20,
  },
  placeholderText: {
    ...textStyles,
    color: WHITE,
    justifyContent: 'center',
    alignItems: 'center',
    textAlign: 'center',
  },
});

GenericChoiceList.defaultProps = {
  highlightIndex: 0,
  highlightValue: null,
  renderLeftComponent: null,
  placeholderText: '',
};

GenericChoiceList.propTypes = {
  keyToDisplay: PropTypes.string.isRequired,
  onPress: PropTypes.func.isRequired,
  data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
  highlightIndex: PropTypes.number,
  highlightValue: PropTypes.string,
  renderLeftComponent: PropTypes.func,
  placeholderText: PropTypes.string,
};
<|endoftext|>


<commit_before>var compressor = require('node-minify');

// Use GCC Google Closure Compiler
new compressor.minify({
    type: 'gcc',
    fileIn: 'dist/Grammar.js',
    fileOut: 'dist/Grammar.min.js',
    callback: function(err){
      console.log(err);
    }
});
<commit_msg>Don't force 'gcc' as compressor type in Grammar compression<commit_after>var compressor = require('node-minify');

var compressors = ['gcc','yui-js','uglifyjs','sqwish'];

// Use GCC Google Closure Compiler by default
var compressorType = process.argv[2] || 'gcc';

if (compressors.indexOf(compressorType) === -1 ) {
  console.log(
    'Wrong compressor type specified. ' +
    'The supported compressor types are: '+ compressors.join(', ')
  );
} else {
  new compressor.minify({
      type: compressorType,
      fileIn: 'dist/Grammar.js',
      fileOut: 'dist/Grammar.min.js',
      callback: function(err){
        console.log(err);
      }
  });
}
<|endoftext|>


<commit_before>(function(){
	if(typeof H3 == 'undefined') H3 = {};

	H3.lo = {
		storage:{}
	};

	var haveDiff = function(dom, str, html){
		if(html){
			return dom.innerHTML !== str;
		}else{
			return dom.innerText !== str;
		}
	}

	var templateParser = function(src, data, dom){
		for(var key in data){
			src = src.replace(/{{(.*?)}}/, function(match, key) {
				return data[key];
			});
		}

		return src;
	}

	H3.lo.simpleParse = function(block, dom, str, html){
		if(!block.dataObject) return str;

		var data = block.dataObject;

		for(var key in data){
			if(data.hasOwnProperty(key)){
				var val = data[key];
				Object.defineProperty(data, key, {
				    get:function(){
				    	return val;
				    },
				    set:function(newval){
				    	val = newval;
				    	console.log(templateParser(str, data, dom));
				    }
				});
			}
		}
		/*

		for(var key in data){
			var val = data[key];
			Object.defineProperty(data, key, {
				get: function(){
					return val;
				},
				set: function(newval){
					val = newval
					console.log(templateParser(str, data, dom));
					return val;
				}
			});
		}
		*/

		return templateParser(str, data, dom);
	}

})();<commit_msg>SRC Update<commit_after>(function(){
	if(typeof H3 == 'undefined') H3 = {};

	H3.lo = {
		storage:{}
	};

	var haveDiff = function(dom, str, html){
		if(html){
			return dom.innerHTML !== str;
		}else{
			return dom.innerText !== str;
		}
	}

	var templateParser = function(src, data, dom){
		for(var key in data){
			src = src.replace(/{{(.*?)}}/, function(match, key) {
				return data[key];
			});
		}

		return src;
	}

	H3.lo.simpleParse = function(block, dom, str, html){
		if(!block.dataObject) return str;

		var data = block.dataObject;

		for(var key in data){
			if(data.hasOwnProperty(key)){
				var val = data[key];
				Object.defineProperty(data, key, {
				    get:function(){
				    	return val;
				    },
				    set:function(newval){
				    	val = newval;
				    	console.log(templateParser(str, data, dom));
				    	return val;
				    }
				});
			}
		}
		/*

		for(var key in data){
			var val = data[key];
			Object.defineProperty(data, key, {
				get: function(){
					return val;
				},
				set: function(newval){
					val = newval
					console.log(templateParser(str, data, dom));
					return val;
				}
			});
		}
		*/

		return templateParser(str, data, dom);
	}

})();<|endoftext|>


<commit_before>import R from 'ramda';
import XP from 'xod-project';

import { ENTITY } from './constants';

import {
  EDITOR_DESELECT_ALL,
  EDITOR_SELECT_NODE,
  EDITOR_SELECT_PIN,
  EDITOR_SELECT_LINK,
  EDITOR_SET_MODE,
  EDITOR_SET_SELECTED_NODETYPE,
  EDITOR_SWITCH_PATCH,
  TAB_CLOSE,
  TAB_SORT,
} from './actionTypes';
import {
  PROJECT_CREATE,
  PROJECT_OPEN,
  PROJECT_IMPORT,
  PROJECT_OPEN_WORKSPACE,
  PATCH_ADD,
  PATCH_DELETE,
  NODE_DELETE,
  LINK_DELETE,
} from '../project/actionTypes';

const addSelection = (entityName, action, state) => {
  const select = {
    entity: entityName,
    id: action.payload.id,
  };
  const newSelection = R.append(select, state.selection);
  return R.set(R.lensProp('selection'), newSelection, state);
};

const addTab = R.curry((patchId, state) => {
  if (!patchId) return state;

  const tabs = R.prop('tabs')(state);
  const lastIndex = R.reduce(
    (acc, tab) => R.pipe(
      R.prop('index'),
      R.max(acc)
    )(tab),
    -Infinity,
    R.values(tabs)
  );
  const newIndex = R.inc(lastIndex);

  return R.assocPath(['tabs', patchId], {
    id: patchId,
    index: newIndex,
  }, state);
});

const applyTabSort = (tab, payload) => {
  if (R.not(R.has(tab.id, payload))) { return tab; }

  return R.assoc('index', payload[tab.id].index, tab);
};

const isPatchOpened = R.curry((patchId, state) =>
  R.any(R.propEq('id', patchId))(R.values(state.tabs))
);

const resetCurrentPatchId = (reducer, state, payload) => {
  const newState = R.assoc('tabs', [], state);
  const firstPatchId = R.pipe(
    XP.listLocalPatches,
    R.head,
    R.ifElse(
      R.isNil,
      R.always(null),
      XP.getPatchPath
    )
  )(payload);

  return reducer(newState, {
    type: EDITOR_SWITCH_PATCH,
    payload: {
      id: firstPatchId,
    },
  });
};

const openPatchById = (patchId, state) => R.compose(
  R.assoc('currentPatchId', patchId),
  R.unless(isPatchOpened(patchId), addTab(patchId))
)(state);

const editorReducer = (state = {}, action) => {
  switch (action.type) {
    case NODE_DELETE:
    case LINK_DELETE:
    case EDITOR_DESELECT_ALL:
      return R.merge(state, {
        selection: [],
        linkingPin: null,
      });
    case EDITOR_SELECT_NODE:
      return addSelection(ENTITY.NODE, action, state);
    case EDITOR_SELECT_LINK:
      return addSelection(ENTITY.LINK, action, state);
    case EDITOR_SELECT_PIN:
      return R.assoc('linkingPin', action.payload, state);
    case EDITOR_SET_MODE:
      return R.assoc('mode', action.payload.mode, state);
    case EDITOR_SET_SELECTED_NODETYPE:
      return R.assoc('selectedNodeType', action.payload.id, state);
    case PROJECT_CREATE: {
      const newState = R.assoc('tabs', [], state);
      return editorReducer(newState, {
        type: EDITOR_SWITCH_PATCH,
        payload: {
          id: action.payload.mainPatchId,
        },
      });
    }
    case PROJECT_OPEN:
    case PROJECT_IMPORT:
    case PROJECT_OPEN_WORKSPACE:
      return resetCurrentPatchId(editorReducer, state, action.payload);
    case PATCH_ADD:
    case EDITOR_SWITCH_PATCH:
      return openPatchById(action.payload.id, state);
    case PATCH_DELETE:
    case TAB_CLOSE:
      return R.compose(
        R.converge(
          R.assoc('currentPatchId'),
          [
            R.compose( // get patch id from last of remaining tabs
              R.propOr(null, 'id'),
              R.last,
              R.values,
              R.prop('tabs')
            ),
            R.identity,
          ]
        ),
        R.dissocPath(['tabs', action.payload.id.toString()])
      )(state);
    case TAB_SORT:
      return R.assoc(
        'tabs',
        R.reduce(
          (p, cur) => R.assoc(cur.id, applyTabSort(cur, action.payload), p),
          {},
          R.values(state.tabs)
        ),
        state
      );
    default:
      return state;
  }
};

export default editorReducer;
<commit_msg>fix(xod-client): properly reset tabs and selection when switching project<commit_after>import R from 'ramda';
import XP from 'xod-project';

import { ENTITY } from './constants';

import {
  EDITOR_DESELECT_ALL,
  EDITOR_SELECT_NODE,
  EDITOR_SELECT_PIN,
  EDITOR_SELECT_LINK,
  EDITOR_SET_MODE,
  EDITOR_SET_SELECTED_NODETYPE,
  EDITOR_SWITCH_PATCH,
  TAB_CLOSE,
  TAB_SORT,
} from './actionTypes';
import {
  PROJECT_CREATE,
  PROJECT_OPEN,
  PROJECT_IMPORT,
  PROJECT_OPEN_WORKSPACE,
  PATCH_ADD,
  PATCH_DELETE,
  NODE_DELETE,
  LINK_DELETE,
} from '../project/actionTypes';

const addSelection = (entityName, action, state) => {
  const select = {
    entity: entityName,
    id: action.payload.id,
  };
  const newSelection = R.append(select, state.selection);
  return R.set(R.lensProp('selection'), newSelection, state);
};

const addTab = R.curry((patchId, state) => {
  if (!patchId) return state;

  const tabs = R.prop('tabs')(state);
  const lastIndex = R.reduce(
    (acc, tab) => R.pipe(
      R.prop('index'),
      R.max(acc)
    )(tab),
    -1,
    R.values(tabs)
  );
  const newIndex = R.inc(lastIndex);

  return R.assocPath(['tabs', patchId], {
    id: patchId,
    index: newIndex,
  }, state);
});

const applyTabSort = (tab, payload) => {
  if (R.not(R.has(tab.id, payload))) { return tab; }

  return R.assoc('index', payload[tab.id].index, tab);
};

const isPatchOpened = R.curry((patchId, state) =>
  R.any(R.propEq('id', patchId))(R.values(state.tabs))
);

const resetCurrentPatchId = (reducer, state, payload) => {
  const newState = R.assoc('tabs', {}, state);
  const firstPatchId = R.pipe(
    XP.listLocalPatches,
    R.head,
    R.ifElse(
      R.isNil,
      R.always(null),
      XP.getPatchPath
    )
  )(payload);

  return reducer(newState, {
    type: EDITOR_SWITCH_PATCH,
    payload: {
      id: firstPatchId,
    },
  });
};

const openPatchById = (patchId, state) => R.compose(
  R.assoc('currentPatchId', patchId),
  R.unless(isPatchOpened(patchId), addTab(patchId))
)(state);

const editorReducer = (state = {}, action) => {
  switch (action.type) {
    case NODE_DELETE:
    case LINK_DELETE:
    case EDITOR_DESELECT_ALL:
      return R.merge(state, {
        selection: [],
        linkingPin: null,
      });
    case EDITOR_SELECT_NODE:
      return addSelection(ENTITY.NODE, action, state);
    case EDITOR_SELECT_LINK:
      return addSelection(ENTITY.LINK, action, state);
    case EDITOR_SELECT_PIN:
      return R.assoc('linkingPin', action.payload, state);
    case EDITOR_SET_MODE:
      return R.assoc('mode', action.payload.mode, state);
    case EDITOR_SET_SELECTED_NODETYPE:
      return R.assoc('selectedNodeType', action.payload.id, state);
    case PROJECT_CREATE: {
      const newState = R.assoc('tabs', {}, state);
      return editorReducer(newState, {
        type: EDITOR_SWITCH_PATCH,
        payload: {
          id: action.payload.mainPatchId,
        },
      });
    }
    case PROJECT_OPEN:
    case PROJECT_IMPORT: {
      const newState = R.merge(state, {
        currentPatchId: null,
        selection: [],
        tabs: {},
        linkingPin: null,
      });
      return resetCurrentPatchId(editorReducer, newState, action.payload);
    }
    case PROJECT_OPEN_WORKSPACE:
      return R.merge(state, {
        currentPatchId: null,
        selection: [],
        tabs: {},
        linkingPin: null,
      });
    case PATCH_ADD:
    case EDITOR_SWITCH_PATCH:
      return openPatchById(action.payload.id, state);
    case PATCH_DELETE:
    case TAB_CLOSE:
      return R.compose(
        R.converge(
          R.assoc('currentPatchId'),
          [
            R.compose( // get patch id from last of remaining tabs
              R.propOr(null, 'id'),
              R.last,
              R.values,
              R.prop('tabs')
            ),
            R.identity,
          ]
        ),
        R.dissocPath(['tabs', action.payload.id.toString()])
      )(state);
    case TAB_SORT:
      return R.assoc(
        'tabs',
        R.reduce(
          (p, cur) => R.assoc(cur.id, applyTabSort(cur, action.payload), p),
          {},
          R.values(state.tabs)
        ),
        state
      );
    default:
      return state;
  }
};

export default editorReducer;
<|endoftext|>


<commit_before>/*
Copyright 2017 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import sdk from 'matrix-react-sdk';
import Modal from 'matrix-react-sdk/lib/Modal';
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';

class SendCustomEvent extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
    };

    constructor(props, context) {
        super(props, context);
        this._send = this._send.bind(this);
        this.onCancel = this.onCancel.bind(this);
    }

    async _send() {
        try {
            const content = JSON.parse(this.refs.evContent.value);
            await MatrixClientPeg.get().sendEvent(this.refs.roomId.value, this.refs.eventType.value, content);
            this.props.onFinished(true);
        } catch (e) {
            this.props.onFinished(false);
            const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
            Modal.createDialog(ErrorDialog, {
                title: 'Failed to send custom event',
                description: e.toString(),
            });
        }
    }

    onCancel() {
        this.props.onFinished(false);
    }

    render() {
        return

                     Room ID

                    <input id="roomId" ref="roomId" className="mx_TextInputDialog_input" defaultValue={this.props.roomId} size="64" />

                     Event Type

                    <input id="eventType" ref="eventType" className="mx_TextInputDialog_input" size="64" />

                     Event Content

                    <textarea id="evContent" ref="evContent" className="mx_TextInputDialog_input" defaultValue={"{\n\n}"} size="64" />

                <button onClick={this._send}>Send</button>
                <button onClick={this.onCancel}>Cancel</button>

;
    }
}

class SendCustomStateEvent extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
    };

    constructor(props, context) {
        super(props, context);
        this._send = this._send.bind(this);
        this.onCancel = this.onCancel.bind(this);
    }

    async _send() {
        try {
            const content = JSON.parse(this.refs.evContent.value);
            await MatrixClientPeg.get().sendStateEvent(this.refs.roomId.value, this.refs.eventType.value, content, this.refs.stateKey.value);
            this.props.onFinished(true);
        } catch (e) {
            this.props.onFinished(false);
            const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
            Modal.createDialog(ErrorDialog, {
                title: 'Failed to send custom event',
                description: e.toString(),
            });
        }
    }

    onCancel() {
        this.props.onFinished(false);
    }

    render() {
        return

                     Room ID

                    <input id="roomId" ref="roomId" className="mx_TextInputDialog_input" defaultValue={this.props.roomId} size="64" />

                     State Key

                    <input id="stateKey" ref="stateKey" className="mx_TextInputDialog_input" size="64" />

                     Event Type

                    <input id="eventType" ref="eventType" className="mx_TextInputDialog_input" size="64" />

                     Event Content

                    <textarea id="evContent" ref="evContent" className="mx_TextInputDialog_input" defaultValue={"{\n\n}"} size="64" />

                <button onClick={this._send}>Send</button>
                <button onClick={this.onCancel}>Cancel</button>

;
    }
}

class RoomStateExplorer extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
        onBack: React.PropTypes.func.isRequired,
    };

    constructor(props, context) {
        super(props, context);

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

    state = {
        eventType: null,
    };

    browseEventType(eventType) {
        const self = this;
        return () => {
            self.setState({ eventType });
        };
    }

    onViewSourceClick(ev) {
        const ViewSource = sdk.getComponent('structures.ViewSource');
        return () => {
            Modal.createDialog(ViewSource, {
                content: ev,
            }, 'mx_Dialog_viewsource');
        };
    }

    onBack() {
        if (this.state.eventType === null) {
            this.props.onBack();
        } else {
            this.setState({ eventType: null });
        }
    }

    render() {
        const room = MatrixClientPeg.get().getRoom(this.props.roomId);
        const roomStateEvents = room.currentState.events;

        const rows = [];

        if (this.state.eventType === null) {
            Object.keys(roomStateEvents).forEach((evType) => {
                const stateGroup = roomStateEvents[evType];
                const keys = Object.keys(stateGroup);

                if (keys.length > 1) {
                    rows.push(<button key={evType} onClick={this.browseEventType(evType)}>{evType}</button>);
                } else if (keys.length === 1) {
                    rows.push(<button key={evType} onClick={this.onViewSourceClick(stateGroup[keys[0]])}>{evType}</button>);
                }
            });
        } else {
            const evType = this.state.eventType;
            const stateGroup = roomStateEvents[evType];
            Object.keys(stateGroup).forEach((stateKey) => {
                const ev = stateGroup[stateKey];
                rows.push(<button key={stateKey} onClick={this.onViewSourceClick(ev)}>{stateKey}</button>);
            });
        }

        return

                    Room ID: {this.props.roomId}

                    {rows}

                <button onClick={this.onBack}>Back</button>

;
    }
}

export default class DevtoolsDialog extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
    };

    state = {
        mode: null,
    };

    constructor(props, context) {
        super(props, context);
        this.onBack = this.onBack.bind(this);
        this.onCancel = this.onCancel.bind(this);
    }

    componentWillUnmount() {
        this._unmounted = true;
    }

    _setMode(mode) {
        return () => {
            this.setState({ mode });
        };
    }

    onBack() {
        this.setState({ mode: null });
    }

    onCancel() {
        this.props.onFinished(false);
    }

    render() {
        let body;

        if (this.state.mode) {
            body = <this.state.mode {...this.props} onBack={this.onBack} />;
        } else {
            body =

                    <button onClick={this._setMode(SendCustomEvent)}>Send Custom Event</button>
                    <button onClick={this._setMode(SendCustomStateEvent)}>Send Custom State Event</button>
                    <button onClick={this._setMode(RoomStateExplorer)}>Explore Room State</button>

                    <button onClick={this.onCancel}>Cancel</button>

;
        }

        const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
        return <BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title="Developer Tools">
            { body }
        </BaseDialog>;
    }
}
<commit_msg>fix line-length and copyright header<commit_after>/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import sdk from 'matrix-react-sdk';
import Modal from 'matrix-react-sdk/lib/Modal';
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';

class SendCustomEvent extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
    };

    constructor(props, context) {
        super(props, context);
        this._send = this._send.bind(this);
        this.onCancel = this.onCancel.bind(this);
    }

    async _send() {
        try {
            const content = JSON.parse(this.refs.evContent.value);
            await MatrixClientPeg.get().sendEvent(this.refs.roomId.value, this.refs.eventType.value, content);
            this.props.onFinished(true);
        } catch (e) {
            this.props.onFinished(false);
            const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
            Modal.createDialog(ErrorDialog, {
                title: 'Failed to send custom event',
                description: e.toString(),
            });
        }
    }

    onCancel() {
        this.props.onFinished(false);
    }

    render() {
        return

                     Room ID

                    <input id="roomId" ref="roomId" className="mx_TextInputDialog_input" defaultValue={this.props.roomId} size="64" />

                     Event Type

                    <input id="eventType" ref="eventType" className="mx_TextInputDialog_input" size="64" />

                     Event Content

                    <textarea id="evContent" ref="evContent" className="mx_TextInputDialog_input" defaultValue={"{\n\n}"} size="64" />

                <button onClick={this._send}>Send</button>
                <button onClick={this.onCancel}>Cancel</button>

;
    }
}

class SendCustomStateEvent extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
    };

    constructor(props, context) {
        super(props, context);
        this._send = this._send.bind(this);
        this.onCancel = this.onCancel.bind(this);
    }

    async _send() {
        try {
            const content = JSON.parse(this.refs.evContent.value);
            await MatrixClientPeg.get().sendStateEvent(this.refs.roomId.value, this.refs.eventType.value, content,
                this.refs.stateKey.value);

            this.props.onFinished(true);
        } catch (e) {
            this.props.onFinished(false);
            const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
            Modal.createDialog(ErrorDialog, {
                title: 'Failed to send custom event',
                description: e.toString(),
            });
        }
    }

    onCancel() {
        this.props.onFinished(false);
    }

    render() {
        return

                     Room ID

                    <input id="roomId" ref="roomId" className="mx_TextInputDialog_input" defaultValue={this.props.roomId} size="64" />

                     State Key

                    <input id="stateKey" ref="stateKey" className="mx_TextInputDialog_input" size="64" />

                     Event Type

                    <input id="eventType" ref="eventType" className="mx_TextInputDialog_input" size="64" />

                     Event Content

                    <textarea id="evContent" ref="evContent" className="mx_TextInputDialog_input" defaultValue={"{\n\n}"} size="64" />

                <button onClick={this._send}>Send</button>
                <button onClick={this.onCancel}>Cancel</button>

;
    }
}

class RoomStateExplorer extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
        onBack: React.PropTypes.func.isRequired,
    };

    constructor(props, context) {
        super(props, context);

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

    state = {
        eventType: null,
    };

    browseEventType(eventType) {
        const self = this;
        return () => {
            self.setState({ eventType });
        };
    }

    onViewSourceClick(ev) {
        const ViewSource = sdk.getComponent('structures.ViewSource');
        return () => {
            Modal.createDialog(ViewSource, {
                content: ev,
            }, 'mx_Dialog_viewsource');
        };
    }

    onBack() {
        if (this.state.eventType === null) {
            this.props.onBack();
        } else {
            this.setState({ eventType: null });
        }
    }

    render() {
        const room = MatrixClientPeg.get().getRoom(this.props.roomId);
        const roomStateEvents = room.currentState.events;

        const rows = [];

        if (this.state.eventType === null) {
            Object.keys(roomStateEvents).forEach((evType) => {
                const stateGroup = roomStateEvents[evType];
                const keys = Object.keys(stateGroup);

                if (keys.length > 1) {
                    rows.push(<button key={evType} onClick={this.browseEventType(evType)}>{evType}</button>);
                } else if (keys.length === 1) {
                    rows.push(
                        <button key={evType} onClick={this.onViewSourceClick(stateGroup[keys[0]])}>{evType}</button>
                    );
                }
            });
        } else {
            const evType = this.state.eventType;
            const stateGroup = roomStateEvents[evType];
            Object.keys(stateGroup).forEach((stateKey) => {
                const ev = stateGroup[stateKey];
                rows.push(<button key={stateKey} onClick={this.onViewSourceClick(ev)}>{stateKey}</button>);
            });
        }

        return

                    Room ID: {this.props.roomId}

                    {rows}

                <button onClick={this.onBack}>Back</button>

;
    }
}

export default class DevtoolsDialog extends React.Component {
    static propTypes = {
        roomId: React.PropTypes.string.isRequired,
        onFinished: React.PropTypes.func.isRequired,
    };

    state = {
        mode: null,
    };

    constructor(props, context) {
        super(props, context);
        this.onBack = this.onBack.bind(this);
        this.onCancel = this.onCancel.bind(this);
    }

    componentWillUnmount() {
        this._unmounted = true;
    }

    _setMode(mode) {
        return () => {
            this.setState({ mode });
        };
    }

    onBack() {
        this.setState({ mode: null });
    }

    onCancel() {
        this.props.onFinished(false);
    }

    render() {
        let body;

        if (this.state.mode) {
            body = <this.state.mode {...this.props} onBack={this.onBack} />;
        } else {
            body =

                    <button onClick={this._setMode(SendCustomEvent)}>Send Custom Event</button>
                    <button onClick={this._setMode(SendCustomStateEvent)}>Send Custom State Event</button>
                    <button onClick={this._setMode(RoomStateExplorer)}>Explore Room State</button>

                    <button onClick={this.onCancel}>Cancel</button>

;
        }

        const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
        return <BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title="Developer Tools">
            { body }
        </BaseDialog>;
    }
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */

'use strict';

var startButton = document.getElementById('startButton');
var callButton = document.getElementById('callButton');
var hangupButton = document.getElementById('hangupButton');
callButton.disabled = true;
hangupButton.disabled = true;
startButton.onclick = start;
callButton.onclick = call;
hangupButton.onclick = hangup;

var video1 = document.querySelector('video#video1');
var video2 = document.querySelector('video#video2');
var video3 = document.querySelector('video#video3');

var pc1Local;
var pc1Remote;
var pc2Local;
var pc2Remote;
var offerOptions = {
  offerToReceiveAudio: 1,
  offerToReceiveVideo: 1
};

function gotStream(stream) {
  trace('Received local stream');
  video1.srcObject = stream;
  window.localstream = stream;
  callButton.disabled = false;
}

function start() {
  trace('Requesting local stream');
  startButton.disabled = true;
  navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    console.log('getUserMedia() error: ', e);
  });
}

function call() {
  callButton.disabled = true;
  hangupButton.disabled = false;
  trace('Starting calls');
  var audioTracks = window.localstream.getAudioTracks();
  var videoTracks = window.localstream.getVideoTracks();
  if (audioTracks.length > 0) {
    trace('Using audio device: ' + audioTracks[0].label);
  }
  if (videoTracks.length > 0) {
    trace('Using video device: ' + videoTracks[0].label);
  }
  // Create an RTCPeerConnection via the polyfill.
  var servers = null;
  pc1Local = new RTCPeerConnection(servers);
  pc1Remote = new RTCPeerConnection(servers);
  pc1Remote.onaddstream = gotRemoteStream1;
  pc1Local.onicecandidate = iceCallback1Local;
  pc1Remote.onicecandidate = iceCallback1Remote;
  trace('pc1: created local and remote peer connection objects');

  pc2Local = new RTCPeerConnection(servers);
  pc2Remote = new RTCPeerConnection(servers);
  pc2Remote.onaddstream = gotRemoteStream2;
  pc2Local.onicecandidate = iceCallback2Local;
  pc2Remote.onicecandidate = iceCallback2Remote;
  trace('pc2: created local and remote peer connection objects');

  pc1Local.addStream(window.localstream);
  trace('Adding local stream to pc1Local');
  pc1Local.createOffer(
    offerOptions
  ).then(
    gotDescription1Local,
    onCreateSessionDescriptionError
  );

  pc2Local.addStream(window.localstream);
  trace('Adding local stream to pc2Local');
  pc2Local.createOffer(
    offerOptions
  ).then(
    gotDescription2Local,
    onCreateSessionDescriptionError
  );
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function gotDescription1Local(desc) {
  pc1Local.setLocalDescription(desc);
  trace('Offer from pc1Local \n' + desc.sdp);
  pc1Remote.setRemoteDescription(desc);
  // Since the 'remote' side has no media stream we need
  // to pass in the right constraints in order for it to
  // accept the incoming offer of audio and video.
  pc1Remote.createAnswer().then(
    gotDescription1Remote,
    onCreateSessionDescriptionError
  );
}

function gotDescription1Remote(desc) {
  pc1Remote.setLocalDescription(desc);
  trace('Answer from pc1Remote \n' + desc.sdp);
  pc1Local.setRemoteDescription(desc);
}

function gotDescription2Local(desc) {
  pc2Local.setLocalDescription(desc);
  trace('Offer from pc2Local \n' + desc.sdp);
  pc2Remote.setRemoteDescription(desc);
  // Since the 'remote' side has no media stream we need
  // to pass in the right constraints in order for it to
  // accept the incoming offer of audio and video.
  pc2Remote.createAnswer().then(
    gotDescription2Remote,
    onCreateSessionDescriptionError
  );
}

function gotDescription2Remote(desc) {
  pc2Remote.setLocalDescription(desc);
  trace('Answer from pc2Remote \n' + desc.sdp);
  pc2Local.setRemoteDescription(desc);
}

function hangup() {
  trace('Ending calls');
  pc1Local.close();
  pc1Remote.close();
  pc2Local.close();
  pc2Remote.close();
  pc1Local = pc1Remote = null;
  pc2Local = pc2Remote = null;
  hangupButton.disabled = true;
  callButton.disabled = false;
}

function gotRemoteStream1(e) {
  video2.srcObject = e.stream;
  trace('pc1: received remote stream');
}

function gotRemoteStream2(e) {
  video3.srcObject = e.stream;
  trace('pc2: received remote stream');
}

function iceCallback1Local(event) {
  handleCandidate(event.candidate, pc1Remote, 'pc1: ', 'local');
}

function iceCallback1Remote(event) {
  handleCandidate(event.candidate, pc1Local, 'pc1: ', 'remote');
}

function iceCallback2Local(event) {
  handleCandidate(event.candidate, pc2Remote, 'pc2: ', 'local');
}

function iceCallback2Remote(event) {
  handleCandidate(event.candidate, pc2Local, 'pc2: ', 'remote');
}

function handleCandidate(candidate, dest, prefix, type) {
  if (candidate) {
    dest.addIceCandidate(candidate)
    .then(
      onAddIceCandidateSuccess,
      onAddIceCandidateError
    );
    trace(prefix + 'New ' + type + ' ICE candidate: ' + candidate.candidate);
  }
}

function onAddIceCandidateSuccess() {
  trace('AddIceCandidate success.');
}

function onAddIceCandidateError(error) {
  trace('Failed to add ICE candidate: ' + error.toString());
}
<commit_msg>multiple: fix addIceCandidate (#870)<commit_after>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */

'use strict';

var startButton = document.getElementById('startButton');
var callButton = document.getElementById('callButton');
var hangupButton = document.getElementById('hangupButton');
callButton.disabled = true;
hangupButton.disabled = true;
startButton.onclick = start;
callButton.onclick = call;
hangupButton.onclick = hangup;

var video1 = document.querySelector('video#video1');
var video2 = document.querySelector('video#video2');
var video3 = document.querySelector('video#video3');

var pc1Local;
var pc1Remote;
var pc2Local;
var pc2Remote;
var offerOptions = {
  offerToReceiveAudio: 1,
  offerToReceiveVideo: 1
};

function gotStream(stream) {
  trace('Received local stream');
  video1.srcObject = stream;
  window.localStream = stream;
  callButton.disabled = false;
}

function start() {
  trace('Requesting local stream');
  startButton.disabled = true;
  navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true
  })
  .then(gotStream)
  .catch(function(e) {
    console.log('getUserMedia() error: ', e);
  });
}

function call() {
  callButton.disabled = true;
  hangupButton.disabled = false;
  trace('Starting calls');
  var audioTracks = window.localStream.getAudioTracks();
  var videoTracks = window.localStream.getVideoTracks();
  if (audioTracks.length > 0) {
    trace('Using audio device: ' + audioTracks[0].label);
  }
  if (videoTracks.length > 0) {
    trace('Using video device: ' + videoTracks[0].label);
  }
  // Create an RTCPeerConnection via the polyfill.
  var servers = null;
  pc1Local = new RTCPeerConnection(servers);
  pc1Remote = new RTCPeerConnection(servers);
  pc1Remote.onaddstream = gotRemoteStream1;
  pc1Local.onicecandidate = iceCallback1Local;
  pc1Remote.onicecandidate = iceCallback1Remote;
  trace('pc1: created local and remote peer connection objects');

  pc2Local = new RTCPeerConnection(servers);
  pc2Remote = new RTCPeerConnection(servers);
  pc2Remote.onaddstream = gotRemoteStream2;
  pc2Local.onicecandidate = iceCallback2Local;
  pc2Remote.onicecandidate = iceCallback2Remote;
  trace('pc2: created local and remote peer connection objects');

  pc1Local.addStream(window.localStream);
  trace('Adding local stream to pc1Local');
  pc1Local.createOffer(
    offerOptions
  ).then(
    gotDescription1Local,
    onCreateSessionDescriptionError
  );

  pc2Local.addStream(window.localStream);
  trace('Adding local stream to pc2Local');
  pc2Local.createOffer(
    offerOptions
  ).then(
    gotDescription2Local,
    onCreateSessionDescriptionError
  );
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function gotDescription1Local(desc) {
  pc1Local.setLocalDescription(desc);
  trace('Offer from pc1Local \n' + desc.sdp);
  pc1Remote.setRemoteDescription(desc);
  // Since the 'remote' side has no media stream we need
  // to pass in the right constraints in order for it to
  // accept the incoming offer of audio and video.
  pc1Remote.createAnswer().then(
    gotDescription1Remote,
    onCreateSessionDescriptionError
  );
}

function gotDescription1Remote(desc) {
  pc1Remote.setLocalDescription(desc);
  trace('Answer from pc1Remote \n' + desc.sdp);
  pc1Local.setRemoteDescription(desc);
}

function gotDescription2Local(desc) {
  pc2Local.setLocalDescription(desc);
  trace('Offer from pc2Local \n' + desc.sdp);
  pc2Remote.setRemoteDescription(desc);
  // Since the 'remote' side has no media stream we need
  // to pass in the right constraints in order for it to
  // accept the incoming offer of audio and video.
  pc2Remote.createAnswer().then(
    gotDescription2Remote,
    onCreateSessionDescriptionError
  );
}

function gotDescription2Remote(desc) {
  pc2Remote.setLocalDescription(desc);
  trace('Answer from pc2Remote \n' + desc.sdp);
  pc2Local.setRemoteDescription(desc);
}

function hangup() {
  trace('Ending calls');
  pc1Local.close();
  pc1Remote.close();
  pc2Local.close();
  pc2Remote.close();
  pc1Local = pc1Remote = null;
  pc2Local = pc2Remote = null;
  hangupButton.disabled = true;
  callButton.disabled = false;
}

function gotRemoteStream1(e) {
  video2.srcObject = e.stream;
  trace('pc1: received remote stream');
}

function gotRemoteStream2(e) {
  video3.srcObject = e.stream;
  trace('pc2: received remote stream');
}

function iceCallback1Local(event) {
  handleCandidate(event.candidate, pc1Remote, 'pc1: ', 'local');
}

function iceCallback1Remote(event) {
  handleCandidate(event.candidate, pc1Local, 'pc1: ', 'remote');
}

function iceCallback2Local(event) {
  handleCandidate(event.candidate, pc2Remote, 'pc2: ', 'local');
}

function iceCallback2Remote(event) {
  handleCandidate(event.candidate, pc2Local, 'pc2: ', 'remote');
}

function handleCandidate(candidate, dest, prefix, type) {
  dest.addIceCandidate(candidate)
  .then(
    onAddIceCandidateSuccess,
    onAddIceCandidateError
  );
  trace(prefix + 'New ' + type + ' ICE candidate: ' +
      (candidate ? candidate.candidate : '(null)'));
}

function onAddIceCandidateSuccess() {
  trace('AddIceCandidate success.');
}

function onAddIceCandidateError(error) {
  trace('Failed to add ICE candidate: ' + error.toString());
}
<|endoftext|>


<commit_before>(function (fluid) {
    "use strict";

    fluid.defaults("sisiliano.linearSlider", {
        gradeNames: ["sisiliano.slider"],
        defaultViewBox: [0 ,0, 500, 50],
        ariaDescription: "Linear slider, the value can be adjusted by arrow keys. If you are using the mouse, drag along the slider",
        template: "src/controllers/linear-slider/linear-slider.html",
        model: {
            styles: {
                padding: {
                    top: 2,
                    bottom: 2,
                    left: 2,
                    right: 2
                },
                pointer: {
                    radius: 10
                },
                sliderBar: {
                    width: 100,
                    height: 100
                }
            },
            viewBox: {
                width: 500,
                height: 100
            },
            orientation: "vertical", // vertical or horizontal
            title: "linearSlider Controller",
            description: ""
        },
        selectors: {
            controller: ".sisiliano",
            svg: "svg",
            valueLabel: ".sisiliano-linear-slider-value-text",
            valueLabelRect: ".sisiliano-linear-slider-value-label",
            valueRect: ".sisiliano-linear-slider-value-rect",
            backgroundRect: ".sisiliano-linear-slider-background-rect",
            rects: ".sisiliano-linear-slider-rect",
            valueCircle: ".sisiliano-linear-slider-value-circle",
            valueCircleHover: ".sisiliano-linear-slider-value-circle-hover",
            notches: ".sisiliano-linear-slider-notches"
        },
        listeners: {
            onValueChange: {
                func: "sisiliano.linearSlider.onValueChange",
                args: ["{arguments}.0", "{arguments}.1"]
            },
            onColorChange: {
                func: "sisiliano.linearSlider.onColorChange",
                args: ["{that}", "{that}.model.color"]
            },
            onStatusChange: {
                func: "sisiliano.knob.onStatusChange",
                args: ["{that}", "{that}.model.status.isActive"]
            },
            onReady: [
                {
                    func: "sisiliano.linearSlider.drawNotches",
                    args: ["{that}"]
                },
                {
                    func: "sisiliano.linearSlider.addListeners",
                    args: ["{that}"]
                }
            ]
        },
        modelListeners: {
        }
    });

    sisiliano.knob.onStatusChange = function (that, isActive) {
        that.locate("valueCircleHover").css("display", isActive ? "block" : "none");
    };

    sisiliano.linearSlider.onValueChange = function (that, obviousValue) {
        var valueRange = that.model.size;
        var maxWidth = parseInt(that.locate("backgroundRect").attr("width"), null);
        var leftPadding = parseInt(that.locate("backgroundRect").attr("x"), null);
        var newWidth = maxWidth * (obviousValue / valueRange);
        that.locate("valueRect").attr("width", newWidth);
        that.locate("valueCircle").attr("cx", newWidth + leftPadding);
        that.locate("valueLabelRect").attr("x", newWidth + leftPadding - 25);
        that.locate("valueLabel").attr("x", newWidth + 3 + leftPadding - 25);
    };

    sisiliano.linearSlider.onColorChange = function (that, newColor) {
        that.locate("rects").attr("fill", newColor[0]);
        that.locate("valueLabelRect").attr("fill", newColor[0]);
        that.locate("valueLabel").attr("fill", newColor[1]);
        that.locate("valueCircle").attr("fill", newColor[0]);
    };

    sisiliano.linearSlider.setValueByPosition = function (that, clickedPosition) {
        var maxWidth = parseInt(that.locate("backgroundRect").attr("width"), null);
        var leftPadding = parseInt(that.locate("valueRect").attr("x"), null);
        var value = ((clickedPosition.x - leftPadding) / maxWidth) * that.model.size;
        if (that.model.value !== value) {
            that.applier.change("value", value + that.model.min);
        }
    };

    sisiliano.linearSlider.addListeners = function (that) {
        var mouseMoveHandler = function () {
            var position = d3.mouse(that.container.find("svg").eq(0).get(0));
            var clickedPosition = {x: position[0], y: position[1]};
            if (that.model.status.isActive) {
                sisiliano.linearSlider.setValueByPosition(that, clickedPosition);
                d3.event.preventDefault();
            }
        };

        document.addEventListener("mousemove", function (evt) {
            var svgElm = that.locate("svg");
            if (svgElm && svgElm.length > 0) {
                var svgPosition = svgElm.position();
                var svgWidth = svgElm.width();
                var clickedPosition = {x: ((evt.pageX - svgPosition.left) / svgWidth) * that.model.viewBox.width, y: 0};
                if (that.model.status.isActive) {
                    sisiliano.linearSlider.setValueByPosition(that, clickedPosition);
                    evt.preventDefault(evt);
                }
            }
        });
        document.addEventListener("mouseup", function () {
            that.applier.change("status.isActive", false);
        });

        d3.select(that.container.get(0))
            .on("touchmove", mouseMoveHandler)
            .on("mousemove", mouseMoveHandler);
    };

    sisiliano.linearSlider.getNotches = function () {
        //TODO define
        return [0,  20, 30, 40, 50, 60, 70, 80, 90, 100];
    };

    sisiliano.linearSlider.drawNotches = function (that) {
        var notches = sisiliano.linearSlider.getNotches(that);
        var notchesPane = d3.select(that.locate("notches").get(0));
        var sliderWidth = parseInt(that.locate("backgroundRect").attr("width"), null);
        var sliderX = parseInt(that.locate("backgroundRect").attr("x"), null);
        var sliderY = parseInt(that.locate("backgroundRect").attr("y"), null);
        fluid.each(notches, function (notchValue) {
            var x = sliderX + (sliderWidth * (notchValue - that.model.min) / (that.model.max - that.model.min));
            notchesPane.append("rect")
                .attr("width", 1)
                .attr("fill", that.model.color[0])
                .attr("fill-opacity", 0.3)
                .attr("height", 25)
                .attr("x", x - 0.5)
                .attr("y", sliderY);

            notchesPane.append("text")
                .attr("fill", that.model.color[0])
                .attr("fill-opacity", 0.6)
                .attr("x", x + 0.5)
                .attr("y", sliderY + 25)
                .attr("transform","rotate(90, " + (x + 1) + ", " + (sliderY + 25) + ")")
                .text(notchValue);
        });
    };
})(fluid);<commit_msg>{linear Slider] Updated the appearance of the ruler and the pointer<commit_after>(function (fluid) {
    "use strict";

    fluid.defaults("sisiliano.linearSlider", {
        gradeNames: ["sisiliano.slider"],
        defaultViewBox: [0 ,0, 500, 50],
        ariaDescription: "Linear slider, the value can be adjusted by arrow keys. If you are using the mouse, drag along the slider",
        template: "src/controllers/linear-slider/linear-slider.html",
        model: {
            styles: {
                pointer: {
                    label: {
                        "font-size": "10px"
                    }
                },
                ruler: {
                    line: {
                        width: 1,
                        height: 20
                    },
                    value: {
                        "font-size": "10px"
                    }
                }
            },
            rulerPoints: [0,  20, 30, 40, 50, 60, 70, 80, 90, 100],
            viewBox: {
                width: 500,
                height: 100
            },
            orientation: "vertical", // vertical or horizontal
            title: "linearSlider Controller",
            description: ""
        },
        selectors: {
            controller: ".sisiliano",
            svg: "svg",
            valueLabel: ".sisiliano-linear-slider-value-text",
            valueLabelRect: ".sisiliano-linear-slider-value-label",
            valueRect: ".sisiliano-linear-slider-value-rect",
            backgroundRect: ".sisiliano-linear-slider-background-rect",
            rects: ".sisiliano-linear-slider-rect",
            valueCircle: ".sisiliano-linear-slider-value-circle",
            valueCircleHover: ".sisiliano-linear-slider-value-circle-hover",
            ruler: ".sisiliano-linear-slider-ruler",
            valuePointer: ".sisiliano-linear-slider-value-pointer",
            valuePointerHover: ".sisiliano-linear-slider-value-pointer-hover"
        },
        listeners: {
            onValueChange: {
                func: "sisiliano.linearSlider.onValueChange",
                args: ["{arguments}.0", "{arguments}.1"]
            },
            onColorChange: {
                func: "sisiliano.linearSlider.onColorChange",
                args: ["{that}", "{that}.model.color"]
            },
            onStatusChange: {
                func: "sisiliano.linearSlider.onStatusChange",
                args: ["{that}", "{that}.model.status.isActive"]
            },
            onReady: [
                {
                    func: "sisiliano.linearSlider.drawNotches",
                    args: ["{that}"]
                },
                {
                    func: "sisiliano.linearSlider.addListeners",
                    args: ["{that}"]
                }
            ]
        },
        modelListeners: {
        }
    });

    sisiliano.linearSlider.onStatusChange = function (that, isActive) {
        that.locate("valuePointerHover").css("display", isActive ? "block" : "none");
    };

    sisiliano.linearSlider.onValueChange = function (that, obviousValue) {
        var valueRange = that.model.size;
        var maxWidth = parseInt(that.locate("backgroundRect").attr("width"), null);
        var leftPadding = parseInt(that.locate("backgroundRect").attr("x"), null);
        var newWidth = maxWidth * (obviousValue / valueRange);
        that.locate("valueRect").attr("width", newWidth);
        that.locate("valueLabelRect").attr("x", newWidth + leftPadding - 25);
        that.locate("valuePointer").attr("x", newWidth + leftPadding - 8);
        that.locate("valuePointerHover").attr("x", newWidth + leftPadding - 13);
        that.locate("valueLabel").attr("x", newWidth + 3 + leftPadding - 25);
    };

    sisiliano.linearSlider.onColorChange = function (that, newColor) {
        that.locate("rects").attr("fill", newColor[0]);
        that.locate("valueLabelRect").attr("fill", newColor[0]);
        that.locate("valueLabel").attr("fill", newColor[1]);
        that.locate("valuePointer").attr("fill", newColor[0]);
        that.locate("valuePointerHover").attr("fill", newColor[0]);
    };

    sisiliano.linearSlider.setValueByPosition = function (that, clickedPosition) {
        var maxWidth = parseInt(that.locate("backgroundRect").attr("width"), null);
        var leftPadding = parseInt(that.locate("valueRect").attr("x"), null);
        var value = ((clickedPosition.x - leftPadding) / maxWidth) * that.model.size;
        if (that.model.value !== value) {
            that.applier.change("value", value + that.model.min);
        }
    };

    sisiliano.linearSlider.addListeners = function (that) {
        var mouseMoveHandler = function () {
            var position = d3.mouse(that.container.find("svg").eq(0).get(0));
            var clickedPosition = {x: position[0], y: position[1]};
            if (that.model.status.isActive) {
                sisiliano.linearSlider.setValueByPosition(that, clickedPosition);
                d3.event.preventDefault();
            }
        };

        document.addEventListener("mousemove", function (evt) {
            var svgElm = that.locate("svg");
            if (svgElm && svgElm.length > 0) {
                var svgPosition = svgElm.position();
                var svgWidth = svgElm.width();
                var clickedPosition = {x: ((evt.pageX - svgPosition.left) / svgWidth) * that.model.viewBox.width, y: 0};
                if (that.model.status.isActive) {
                    sisiliano.linearSlider.setValueByPosition(that, clickedPosition);
                    evt.preventDefault(evt);
                }
            }
        });
        document.addEventListener("mouseup", function () {
            that.applier.change("status.isActive", false);
        });

        d3.select(that.container.get(0))
            .on("touchmove", mouseMoveHandler)
            .on("mousemove", mouseMoveHandler);
    };

    sisiliano.linearSlider.getNotches = function (that) {
        //TODO define
        return that.model.rulerPoints;
    };

    sisiliano.linearSlider.drawNotches = function (that) {
        var notches = sisiliano.linearSlider.getNotches(that);
        var notchesPane = d3.select(that.locate("ruler").get(0));
        var sliderWidth = parseInt(that.locate("backgroundRect").attr("width"), null);
        var sliderX = parseInt(that.locate("backgroundRect").attr("x"), null);
        var sliderY = parseInt(that.locate("backgroundRect").attr("y"), null);
        fluid.each(notches, function (notchValue) {
            var x = sliderX + (sliderWidth * (notchValue - that.model.min) / (that.model.max - that.model.min));
            var rulerLine = notchesPane.append("rect")
                .attr("class", "sisiliano-linear-slider-ruler-line")
                .attr("fill", that.model.color[0])
                .attr("fill-opacity", 0.3)
                .attr("x", x - 0.5)
                .attr("y", sliderY);
            sisiliano.util.applyStyles(rulerLine, that.model.styles.ruler.line);

            var rulerValue = notchesPane.append("text")
                .attr("fill", that.model.color[0])
                .attr("fill-opacity", 0.6)
                .attr("class", "sisiliano-linear-slider-ruler-value")
                .attr("text-anchor", "end")
                .attr("x", x + 0.5)
                .attr("y", sliderY + 25)
                .attr("transform","rotate(-90, " + (x + 1) + ", " + (sliderY + 25) + ")")
                .text(notchValue);
            sisiliano.util.applyStyles(rulerValue, that.model.styles.ruler.value);
        });
    };
})(fluid);<|endoftext|>


<commit_before>import {TableCommandManager} from './table.command.manager';

describe('CompositeCommandManager', () => {
	let arrayOfCommands = [],
		i = 5,
		arr = [],
		tableWithFocus = {view: {isFocused: () => true}},
		tableWithoutFocus = {view: {isFocused: () => false}},
		apply = f => f();

	while (i > 0) {
		if (i % 2 === 0) {
			arrayOfCommands.push({
				execute: () => arr.push(1),
				canExecute: () => true
			});
		} else {
			arrayOfCommands.push({
				execute: () => arr.push(2),
				canExecute: () => false
			});
		}
		i--;
	}

	describe('filter', () => {
		it('should return filtered commands if ViewActive = true', () => {
			let tableCommandManager = new TableCommandManager(apply, tableWithFocus);
			let filtered = tableCommandManager.filter(arrayOfCommands);
			expect(filtered.length).to.equal(2);
		});

		it('should return empty array if ViewActive = false', () => {
			let tableCommandManager = new TableCommandManager(apply, tableWithoutFocus);
			let filtered = tableCommandManager.filter(arrayOfCommands);
			expect(filtered.length).to.equal(0);
		});
	});

});

<commit_msg>Update table.command.manager.spec.js<commit_after>import {TableCommandManager} from './table.command.manager';

describe('CompositeCommandManager', () => {
	let commands = [],
		i = 5,
		arr = [],
		tableWithFocus = {view: {isFocused: () => true}},
		tableWithoutFocus = {view: {isFocused: () => false}},
		apply = f => f();

	while (i > 0) {
		if (i % 2 === 0) {
			commands.push({
				execute: () => arr.push(1),
				canExecute: () => true
			});
		} else {
			commands.push({
				execute: () => arr.push(2),
				canExecute: () => false
			});
		}
		i--;
	}

	describe('filter', () => {
		it('should return filtered commands if ViewActive = true', () => {
			let tableCommandManager = new TableCommandManager(apply, tableWithFocus);
			let filtered = tableCommandManager.filter(commands);
			expect(filtered.length).to.equal(2);
		});

		it('should return empty array if ViewActive = false', () => {
			let tableCommandManager = new TableCommandManager(apply, tableWithoutFocus);
			let filtered = tableCommandManager.filter(commands);
			expect(filtered.length).to.equal(0);
		});
	});

});

<|endoftext|>


<commit_before>var deviceDetector = (function (ua)
{
    var detect = (function()
    {
        if(/(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|skyfire|s40Ovibrowser|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard )/.test(ua))
            return 'phone';
        else
            if(/(ipad|tablet|Android|(windows(.)*touch)|kindle|playbook)/.test(ua))
                return 'tablet';
            else return 'desktop';
    });
    return{
        device:detect(),
        detect:detect,
        isMobile:((detect()!='desktop')?true:false),
        userAgent:ua
    };
}(navigator.userAgent.toLowerCase()));
<commit_msg>small bugfix<commit_after>var deviceDetector = (function (ua)
{
    var detect = (function()
    {
		if(/(ipad|tablet|android|(windows(.)*touch)|kindle|playbook)/.test(ua))
            return 'tablet';
        else
			if(/(mobi|ipod|phone|blackberry|opera mini|fennec|minimo|symbian|psp|skyfire|s40Ovibrowser|blazer|bolt|gobrowser|iris|maemo|semc|teashark|uzard )/.test(ua))
                return 'phone';
            else return 'desktop';
    });
    return{
        device:detect(),
        detect:detect,
        isMobile:((detect()!='desktop')?true:false),
        userAgent:ua
    };
}(navigator.userAgent.toLowerCase()));
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import Toggle from 'react-toggle';
import 'react-toggle/style.css';

import Wrapper from 'common/Wrapper';

import CORE_CHANGELOG from '../CHANGELOG';

let _stringChangelogDeprecatedWarningSent = false;

class Changelog extends React.PureComponent {
  static propTypes = {
    changelog: PropTypes.oneOfType([PropTypes.array, PropTypes.string]).isRequired,
    limit: PropTypes.number,
    includeCore: PropTypes.bool,
  };
  static defaultProps = {
    includeCore: true,
  };
  constructor() {
    super();
    this.state = {
      includeCore: true,
    };
  }

  render() {
    const { changelog, limit, includeCore } = this.props;

    if (typeof changelog === 'string') {
      if (!_stringChangelogDeprecatedWarningSent) {
        console.error('String based changelogs are deprecated. Convert the changelog to an array basis for more features, see the Holy Paladin changelog for an example.');
        _stringChangelogDeprecatedWarningSent = true;
      }
      return (

            {changelog
              .trim()
              .split('\n')
              .filter((_, i) => !limit || i < limit)
              .map((change, i) => (

              ))}

      );
    }
    if (changelog instanceof Array) {
      const mergedChangelog = includeCore && this.state.includeCore ? [ ...CORE_CHANGELOG, ...changelog ].sort((a, b) => b.date - a.date) : changelog;
      return (

          {includeCore && (

              <Toggle
                defaultChecked={this.state.includeCore}
                icons={false}
                onChange={event => this.setState({ includeCore: event.target.checked })}
                id="core-entries-toggle"
              />

                <dfn data-tip="Turn this off to only see changes to this spec's implementation.">Shared changes</dfn>

          )}

            {mergedChangelog
              .filter((_, i) => !limit || i < limit)
              .map((entry, i) => {
                const { date, changes, contributors } = entry;
                const isFromCoreChangelog = CORE_CHANGELOG.includes(entry);
                // The index of the entry provides us with a unique never changing key, which speeds up the Shared Changes toggle
                const index = isFromCoreChangelog ? CORE_CHANGELOG.indexOf(entry) : changelog.indexOf(entry);

                return (
                  <li
                    key={isFromCoreChangelog ? `core-${index}` : `spec-${index}`}
                    className={`flex wrapable ${includeCore && isFromCoreChangelog ? 'text-muted' : ''}`}
                  >

                      {date.toLocaleDateString(undefined, {timeZone: 'UTC'})}

                      {changes}

                      {contributors.map(contributor => {
                        if (typeof contributor === 'string') {
                          return contributor;
                        }
                        if (contributor instanceof Array) {
                          return (
                            <Wrapper key={contributor[0]}>
                               {contributor[0]}
                            </Wrapper>
                          );
                        }
                        if (typeof contributor === 'object') {
                          return (
                            <Wrapper key={contributor.nickname}>
                              {contributor.avatar && }{' '}
                              {contributor.nickname}
                            </Wrapper>
                          );
                        }
                        return null;
                      })}

                );
              })}

      );
    }
    return null;
  }
}

export default Changelog;
<commit_msg>Revert "display date in UTC": this breaks Googlebot indexing (currently more important)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import Toggle from 'react-toggle';
import 'react-toggle/style.css';

import Wrapper from 'common/Wrapper';

import CORE_CHANGELOG from '../CHANGELOG';

let _stringChangelogDeprecatedWarningSent = false;

class Changelog extends React.PureComponent {
  static propTypes = {
    changelog: PropTypes.oneOfType([PropTypes.array, PropTypes.string]).isRequired,
    limit: PropTypes.number,
    includeCore: PropTypes.bool,
  };
  static defaultProps = {
    includeCore: true,
  };
  constructor() {
    super();
    this.state = {
      includeCore: true,
    };
  }

  render() {
    const { changelog, limit, includeCore } = this.props;

    if (typeof changelog === 'string') {
      if (!_stringChangelogDeprecatedWarningSent) {
        console.error('String based changelogs are deprecated. Convert the changelog to an array basis for more features, see the Holy Paladin changelog for an example.');
        _stringChangelogDeprecatedWarningSent = true;
      }
      return (

            {changelog
              .trim()
              .split('\n')
              .filter((_, i) => !limit || i < limit)
              .map((change, i) => (

              ))}

      );
    }
    if (changelog instanceof Array) {
      const mergedChangelog = includeCore && this.state.includeCore ? [ ...CORE_CHANGELOG, ...changelog ].sort((a, b) => b.date - a.date) : changelog;
      return (

          {includeCore && (

              <Toggle
                defaultChecked={this.state.includeCore}
                icons={false}
                onChange={event => this.setState({ includeCore: event.target.checked })}
                id="core-entries-toggle"
              />

                <dfn data-tip="Turn this off to only see changes to this spec's implementation.">Shared changes</dfn>

          )}

            {mergedChangelog
              .filter((_, i) => !limit || i < limit)
              .map((entry, i) => {
                const { date, changes, contributors } = entry;
                const isFromCoreChangelog = CORE_CHANGELOG.includes(entry);
                // The index of the entry provides us with a unique never changing key, which speeds up the Shared Changes toggle
                const index = isFromCoreChangelog ? CORE_CHANGELOG.indexOf(entry) : changelog.indexOf(entry);

                return (
                  <li
                    key={isFromCoreChangelog ? `core-${index}` : `spec-${index}`}
                    className={`flex wrapable ${includeCore && isFromCoreChangelog ? 'text-muted' : ''}`}
                  >

                      {date.toLocaleDateString()}

                      {changes}

                      {contributors.map(contributor => {
                        if (typeof contributor === 'string') {
                          return contributor;
                        }
                        if (contributor instanceof Array) {
                          return (
                            <Wrapper key={contributor[0]}>
                               {contributor[0]}
                            </Wrapper>
                          );
                        }
                        if (typeof contributor === 'object') {
                          return (
                            <Wrapper key={contributor.nickname}>
                              {contributor.avatar && }{' '}
                              {contributor.nickname}
                            </Wrapper>
                          );
                        }
                        return null;
                      })}

                );
              })}

      );
    }
    return null;
  }
}

export default Changelog;
<|endoftext|>


<commit_before>// This is the JavaScript interface between CodeBox.elm and ace.js.

// Relevant Ace API functions:
//  new EditSession(String text, TextMode mode)
//  new Document(String)
//  Document.setValue(String text)
//    - probably what will be needed to assert contents of the box
//  Document.on("change", function(Object e))
//    - probably what will be needed to send CodeEvents
//  new Editor(VirtualRenderer renderer, EditSession session)
//    - The main entry point into Ace
//
// Relevant Ports:
//   aceInTheHole -- Is where new Models to be rendered come from
//   theTurn      -- Is where what become Events for things like code updates go
//   theRiver     -- Is where new totally rendered Html for the code box goes
//
// This means that things like typing in a new character into the code box will
// send an event to update the model, which will send an update to the
// aceInTheHole, which will (probably, to start with) rerender the code box,
// which will be sent to theRiver, which will display the newly rendered box
// (for a second time?). We could maybe filter out updates to aceInTheHole if
// the last event was a codeUpdate and Ace already has rendered it.
//
// Big things to do: Figure out how to get a given instance of Ace working with
// the correct dimensions. Figure out how Html looks when sent over a port.
// Figure out how to turn that Html into a Graphics.Element.Element. Wire up the
// Model signal and grab the code contents to render.
//
// With that, we should be able to at least load the page.
//
// Then: Figure out the appropriate signal handlers to send what will be events over
// theTurn. Interpret the events appropriately so that sigModel is updated.
// Update the model to remember the last event, and don't send a new model down
// the aceInTheHole unless the rerender actually has to happen (it might!). Be
// careful with things like cursor information, which might need to preserved
// (the field in the model other than code might be a big JSON block of state
// information for Ace that can just be poked at where need be).
//
// Then, we should have a usable replacement for what we currently have. So,
// more or less where we started.
//
// Then: Figure out how to add syntax highlighting (likely just a static file
// pointed to when the Editor is set up). Figure out how to set other options,
// like Vim keybindings and color themes. Add these option choices to the model.
// Add an options dialog to choose between these. Figure out how to highlight
// certain tokens. Add information about what should currently be highlighted
// and how to the model. Implement said highlighting.
//
// With this, we'll have all the really nice things from Ace that we wanted with
// the potential to extend it even further.

// For convenience during development, the page controller is here
var runtime = Elm.fullscreen(Elm.Main, {
    theTurn : { evt : "init"
              , strArg : ""
              , cursorArg : { row : 0 , column : 0 }
              , selectionArg : []
              }
});

window.oncrash = function(msg, url, linenumber) {
  var s = '';
  s += 'We crashed... Sorry! Hit OK to restart.\n\n';
  s += 'Error message: ' + msg + '\n\n';
  s += 'The JS error console may contain more info.';
  alert(s);
  location.reload();
}

var Range = ace.require('ace/range').Range

//So we can dynamically add CSS classes, which is needed to style the markers
//Generally only touches the DOM to add new ones if absolutely needed
var style = document.createElement('style');
style.id = "autogenerated_css_classes";
style.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(style);
var classDict = {};

function makeCSSClass(colorStr) {
    var autogenName = ".autogenerated_class_" + colorStr
    if (!(classDict.hasOwnProperty(autogenName))) {
        style.innerHTML = style.innerHTML
                            + ".ace_marker-layer "
                            + autogenName
                            + " { background-color : " + colorStr+ "; "
                            + "position : absolute; "
                            + "z-index : 2; "
                            + "} ";
        document.getElementById("autogenerated_css_classes").innerHTML = style.innerHTML;
        classDict[autogenName] = true;
    }
    return "autogenerated_class_" + colorStr;
}

var editor = ace.edit("editor");
editor.$blockScrolling = Infinity;
editor.setTheme("ace/theme/chrome");
editor.setFontSize(14);
editor.getSession().setMode("ace/mode/little");
editor.getSession().getDocument().on("change", maybeSendUpdate);
editor.getSession().selection.on("changeCursor", maybeSendUpdate);
editor.getSession().selection.on("changeSelection", maybeSendUpdate);

//Set the default scratch text to display before the first load
var defaultScratch =
"\n" +
"; Write a little program below.\n" +
"; Or choose an example from the list.\n" +
";\n" +
"; Changes to this *Scratch example will be saved and\n" +
"; restored when navigating to and from other examples.\n" +
"; For the remaining named examples, changes will be\n" +
"; discarded when choosing a different example.\n" +
"\n" +
"(svg [(rect 'maroon' 100 15 200 15)])\n" +
"\n";
editor.setValue(defaultScratch);
editor.moveCursorTo(0,0);

//Check to see if there are changes to the Editor element that we didn't cause
//See: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
/*
var observer = new MutationObserver(function(mutations) {
    console.log(mutations);
    mutations.forEach(function(mutation) {
        console.log(mutation.type);
    });
});
var mutationConfig = { childList : true, characterData : true, attributes : true, subtree : true };
observer.observe(
    document.getElementById("editor"),
    mutationConfig
);
*/

var updateWasFromElm = false;
var markers = [];

runtime.ports.aceInTheHole.subscribe(function(codeBoxInfo) {
    //Set our flag so that our event handlers don't update on any updates from
    // Elm
    updateWasFromElm = true;
    //Resize the editor window if the div size has changed
    editor.resize();
    //Set the value of the code editor with what we got from Elm
    editor.getSession().setValue(codeBoxInfo.code, 0);
    //Set the cursor position to what we got from Elm
    editor.moveCursorTo(codeBoxInfo.cursorPos.row, codeBoxInfo.cursorPos.column);
    //Set the selections appropriately
    //Note that we need to turn the Elm Ranges into Ace Range Objects
    editor.selection.clearSelection();

    //We need to treat the first one differently, so we must iterate
    //This is undocumented outside of the tests, see:
    // https://github.com/ajaxorg/ace/blob/0a3b002e285a41a4884ce42f94d2f68673e43b30/lib/ace/multi_select_test.js#L168
    //For the special namespacing, see:
    // http://stackoverflow.com/questions/10452869/when-i-try-to-create-a-range-object-in-ace-js-an-illegal-constructor-error-is
    var numRanges = codeBoxInfo.selections.length;
    for (var i = 0; i < numRanges; i++) {
        elmRange = codeBoxInfo.selections[i];
        var aceRange = new Range(elmRange.start.row,
                                 elmRange.start.column,
                                 elmRange.end.row,
                                 elmRange.end.column);
        if (i === 0) {
            //We only allow backwards selections if there is just one selection
            //We deduce if it's backwards by comparing the cursor position to
            // the end of the selection (which always orients itself such that
            // it's > the start in the process of constucting the Range)
            //Note that we use compareEnd here because the compare functions are
            // a little wonky - compareStart returns -1, for instance, if the
            // cursor is at the start.
            if (numRanges === 1) {
                if (aceRange.compareEnd(codeBoxInfo.cursorPos.row,
                                        codeBoxInfo.cursorPos.column) === 1) {
                    var backwards = false;
                } else {
                    var backwards = true;
                }
                editor.selection.setSelectionRange(aceRange, backwards);
            } else {
                editor.selection.fromOrientedRange(aceRange);
            }
        } else {
            editor.selection.addRange(aceRange, false);
        }
    }

    //Add the special syntax highlighting for changes and such
    //These are 'markers', see:
    //http://ace.c9.io/#nav=api&api=edit_session
    //ctl+f addMarker
    for (mi in markers) {
        editor.getSession().removeMarker(markers[mi]);
    }
    markers = [];
    for (hi in codeBoxInfo.highlights) {
        hiRange = codeBoxInfo.highlights[hi].range;
        var aceRange = new Range(hiRange.start.row - 1, //Indexing from 1 in Elm?
                                 hiRange.start.column - 1,
                                 hiRange.end.row - 1,
                                 hiRange.end.column - 1);
        var hiClass = makeCSSClass(codeBoxInfo.highlights[hi].color);
        var mid = editor.getSession().addMarker(aceRange,hiClass,"text", false);
        editor.resize();
        markers.push(mid);
    }
    editor.updateSelectionMarkers();

    //If the div rerendered (kept track of with a special attribute) then we
    // should copy the editor back into it
    var editorDiv = document.getElementById("editor");
    if (editorDiv.getAttribute("rendered") !== "true") {
      editorDiv.parentNode.replaceChild(editor.container, editorDiv);
      editorDiv.setAttribute("rendered","true");
    }

    //Set the contents to the correct manipulable state
    if (!codeBoxInfo.manipulable) {
        editor.container.style.zIndex = "0";
        editor.container.style.pointerEvents = "auto";
        editor.setReadOnly(true);
    } else {
        editor.container.style.zIndex = "1";
        editor.container.style.pointerEvents = "auto";
        editor.setReadOnly(false);
    }

    //If we should bounce an update back (force a rerender) then do so
    if (codeBoxInfo.bounce) {
        runtime.ports.theTurn.send(
            { evt : "Rerender"
            , strArg : ""
            , cursorArg : editor.getCursorPosition()
            , selectionArg : []
            }
        );
    }

    //Set our flag back to enable the event handlers again
    updateWasFromElm = false;
});

function maybeSendUpdate(e) {
    if (!updateWasFromElm) {
      runtime.ports.theTurn.send(
        { evt : "AceCodeUpdate"
        , strArg : editor.getSession().getDocument().getValue()
        , cursorArg : editor.getCursorPosition()
        , selectionArg : editor.selection.getAllRanges()
        }
      );
    }
}

function removeAllChildren(container) {
    var nodes = container.childNodes;
    for (var i = 0; i < nodes.length; i++) {
        nodes[i].remove();
    }
}
<commit_msg>Cleanup<commit_after>// This is the JavaScript interface between CodeBox.elm and ace.js.
//
// Relevant Ports:
//   aceInTheHole -- Is where new (partial) Models to be rendered come from
//   theTurn      -- Is where what become Events for things like code updates go
//

// For convenience, the page controller is here
var runtime = Elm.fullscreen(Elm.Main, {
    theTurn : { evt : "init"
              , strArg : ""
              , cursorArg : { row : 0 , column : 0 }
              , selectionArg : []
              }
});

window.oncrash = function(msg, url, linenumber) {
  var s = '';
  s += 'We crashed... Sorry! Hit OK to restart.\n\n';
  s += 'Error message: ' + msg + '\n\n';
  s += 'The JS error console may contain more info.';
  alert(s);
  location.reload();
}

var Range = ace.require('ace/range').Range

//So we can dynamically add CSS classes, which is needed to style the markers
//Generally only touches the DOM to add new ones if absolutely needed
var style = document.createElement('style');
style.id = "autogenerated_css_classes";
style.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(style);
var classDict = {};

function makeCSSClass(colorStr) {
    var autogenName = ".autogenerated_class_" + colorStr
    if (!(classDict.hasOwnProperty(autogenName))) {
        style.innerHTML = style.innerHTML
                            + ".ace_marker-layer "
                            + autogenName
                            + " { background-color : " + colorStr+ "; "
                            + "position : absolute; "
                            + "z-index : 2; "
                            + "} ";
        document.getElementById("autogenerated_css_classes").innerHTML = style.innerHTML;
        classDict[autogenName] = true;
    }
    return "autogenerated_class_" + colorStr;
}

var editor = ace.edit("editor");
editor.$blockScrolling = Infinity;
editor.setTheme("ace/theme/chrome");
editor.setFontSize(14);
editor.getSession().setMode("ace/mode/little");
editor.getSession().getDocument().on("change", maybeSendUpdate);
editor.getSession().selection.on("changeCursor", maybeSendUpdate);
editor.getSession().selection.on("changeSelection", maybeSendUpdate);

//Set the default scratch text to display before the first load
var defaultScratch =
"\n" +
"; Write a little program below.\n" +
"; Or choose an example from the list.\n" +
";\n" +
"; Changes to this *Scratch example will be saved and\n" +
"; restored when navigating to and from other examples.\n" +
"; For the remaining named examples, changes will be\n" +
"; discarded when choosing a different example.\n" +
"\n" +
"(svg [(rect 'maroon' 100 15 200 15)])\n" +
"\n";
editor.setValue(defaultScratch);
editor.moveCursorTo(0,0);

var updateWasFromElm = false;
var markers = [];

runtime.ports.aceInTheHole.subscribe(function(codeBoxInfo) {
    //Set our flag so that our event handlers don't update on any updates from
    // Elm
    updateWasFromElm = true;
    //Resize the editor window if the div size has changed
    editor.resize();
    //Set the value of the code editor with what we got from Elm
    editor.getSession().setValue(codeBoxInfo.code, 0);
    //Set the cursor position to what we got from Elm
    editor.moveCursorTo(codeBoxInfo.cursorPos.row, codeBoxInfo.cursorPos.column);
    //Set the selections appropriately
    //Note that we need to turn the Elm Ranges into Ace Range Objects
    editor.selection.clearSelection();

    //We need to treat the first one differently, so we must iterate
    //This is undocumented outside of the tests, see:
    // https://github.com/ajaxorg/ace/blob/0a3b002e285a41a4884ce42f94d2f68673e43b30/lib/ace/multi_select_test.js#L168
    //For the special namespacing, see:
    // http://stackoverflow.com/questions/10452869/when-i-try-to-create-a-range-object-in-ace-js-an-illegal-constructor-error-is
    var numRanges = codeBoxInfo.selections.length;
    for (var i = 0; i < numRanges; i++) {
        elmRange = codeBoxInfo.selections[i];
        var aceRange = new Range(elmRange.start.row,
                                 elmRange.start.column,
                                 elmRange.end.row,
                                 elmRange.end.column);
        if (i === 0) {
            //We only allow backwards selections if there is just one selection
            //We deduce if it's backwards by comparing the cursor position to
            // the end of the selection (which always orients itself such that
            // it's > the start in the process of constucting the Range)
            //Note that we use compareEnd here because the compare functions are
            // a little wonky - compareStart returns -1, for instance, if the
            // cursor is at the start.
            if (numRanges === 1) {
                if (aceRange.compareEnd(codeBoxInfo.cursorPos.row,
                                        codeBoxInfo.cursorPos.column) === 1) {
                    var backwards = false;
                } else {
                    var backwards = true;
                }
                editor.selection.setSelectionRange(aceRange, backwards);
            } else {
                editor.selection.fromOrientedRange(aceRange);
            }
        } else {
            editor.selection.addRange(aceRange, false);
        }
    }

    //Add the special syntax highlighting for changes and such
    //These are 'markers', see:
    //http://ace.c9.io/#nav=api&api=edit_session
    //ctl+f addMarker
    for (mi in markers) {
        editor.getSession().removeMarker(markers[mi]);
    }
    markers = [];
    for (hi in codeBoxInfo.highlights) {
        hiRange = codeBoxInfo.highlights[hi].range;
        var aceRange = new Range(hiRange.start.row - 1, //Indexing from 1 in Elm?
                                 hiRange.start.column - 1,
                                 hiRange.end.row - 1,
                                 hiRange.end.column - 1);
        var hiClass = makeCSSClass(codeBoxInfo.highlights[hi].color);
        var mid = editor.getSession().addMarker(aceRange,hiClass,"text", false);
        editor.resize();
        markers.push(mid);
    }
    editor.updateSelectionMarkers();

    //If the div rerendered (kept track of with a special attribute) then we
    // should copy the editor back into it
    var editorDiv = document.getElementById("editor");
    if (editorDiv.getAttribute("rendered") !== "true") {
      editorDiv.parentNode.replaceChild(editor.container, editorDiv);
      editorDiv.setAttribute("rendered","true");
    }

    //Set the contents to the correct manipulable state
    if (!codeBoxInfo.manipulable) {
        editor.container.style.zIndex = "0";
        editor.container.style.pointerEvents = "auto";
        editor.setReadOnly(true);
    } else {
        editor.container.style.zIndex = "1";
        editor.container.style.pointerEvents = "auto";
        editor.setReadOnly(false);
    }

    //If we should bounce an update back (force a rerender) then do so
    if (codeBoxInfo.bounce) {
        runtime.ports.theTurn.send(
            { evt : "Rerender"
            , strArg : ""
            , cursorArg : editor.getCursorPosition()
            , selectionArg : []
            }
        );
    }

    //Set our flag back to enable the event handlers again
    updateWasFromElm = false;
});

function maybeSendUpdate(e) {
    if (!updateWasFromElm) {
      runtime.ports.theTurn.send(
        { evt : "AceCodeUpdate"
        , strArg : editor.getSession().getDocument().getValue()
        , cursorArg : editor.getCursorPosition()
        , selectionArg : editor.selection.getAllRanges()
        }
      );
    }
}
<|endoftext|>