<commit_before>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import VirtualizedSelect from 'react-virtualized-select';
import createFilterOptions from 'react-select-fast-filter-options';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import FABButton from 'react-mdl/lib/FABButton';
import Icon from 'react-mdl/lib/Icon';
import Button from 'react-mdl/lib/Button';
import {
  addModule,
  removeModule,

  fetchTimetable,
  submitTimetable,

  loadTimetable,
  saveTimetable,
} from '../../actions/timetable';
import { fetchModules } from '../../actions/module';
import { timetableLessonsArray } from '../../utils/modules';
import Timetable from './Timetable';
import s from './timetable.scss';
import ModuleTable from './ModuleTable';
import ModuleSearch from '../ModuleSearch/ModuleSearch'
import SearchOverlay from '../../components/SearchOverlay/';

// Ref: https://github.com/yangshun/nusmods-v3/tree/master/src/js

class TimetableContainer extends Component {
  componentDidMount() {
    const {
      year,
      semester,
    } = this.props;
    const {
      isInitialized,
    } = this.props.timetable;

    if (!isInitialized) {
      if (this.props.loggedIn) {
        this.props.fetchTimetable({ year, semester });
      } else {
        this.props.loadTimetable({ year, semester });
      }
    }
    if (this.props.semesterModuleList && this.props.semesterModuleList.length === 0) {
      this.props.fetchModules({ year, semester });
    }
  }

  state = {
    showSearch: false,
  }

  showSearch= () => this.setState({ showSearch: true })
  hideSearch= () => this.setState({ showSearch: false })

  sync = ({ year, semester, timetable }) => () => {
    this.props.saveTimetable({ year, semester, timetable });

    if (this.props.loggedIn) {
      this.props.submitTimetable({ year, semester, timetable });
    }
  }

  addModuleAndHideSearch = (module) => {
    let { year, semester } = this.props;
    this.props.addModule({ year, semester, module });
    this.hideSearch();
  }

  render() {
    const {
      year,
      semester,
      timetable,
      timetableForYearAndSem,
      semesterTimetable,
      semesterModuleList,
    } = this.props;

    const lessons = timetableLessonsArray(semesterTimetable);

    const moduleTableModules = Object.values(
      timetableForYearAndSem.reduce(
        (p, c) => ({ ...p, [c.ModuleCode]: c }), {}));

    return (

        <Timetable lessons={lessons} timetable={timetable} />

            <Button
              raised
              ripple
              onClick={this.sync({ year, semester, timetable: timetableForYearAndSem })}
            >
              Sync
            </Button>

            <FABButton colored ripple className="fab" onClick={this.showSearch}>
                <Icon name="add" />
            </FABButton>

            <SearchOverlay
              shown={this.state.showSearch}
              hideSearch={this.hideSearch}
              semesterModuleList={semesterModuleList}
              semesterTimetable={semesterTimetable}
              addModule={module => this.addModuleAndHideSearch(module)}
            />

    );
  }
}

TimetableContainer.propTypes = {
  loggedIn: PropTypes.bool.isRequired,
  semester: PropTypes.string.isRequired,
  year: PropTypes.string.isRequired,
  timetableForYearAndSem: PropTypes.array.isRequired,
  semesterModuleList: PropTypes.array,
  semesterTimetable: PropTypes.object,
  addModule: PropTypes.func,
  removeModule: PropTypes.func,
  timetable: PropTypes.object,
  isInitialized: PropTypes.bool,
  fetchTimetable: PropTypes.func.isRequired,
  fetchModules: PropTypes.func.isRequired,
  submitTimetable: PropTypes.func.isRequired,
  saveTimetable: PropTypes.func.isRequired,
  loadTimetable: PropTypes.func.isRequired,
};

TimetableContainer.contextTypes = {
  router: PropTypes.object,
};

function mapStateToProps(state) {
  const { timetable, selection, module } = state;
  const { year, semester } = selection;
  const timetableForYearAndSem =
    (timetable.data
    && timetable.data[year]
    && timetable.data[year][semester]) || [];

  // convert to v3 compatible format first for display
  const semesterTimetable = {};

  timetableForYearAndSem.map(mod => {
    if (!semesterTimetable[mod.ModuleCode]) {
      semesterTimetable[mod.ModuleCode] = {};
    }
    if (!semesterTimetable[mod.ModuleCode][mod.LessonType]) {
      semesterTimetable[mod.ModuleCode][mod.LessonType] = [];
    }
    semesterTimetable[mod.ModuleCode][mod.LessonType].push(mod);
    return mod;
  });

  const semesterModuleList = (module.data
    && module.data[year]
    && module.data[year][semester]) || [];

  return {
    loggedIn: !!state.user.data.id,
    year,
    semester,
    semesterModuleList,
    semesterTimetable,
    timetableForYearAndSem,
    timetable,
  };
}

const mapDispatch = {
  fetchTimetable,
  fetchModules,
  addModule,
  removeModule,
  saveTimetable,
  submitTimetable,
  loadTimetable,
};

export default connect(
  mapStateToProps, mapDispatch)(withStyles(s)(TimetableContainer));
<commit_msg>Oops accidentally removed ModuleTable<commit_after>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import VirtualizedSelect from 'react-virtualized-select';
import createFilterOptions from 'react-select-fast-filter-options';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import FABButton from 'react-mdl/lib/FABButton';
import Icon from 'react-mdl/lib/Icon';
import Button from 'react-mdl/lib/Button';
import {
  addModule,
  removeModule,

  fetchTimetable,
  submitTimetable,

  loadTimetable,
  saveTimetable,
} from '../../actions/timetable';
import { fetchModules } from '../../actions/module';
import { timetableLessonsArray } from '../../utils/modules';
import Timetable from './Timetable';
import s from './timetable.scss';
import ModuleTable from './ModuleTable';
import ModuleSearch from '../ModuleSearch/ModuleSearch'
import SearchOverlay from '../../components/SearchOverlay/';

// Ref: https://github.com/yangshun/nusmods-v3/tree/master/src/js

class TimetableContainer extends Component {
  componentDidMount() {
    const {
      year,
      semester,
    } = this.props;
    const {
      isInitialized,
    } = this.props.timetable;

    if (!isInitialized) {
      if (this.props.loggedIn) {
        this.props.fetchTimetable({ year, semester });
      } else {
        this.props.loadTimetable({ year, semester });
      }
    }
    if (this.props.semesterModuleList && this.props.semesterModuleList.length === 0) {
      this.props.fetchModules({ year, semester });
    }
  }

  state = {
    showSearch: false,
  }

  showSearch= () => this.setState({ showSearch: true })
  hideSearch= () => this.setState({ showSearch: false })

  sync = ({ year, semester, timetable }) => () => {
    this.props.saveTimetable({ year, semester, timetable });

    if (this.props.loggedIn) {
      this.props.submitTimetable({ year, semester, timetable });
    }
  }

  addModuleAndHideSearch = (module) => {
    let { year, semester } = this.props;
    this.props.addModule({ year, semester, module });
    this.hideSearch();
  }

  render() {
    const {
      year,
      semester,
      timetable,
      timetableForYearAndSem,
      semesterTimetable,
      semesterModuleList,
    } = this.props;

    const lessons = timetableLessonsArray(semesterTimetable);

    const moduleTableModules = Object.values(
      timetableForYearAndSem.reduce(
        (p, c) => ({ ...p, [c.ModuleCode]: c }), {}));

    return (

        <Timetable lessons={lessons} timetable={timetable} />

            <ModuleTable
              modules={moduleTableModules}
              removeModule={(code) => this.props.removeModule({ year, semester, code })}
            />
            <Button
              raised
              ripple
              onClick={this.sync({ year, semester, timetable: timetableForYearAndSem })}
            >
              Sync
            </Button>

            <FABButton colored ripple className="fab" onClick={this.showSearch}>
                <Icon name="add" />
            </FABButton>

            <SearchOverlay
              shown={this.state.showSearch}
              hideSearch={this.hideSearch}
              semesterModuleList={semesterModuleList}
              semesterTimetable={semesterTimetable}
              addModule={module => this.addModuleAndHideSearch(module)}
            />

    );
  }
}

TimetableContainer.propTypes = {
  loggedIn: PropTypes.bool.isRequired,
  semester: PropTypes.string.isRequired,
  year: PropTypes.string.isRequired,
  timetableForYearAndSem: PropTypes.array.isRequired,
  semesterModuleList: PropTypes.array,
  semesterTimetable: PropTypes.object,
  addModule: PropTypes.func,
  removeModule: PropTypes.func,
  timetable: PropTypes.object,
  isInitialized: PropTypes.bool,
  fetchTimetable: PropTypes.func.isRequired,
  fetchModules: PropTypes.func.isRequired,
  submitTimetable: PropTypes.func.isRequired,
  saveTimetable: PropTypes.func.isRequired,
  loadTimetable: PropTypes.func.isRequired,
};

TimetableContainer.contextTypes = {
  router: PropTypes.object,
};

function mapStateToProps(state) {
  const { timetable, selection, module } = state;
  const { year, semester } = selection;
  const timetableForYearAndSem =
    (timetable.data
    && timetable.data[year]
    && timetable.data[year][semester]) || [];

  // convert to v3 compatible format first for display
  const semesterTimetable = {};

  timetableForYearAndSem.map(mod => {
    if (!semesterTimetable[mod.ModuleCode]) {
      semesterTimetable[mod.ModuleCode] = {};
    }
    if (!semesterTimetable[mod.ModuleCode][mod.LessonType]) {
      semesterTimetable[mod.ModuleCode][mod.LessonType] = [];
    }
    semesterTimetable[mod.ModuleCode][mod.LessonType].push(mod);
    return mod;
  });

  const semesterModuleList = (module.data
    && module.data[year]
    && module.data[year][semester]) || [];

  return {
    loggedIn: !!state.user.data.id,
    year,
    semester,
    semesterModuleList,
    semesterTimetable,
    timetableForYearAndSem,
    timetable,
  };
}

const mapDispatch = {
  fetchTimetable,
  fetchModules,
  addModule,
  removeModule,
  saveTimetable,
  submitTimetable,
  loadTimetable,
};

export default connect(
  mapStateToProps, mapDispatch)(withStyles(s)(TimetableContainer));
<|endoftext|>


<commit_before>var express = require('express');
var compression = require('compression');

var app = express();
app.use(compression());
app.set('view engine', 'ejs');

app.get('/', function (req, res) {
  res.sendFile(__dirname+'/index.html');
});
app.use("/",  express.static(__dirname));

var myport = process.env.PORT || 3000;
if (process.argv.length>2) myport = process.argv[2];

var server = app.listen(myport, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Arapaho server listening at http://%s:%s', host, port);
});<commit_msg>Comment out arapaho commandline port usage for now<commit_after>var express = require('express');
var compression = require('compression');

var app = express();
app.use(compression());
app.set('view engine', 'ejs');

app.get('/', function (req, res) {
  res.sendFile(__dirname+'/index.html');
});
app.use("/",  express.static(__dirname));

var myport = process.env.PORT || 3000;
//if (process.argv.length>2) myport = process.argv[2];

var server = app.listen(myport, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Arapaho server listening at http://%s:%s', host, port);
});
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import {
  Avatar,
  Badge,
  Box2,
  ClickableBox,
  Icon,
  iconCastPlatformStyles,
  Text,
  ConnectedUsernames,
} from '../../../../common-adapters'
import {collapseStyles, globalStyles, globalColors, globalMargins, styleSheetCreate} from '../../../../styles'
import type {Props} from './index.types'

// width of containers for back button and info button.
// must be increased if something else will go in those,
// remember to check that nothing overflows on android!
const marginWidth = 60
const shhIconColor = globalColors.black_20
const shhIconFontSize = 20

const Wrapper = (props: {
  badgeNumber: number,
  children: React.Node,
  onBack: () => void,
  onToggleInfoPanel: () => void,
}) => (
  <Box2 direction="horizontal" style={styles.container}>
    <ClickableBox onClick={props.onBack} style={styles.leftMargin}>
      <Icon type="iconfont-arrow-left" fontSize={24} color={globalColors.blue} />
      {!!props.badgeNumber && <Badge badgeNumber={props.badgeNumber} badgeStyle={styles.badge} />}
    </ClickableBox>
    <Box2
      direction="vertical"
      style={collapseStyles([styles.contentContainer, !!props.badgeNumber && styles.extraCenterPadding])}
    >
      {props.children}
    </Box2>
    <ClickableBox onClick={props.onToggleInfoPanel} style={styles.rightMargin}>
      <Icon type="iconfont-info" fontSize={24} />
    </ClickableBox>
  </Box2>
)

const ShhIcon = () => (
  <Icon
    type="iconfont-shh"
    style={iconCastPlatformStyles(styles.shhIcon)}
    color={shhIconColor}
    fontSize={shhIconFontSize}
  />
)

const ChannelHeader = (props: Props) => (
  <Wrapper {...props}>
    <Box2 direction="horizontal" style={styles.channelHeaderContainer}>
      <Avatar teamname={props.teamName} size={16} />
      <Text
        type={props.smallTeam ? 'BodyBig' : 'BodySmallSemibold'}
        lineClamp={1}
        ellipsizeMode="middle"
        style={{color: props.smallTeam ? globalColors.black_75 : globalColors.black_40}}
      >
         {props.teamName}
      </Text>
      {props.smallTeam && props.muted && <ShhIcon />}
    </Box2>
    {!props.smallTeam && (
      <Box2 direction="horizontal" style={styles.channelHeaderContainer}>
        <Text type="BodyBig" style={styles.channelName}>
          #{props.channelName}
        </Text>
        {props.muted && <ShhIcon />}
      </Box2>
    )}
  </Wrapper>
)

const UsernameHeader = (props: Props) => (
  <Wrapper {...props}>
    <Box2 direction="horizontal" style={styles.usernameHeaderContainer}>
      <ConnectedUsernames
        colorFollowing={true}
        inline={false}
        commaColor={globalColors.black_40}
        type="BodyBig"
        usernames={props.participants}
        containerStyle={styles.center}
        onUsernameClicked={props.onShowProfile}
        skipSelf={props.participants.length > 1}
      />
      {props.muted && <ShhIcon />}
    </Box2>
  </Wrapper>
)

const marginStyle = {
  ...globalStyles.flexBoxRow,
  alignItems: 'center',
  width: marginWidth,
}

const styles = styleSheetCreate({
  badge: {marginLeft: -5, marginTop: -3},
  center: {
    justifyContent: 'center',
    textAlign: 'center',
  },
  channelHeaderContainer: {alignItems: 'center', alignSelf: 'center'},
  channelName: {color: globalColors.black_75},
  container: {
    alignItems: 'stretch',
    backgroundColor: globalColors.fastBlank,
    borderBottomColor: globalColors.black_05,
    borderBottomWidth: 1,
    minHeight: 44,
  },
  contentContainer: {
    alignItems: 'center',
    flex: 1,
    justifyContent: 'center',
    paddingBottom: globalMargins.tiny,
    paddingTop: globalMargins.tiny,
  },
  extraCenterPadding: {paddingLeft: globalMargins.tiny, paddingRight: globalMargins.tiny},
  leftMargin: {
    ...marginStyle,
    justifyContent: 'flex-start',
    paddingLeft: globalMargins.small,
  },
  rightMargin: {
    ...marginStyle,
    justifyContent: 'flex-end',
    paddingRight: globalMargins.small,
  },
  shhIcon: {marginLeft: globalMargins.xtiny},
  usernameHeaderContainer: {alignItems: 'center', justifyContent: 'center'},
})

export {ChannelHeader, UsernameHeader}
<commit_msg>Back button in convo header now grey (#12836)<commit_after>// @flow
import * as React from 'react'
import {
  Avatar,
  Badge,
  Box2,
  ClickableBox,
  Icon,
  iconCastPlatformStyles,
  Text,
  ConnectedUsernames,
} from '../../../../common-adapters'
import {collapseStyles, globalStyles, globalColors, globalMargins, styleSheetCreate} from '../../../../styles'
import type {Props} from './index.types'

// width of containers for back button and info button.
// must be increased if something else will go in those,
// remember to check that nothing overflows on android!
const marginWidth = 60
const shhIconColor = globalColors.black_20
const shhIconFontSize = 20

const Wrapper = (props: {
  badgeNumber: number,
  children: React.Node,
  onBack: () => void,
  onToggleInfoPanel: () => void,
}) => (
  <Box2 direction="horizontal" style={styles.container}>
    <ClickableBox onClick={props.onBack} style={styles.leftMargin}>
      <Icon type="iconfont-arrow-left" fontSize={24} color={globalColors.black_40} />
      {!!props.badgeNumber && <Badge badgeNumber={props.badgeNumber} badgeStyle={styles.badge} />}
    </ClickableBox>
    <Box2
      direction="vertical"
      style={collapseStyles([styles.contentContainer, !!props.badgeNumber && styles.extraCenterPadding])}
    >
      {props.children}
    </Box2>
    <ClickableBox onClick={props.onToggleInfoPanel} style={styles.rightMargin}>
      <Icon type="iconfont-info" fontSize={24} />
    </ClickableBox>
  </Box2>
)

const ShhIcon = () => (
  <Icon
    type="iconfont-shh"
    style={iconCastPlatformStyles(styles.shhIcon)}
    color={shhIconColor}
    fontSize={shhIconFontSize}
  />
)

const ChannelHeader = (props: Props) => (
  <Wrapper {...props}>
    <Box2 direction="horizontal" style={styles.channelHeaderContainer}>
      <Avatar teamname={props.teamName} size={16} />
      <Text
        type={props.smallTeam ? 'BodyBig' : 'BodySmallSemibold'}
        lineClamp={1}
        ellipsizeMode="middle"
        style={{color: props.smallTeam ? globalColors.black_75 : globalColors.black_40}}
      >
         {props.teamName}
      </Text>
      {props.smallTeam && props.muted && <ShhIcon />}
    </Box2>
    {!props.smallTeam && (
      <Box2 direction="horizontal" style={styles.channelHeaderContainer}>
        <Text type="BodyBig" style={styles.channelName}>
          #{props.channelName}
        </Text>
        {props.muted && <ShhIcon />}
      </Box2>
    )}
  </Wrapper>
)

const UsernameHeader = (props: Props) => (
  <Wrapper {...props}>
    <Box2 direction="horizontal" style={styles.usernameHeaderContainer}>
      <ConnectedUsernames
        colorFollowing={true}
        inline={false}
        commaColor={globalColors.black_40}
        type="BodyBig"
        usernames={props.participants}
        containerStyle={styles.center}
        onUsernameClicked={props.onShowProfile}
        skipSelf={props.participants.length > 1}
      />
      {props.muted && <ShhIcon />}
    </Box2>
  </Wrapper>
)

const marginStyle = {
  ...globalStyles.flexBoxRow,
  alignItems: 'center',
  width: marginWidth,
}

const styles = styleSheetCreate({
  badge: {marginLeft: -5, marginTop: -3},
  center: {
    justifyContent: 'center',
    textAlign: 'center',
  },
  channelHeaderContainer: {alignItems: 'center', alignSelf: 'center'},
  channelName: {color: globalColors.black_75},
  container: {
    alignItems: 'stretch',
    backgroundColor: globalColors.fastBlank,
    borderBottomColor: globalColors.black_05,
    borderBottomWidth: 1,
    minHeight: 44,
  },
  contentContainer: {
    alignItems: 'center',
    flex: 1,
    justifyContent: 'center',
    paddingBottom: globalMargins.tiny,
    paddingTop: globalMargins.tiny,
  },
  extraCenterPadding: {paddingLeft: globalMargins.tiny, paddingRight: globalMargins.tiny},
  leftMargin: {
    ...marginStyle,
    justifyContent: 'flex-start',
    paddingLeft: globalMargins.small,
  },
  rightMargin: {
    ...marginStyle,
    justifyContent: 'flex-end',
    paddingRight: globalMargins.small,
  },
  shhIcon: {marginLeft: globalMargins.xtiny},
  usernameHeaderContainer: {alignItems: 'center', justifyContent: 'center'},
})

export {ChannelHeader, UsernameHeader}
<|endoftext|>


<commit_before>jQuery(function($) {
	var pluploadNb = 0,
		searchFuncOrRegexp = function(data) {
			if (typeof data == 'string') {
				if (data.indexOf('function(') === 0) {
					eval('window.tinyval = '+data+';');
					data = window.tinyval;
					delete(window.tinyval);
				} else if (data.indexOf('reg/') === 0 && data.lastIndexOf('/') === data.length - 1) {
					data = new RegExp(data.substring(4, data.length - 1));
				}
			} else if (typeof data == 'object') {
				var tmp = Object.prototype.toString.call(data) == '[object Array]' ? [] : {};
				$.each(data, function(k, v) {
					tmp[k] = searchFuncOrRegexp(v);
				});
				data = tmp;
			}
			return data;
		},
		pluploadKey = 'plupload_';

	$.fn.extend({
		nyroPlupload: function(opts) {
			return this.each(function() {
				var me = $(this).hide(),
					myPluploadNb = pluploadNb++,
					curOpts = opts,
					cont = $('
').insertAfter(me),
					curFiles = {},
					browse = $(''+curOpts.texts.browse+'').appendTo(cont);

				if (opts.showCancelAll) {
					var cancelAll = $(''+curOpts.texts.cancelAll+'').appendTo(cont).hide();
                }

				var list = $('
').appendTo(cont);

				if (!curOpts.file_data_name) {
					curOpts.file_data_name = me.attr('name');
                }
				curOpts.container = 'pluploadCont'+myPluploadNb;
				curOpts.drop_element = 'pluploadCont'+myPluploadNb;
				curOpts.browse_button = 'pluploadBrowse'+myPluploadNb;
				if (!curOpts.url) {
					curOpts.url = me.closest('form').attr('action');
                }
				if (!curOpts.url) {
					curOpts.url = document.location.href;
                }
				curOpts.headers = {
					'X-Requested-With': 'XMLHttpRequest'
				};

				var uploader = new plupload.Uploader(curOpts);
				uploader.bind('FilesAdded', function(up, files) {
					for (var i in files) {
						var curFile = files[i],
							name = curFile.name;
						if (name.length > 30) {
							name = name.substr(0, 30) + '...';
                        }
						curFiles[curFile.id] = $('
'+name+' (' + plupload.formatSize(curFile.size) + ') - '+curOpts.texts.waiting+'

Cancel
');
						curFiles[curFile.id].find('.pluploadCancel').on('click', function(e) {
							e.preventDefault();
							uploader.removeFile(uploader.getFile($(this).attr('rel')));
						});
						list.append(curFiles[curFile.id]);
					}
					if (opts.showCancelAll) {
						cancelAll.show();
                    }
					setTimeout(function() {uploader.start();}, 1);
				});
				if (opts.showCancelAll) {
					cancelAll.on('click', function(e) {
						e.preventDefault();
						cont.find('.pluploadCancel').trigger('click');
					});
				}
				if (opts.addFormVars) {
					var form = me.closest('form');
					uploader.bind('BeforeUpload', function(up, file) {
						var tmp = {};
						$.each(form.serializeArray(), function(index, val) {
							tmp[val.name] = val.value;
						});
						up.settings.multipart_params = tmp;
					});
				}
				uploader.bind('UploadProgress', function(up, file) {
					if (curFiles[file.id] && !isNaN(file.percent)) {
						curFiles[file.id]
							.children('strong').text(file.percent+' %').end()
							.find('.pluploadProgressBar').css('width', file.percent+'%');
					}
				});
				uploader.bind('FileUploaded', function(up, file) {
					if (curFiles[file.id]) {
						curFiles[file.id].children('strong').text(curOpts.texts.complete);
						curFiles[file.id].delay(curOpts.hideDelay).fadeOut(function() {
							curFiles[file.id].remove();
							curFiles[file.id] = undefined;
							delete(curFiles[file.id]);
						});
					}
				});
				uploader.bind('FilesRemoved', function(up, files) {
					for (var i in files) {
						var file = files[i];
						if (curFiles[file.id]) {
							curFiles[file.id].children('strong').text(curOpts.texts.cancel);
							curFiles[file.id].delay(curOpts.hideDelay * 3).fadeOut(function() {
								curFiles[file.id].remove();
								curFiles[file.id] = undefined;
								delete(curFiles[file.id]);
							});
						}
					}
				});
				uploader.bind('Error', function(up, obj) {
					if (obj.file && curFiles[obj.file.id]) {
						curFiles[obj.file.id]
							.addClass('pluploadError')
							.children('strong').html(curOpts.texts.error+'
'+obj.message+(obj.status ? ' ('+obj.status+')' : ''));
					}
				});
				if (opts.showCancelAll) {
					uploader.bind('UploadComplete', function() {cancelAll.hide();});
                }
				if (curOpts.onAllComplete && $.isFunction(curOpts.onAllComplete)) {
					uploader.bind('UploadComplete', function() {setTimeout(curOpts.onAllComplete, 20);});
				}

				if (curOpts.events) {
					$.each(curOpts.events, function(k, v) {
						uploader.bind(k, v);
					});
				}

				uploader.init();
                me.data({
                    nyroPluploader: uploader,
                    nyroPluploadCont: cont
                });
			});
		},
        nyroPluploadOpen: function(clb) {
            var me = this.first();
            if (me.data('nyroPluploader') && me.data('nyroPluploadCont')) {

                if (clb && $.isFunction(clb)) {
                    var openClbComplete = function() {
                        me.data('nyroPluploader').unbind('FileUploaded', clb);
                        me.data('nyroPluploader').unbind('UploadComplete', openClbComplete);
                        me.data('nyroPluploader').unbind('Error', openClbComplete);
                    };

                    me.data('nyroPluploader').bind('FileUploaded', clb);
                    me.data('nyroPluploader').bind('UploadComplete', openClbComplete);
                    me.data('nyroPluploader').bind('Error', openClbComplete);
                }

                me.data('nyroPluploadCont').find('input').trigger('click');
            }
            return this;
        },
		nyroPluploadDataSearch: function(defOpts, pKey) {
			if (!defOpts)
				defOpts = {};
			pKey = pKey || pluploadKey;
			var me = $(this).first(),
				opts = $.extend(true, {}, {
					showCancelAll: true,
					addFormVars: true,
					hideDelay: 750,
					filters: me.data(pKey+'filters'),
					flash_swf_url: me.data(pKey+'swf'),
					silverlight_xap_url: me.data(pKey+'xap'),
					texts: {
						browse: me.data(pKey+'browse'),
						waiting: me.data(pKey+'waiting'),
						error: me.data(pKey+'error'),
						cancel: me.data(pKey+'cancel'),
						complete: me.data(pKey+'complete'),
						cancelAll: me.data(pKey+'cancelall')
					},
					onAllComplete: function() {
						document.location.reload();
					}
				}, defOpts),
				pKeyLn = pKey.length;
			$.each(me.data(), function(i, e) {
				if (i.indexOf(pKey) == 0) {
					opts[i.substring(pKeyLn)] = searchFuncOrRegexp(e);
				}
			});
			return opts;
		}
	});

	$('form.pluploadInit').each(function() {
		var me = $(this).addClass('pluploadForm'),
			input = me.find('input[type="file"]');
		input.nyroPlupload(me.nyroPluploadDataSearch());
	});
});<commit_msg>Add listContainer option for nyroPlupload<commit_after>jQuery(function($) {
	var pluploadNb = 0,
		searchFuncOrRegexp = function(data) {
			if (typeof data == 'string') {
				if (data.indexOf('function(') === 0) {
					eval('window.tinyval = '+data+';');
					data = window.tinyval;
					delete(window.tinyval);
				} else if (data.indexOf('reg/') === 0 && data.lastIndexOf('/') === data.length - 1) {
					data = new RegExp(data.substring(4, data.length - 1));
				}
			} else if (typeof data == 'object') {
				var tmp = Object.prototype.toString.call(data) == '[object Array]' ? [] : {};
				$.each(data, function(k, v) {
					tmp[k] = searchFuncOrRegexp(v);
				});
				data = tmp;
			}
			return data;
		},
		pluploadKey = 'plupload_';

	$.fn.extend({
		nyroPlupload: function(opts) {
			return this.each(function() {
				var me = $(this).hide(),
					myPluploadNb = pluploadNb++,
					curOpts = opts,
					cont = $('
').insertAfter(me),
					curFiles = {},
					browse = $(''+curOpts.texts.browse+'').appendTo(cont);

				if (opts.showCancelAll) {
					var cancelAll = $(''+curOpts.texts.cancelAll+'').appendTo(cont).hide();
                }

				var list = $('
').appendTo(opts.listContainer ? $(opts.listContainer) : cont);

				if (!curOpts.file_data_name) {
					curOpts.file_data_name = me.attr('name');
                }
				curOpts.container = 'pluploadCont'+myPluploadNb;
				curOpts.drop_element = 'pluploadCont'+myPluploadNb;
				curOpts.browse_button = 'pluploadBrowse'+myPluploadNb;
				if (!curOpts.url) {
					curOpts.url = me.closest('form').attr('action');
                }
				if (!curOpts.url) {
					curOpts.url = document.location.href;
                }
				curOpts.headers = {
					'X-Requested-With': 'XMLHttpRequest'
				};

				var uploader = new plupload.Uploader(curOpts);
				uploader.bind('FilesAdded', function(up, files) {
					for (var i in files) {
						var curFile = files[i],
							name = curFile.name;
						if (name.length > 30) {
							name = name.substr(0, 30) + '...';
                        }
						curFiles[curFile.id] = $('
'+name+' (' + plupload.formatSize(curFile.size) + ') - '+curOpts.texts.waiting+'

Cancel
');
						curFiles[curFile.id].find('.pluploadCancel').on('click', function(e) {
							e.preventDefault();
							uploader.removeFile(uploader.getFile($(this).attr('rel')));
						});
						list.append(curFiles[curFile.id]);
					}
					if (opts.showCancelAll) {
						cancelAll.show();
                    }
					setTimeout(function() {uploader.start();}, 1);
				});
				if (opts.showCancelAll) {
					cancelAll.on('click', function(e) {
						e.preventDefault();
						cont.find('.pluploadCancel').trigger('click');
					});
				}
				if (opts.addFormVars) {
					var form = me.closest('form');
					uploader.bind('BeforeUpload', function(up, file) {
						var tmp = {};
						$.each(form.serializeArray(), function(index, val) {
							tmp[val.name] = val.value;
						});
						up.settings.multipart_params = tmp;
					});
				}
				uploader.bind('UploadProgress', function(up, file) {
					if (curFiles[file.id] && !isNaN(file.percent)) {
						curFiles[file.id]
							.children('strong').text(file.percent+' %').end()
							.find('.pluploadProgressBar').css('width', file.percent+'%');
					}
				});
				uploader.bind('FileUploaded', function(up, file) {
					if (curFiles[file.id]) {
						curFiles[file.id].children('strong').text(curOpts.texts.complete);
						curFiles[file.id].delay(curOpts.hideDelay).fadeOut(function() {
							curFiles[file.id].remove();
							curFiles[file.id] = undefined;
							delete(curFiles[file.id]);
						});
					}
				});
				uploader.bind('FilesRemoved', function(up, files) {
					for (var i in files) {
						var file = files[i];
						if (curFiles[file.id]) {
							curFiles[file.id].children('strong').text(curOpts.texts.cancel);
							curFiles[file.id].delay(curOpts.hideDelay * 3).fadeOut(function() {
								curFiles[file.id].remove();
								curFiles[file.id] = undefined;
								delete(curFiles[file.id]);
							});
						}
					}
				});
				uploader.bind('Error', function(up, obj) {
					if (obj.file && curFiles[obj.file.id]) {
						curFiles[obj.file.id]
							.addClass('pluploadError')
							.children('strong').html(curOpts.texts.error+'
'+obj.message+(obj.status ? ' ('+obj.status+')' : ''));
					}
				});
				if (opts.showCancelAll) {
					uploader.bind('UploadComplete', function() {cancelAll.hide();});
                }
				if (curOpts.onAllComplete && $.isFunction(curOpts.onAllComplete)) {
					uploader.bind('UploadComplete', function() {setTimeout(curOpts.onAllComplete, 20);});
				}

				if (curOpts.events) {
					$.each(curOpts.events, function(k, v) {
						uploader.bind(k, v);
					});
				}

				uploader.init();
                me.data({
                    nyroPluploader: uploader,
                    nyroPluploadCont: cont
                });
			});
		},
        nyroPluploadOpen: function(clb) {
            var me = this.first();
            if (me.data('nyroPluploader') && me.data('nyroPluploadCont')) {

                if (clb && $.isFunction(clb)) {
                    var openClbComplete = function() {
                        me.data('nyroPluploader').unbind('FileUploaded', clb);
                        me.data('nyroPluploader').unbind('UploadComplete', openClbComplete);
                        me.data('nyroPluploader').unbind('Error', openClbComplete);
                    };

                    me.data('nyroPluploader').bind('FileUploaded', clb);
                    me.data('nyroPluploader').bind('UploadComplete', openClbComplete);
                    me.data('nyroPluploader').bind('Error', openClbComplete);
                }

                me.data('nyroPluploadCont').find('input').trigger('click');
            }
            return this;
        },
		nyroPluploadDataSearch: function(defOpts, pKey) {
			if (!defOpts)
				defOpts = {};
			pKey = pKey || pluploadKey;
			var me = $(this).first(),
				opts = $.extend(true, {}, {
					showCancelAll: true,
					addFormVars: true,
					hideDelay: 750,
					filters: me.data(pKey+'filters'),
					flash_swf_url: me.data(pKey+'swf'),
					silverlight_xap_url: me.data(pKey+'xap'),
					texts: {
						browse: me.data(pKey+'browse'),
						waiting: me.data(pKey+'waiting'),
						error: me.data(pKey+'error'),
						cancel: me.data(pKey+'cancel'),
						complete: me.data(pKey+'complete'),
						cancelAll: me.data(pKey+'cancelall')
					},
					onAllComplete: function() {
						document.location.reload();
					}
				}, defOpts),
				pKeyLn = pKey.length;
			$.each(me.data(), function(i, e) {
				if (i.indexOf(pKey) == 0) {
					opts[i.substring(pKeyLn)] = searchFuncOrRegexp(e);
				}
			});
			return opts;
		}
	});

	$('form.pluploadInit').each(function() {
		var me = $(this).addClass('pluploadForm'),
			input = me.find('input[type="file"]');
		input.nyroPlupload(me.nyroPluploadDataSearch());
	});
});<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//
// PersonalOptions class
// Encapsulated handling of personal options page.
//
function PersonalOptions() {
  OptionsPage.call(this, 'personal', templateData.personalPage, 'personalPage');
}

cr.addSingletonGetter(PersonalOptions);

PersonalOptions.prototype = {
  // Inherit PersonalOptions from OptionsPage.
  __proto__: OptionsPage.prototype,

  // Initialize PersonalOptions page.
  initializePage: function() {
    // Call base class implementation to starts preference initialization.
    OptionsPage.prototype.initializePage.call(this);

    // Listen to pref changes.
    Preferences.getInstance().addEventListener('sync.has_setup_completed',
        function(event) {
          if(event.value) {
            $('text-when-synced').style.display = 'block';
            $('button-when-synced').style.display = 'block';
          }
          else {
            $('text-when-not-synced').style.display = 'block';
            $('button-when-not-synced').style.display = 'block';
          }
        });

    $('sync-customize').onclick = function(event) {
      OptionsPage.showPageByName('sync');
    };

    $('showpasswords').onclick = function(event) {
      //TODO(sargrass): Show passwords dialog here.
    };

    $('autofill_options').onclick = function(event) {
      //TODO(sargrass): Show autofill dialog here.
    };

    $('import_data').onclick = function(event) {
      //TODO(sargrass): Show import_data dialog here.
    };

    if(navigator.platform.match(/linux|BSD/i)) {
      $('themes_GTK_button').onclick = function(event) {
        //TODO(sargrass): Show themes GTK dialog here.
      };

      $('themes_set_classic').onclick = function(event) {
        //TODO(sargrass): Show themes set classic dialog here.
      };
    }

    if(navigator.platform.match(/Mac|Win|CrOS/i)) {
      $('themes_reset').onclick = function(event) {
        //TODO(sargrass): Show themes reset dialog here.
      };
    }

  },
};
<commit_msg>Fix DOMUI options for Chromium OS.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//
// PersonalOptions class
// Encapsulated handling of personal options page.
//
function PersonalOptions() {
  OptionsPage.call(this, 'personal', templateData.personalPage, 'personalPage');
}

cr.addSingletonGetter(PersonalOptions);

PersonalOptions.prototype = {
  // Inherit PersonalOptions from OptionsPage.
  __proto__: OptionsPage.prototype,

  // Initialize PersonalOptions page.
  initializePage: function() {
    // Call base class implementation to starts preference initialization.
    OptionsPage.prototype.initializePage.call(this);

    // Listen to pref changes.
    Preferences.getInstance().addEventListener('sync.has_setup_completed',
        function(event) {
          if(event.value) {
            $('text-when-synced').style.display = 'block';
            $('button-when-synced').style.display = 'block';
          }
          else {
            $('text-when-not-synced').style.display = 'block';
            $('button-when-not-synced').style.display = 'block';
          }
        });

    $('sync-customize').onclick = function(event) {
      OptionsPage.showPageByName('sync');
    };

    $('showpasswords').onclick = function(event) {
      //TODO(sargrass): Show passwords dialog here.
    };

    $('autofill_options').onclick = function(event) {
      //TODO(sargrass): Show autofill dialog here.
    };

    $('import_data').onclick = function(event) {
      //TODO(sargrass): Show import_data dialog here.
    };

    if(!cr.isChromeOS && navigator.platform.match(/linux|BSD/i)) {
      $('themes_GTK_button').onclick = function(event) {
        //TODO(sargrass): Show themes GTK dialog here.
      };

      $('themes_set_classic').onclick = function(event) {
        //TODO(sargrass): Show themes set classic dialog here.
      };
    }

    if(cr.isMac || cr.isWindows || cr.isChromeOS) {
      $('themes_reset').onclick = function(event) {
        //TODO(sargrass): Show themes reset dialog here.
      };
    }

  },
};
<|endoftext|>


<commit_before>let tiger;
let tigerImg;
let obstacleImg1;
let obstacleImg2;
let backgroundImg;
let obstacles = [];
let startGame = false
let landingImg;
let obstacleCounter = 0
function preload() {
  const options = {
    probabilityThreshold: 0.95
  };
  tigerImg = loadImage('tiger.png');
  obstacleImg1 = loadImage('obstacle1.png');
  obstacleImg2 = loadImage('obstacle2.png');
  backgroundImg = loadImage('road.gif');
  landingImg =  loadImage("home.jpeg");
}

function mousePressed() {
  obstacles.push(new SpeedBreaker());
}

function setup() {
  createCanvas(windowWidth, windowHeight);
  tiger = new Tiger();
}

function gotCommand(error, results) {
  if (error) {
    console.error(error);
  }
  if (results[0].label == 'up') {
    tiger.jump();
  }
}

function keyPressed() {
  if(startGame === false)
    startGame = true;
  else{
    if (key == ' ') {
        tiger.jump();
      }
  }
}

function draw() {
  if(startGame){
    if (random(1) < 0.005) {
      obstacles.push(new SpeedBreaker());
    }

    background(backgroundImg);
    for (let t of obstacles) {
      t.move();
      t.show();
      if (tiger.hits(t)) {
        console.log('game over');
        noLoop();
        alert("Tiger Mar Gaya");
      }
    }

    tiger.show();
    tiger.move();
  }
  else{
    background(landingImg);
  }
}
<commit_msg>Added popup on game end<commit_after>let tiger;
let tigerImg;
let obstacleImg1;
let obstacleImg2;
let backgroundImg;
let obstacles = [];
let startGame = false
let landingImg;
let obstacleCounter = 0
function preload() {
  const options = {
    probabilityThreshold: 0.95
  };
  tigerImg = loadImage('tiger.png');
  obstacleImg1 = loadImage('obstacle1.png');
  obstacleImg2 = loadImage('obstacle2.png');
  backgroundImg = loadImage('road.gif');
  landingImg =  loadImage("home.jpeg");
}

function mousePressed() {
  obstacles.push(new SpeedBreaker());
}

function setup() {
  createCanvas(windowWidth, windowHeight);
  tiger = new Tiger();
}

function gotCommand(error, results) {
  if (error) {
    console.error(error);
  }
  if (results[0].label == 'up') {
    tiger.jump();
  }
}

function keyPressed() {
  if(startGame === false)
    startGame = true;
  else{
    if (key == ' ') {
        tiger.jump();
      }
  }
}

function draw() {
  if(startGame){
    if (random(1) < 0.005) {
      obstacles.push(new SpeedBreaker());
    }

    background(backgroundImg);
    for (let t of obstacles) {
      t.move();
      t.show();
      if (tiger.hits(t)) {
        console.log('game over');
        noLoop();
        alert("Khel Katam! Tiger Mar Gaya!");
      }
    }

    tiger.show();
    tiger.move();
  }
  else{
    background(landingImg);
  }
}
<|endoftext|>


<commit_before>mxn.register('googlev3', {

Mapstraction: {

	init: function(element, api){
		var me = this;
		if ( google && google.maps ){
			// by default add road map and no controls
			var myOptions = {
				disableDefaultUI: true,
				mapTypeId: google.maps.MapTypeId.ROADMAP,
				mapTypeControl: false,
				mapTypeControlOptions: null,
				navigationControl: false,
			        navigationControlOptions: null,
				scrollwheel: false
			};

		    // find controls
		    if (!this.addControlsArgs && loadoptions.addControlsArgs) {
		    	this.addControlsArgs = loadoptions.addControlsArgs;
		    }
		    if (this.addControlsArgs) {
			    if (this.addControlsArgs.zoom) {
			    	myOptions.navigationControl = true;
			    	if (this.addControlsArgs.zoom == 'small') {
			    		myOptions.navigationControlOptions = {style: google.maps.NavigationControlStyle.SMALL};
			    	}
			    	if (this.addControlsArgs.zoom == 'large') {
			    		myOptions.navigationControlOptions = {style: google.maps.NavigationControlStyle.ZOOM_PAN};
			    	}
			    }
			    if (this.addControlsArgs.map_type) {
					myOptions.mapTypeControl = true;
					myOptions.mapTypeControlOptions = {style: google.maps.MapTypeControlStyle.DEFAULT};
			    }
		    }

			var map = new google.maps.Map(element, myOptions);

			// deal with click
			google.maps.event.addListener(map, 'click', function(location){
				me.clickHandler(location.latLng.lat(),location.latLng.lng(),location,me);
			});

			// deal with zoom change
			google.maps.event.addListener(map, 'zoom_changed', function(){
				me.changeZoom.fire();
			});
			// deal with map movement
			google.maps.event.addListener(map, 'dragend', function(){
				me.moveendHandler(me);
				me.endPan.fire();
			});
			this.maps[api] = map;
			this.loaded[api] = true;
			me.load.fire();
		}
		else {
			alert(api + ' map script not imported');
		}
	},

	applyOptions: function(){
		var map = this.maps[this.api];
		var myOptions = [];
		if (this.options.enableDragging) {
			myOptions.draggable = true;
		}
		if (this.options.enableScrollWheelZoom){
			myOptions.scrollwheel = true;
		}
		map.setOptions(myOptions);
	},

	resizeTo: function(width, height){
		this.currentElement.style.width = width;
		this.currentElement.style.height = height;
		var map = this.maps[this.api];
		google.maps.event.trigger(map,'resize');
  	},

	addControls: function( args ) {
		var map = this.maps[this.api];
		// remove old controls

		// Google has a combined zoom and pan control.
		if (args.zoom || args.pan) {
			if (args.zoom == 'large'){
				this.addLargeControls();
			} else {
				this.addSmallControls();
			}
		}
		if (args.scale){
			var myOptions = {
				scaleControl:true,
				scaleControlOptions: {style:google.maps.ScaleControlStyle.DEFAULT}
			};
			map.setOptions(myOptions);
			this.addControlsArgs.scale = true;
		}
		if (args.map_type){
		    this.addMapTypeControls();
		}
	},

	addSmallControls: function() {
		var map = this.maps[this.api];
		var myOptions = {
			navigationControl: true,
			navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL}
		};
		map.setOptions(myOptions);

		this.addControlsArgs.pan = false;
		this.addControlsArgs.scale = false;
		this.addControlsArgs.zoom = 'small';
	},

	addLargeControls: function() {
		var map = this.maps[this.api];
		var myOptions = {
			navigationControl: true,
			navigationControlOptions: {style:google.maps.NavigationControlStyle.DEFAULT}
		};
		map.setOptions(myOptions);
		this.addControlsArgs.pan = true;
		this.addControlsArgs.zoom = 'large';
	},

	addMapTypeControls: function() {
		var map = this.maps[this.api];
		var myOptions = {
			mapTypeControl: true,
			mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DEFAULT}
		};
		map.setOptions(myOptions);
		this.addControlsArgs.map_type = true;
	},

	setCenterAndZoom: function(point, zoom) {
		var map = this.maps[this.api];
		var pt = point.toProprietary(this.api);
		map.setCenter(pt);
		map.setZoom(zoom);
	},

	addMarker: function(marker, old) {
	   return marker.toProprietary(this.api);
	},

	removeMarker: function(marker) {
		// doesn't really remove them, just hides them
		marker.hide();
	},

	declutterMarkers: function(opts) {
		var map = this.maps[this.api];
		// TODO: Add provider code
	},

	addPolyline: function(polyline, old) {
		var map = this.maps[this.api];
		return polyline.toProprietary(this.api);
	},

	removePolyline: function(polyline) {
		var map = this.maps[this.api];
		// TODO: Add provider code
	},

	getCenter: function() {
		var map = this.maps[this.api];
		var pt = map.getCenter();
		return new mxn.LatLonPoint(pt.lat(),pt.lng());
	},

	setCenter: function(point, options) {
		var map = this.maps[this.api];
		var pt = point.toProprietary(this.api);
		if(options && options.pan) {
			map.panTo(pt);
		}
		else {
			map.setCenter(pt);
		}
	},

	setZoom: function(zoom) {
		var map = this.maps[this.api];
		map.setZoom(zoom);
	},

	getZoom: function() {
		var map = this.maps[this.api];
		return map.getZoom();
	},

	getZoomLevelForBoundingBox: function( bbox ) {
		var map = this.maps[this.api];
		var sw = bbox.getSouthWest().toProprietary(this.api);
		var ne = bbox.getNorthEast().toProprietary(this.api);
		var gLatLngBounds = new google.maps.LatLngBounds(sw, ne);
		map.fitBounds(gLatLngBounds);
		return map.getZoom();
	},

	setMapType: function(type) {
		var map = this.maps[this.api];
		switch(type) {
			case mxn.Mapstraction.ROAD:
				map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
				break;
			case mxn.Mapstraction.SATELLITE:
				map.setMapTypeId(google.maps.MapTypeId.SATELLITE);
				break;
			case mxn.Mapstraction.HYBRID:
				map.setMapTypeId(google.maps.MapTypeId.HYBRID);
				break;
			case mxn.Mapstraction.PHYSICAL:
				map.setMapTypeId(google.maps.MapTypeId.TERRAIN);
				break;
			default:
				map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
		}
	},

	getMapType: function() {
		var map = this.maps[this.api];
		var type = map.getMapTypeId();
		switch(type) {
			case google.maps.MapTypeId.ROADMAP:
				return mxn.Mapstraction.ROAD;
			case google.maps.MapTypeId.SATELLITE:
				return mxn.Mapstraction.SATELLITE;
			case google.maps.MapTypeId.HYBRID:
				return mxn.Mapstraction.HYBRID;
			case google.maps.MapTypeId.TERRAIN:
				return mxn.Mapstraction.PHYSICAL;
			default:
				return null;
		}
	},

	getBounds: function () {
		var map = this.maps[this.api];
		var gLatLngBounds = map.getBounds();
		var sw = gLatLngBounds.getSouthWest();
		var ne = gLatLngBounds.getNorthEast();
		return new mxn.BoundingBox(sw.lat(), sw.lng(), ne.lat(), ne.lng());
	},

	setBounds: function(bounds){
		var map = this.maps[this.api];
		var sw = bounds.getSouthWest().toProprietary(this.api);
		var ne = bounds.getNorthEast().toProprietary(this.api);
		var gLatLngBounds = new google.maps.LatLngBounds(sw, ne);
		map.fitBounds(gLatLngBounds);
	},

	addImageOverlay: function(id, src, opacity, west, south, east, north, oContext) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	setImagePosition: function(id, oContext) {
		var map = this.maps[this.api];
		var topLeftPoint; var bottomRightPoint;

		// TODO: Add provider code

		//oContext.pixels.top = ...;
		//oContext.pixels.left = ...;
		//oContext.pixels.bottom = ...;
		//oContext.pixels.right = ...;
	},

	addOverlay: function(url, autoCenterAndZoom) {
		var map = this.maps[this.api];

		// TODO: Add provider code

	},

	addTileLayer: function(tile_url, opacity, copyright_text, min_zoom, max_zoom, map_type) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	toggleTileLayer: function(tile_url) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	getPixelRatio: function() {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	mousePosition: function(element) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	}
},

LatLonPoint: {

	toProprietary: function() {
		return new google.maps.LatLng(this.lat, this.lon);
	},

	fromProprietary: function(googlePoint) {
		this.lat = googlePoint.lat();
		this.lon = googlePoint.lng();
	}

},

Marker: {

	toProprietary: function() {
		var options = {};

		// do we have an Anchor?
		var ax = 0;  // anchor x
		var ay = 0;  // anchor y

		if (this.iconAnchor) {
			ax = this.iconAnchor[0];
			ay = this.iconAnchor[1];
		}
		var gAnchorPoint = new google.maps.Point(ax,ay);

		if (this.iconUrl) {
 			options.icon = new google.maps.MarkerImage(
				this.iconUrl,
				new google.maps.Size(this.iconSize[0], this.iconSize[1]),
				new google.maps.Point(0,0),
				gAnchorPoint
			);

			// do we have a Shadow?
			if (this.iconShadowUrl) {
				if (this.iconShadowSize) {
					var x = this.iconShadowSize[0];
					var y = this.iconShadowSize[1];
					options.shadow = new google.maps.MarkerImage(
						this.iconShadowUrl,
						new google.maps.Size(x,y),
						new google.maps.Point(0,0),
						gAnchorPoint
					);
				}
				else {
					options.shadow = new google.maps.MarkerImage(this.iconShadowUrl);
				}
			}
		}
		if (this.draggable){
			options.draggable = this.draggable;
		}
		if (this.labelText){
			options.title =  this.labelText;
		}
		if (this.imageMap){
			options.shape = {
				coord: this.imageMap,
				type: 'poly'
			};
		}

		options.position = this.location.toProprietary(this.api);
		options.map = this.map;

		var marker = new google.maps.Marker(options);

		if (this.infoBubble){
			var infowindow = new google.maps.InfoWindow({
				content: this.infoBubble
			});

			var event_action = "click";
			if (this.hover) {
				event_action = "mouseover";
			}
			google.maps.event.addListener(marker, event_action, function() { infowindow.open(this.map,marker); });
		}

		if (this.hoverIconUrl){
			var gSize = new google.maps.Size(this.iconSize[0], this.iconSize[1]);
			var zerozero = new google.maps.Point(0,0);
 			var hIcon = new google.maps.MarkerImage(
				this.hoverIconUrl,
				gSize,
				zerozero,
				gAnchorPoint
			);
 			var Icon = new google.maps.MarkerImage(
				this.iconUrl,
				gSize,
				zerozero,
				gAnchorPoint
			);
			google.maps.event.addListener(
				marker,
				"mouseover",
				function(){
					marker.setIcon(hIcon);
				}
			);
			google.maps.event.addListener(
				marker,
				"mouseout",
				function(){ marker.setIcon(Icon); }
			);
		}

		google.maps.event.addListener(marker, 'click', function() {
			marker.mapstraction_marker.click.fire();
		});

		return marker;
	},

	openBubble: function() {
		var infowindow = new google.maps.InfoWindow({
	   		content: this.infoBubble
		});
		infowindow.open(this.map,this.proprietary_marker);
	},

	hide: function() {
		this.proprietary_marker.setOptions({visible:false});
	},

	show: function() {
		this.proprietary_marker.setOptions({visible:true});
	},

	update: function() {
		// TODO: Add provider code
	}

},

Polyline: {

	toProprietary: function() {
			throw 'Not implemented';
	},

	show: function() {
			throw 'Not implemented';
	},

	hide: function() {
			throw 'Not implemented';
	}

}

});
<commit_msg>Added polygon/polyline support and fixed click event in Google Maps V3<commit_after>mxn.register('googlev3', {

Mapstraction: {

	init: function(element, api){
		var me = this;
		if ( google && google.maps ){
			// by default add road map and no controls
			var myOptions = {
				disableDefaultUI: true,
				mapTypeId: google.maps.MapTypeId.ROADMAP,
				mapTypeControl: false,
				mapTypeControlOptions: null,
				navigationControl: false,
			        navigationControlOptions: null,
				scrollwheel: false
			};

		    // find controls
		    if (!this.addControlsArgs && loadoptions.addControlsArgs) {
		    	this.addControlsArgs = loadoptions.addControlsArgs;
		    }
		    if (this.addControlsArgs) {
			    if (this.addControlsArgs.zoom) {
			    	myOptions.navigationControl = true;
			    	if (this.addControlsArgs.zoom == 'small') {
			    		myOptions.navigationControlOptions = {style: google.maps.NavigationControlStyle.SMALL};
			    	}
			    	if (this.addControlsArgs.zoom == 'large') {
			    		myOptions.navigationControlOptions = {style: google.maps.NavigationControlStyle.ZOOM_PAN};
			    	}
			    }
			    if (this.addControlsArgs.map_type) {
					myOptions.mapTypeControl = true;
					myOptions.mapTypeControlOptions = {style: google.maps.MapTypeControlStyle.DEFAULT};
			    }
		    }

			var map = new google.maps.Map(element, myOptions);

			// deal with click
			google.maps.event.addListener(map, 'click', function(location){
				me.click.fire({'location':
					new mxn.LatLonPoint(location.latLng.lat(),location.latLng.lng())
				});
			});

			// deal with zoom change
			google.maps.event.addListener(map, 'zoom_changed', function(){
				me.changeZoom.fire();
			});
			// deal with map movement
			google.maps.event.addListener(map, 'dragend', function(){
				me.moveendHandler(me);
				me.endPan.fire();
			});
			this.maps[api] = map;
			this.loaded[api] = true;
			me.load.fire();
		}
		else {
			alert(api + ' map script not imported');
		}
	},

	applyOptions: function(){
		var map = this.maps[this.api];
		var myOptions = [];
		if (this.options.enableDragging) {
			myOptions.draggable = true;
		}
		if (this.options.enableScrollWheelZoom){
			myOptions.scrollwheel = true;
		}
		map.setOptions(myOptions);
	},

	resizeTo: function(width, height){
		this.currentElement.style.width = width;
		this.currentElement.style.height = height;
		var map = this.maps[this.api];
		google.maps.event.trigger(map,'resize');
  	},

	addControls: function( args ) {
		var map = this.maps[this.api];
		// remove old controls

		// Google has a combined zoom and pan control.
		if (args.zoom || args.pan) {
			if (args.zoom == 'large'){
				this.addLargeControls();
			} else {
				this.addSmallControls();
			}
		}
		if (args.scale){
			var myOptions = {
				scaleControl:true,
				scaleControlOptions: {style:google.maps.ScaleControlStyle.DEFAULT}
			};
			map.setOptions(myOptions);
			this.addControlsArgs.scale = true;
		}
		if (args.map_type){
		    this.addMapTypeControls();
		}
	},

	addSmallControls: function() {
		var map = this.maps[this.api];
		var myOptions = {
			navigationControl: true,
			navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL}
		};
		map.setOptions(myOptions);

		this.addControlsArgs.pan = false;
		this.addControlsArgs.scale = false;
		this.addControlsArgs.zoom = 'small';
	},

	addLargeControls: function() {
		var map = this.maps[this.api];
		var myOptions = {
			navigationControl: true,
			navigationControlOptions: {style:google.maps.NavigationControlStyle.DEFAULT}
		};
		map.setOptions(myOptions);
		this.addControlsArgs.pan = true;
		this.addControlsArgs.zoom = 'large';
	},

	addMapTypeControls: function() {
		var map = this.maps[this.api];
		var myOptions = {
			mapTypeControl: true,
			mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DEFAULT}
		};
		map.setOptions(myOptions);
		this.addControlsArgs.map_type = true;
	},

	setCenterAndZoom: function(point, zoom) {
		var map = this.maps[this.api];
		var pt = point.toProprietary(this.api);
		map.setCenter(pt);
		map.setZoom(zoom);
	},

	addMarker: function(marker, old) {
	   return marker.toProprietary(this.api);
	},

	removeMarker: function(marker) {
		// doesn't really remove them, just hides them
		marker.hide();
	},

	declutterMarkers: function(opts) {
		var map = this.maps[this.api];
		// TODO: Add provider code
	},

	addPolyline: function(polyline, old) {
		var map = this.maps[this.api];
		var gpoly = polyline.toProprietary(this.api);
		gpoly.setMap(map);
		return gpoly;
	},

	removePolyline: function(polyline) {
		var map = this.maps[this.api];
		polyline.proprietary_polyline.setMap(null);
	},

	getCenter: function() {
		var map = this.maps[this.api];
		var pt = map.getCenter();
		return new mxn.LatLonPoint(pt.lat(),pt.lng());
	},

	setCenter: function(point, options) {
		var map = this.maps[this.api];
		var pt = point.toProprietary(this.api);
		if(options && options.pan) {
			map.panTo(pt);
		}
		else {
			map.setCenter(pt);
		}
	},

	setZoom: function(zoom) {
		var map = this.maps[this.api];
		map.setZoom(zoom);
	},

	getZoom: function() {
		var map = this.maps[this.api];
		return map.getZoom();
	},

	getZoomLevelForBoundingBox: function( bbox ) {
		var map = this.maps[this.api];
		var sw = bbox.getSouthWest().toProprietary(this.api);
		var ne = bbox.getNorthEast().toProprietary(this.api);
		var gLatLngBounds = new google.maps.LatLngBounds(sw, ne);
		map.fitBounds(gLatLngBounds);
		return map.getZoom();
	},

	setMapType: function(type) {
		var map = this.maps[this.api];
		switch(type) {
			case mxn.Mapstraction.ROAD:
				map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
				break;
			case mxn.Mapstraction.SATELLITE:
				map.setMapTypeId(google.maps.MapTypeId.SATELLITE);
				break;
			case mxn.Mapstraction.HYBRID:
				map.setMapTypeId(google.maps.MapTypeId.HYBRID);
				break;
			case mxn.Mapstraction.PHYSICAL:
				map.setMapTypeId(google.maps.MapTypeId.TERRAIN);
				break;
			default:
				map.setMapTypeId(google.maps.MapTypeId.ROADMAP);
		}
	},

	getMapType: function() {
		var map = this.maps[this.api];
		var type = map.getMapTypeId();
		switch(type) {
			case google.maps.MapTypeId.ROADMAP:
				return mxn.Mapstraction.ROAD;
			case google.maps.MapTypeId.SATELLITE:
				return mxn.Mapstraction.SATELLITE;
			case google.maps.MapTypeId.HYBRID:
				return mxn.Mapstraction.HYBRID;
			case google.maps.MapTypeId.TERRAIN:
				return mxn.Mapstraction.PHYSICAL;
			default:
				return null;
		}
	},

	getBounds: function () {
		var map = this.maps[this.api];
		var gLatLngBounds = map.getBounds();
		var sw = gLatLngBounds.getSouthWest();
		var ne = gLatLngBounds.getNorthEast();
		return new mxn.BoundingBox(sw.lat(), sw.lng(), ne.lat(), ne.lng());
	},

	setBounds: function(bounds){
		var map = this.maps[this.api];
		var sw = bounds.getSouthWest().toProprietary(this.api);
		var ne = bounds.getNorthEast().toProprietary(this.api);
		var gLatLngBounds = new google.maps.LatLngBounds(sw, ne);
		map.fitBounds(gLatLngBounds);
	},

	addImageOverlay: function(id, src, opacity, west, south, east, north, oContext) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	setImagePosition: function(id, oContext) {
		var map = this.maps[this.api];
		var topLeftPoint; var bottomRightPoint;

		// TODO: Add provider code

		//oContext.pixels.top = ...;
		//oContext.pixels.left = ...;
		//oContext.pixels.bottom = ...;
		//oContext.pixels.right = ...;
	},

	addOverlay: function(url, autoCenterAndZoom) {
		var map = this.maps[this.api];

		// TODO: Add provider code

	},

	addTileLayer: function(tile_url, opacity, copyright_text, min_zoom, max_zoom, map_type) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	toggleTileLayer: function(tile_url) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	getPixelRatio: function() {
		var map = this.maps[this.api];

		// TODO: Add provider code
	},

	mousePosition: function(element) {
		var map = this.maps[this.api];

		// TODO: Add provider code
	}
},

LatLonPoint: {

	toProprietary: function() {
		return new google.maps.LatLng(this.lat, this.lon);
	},

	fromProprietary: function(googlePoint) {
		this.lat = googlePoint.lat();
		this.lon = googlePoint.lng();
	}

},

Marker: {

	toProprietary: function() {
		var options = {};

		// do we have an Anchor?
		var ax = 0;  // anchor x
		var ay = 0;  // anchor y

		if (this.iconAnchor) {
			ax = this.iconAnchor[0];
			ay = this.iconAnchor[1];
		}
		var gAnchorPoint = new google.maps.Point(ax,ay);

		if (this.iconUrl) {
 			options.icon = new google.maps.MarkerImage(
				this.iconUrl,
				new google.maps.Size(this.iconSize[0], this.iconSize[1]),
				new google.maps.Point(0,0),
				gAnchorPoint
			);

			// do we have a Shadow?
			if (this.iconShadowUrl) {
				if (this.iconShadowSize) {
					var x = this.iconShadowSize[0];
					var y = this.iconShadowSize[1];
					options.shadow = new google.maps.MarkerImage(
						this.iconShadowUrl,
						new google.maps.Size(x,y),
						new google.maps.Point(0,0),
						gAnchorPoint
					);
				}
				else {
					options.shadow = new google.maps.MarkerImage(this.iconShadowUrl);
				}
			}
		}
		if (this.draggable){
			options.draggable = this.draggable;
		}
		if (this.labelText){
			options.title =  this.labelText;
		}
		if (this.imageMap){
			options.shape = {
				coord: this.imageMap,
				type: 'poly'
			};
		}

		options.position = this.location.toProprietary(this.api);
		options.map = this.map;

		var marker = new google.maps.Marker(options);

		if (this.infoBubble){
			var infowindow = new google.maps.InfoWindow({
				content: this.infoBubble
			});

			var event_action = "click";
			if (this.hover) {
				event_action = "mouseover";
			}
			google.maps.event.addListener(marker, event_action, function() { infowindow.open(this.map,marker); });
		}

		if (this.hoverIconUrl){
			var gSize = new google.maps.Size(this.iconSize[0], this.iconSize[1]);
			var zerozero = new google.maps.Point(0,0);
 			var hIcon = new google.maps.MarkerImage(
				this.hoverIconUrl,
				gSize,
				zerozero,
				gAnchorPoint
			);
 			var Icon = new google.maps.MarkerImage(
				this.iconUrl,
				gSize,
				zerozero,
				gAnchorPoint
			);
			google.maps.event.addListener(
				marker,
				"mouseover",
				function(){
					marker.setIcon(hIcon);
				}
			);
			google.maps.event.addListener(
				marker,
				"mouseout",
				function(){ marker.setIcon(Icon); }
			);
		}

		google.maps.event.addListener(marker, 'click', function() {
			marker.mapstraction_marker.click.fire();
		});

		return marker;
	},

	openBubble: function() {
		var infowindow = new google.maps.InfoWindow({
	   		content: this.infoBubble
		});
		infowindow.open(this.map,this.proprietary_marker);
	},

	hide: function() {
		this.proprietary_marker.setOptions({visible:false});
	},

	show: function() {
		this.proprietary_marker.setOptions({visible:true});
	},

	update: function() {
		// TODO: Add provider code
	}

},

Polyline: {
	toProprietary: function() {
		var pts = this.points;
		var gpts = [];
		for (var i=0; i<pts.length; i++) {
		  gpts.push(pts[i].toProprietary(this.api));
		}
		if (this.closed || pts[0].equals(pts[pts.length - 1])) {
		  return new google.maps.Polygon({
		    path: gpts,
    		strokeColor: this.color,
   			strokeOpacity: this.opacity,
    		strokeWeight: this.width,
    		fillColor: this.fillColor || "#5462E3",
    		fillOpacity: this.opacity || "0.3"
		  });
		}
		else {
		  return new google.maps.Polyline({
		    path: gpts,
    		strokeColor: this.color,
   			strokeOpacity: this.opacity,
    		strokeWeight: this.width
		  });
		}
	},

	show: function() {
			throw 'Not implemented';
	},

	hide: function() {
			throw 'Not implemented';
	}

}

});
<|endoftext|>


<commit_before>/**
 * Wheel, copyright (c) 2020 - present by Arno van der Vegt
 * Distributed under an MIT license: https://arnovandervegt.github.io/wheel/license.txt
**/
const dispatcher              = require('../../../../lib/dispatcher').dispatcher;
const Emitter                 = require('../../../../lib/Emitter').Emitter;

const COMPONENT_BUTTON        = 0;
const COMPONENT_SELECT_BUTTON = 1;
const COMPONENT_LABEL         = 2;
const COMPONENT_CHECKBOX      = 3;
const COMPONENT_TABS          = 4;

const ACTION_ADD_COMPONENT    = 0;
const ACTION_DELETE_COMPONENT = 1;
const ACTION_CHANGE_POSITION  = 2;
const ACTION_CHANGE_PROPERTY  = 3;

const PROPERTIES_BY_TYPE      = {
        BUTTON: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'boolean',     name: 'disabled'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'color',       name: 'color'},
            {type: 'string',      name: 'value'},
            {type: 'string',      name: 'title'}
        ],
        SELECT_BUTTON: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'boolean',     name: 'disabled'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'color',       name: 'color'},
            {type: 'stringList',  name: 'options'}
        ],
        LABEL: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'string',      name: 'text'}
        ],
        CHECKBOX: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'boolean',     name: 'disabled'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'string',      name: 'text'},
            {type: 'boolean',     name: 'checked'}
        ],
        TABS: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'containerId', name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'integer',     name: 'width'},
            {type: 'integer',     name: 'height'},
            {type: 'stringList',  name: 'tabs'}
        ]
    };

let nextId = 0;

exports.FormEditorState = class extends Emitter {
    constructor(opts) {
        super(opts);
        this._getOwnerByParentId = opts.getOwnerByParentId;
        this._data               = opts.data || [];
        this._width              = opts.width;
        this._height             = opts.height;
        this._undoStack          = [];
        this._component          = COMPONENT_BUTTON;
        this._componentsById     = {};
        this._activeComponentId  = null;
        dispatcher
            .on('Properties.Property.Change', this, this.onChangeProperty)
            .on('Properties.Select',          this, this.onSelectProperties)
            .on('Properties.SelectComponent', this, this.onSelectComponent);
    }

    peekId() {
        return nextId + 1;
    }

    getNextId() {
        nextId++;
        return nextId;
    }

    setComponent(component) {
        this._component = component;
    }

    getUndoStackLength() {
        return this._undoStack.length;
    }

    getWidth() {
        return this._width;
    }

    getHeight() {
        return this._height;
    }

    setComponentPositionById(id, position) {
        let component = this._componentsById[id] || {};
        this.undoStackPush({
            action:   ACTION_CHANGE_POSITION,
            id:       id,
            position: {x: component.x, y: component.y}
        });
        component.x = position.x;
        component.y = position.y;
    }

    getComponentById(id) {
        return this._componentsById[id];
    }

    getActiveComponentId() {
        return this._activeComponentId;
    }

    getItems() {
        let componentsById = this._componentsById;
        let items          = [];
        for (let id in componentsById) {
            let component = componentsById[id];
            items.push({
                value: component.id,
                title: component.name + ' ' + component.type + ''
            });
        }
        return items;
    }

    getHasUndo() {
        return this._undoStack.length;
    }

    undoStackPop() {
        return this._undoStack.pop();
    }

    undoStackPush(item) {
        let undoStack = this._undoStack;
        let lastItem  = undoStack.length ? undoStack[undoStack.length - 1] : null;
        if (lastItem) {
            switch (lastItem.action) {
                case ACTION_CHANGE_POSITION:
                    if (lastItem.id === item.id) {
                        return this;
                    }
                    break;
                case ACTION_CHANGE_PROPERTY:
                    if ((lastItem.id === item.id) && (lastItem.property === item.property)) {
                        return this;
                    }
                    break;
            }
        }
        this._undoStack.push(item);
        return this;
    }

    undo() {
        let item = this.undoStackPop();
        let component;
        switch (item.action) {
            case ACTION_ADD_COMPONENT:
                this.deleteComponentById(item.id, false);
                break;
            case ACTION_DELETE_COMPONENT:
                component                          = item.component;
                component.owner                    = this._getOwnerByParentId(component.parentId);
                this._componentsById[component.id] = component;
                this._activeComponentId            = component.id;
                switch (component.type) {
                    case 'button':       this.addButtonComponent(component); break;
                    case 'selectButton': this.addSelectButton   (component); break;
                    case 'label':        this.addLabel          (component); break;
                    case 'checkbox':     this.addCheckBox       (component); break;
                    case 'tabs':         this.addTabs           (component); break;
                }
                this
                    .emit('AddComponent', Object.assign({}, component))
                    .updateComponents(component.id);
                break;
            case ACTION_CHANGE_POSITION:
                component   = this._componentsById[item.id];
                component.x = item.position.x;
                component.y = item.position.y;
                this.onSelectComponent(item.id);
                this.emit('ChangePosition', item.id, item.position);
                break;
        }
        this.emit('Undo');
    }

    addButtonComponent(component) {
        component.type       = 'button';
        component.name       = 'Button' + component.id;
        component.value      = 'Button' + component.id;
        component.title      = 'Button' + component.id;
        component.color      = 'green';
        component.properties = [].concat(PROPERTIES_BY_TYPE.BUTTON);
    }

    addSelectButton(component) {
        component.type       = 'selectButton';
        component.name       = 'SelectButton' + component.id;
        component.options    = ['A', 'B'];
        component.color      = 'green';
        component.properties = [].concat(PROPERTIES_BY_TYPE.SELECT_BUTTON);
    }

    addLabel(component) {
        component.type       = 'label';
        component.name       = 'Label' + component.id;
        component.text       = 'Label' + component.id;
        component.properties = [].concat(PROPERTIES_BY_TYPE.LABEL);
    }

    addCheckBox(component) {
        component.type       = 'checkbox';
        component.name       = 'Checkbox' + component.id;
        component.text       = 'Checkbox' + component.id;
        component.checked    = false;
        component.properties = [].concat(PROPERTIES_BY_TYPE.CHECKBOX);
    }

    addTabs(component) {
        component.type        = 'tabs';
        component.name        = 'Tabs' + component.id;
        component.tabs        = ['A', 'B'];
        component.width       = 200;
        component.height      = 128;
        component.containerId = [this.peekId(), this.peekId() + 1];
        component.properties  = [].concat(PROPERTIES_BY_TYPE.TABS);
    }

    addComponent(opts) {
        let component = {
                tabIndex: 0,
                id:       this.getNextId(),
                x:        opts.x,
                y:        opts.y,
                owner:    opts.owner,
                parentId: opts.parentId
            };
        this._componentsById[component.id] = component;
        switch (this._component) {
            case COMPONENT_BUTTON:        this.addButtonComponent(component); break;
            case COMPONENT_SELECT_BUTTON: this.addSelectButton   (component); break;
            case COMPONENT_LABEL:         this.addLabel          (component); break;
            case COMPONENT_CHECKBOX:      this.addCheckBox       (component); break;
            case COMPONENT_TABS:          this.addTabs           (component); break;
        }
        this._activeComponentId = component.id;
        this
            .undoStackPush({action: ACTION_ADD_COMPONENT, id: component.id})
            .emit('AddComponent', Object.assign({}, component))
            .updateComponents(component.id);
    }

    deleteActiveComponent() {
        this.deleteComponentById(this._activeComponentId, true);
    }

    deleteComponentById(id, saveUndo) {
        let componentsById = this._componentsById;
        if ((id === null) || !componentsById[id]) {
            return;
        }
        let component = Object.assign({}, componentsById[id]);
        component.parentId = component.owner.getParentId();
        delete component.owner;
        delete componentsById[id];
        this._activeComponentId = null;
        if (saveUndo) {
            this.undoStackPush({action: ACTION_DELETE_COMPONENT, component: component});
        }
        this.emit('DeleteComponent', id);
        dispatcher.dispatch('Properties.ComponentList', {value: null, items: this.getItems()});
    }

    selectComponentById(id) {
        let component = this._componentsById[id];
        if (component) {
            this._activeComponentId = id;
            this.emit('SelectComponent', id);
            return component;
        }
        return false;
    }

    onChangeProperty(id, property, value) {
        let component = this._componentsById[id];
        if (!component) {
            return;
        }
        component[property] = value;
    }

    onSelectProperties(properties) {
        this.selectComponentById(properties.id);
    }

    onSelectComponent(id) {
        let component = this.selectComponentById(id);
        if (component) {
            let properties = [].concat(PROPERTIES_BY_TYPE[component.type.toUpperCase()]);
            properties.id = id;
            dispatcher.dispatch('Properties.Select', properties, this);
        }
    }

    updateComponents(id) {
        dispatcher.dispatch('Properties.ComponentList', {value: id, items: this.getItems()});
    }
};
<commit_msg>Added undo for property editing.<commit_after>/**
 * Wheel, copyright (c) 2020 - present by Arno van der Vegt
 * Distributed under an MIT license: https://arnovandervegt.github.io/wheel/license.txt
**/
const dispatcher              = require('../../../../lib/dispatcher').dispatcher;
const Emitter                 = require('../../../../lib/Emitter').Emitter;

const COMPONENT_BUTTON        = 0;
const COMPONENT_SELECT_BUTTON = 1;
const COMPONENT_LABEL         = 2;
const COMPONENT_CHECKBOX      = 3;
const COMPONENT_TABS          = 4;

const ACTION_ADD_COMPONENT    = 0;
const ACTION_DELETE_COMPONENT = 1;
const ACTION_CHANGE_POSITION  = 2;
const ACTION_CHANGE_PROPERTY  = 3;

const PROPERTIES_BY_TYPE      = {
        BUTTON: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'boolean',     name: 'disabled'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'color',       name: 'color'},
            {type: 'string',      name: 'value'},
            {type: 'string',      name: 'title'}
        ],
        SELECT_BUTTON: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'boolean',     name: 'disabled'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'color',       name: 'color'},
            {type: 'stringList',  name: 'options'}
        ],
        LABEL: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'string',      name: 'text'}
        ],
        CHECKBOX: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'boolean',     name: 'disabled'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'string',      name: 'text'},
            {type: 'boolean',     name: 'checked'}
        ],
        TABS: [
            {type: 'type',        name: null},
            {type: 'id',          name: null},
            {type: 'parentId',    name: null},
            {type: 'containerId', name: null},
            {type: 'string',      name: 'name'},
            {type: 'integer',     name: 'tabIndex'},
            {type: 'boolean',     name: 'hidden'},
            {type: 'integer',     name: 'x'},
            {type: 'integer',     name: 'y'},
            {type: 'integer',     name: 'width'},
            {type: 'integer',     name: 'height'},
            {type: 'stringList',  name: 'tabs'}
        ]
    };

let nextId = 0;

exports.FormEditorState = class extends Emitter {
    constructor(opts) {
        super(opts);
        this._getOwnerByParentId = opts.getOwnerByParentId;
        this._data               = opts.data || [];
        this._width              = opts.width;
        this._height             = opts.height;
        this._undoStack          = [];
        this._component          = COMPONENT_BUTTON;
        this._componentsById     = {};
        this._activeComponentId  = null;
        dispatcher
            .on('Properties.Property.Change', this, this.onChangeProperty)
            .on('Properties.Select',          this, this.onSelectProperties)
            .on('Properties.SelectComponent', this, this.onSelectComponent);
    }

    peekId() {
        return nextId + 1;
    }

    getNextId() {
        nextId++;
        return nextId;
    }

    setComponent(component) {
        this._component = component;
    }

    getUndoStackLength() {
        return this._undoStack.length;
    }

    getWidth() {
        return this._width;
    }

    getHeight() {
        return this._height;
    }

    setComponentPositionById(id, position) {
        let component = this._componentsById[id] || {};
        this.undoStackPush({
            action:   ACTION_CHANGE_POSITION,
            id:       id,
            position: {x: component.x, y: component.y}
        });
        component.x = position.x;
        component.y = position.y;
    }

    getComponentById(id) {
        return this._componentsById[id];
    }

    getActiveComponentId() {
        return this._activeComponentId;
    }

    getItems() {
        let componentsById = this._componentsById;
        let items          = [];
        for (let id in componentsById) {
            let component = componentsById[id];
            items.push({
                value: component.id,
                title: component.name + ' ' + component.type + ''
            });
        }
        return items;
    }

    getHasUndo() {
        return this._undoStack.length;
    }

    undoStackPop() {
        return this._undoStack.pop();
    }

    undoStackPush(item) {
        let undoStack = this._undoStack;
        let lastItem  = undoStack.length ? undoStack[undoStack.length - 1] : null;
        if (lastItem) {
            switch (lastItem.action) {
                case ACTION_CHANGE_POSITION:
                    if (lastItem.id === item.id) {
                        return this;
                    }
                    break;
                case ACTION_CHANGE_PROPERTY:
                    if ((lastItem.id === item.id) && (lastItem.property === item.property)) {
                        return this;
                    }
                    break;
            }
        }
        this._undoStack.push(item);
        return this;
    }

    undo() {
        let item           = this.undoStackPop();
        let componentsById = this._componentsById;
        let component;
        switch (item.action) {
            case ACTION_ADD_COMPONENT:
                this.deleteComponentById(item.id, false);
                break;
            case ACTION_DELETE_COMPONENT:
                component                    = item.component;
                component.owner              = this._getOwnerByParentId(component.parentId);
                componentsById[component.id] = component;
                this._activeComponentId      = component.id;
                switch (component.type) {
                    case 'button':       this.addButtonComponent(component); break;
                    case 'selectButton': this.addSelectButton   (component); break;
                    case 'label':        this.addLabel          (component); break;
                    case 'checkbox':     this.addCheckBox       (component); break;
                    case 'tabs':         this.addTabs           (component); break;
                }
                this
                    .emit('AddComponent', Object.assign({}, component))
                    .updateComponents(component.id);
                break;
            case ACTION_CHANGE_POSITION:
                component   = componentsById[item.id];
                component.x = item.position.x;
                component.y = item.position.y;
                this.onSelectComponent(item.id);
                this.emit('ChangePosition', item.id, item.position);
                break;
            case ACTION_CHANGE_PROPERTY:
                component                = componentsById[item.id];
                component[item.property] = item.value;
                this.onSelectComponent(item.id);
                this.emit('ChangeProperty', item.id, item.property, item.value);
                break;
        }
        this.emit('Undo');
    }

    addButtonComponent(component) {
        component.type       = 'button';
        component.name       = 'Button' + component.id;
        component.value      = 'Button' + component.id;
        component.title      = 'Button' + component.id;
        component.color      = 'green';
        component.properties = [].concat(PROPERTIES_BY_TYPE.BUTTON);
    }

    addSelectButton(component) {
        component.type       = 'selectButton';
        component.name       = 'SelectButton' + component.id;
        component.options    = ['A', 'B'];
        component.color      = 'green';
        component.properties = [].concat(PROPERTIES_BY_TYPE.SELECT_BUTTON);
    }

    addLabel(component) {
        component.type       = 'label';
        component.name       = 'Label' + component.id;
        component.text       = 'Label' + component.id;
        component.properties = [].concat(PROPERTIES_BY_TYPE.LABEL);
    }

    addCheckBox(component) {
        component.type       = 'checkbox';
        component.name       = 'Checkbox' + component.id;
        component.text       = 'Checkbox' + component.id;
        component.checked    = false;
        component.properties = [].concat(PROPERTIES_BY_TYPE.CHECKBOX);
    }

    addTabs(component) {
        component.type        = 'tabs';
        component.name        = 'Tabs' + component.id;
        component.tabs        = ['A', 'B'];
        component.width       = 200;
        component.height      = 128;
        component.containerId = [this.peekId(), this.peekId() + 1];
        component.properties  = [].concat(PROPERTIES_BY_TYPE.TABS);
    }

    addComponent(opts) {
        let component = {
                tabIndex: 0,
                id:       this.getNextId(),
                x:        opts.x,
                y:        opts.y,
                owner:    opts.owner,
                parentId: opts.parentId
            };
        this._componentsById[component.id] = component;
        switch (this._component) {
            case COMPONENT_BUTTON:        this.addButtonComponent(component); break;
            case COMPONENT_SELECT_BUTTON: this.addSelectButton   (component); break;
            case COMPONENT_LABEL:         this.addLabel          (component); break;
            case COMPONENT_CHECKBOX:      this.addCheckBox       (component); break;
            case COMPONENT_TABS:          this.addTabs           (component); break;
        }
        this._activeComponentId = component.id;
        this
            .undoStackPush({action: ACTION_ADD_COMPONENT, id: component.id})
            .emit('AddComponent', Object.assign({}, component))
            .updateComponents(component.id);
    }

    deleteActiveComponent() {
        this.deleteComponentById(this._activeComponentId, true);
    }

    deleteComponentById(id, saveUndo) {
        let componentsById = this._componentsById;
        if ((id === null) || !componentsById[id]) {
            return;
        }
        let component = Object.assign({}, componentsById[id]);
        component.parentId = component.owner.getParentId();
        delete component.owner;
        delete componentsById[id];
        this._activeComponentId = null;
        if (saveUndo) {
            this.undoStackPush({action: ACTION_DELETE_COMPONENT, component: component});
        }
        this.emit('DeleteComponent', id);
        dispatcher.dispatch('Properties.ComponentList', {value: null, items: this.getItems()});
    }

    selectComponentById(id) {
        let component = this._componentsById[id];
        if (component) {
            this._activeComponentId = id;
            this.emit('SelectComponent', id);
            return component;
        }
        return false;
    }

    onChangeProperty(id, property, value) {
        let component = this._componentsById[id];
        if (!component) {
            return;
        }
        this.undoStackPush({
            action:   ACTION_CHANGE_PROPERTY,
            id:       id,
            property: property,
            value:    component[property]
        });
        component[property] = value;
    }

    onSelectProperties(properties) {
        this.selectComponentById(properties.id);
    }

    onSelectComponent(id) {
        let component = this.selectComponentById(id);
        if (component) {
            let properties = [].concat(PROPERTIES_BY_TYPE[component.type.toUpperCase()]);
            properties.id = id;
            dispatcher.dispatch('Properties.Select', properties, this);
        }
    }

    updateComponents(id) {
        dispatcher.dispatch('Properties.ComponentList', {value: id, items: this.getItems()});
    }
};
<|endoftext|>