<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React from 'react';
import uuidv1 from 'uuid';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Divider from '@material-ui/core/Divider';
import OpenFolderIcon from '@material-ui/icons/SubdirectoryArrowLeft';
import AddExistingFileIcon from '@material-ui/icons/ExitToApp';
import OpenFolderNativelyIcon from '@material-ui/icons/Launch';
import AutoRenew from '@material-ui/icons/Autorenew';
import NewFileIcon from '@material-ui/icons/InsertDriveFile';
import NewFolderIcon from '@material-ui/icons/CreateNewFolder';
import RenameFolderIcon from '@material-ui/icons/FormatTextdirectionLToR';
import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
import SettingsIcon from '@material-ui/icons/Settings';
import ConfirmDialog from '../dialogs/ConfirmDialog';
import CreateDirectoryDialog from '../dialogs/CreateDirectoryDialog';
import RenameDirectoryDialog from '../dialogs/RenameDirectoryDialog';
import AppConfig from '../../config';
import i18n from '../../services/i18n';
import { extractFileName, extractParentDirectoryPath, normalizePath } from '../../utils/paths'; // extractFileExtension
import PlatformIO from '../../services/platform-io';
import { formatDateTime4Tag } from '../../utils/misc';

type Props = {
  open?: boolean,
  onClose: () => void,
  anchorEl?: Object | null,
  directoryPath?: string,
  loadDirectoryContent: (path: string) => void,
  openDirectory: (path: string) => void,
  showInFileManager: (path: string) => void,
  openFile: (path: string, isFile: boolean) => void,
  deleteDirectory: (path: string) => void,
  reflectCreateEntry?: (path: string, isFile: boolean) => void,
  toggleCreateFileDialog?: () => void,
  loadParentDirectoryContent?: () => void,
  openFileNatively?: (path: string) => void,
  perspectiveMode: boolean,
  showNotification?: (
    text: string,
    notificationType: string,
    autohide: boolean
  ) => void,
  isReadOnlyMode: boolean
};

type State = {
  isCreateDirectoryDialogOpened?: boolean,
  isDeleteDirectoryDialogOpened?: boolean,
  isRenameDirectoryDialogOpened?: boolean
};

class DirectoryMenu extends React.Component<Props, State> {
  fileInput: Object | null;

  state = {
    isCreateDirectoryDialogOpened: false,
    isDeleteDirectoryDialogOpened: false,
    isRenameDirectoryDialogOpened: false
  };

  reloadDirectory = () => {
    this.props.onClose();
    this.props.loadDirectoryContent(this.props.directoryPath);
  };

  openParentDirectory = () => {
    this.props.onClose();
    this.props.loadParentDirectoryContent();
  };

  openDirectory = () => {
    this.props.onClose();
    this.props.loadDirectoryContent(this.props.directoryPath);
  };

  showProperties = () => {
    this.props.onClose();
    this.props.openFile(this.props.directoryPath, false);
  };

  showDeleteDirectoryDialog = () => {
    this.props.onClose();
    this.setState({ isDeleteDirectoryDialogOpened: true });
  };

  showRenameDirectoryDialog = () => {
    this.props.onClose();
    this.setState({ isRenameDirectoryDialogOpened: true });
  };

  showCreateDirectoryDialog = () => {
    this.props.onClose();
    this.setState({ isCreateDirectoryDialogOpened: true });
  };

  createNewFile = () => {
    this.props.onClose();
    this.props.toggleCreateFileDialog();
  };

  handleCloseDialogs = () => {
    this.setState({
      isCreateDirectoryDialogOpened: false,
      isDeleteDirectoryDialogOpened: false,
      isRenameDirectoryDialogOpened: false
    });
  };

  showInFileManager = () => {
    this.props.onClose();
    this.props.openDirectory(this.props.directoryPath);
  };

  addExistingFile = () => {
    this.props.onClose();
    this.fileInput.click();
  };

  onFail = (message) => {
    console.log('Camera Failed: ' + message);
  };

  onCameraSuccess = (imageURL) => {
    window.resolveLocalFileSystemURL(imageURL, (fp) => {
      this.moveFile(fp.nativeURL);
    }, () => { console.log('Failed to get filesystem url'); });
  };

  moveFile = (filePath) => {
    const fileName = 'IMG_TS' + AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + '.jpg';
    const newFilePath =
      normalizePath(this.props.directoryPath) +
      AppConfig.dirSeparator +
      fileName;

    PlatformIO.renameFilePromise(filePath, newFilePath)
      .then(() => {
        this.props.showNotification(
          'File ' + newFilePath + ' successfully imported.',
          'default',
          true
        );
        this.props.reflectCreateEntry(newFilePath, true);
        return true;
      })
      .catch(error => {
        // TODO showAlertDialog("Saving " + filePath + " failed.");
        console.error('Save to file ' + newFilePath + ' failed ' + error);
        this.props.showNotification(
          'Importing file ' + newFilePath + ' failed.',
          'error',
          true
        );
        return true;
      });
  };

  loadImageLocal = () => {
    this.props.onClose();
    navigator.camera.getPicture(this.onCameraSuccess, this.onFail, {
      destinationType: Camera.DestinationType.FILE_URI,
      sourceType: Camera.PictureSourceType.PHOTOLIBRARY
    });
  };

  cameraTakePicture = () => {
    this.props.onClose();
    navigator.camera.getPicture(this.onCameraSuccess, this.onFail, {
      // quality: 50,
      destinationType: Camera.DestinationType.FILE_URI, // DATA_URL, // Return base64 encoded string
      // encodingType: Camera.EncodingType.JPEG,
      mediaType: Camera.MediaType.PICTURE // ALLMEDIA
    });
  };

  handleFileInputChange = (selection: Object) => {
    // console.log("Selected File: "+JSON.stringify(selection.currentTarget.files[0]));
    const file = selection.currentTarget.files[0];
    const filePath =
      normalizePath(this.props.directoryPath) +
      AppConfig.dirSeparator +
      decodeURIComponent(file.name);

    const reader = new FileReader();
    reader.onload = event => {
      // console.log('Content on file read complete: ' + JSON.stringify(event));
      // change name for ios fakepath
      // if (AppConfig.isCordovaiOS) {
      //   const fileExt = extractFileExtension(addFileInputName);
      //   addFileInputName = AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + fileExt;
      // }
      // TODO event.currentTarget.result is ArrayBuffer
      // Sample call from PRO version using content = Utils.base64ToArrayBuffer(baseString);
      PlatformIO.getPropertiesPromise(filePath).then((entryProps) => {
        if (entryProps) {
          this.props.showNotification(
            'File with the same name already exist, importing skipped!',
            'warning',
            true
          );
        } else {
          PlatformIO.saveBinaryFilePromise(
            filePath,
            event.currentTarget.result,
            true
          )
            .then(() => {
              this.props.showNotification(
                'File ' + filePath + ' successfully imported.',
                'default',
                true
              );
              this.props.reflectCreateEntry(filePath, true);
              return true;
            })
            .catch(error => {
              // TODO showAlertDialog("Saving " + filePath + " failed.");
              console.error('Save to file ' + filePath + ' failed ' + error);
              this.props.showNotification(
                'Importing file ' + filePath + ' failed.',
                'error',
                true
              );
              return true;
            });
        }
        return true;
      }).catch((err) => {
        console.log('Error getting properties ' + err);
      });
    };

    if (AppConfig.isCordova) {
      reader.readAsDataURL(file);
    } else {
      reader.readAsArrayBuffer(file);
    }
  };

  render() {
    return (
      <div style={{ overflowY: 'hidden !important' }}>
        <RenameDirectoryDialog
          key={uuidv1()}
          open={this.state.isRenameDirectoryDialogOpened}
          onClose={this.handleCloseDialogs}
          selectedDirectoryPath={this.props.directoryPath}
        />
        <CreateDirectoryDialog
          key={uuidv1()}
          open={this.state.isCreateDirectoryDialogOpened}
          onClose={this.handleCloseDialogs}
          selectedDirectoryPath={this.props.directoryPath}
        />
        <ConfirmDialog
          open={this.state.isDeleteDirectoryDialogOpened}
          onClose={this.handleCloseDialogs}
          title={i18n.t('core:deleteDirectoryTitleConfirm')}
          content={i18n.t('core:deleteDirectoryContentConfirm', {
            dirPath: this.props.directoryPath
              ? extractFileName(this.props.directoryPath)
              : ''
          })}
          confirmCallback={result => {
            if (result) {
              this.props.deleteDirectory(this.props.directoryPath);
            }
          }}
          confirmDialogContent={'confirmDialogContent'}
          cancelDialogTID={'cancelDeleteDirectoryDialog'}
          confirmDialogTID={'confirmDeleteDirectoryDialog'}
        />
        <Menu
          anchorEl={this.props.anchorEl}
          open={this.props.open}
          onClose={this.props.onClose}
        >
          {this.props.perspectiveMode && (
            <MenuItem
              data-tid="openDirectory"
              onClick={this.openDirectory}
            >
              <ListItemIcon>
                <OpenFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:openDirectory')} />
            </MenuItem>
          )}
          {!this.props.perspectiveMode && (
            <MenuItem
              data-tid="openParentDirectory"
              onClick={this.openParentDirectory}
            >
              <ListItemIcon>
                <OpenFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:openParentDirectory')} />
            </MenuItem>
          )}
          {!this.props.perspectiveMode && (
            <MenuItem data-tid="reloadDirectory" onClick={this.reloadDirectory}>
              <ListItemIcon>
                <AutoRenew />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:reloadDirectory')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && (
            <MenuItem
              data-tid="renameDirectory"
              onClick={this.showRenameDirectoryDialog}
            >
              <ListItemIcon>
                <RenameFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:renameDirectory')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && (
            <MenuItem
              data-tid="deleteDirectory"
              onClick={this.showDeleteDirectoryDialog}
            >
              <ListItemIcon>
                <DeleteForeverIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:deleteDirectory')} />
            </MenuItem>
          )}
          {AppConfig.isWeb && (
            <MenuItem
              data-tid="showInFileManager"
              onClick={this.showInFileManager}
            >
              <ListItemIcon>
                <OpenFolderNativelyIcon />
              </ListItemIcon>
              <ListItemText
                inset
                primary={i18n.t('core:showInFileManager')}
              />
            </MenuItem>
          )}
          {!this.props.perspectiveMode && (
            <Divider />
          )}
          {!this.props.isReadOnlyMode && !this.props.perspectiveMode && (
            <MenuItem
              data-tid="newSubDirectory"
              onClick={this.showCreateDirectoryDialog}
            >
              <ListItemIcon>
                <NewFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:newSubdirectory')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && !this.props.perspectiveMode && (
            <MenuItem data-tid="createNewFile" onClick={this.createNewFile}>
              <ListItemIcon>
                <NewFileIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:newFileNote')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && !this.props.perspectiveMode && (
            <MenuItem data-tid="addExistingFile" onClick={this.addExistingFile}>
              <ListItemIcon>
                <AddExistingFileIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:showAddFileDialog')} />
            </MenuItem>
          )}
          {AppConfig.isCordova && (
            <MenuItem data-tid="takePicture" onClick={this.cameraTakePicture}>
              <ListItemIcon>
                <AddExistingFileIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:cameraTakePicture')} />
            </MenuItem>
          )}
          <Divider />
          <MenuItem data-tid="showProperties" onClick={this.showProperties}>
            <ListItemIcon>
              <SettingsIcon />
            </ListItemIcon>
            <ListItemText
              inset
              primary={i18n.t('core:directoryPropertiesTitle')}
            />
          </MenuItem>
        </Menu>
        <input
          style={{ display: 'none' }}
          ref={input => {
            this.fileInput = input;
          }}
          accept="*"
          type="file"
          onChange={this.handleFileInputChange}
        />
      </div>
    );
  }
}

export default DirectoryMenu;
<commit_msg>fixing removed showinfilemanager button<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import React from 'react';
import uuidv1 from 'uuid';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
import Divider from '@material-ui/core/Divider';
import OpenFolderIcon from '@material-ui/icons/SubdirectoryArrowLeft';
import AddExistingFileIcon from '@material-ui/icons/ExitToApp';
import OpenFolderNativelyIcon from '@material-ui/icons/Launch';
import AutoRenew from '@material-ui/icons/Autorenew';
import NewFileIcon from '@material-ui/icons/InsertDriveFile';
import NewFolderIcon from '@material-ui/icons/CreateNewFolder';
import RenameFolderIcon from '@material-ui/icons/FormatTextdirectionLToR';
import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
import SettingsIcon from '@material-ui/icons/Settings';
import ConfirmDialog from '../dialogs/ConfirmDialog';
import CreateDirectoryDialog from '../dialogs/CreateDirectoryDialog';
import RenameDirectoryDialog from '../dialogs/RenameDirectoryDialog';
import AppConfig from '../../config';
import i18n from '../../services/i18n';
import { extractFileName, extractParentDirectoryPath, normalizePath } from '../../utils/paths'; // extractFileExtension
import PlatformIO from '../../services/platform-io';
import { formatDateTime4Tag } from '../../utils/misc';

type Props = {
  open?: boolean,
  onClose: () => void,
  anchorEl?: Object | null,
  directoryPath?: string,
  loadDirectoryContent: (path: string) => void,
  openDirectory: (path: string) => void,
  showInFileManager: (path: string) => void,
  openFile: (path: string, isFile: boolean) => void,
  deleteDirectory: (path: string) => void,
  reflectCreateEntry?: (path: string, isFile: boolean) => void,
  toggleCreateFileDialog?: () => void,
  loadParentDirectoryContent?: () => void,
  openFileNatively?: (path: string) => void,
  perspectiveMode: boolean,
  showNotification?: (
    text: string,
    notificationType: string,
    autohide: boolean
  ) => void,
  isReadOnlyMode: boolean
};

type State = {
  isCreateDirectoryDialogOpened?: boolean,
  isDeleteDirectoryDialogOpened?: boolean,
  isRenameDirectoryDialogOpened?: boolean
};

class DirectoryMenu extends React.Component<Props, State> {
  fileInput: Object | null;

  state = {
    isCreateDirectoryDialogOpened: false,
    isDeleteDirectoryDialogOpened: false,
    isRenameDirectoryDialogOpened: false
  };

  reloadDirectory = () => {
    this.props.onClose();
    this.props.loadDirectoryContent(this.props.directoryPath);
  };

  openParentDirectory = () => {
    this.props.onClose();
    this.props.loadParentDirectoryContent();
  };

  openDirectory = () => {
    this.props.onClose();
    this.props.loadDirectoryContent(this.props.directoryPath);
  };

  showProperties = () => {
    this.props.onClose();
    this.props.openFile(this.props.directoryPath, false);
  };

  showDeleteDirectoryDialog = () => {
    this.props.onClose();
    this.setState({ isDeleteDirectoryDialogOpened: true });
  };

  showRenameDirectoryDialog = () => {
    this.props.onClose();
    this.setState({ isRenameDirectoryDialogOpened: true });
  };

  showCreateDirectoryDialog = () => {
    this.props.onClose();
    this.setState({ isCreateDirectoryDialogOpened: true });
  };

  createNewFile = () => {
    this.props.onClose();
    this.props.toggleCreateFileDialog();
  };

  handleCloseDialogs = () => {
    this.setState({
      isCreateDirectoryDialogOpened: false,
      isDeleteDirectoryDialogOpened: false,
      isRenameDirectoryDialogOpened: false
    });
  };

  showInFileManager = () => {
    this.props.onClose();
    this.props.openDirectory(this.props.directoryPath);
  };

  addExistingFile = () => {
    this.props.onClose();
    this.fileInput.click();
  };

  onFail = (message) => {
    console.log('Camera Failed: ' + message);
  };

  onCameraSuccess = (imageURL) => {
    window.resolveLocalFileSystemURL(imageURL, (fp) => {
      this.moveFile(fp.nativeURL);
    }, () => { console.log('Failed to get filesystem url'); });
  };

  moveFile = (filePath) => {
    const fileName = 'IMG_TS' + AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + '.jpg';
    const newFilePath =
      normalizePath(this.props.directoryPath) +
      AppConfig.dirSeparator +
      fileName;

    PlatformIO.renameFilePromise(filePath, newFilePath)
      .then(() => {
        this.props.showNotification(
          'File ' + newFilePath + ' successfully imported.',
          'default',
          true
        );
        this.props.reflectCreateEntry(newFilePath, true);
        return true;
      })
      .catch(error => {
        // TODO showAlertDialog("Saving " + filePath + " failed.");
        console.error('Save to file ' + newFilePath + ' failed ' + error);
        this.props.showNotification(
          'Importing file ' + newFilePath + ' failed.',
          'error',
          true
        );
        return true;
      });
  };

  loadImageLocal = () => {
    this.props.onClose();
    navigator.camera.getPicture(this.onCameraSuccess, this.onFail, {
      destinationType: Camera.DestinationType.FILE_URI,
      sourceType: Camera.PictureSourceType.PHOTOLIBRARY
    });
  };

  cameraTakePicture = () => {
    this.props.onClose();
    navigator.camera.getPicture(this.onCameraSuccess, this.onFail, {
      // quality: 50,
      destinationType: Camera.DestinationType.FILE_URI, // DATA_URL, // Return base64 encoded string
      // encodingType: Camera.EncodingType.JPEG,
      mediaType: Camera.MediaType.PICTURE // ALLMEDIA
    });
  };

  handleFileInputChange = (selection: Object) => {
    // console.log("Selected File: "+JSON.stringify(selection.currentTarget.files[0]));
    const file = selection.currentTarget.files[0];
    const filePath =
      normalizePath(this.props.directoryPath) +
      AppConfig.dirSeparator +
      decodeURIComponent(file.name);

    const reader = new FileReader();
    reader.onload = event => {
      // console.log('Content on file read complete: ' + JSON.stringify(event));
      // change name for ios fakepath
      // if (AppConfig.isCordovaiOS) {
      //   const fileExt = extractFileExtension(addFileInputName);
      //   addFileInputName = AppConfig.beginTagContainer + formatDateTime4Tag(new Date(), true) + AppConfig.endTagContainer + fileExt;
      // }
      // TODO event.currentTarget.result is ArrayBuffer
      // Sample call from PRO version using content = Utils.base64ToArrayBuffer(baseString);
      PlatformIO.getPropertiesPromise(filePath).then((entryProps) => {
        if (entryProps) {
          this.props.showNotification(
            'File with the same name already exist, importing skipped!',
            'warning',
            true
          );
        } else {
          PlatformIO.saveBinaryFilePromise(
            filePath,
            event.currentTarget.result,
            true
          )
            .then(() => {
              this.props.showNotification(
                'File ' + filePath + ' successfully imported.',
                'default',
                true
              );
              this.props.reflectCreateEntry(filePath, true);
              return true;
            })
            .catch(error => {
              // TODO showAlertDialog("Saving " + filePath + " failed.");
              console.error('Save to file ' + filePath + ' failed ' + error);
              this.props.showNotification(
                'Importing file ' + filePath + ' failed.',
                'error',
                true
              );
              return true;
            });
        }
        return true;
      }).catch((err) => {
        console.log('Error getting properties ' + err);
      });
    };

    if (AppConfig.isCordova) {
      reader.readAsDataURL(file);
    } else {
      reader.readAsArrayBuffer(file);
    }
  };

  render() {
    return (
      <div style={{ overflowY: 'hidden !important' }}>
        <RenameDirectoryDialog
          key={uuidv1()}
          open={this.state.isRenameDirectoryDialogOpened}
          onClose={this.handleCloseDialogs}
          selectedDirectoryPath={this.props.directoryPath}
        />
        <CreateDirectoryDialog
          key={uuidv1()}
          open={this.state.isCreateDirectoryDialogOpened}
          onClose={this.handleCloseDialogs}
          selectedDirectoryPath={this.props.directoryPath}
        />
        <ConfirmDialog
          open={this.state.isDeleteDirectoryDialogOpened}
          onClose={this.handleCloseDialogs}
          title={i18n.t('core:deleteDirectoryTitleConfirm')}
          content={i18n.t('core:deleteDirectoryContentConfirm', {
            dirPath: this.props.directoryPath
              ? extractFileName(this.props.directoryPath)
              : ''
          })}
          confirmCallback={result => {
            if (result) {
              this.props.deleteDirectory(this.props.directoryPath);
            }
          }}
          confirmDialogContent={'confirmDialogContent'}
          cancelDialogTID={'cancelDeleteDirectoryDialog'}
          confirmDialogTID={'confirmDeleteDirectoryDialog'}
        />
        <Menu
          anchorEl={this.props.anchorEl}
          open={this.props.open}
          onClose={this.props.onClose}
        >
          {this.props.perspectiveMode && (
            <MenuItem
              data-tid="openDirectory"
              onClick={this.openDirectory}
            >
              <ListItemIcon>
                <OpenFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:openDirectory')} />
            </MenuItem>
          )}
          {!this.props.perspectiveMode && (
            <MenuItem
              data-tid="openParentDirectory"
              onClick={this.openParentDirectory}
            >
              <ListItemIcon>
                <OpenFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:openParentDirectory')} />
            </MenuItem>
          )}
          {!this.props.perspectiveMode && (
            <MenuItem data-tid="reloadDirectory" onClick={this.reloadDirectory}>
              <ListItemIcon>
                <AutoRenew />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:reloadDirectory')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && (
            <MenuItem
              data-tid="renameDirectory"
              onClick={this.showRenameDirectoryDialog}
            >
              <ListItemIcon>
                <RenameFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:renameDirectory')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && (
            <MenuItem
              data-tid="deleteDirectory"
              onClick={this.showDeleteDirectoryDialog}
            >
              <ListItemIcon>
                <DeleteForeverIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:deleteDirectory')} />
            </MenuItem>
          )}
          {!AppConfig.isWeb && (
            <MenuItem
              data-tid="showInFileManager"
              onClick={this.showInFileManager}
            >
              <ListItemIcon>
                <OpenFolderNativelyIcon />
              </ListItemIcon>
              <ListItemText
                inset
                primary={i18n.t('core:showInFileManager')}
              />
            </MenuItem>
          )}
          {!this.props.perspectiveMode && (
            <Divider />
          )}
          {!this.props.isReadOnlyMode && !this.props.perspectiveMode && (
            <MenuItem
              data-tid="newSubDirectory"
              onClick={this.showCreateDirectoryDialog}
            >
              <ListItemIcon>
                <NewFolderIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:newSubdirectory')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && !this.props.perspectiveMode && (
            <MenuItem data-tid="createNewFile" onClick={this.createNewFile}>
              <ListItemIcon>
                <NewFileIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:newFileNote')} />
            </MenuItem>
          )}
          {!this.props.isReadOnlyMode && !this.props.perspectiveMode && (
            <MenuItem data-tid="addExistingFile" onClick={this.addExistingFile}>
              <ListItemIcon>
                <AddExistingFileIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:showAddFileDialog')} />
            </MenuItem>
          )}
          {AppConfig.isCordova && (
            <MenuItem data-tid="takePicture" onClick={this.cameraTakePicture}>
              <ListItemIcon>
                <AddExistingFileIcon />
              </ListItemIcon>
              <ListItemText inset primary={i18n.t('core:cameraTakePicture')} />
            </MenuItem>
          )}
          <Divider />
          <MenuItem data-tid="showProperties" onClick={this.showProperties}>
            <ListItemIcon>
              <SettingsIcon />
            </ListItemIcon>
            <ListItemText
              inset
              primary={i18n.t('core:directoryPropertiesTitle')}
            />
          </MenuItem>
        </Menu>
        <input
          style={{ display: 'none' }}
          ref={input => {
            this.fileInput = input;
          }}
          accept="*"
          type="file"
          onChange={this.handleFileInputChange}
        />
      </div>
    );
  }
}

export default DirectoryMenu;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
YUI().use('mojito-client-store', 'test', 'querystring-stringify-simple', function (Y) {

    var suite = new Y.Test.Suite('mojito-client-store-tests'),
        A = Y.Assert,
        OA = Y.ObjectAssert;

    suite.add(new Y.Test.Case({

        setUp: function () {
            this.store = new Y.mojito.ResourceStore({
                appConfig: {},
                pathToRoot: '/root'
            });
        },

        tearDown: function () {

        },

        'test buildUrl': function () {
            var self = this,
                tests = [
                    {
                        input: '/test/',
                        expectation: '/root/test/'
                    },
                    {
                        input: 'test',
                        expectation: '/root/test'
                    },
                    {
                        context: {
                            env: 'dev'
                        },
                        input: 'test',
                        expectation: '/root/test?env=dev'
                    },
                    {
                        context: {
                            env: 'dev',
                            test: 'test'
                        },
                        input: 'test',
                        expectation: '/root/test?env=dev&test=test'
                    }
                ];

            Y.Array.each(tests, function (test) {
                var output = self.store.buildUrl(test.input, test.context);
                A.areEqual(test.expectation, output, 'buildUrl did not create the correct url');
            });
        }

    }));

    Y.Test.Runner.add(suite);

});
<commit_msg>fix up (method name changed, scope issue)<commit_after>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
YUI().use('mojito-client-store', 'test', 'querystring-stringify-simple', function (Y) {

    var suite = new Y.Test.Suite('mojito-client-store-tests'),
        A = Y.Assert,
        OA = Y.ObjectAssert;

    suite.add(new Y.Test.Case({

        setUp: function () {
            this.store = new Y.mojito.ResourceStore({
                appConfig: {},
                pathToRoot: '/root'
            });
        },

        tearDown: function () {

        },

        'test buildUrl': function () {
            var self = this,
                tests = [
                    {
                        input: '/test/',
                        expectation: '/root/test/'
                    },
                    {
                        input: 'test',
                        expectation: '/root/test'
                    },
                    {
                        context: {
                            env: 'dev'
                        },
                        input: 'test',
                        expectation: '/root/test?env=dev'
                    },
                    {
                        context: {
                            env: 'dev',
                            test: 'test'
                        },
                        input: 'test',
                        expectation: '/root/test?env=dev&test=test'
                    }
                ];

            Y.Array.each(tests, function (test) {
                var output = this.store._buildUrl(test.input, test.context);
                A.areEqual(test.expectation, output, 'buildUrl did not create the correct url');
            }, this);
        }

    }));

    Y.Test.Runner.add(suite);

});
<|endoftext|>


<commit_before>var UTIL = require("../util");
var GeoObject = require("../object").GeoObject;
var WS_UTIL = require("./util");
var Schema = require("../feature/schema").Schema;
var Filter = require("../filter").Filter;
var Projection = require("../proj").Projection;

var geotools = Packages.org.geotools;
var DefaultQuery = geotools.data.DefaultQuery;
var ListFeatureCollection = geotools.data.collection.ListFeatureCollection;
var Transaction = geotools.data.Transaction;

/** api: (define)
 *  module = workspace
 *  class = Workspace
 */
var Workspace = UTIL.extend(GeoObject, {

    /** api: constructor
     *  .. class:: Workspace
     *
     *      A Workspace instance should not be created directly.
     *      Create an instance of a Workspace subclass instead.
     */
    constructor: function Workspace(config) {
        if (config) {
            UTIL.applyIf(this, config);
            this._store = this._create(config);
        }
    },

    /** private: method[_create]
     *  :arg config: ``Object``
     *  :returns: ``org.geotools.data.AbstractDataStore?``
     *
     *  Create the underlying store for the workspace.
     */
    _create: function(config) {
        throw new Error("Workspace subclasses must implement _create.");
    },

    /** api: property[names]
     *  ``Array``
     *  The available layer names in the workspace.
     */
    get names() {
        var _names = this._store.getTypeNames();
        var len = _names.length;
        var names = new Array(len);
        for (var i=0; i<len; ++i) {
            names[i] = String(_names[i]);
        }
        return names;
    },

    /** api: method[get]
     *  :arg name: ``String`` Layer name.
     *  :returns: :class:`layer.Layer`
     *
     *  Get a layer by name.  Returns ``undefined`` if name doesn't correspond
     *  to a layer source in the workspace.
     */
    get: function(name) {
        var layer;
        if (this.names.indexOf(name) >= 0) {
            try {
                var _source = this._store.getFeatureSource(name);
            } catch (err) {
                throw new Error("Failed to create layer from source named '" + name + "'.");
            }
            var Layer = require("../layer").Layer;
            layer = Layer.from_(_source, this);
        }
        return layer;
    },

    /** api: property[layers]
     *  ``Array``
     *  The available layers in the workspace.
     */
    get layers() {
        var layers = [];
        this.names.forEach(function(name) {
            try {
                var layer = this.get(name);
                if (layer) {
                    layers.push(layer);
                }
            } catch (err) {
                // pass
            }
        }, this);
        return layers;
    },

    /** private: method[_createSource]
     *  :arg _schema:
     *  :returns: ``FeatureSource``
     */
    _createSource: function(_schema, type) {
        this._store.createSchema(_schema);
        return this._store.getFeatureSource(_schema.getName());
    },

    /** api: method[add]
     *  :arg layer: :class:`layer.Layer` The layer to be added.
     *  :arg options: ``Object`` Options for adding the layer.
     *
     *  Options:
     *   * `name`: ``String`` Name for the new layer.
     *   * `filter`: :class:`filter.Filter` Filter to apply to features before adding.
     *   * `projection: :class:`proj.Projection` Destination projection for the layer.
     *
     *  :returns: :class:`layer.Layer`
     *
     *  Create a new layer in this workspace with the features from an existing
     *  layer.  If a layer with the same name already exists in this workspace,
     *  you must provide a new name for the layer.
     */
    add: function(layer, options) {
        var Layer = require("../layer").Layer;
        options = options || {};

        var filter = options.filter || Filter.PASS;
        if (!(filter instanceof Filter)) {
            filter = new Filter(filter);
        }

        var name = options.name || layer.name;
        if (this.get(name)) {
            throw new Error("A layer named '" + options.name + "' already exists in the workspace.");
        }

        var projection = options.projection;
        if (projection && !(projection instanceof Projection)) {
            projection = new Projection(projection);
        }

        // clone the schema, optionally changing geometry projection
        var geomField = layer.schema.geometry;
        var schema = layer.schema.clone({
            name: options.name,
            fields: [{
                name: geomField.name,
                type: geomField.type,
                projection: projection || geomField.projection
            }]
        });

        var _source = this._createSource(schema._schema);

        var query = new DefaultQuery(layer.name, filter._filter);
        if (projection) {
            if (layer.projection) {
                query.setCoordinateSystem(layer.projection._projection);
            }
            query.setCoordinateSystemReproject(projection._projection);
        }

        // loop through batches of features and add to the new layer
        var iterator = layer._source.dataStore.getFeatureReader(query, Transaction.AUTO_COMMIT);
        var i, _features;
        var _schema = layer._source.getSchema();
        while (true) {
            i = 0;
            _features = new ListFeatureCollection(_schema);
            while (iterator.hasNext() && i < 1000) {
                _features.add(iterator.next());
                ++i;
            }
            if (_features.isEmpty()) {
                break;
            }
            _source.addFeatures(_features);
        }
        iterator.close();
        return Layer.from_(_source, this);
    },

    /** private: method[_onFeatureAdd]
     *  :arg feature: :class:`feature.Feature`
     *
     *  Do any specific processing on a feature before it is added to a layer.
     */
    _onFeatureAdd: function(feature) {
        // pass
    },

    /** private: property[config]
     */
    get config() {
        return {
            type: this.constructor.name
        };
    },

    /** api: method[close]
     *
     *  Close the workspace.  This discards any existing connection to the
     *  underlying data store and discards the reference to the store.
     */
    close: function() {
        if (this._store) {
            this._store.dispose();
            delete this._store;
        }
    },

    /** private: method[toFullString]
     */
    toFullString: function() {
        return '["' + this.names.join('", "') + '"]';
    }

});

/** private: staticmethod[from_]
 *  :arg _store: ``org.geotools.data.DataStore`` A GeoTools store.
 *  :returns: :class`Workspace`
 *
 *  Create a geoscript workspace object from a GeoTools store.
 */
Workspace.from_ = function(_store) {
    var workspace;
    try {
        workspace = WS_UTIL.from_(_store);
    } catch (err) {
        // as a fallback, use a generic workspace and hope for the best
        workspace = new Workspace();
        workspace._store = _store;
    }
    return workspace;
};

exports.Workspace = Workspace;
<commit_msg>Streaming feature copy.<commit_after>var UTIL = require("../util");
var GeoObject = require("../object").GeoObject;
var WS_UTIL = require("./util");
var Schema = require("../feature/schema").Schema;
var Filter = require("../filter").Filter;
var Projection = require("../proj").Projection;

var geotools = Packages.org.geotools;
var DefaultQuery = geotools.data.DefaultQuery;
var Transaction = geotools.data.Transaction;
var DefaultTransaction = geotools.data.DefaultTransaction;

/** api: (define)
 *  module = workspace
 *  class = Workspace
 */
var Workspace = UTIL.extend(GeoObject, {

    /** api: constructor
     *  .. class:: Workspace
     *
     *      A Workspace instance should not be created directly.
     *      Create an instance of a Workspace subclass instead.
     */
    constructor: function Workspace(config) {
        if (config) {
            UTIL.applyIf(this, config);
            this._store = this._create(config);
        }
    },

    /** private: method[_create]
     *  :arg config: ``Object``
     *  :returns: ``org.geotools.data.AbstractDataStore?``
     *
     *  Create the underlying store for the workspace.
     */
    _create: function(config) {
        throw new Error("Workspace subclasses must implement _create.");
    },

    /** api: property[names]
     *  ``Array``
     *  The available layer names in the workspace.
     */
    get names() {
        var _names = this._store.getTypeNames();
        var len = _names.length;
        var names = new Array(len);
        for (var i=0; i<len; ++i) {
            names[i] = String(_names[i]);
        }
        return names;
    },

    /** api: method[get]
     *  :arg name: ``String`` Layer name.
     *  :returns: :class:`layer.Layer`
     *
     *  Get a layer by name.  Returns ``undefined`` if name doesn't correspond
     *  to a layer source in the workspace.
     */
    get: function(name) {
        var layer;
        if (this.names.indexOf(name) >= 0) {
            try {
                var _source = this._store.getFeatureSource(name);
            } catch (err) {
                throw new Error("Failed to create layer from source named '" + name + "'.");
            }
            var Layer = require("../layer").Layer;
            layer = Layer.from_(_source, this);
        }
        return layer;
    },

    /** api: property[layers]
     *  ``Array``
     *  The available layers in the workspace.
     */
    get layers() {
        var layers = [];
        this.names.forEach(function(name) {
            try {
                var layer = this.get(name);
                if (layer) {
                    layers.push(layer);
                }
            } catch (err) {
                // pass
            }
        }, this);
        return layers;
    },

    /** private: method[_createSource]
     *  :arg _schema:
     *  :returns: ``FeatureSource``
     */
    _createSource: function(_schema, type) {
        this._store.createSchema(_schema);
        return this._store.getFeatureSource(_schema.getName());
    },

    /** api: method[add]
     *  :arg layer: :class:`layer.Layer` The layer to be added.
     *  :arg options: ``Object`` Options for adding the layer.
     *
     *  Options:
     *   * `name`: ``String`` Name for the new layer.
     *   * `filter`: :class:`filter.Filter` Filter to apply to features before adding.
     *   * `projection: :class:`proj.Projection` Destination projection for the layer.
     *
     *  :returns: :class:`layer.Layer`
     *
     *  Create a new layer in this workspace with the features from an existing
     *  layer.  If a layer with the same name already exists in this workspace,
     *  you must provide a new name for the layer.
     */
    add: function(layer, options) {
        var Layer = require("../layer").Layer;
        options = options || {};

        var filter = options.filter || Filter.PASS;
        if (!(filter instanceof Filter)) {
            filter = new Filter(filter);
        }

        var name = options.name || layer.name;
        if (this.get(name)) {
            throw new Error("A layer named '" + options.name + "' already exists in the workspace.");
        }

        var projection = options.projection;
        if (projection && !(projection instanceof Projection)) {
            projection = new Projection(projection);
        }

        // clone the schema, optionally changing geometry projection
        var geomField = layer.schema.geometry;
        var schema = layer.schema.clone({
            name: options.name,
            fields: [{
                name: geomField.name,
                type: geomField.type,
                projection: projection || geomField.projection
            }]
        });

        var _source = this._createSource(schema._schema);

        var query = new DefaultQuery(layer.name, filter._filter);
        if (projection) {
            if (layer.projection) {
                query.setCoordinateSystem(layer.projection._projection);
            }
            query.setCoordinateSystemReproject(projection._projection);
        }

        var reader = layer._source.getDataStore().getFeatureReader(query, Transaction.AUTO_COMMIT);
        var transaction = new DefaultTransaction();
        var writer = _source.getDataStore().getFeatureWriterAppend(schema._schema.getName(), transaction);
        var inFeature, outFeature, geom;

        try {
            while (reader.hasNext()) {
                inFeature = reader.next();
                outFeature = writer.next();
                outFeature.setAttributes(inFeature.getAttributes());

                // mask empty geometry or PostGIS will complain
                geom = outFeature.getDefaultGeometry();
                if (geom != null && geom.isEmpty()) {
                    outFeature.setDefaultGeometry(null);
                }

                writer.write();
            }
            transaction.commit();
        } finally {
            // TODO deal with failures closing and before
            transaction.close();
        }

        return Layer.from_(_source, this);
    },

    /** private: method[_onFeatureAdd]
     *  :arg feature: :class:`feature.Feature`
     *
     *  Do any specific processing on a feature before it is added to a layer.
     */
    _onFeatureAdd: function(feature) {
        // pass
    },

    /** private: property[config]
     */
    get config() {
        return {
            type: this.constructor.name
        };
    },

    /** api: method[close]
     *
     *  Close the workspace.  This discards any existing connection to the
     *  underlying data store and discards the reference to the store.
     */
    close: function() {
        if (this._store) {
            this._store.dispose();
            delete this._store;
        }
    },

    /** private: method[toFullString]
     */
    toFullString: function() {
        return '["' + this.names.join('", "') + '"]';
    }

});

/** private: staticmethod[from_]
 *  :arg _store: ``org.geotools.data.DataStore`` A GeoTools store.
 *  :returns: :class`Workspace`
 *
 *  Create a geoscript workspace object from a GeoTools store.
 */
Workspace.from_ = function(_store) {
    var workspace;
    try {
        workspace = WS_UTIL.from_(_store);
    } catch (err) {
        // as a fallback, use a generic workspace and hope for the best
        workspace = new Workspace();
        workspace._store = _store;
    }
    return workspace;
};

exports.Workspace = Workspace;
<|endoftext|>


<commit_before>import Microcontroller from '../components/Microcontroller';
import * as MicrocontrollerActions from '../actions/microcontroller';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { values } from 'ramda';

const getVisiblePins = (inPins, filter) => {
  let pins = inPins;

  if (filter.enabled) {
    pins = pins.filter(pin => pin.enabled);
  }

  if (filter.digital) {
    pins = pins.filter(pin => !pin.isAnalogPin);
  }

  if (filter.analog) {
    pins = pins.filter(pin => pin.isAnalogPin);
  }

  return pins;
};

function mapStateToProps(state) {
  return {
    pins: getVisiblePins(values(state.microcontroller.pins),
              state.microcontroller.visibilityFilter),
    connectionState: state.microcontroller.connectionState,
    mapping: state.microcontroller.mapping,
    name: state.microcontroller.name,
    visibilityFilter: state.microcontroller.visibilityFilter,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(MicrocontrollerActions, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Microcontroller);
<commit_msg>Add replay prop<commit_after>import Microcontroller from '../components/Microcontroller';
import * as MicrocontrollerActions from '../actions/microcontroller';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { values } from 'ramda';

const getVisiblePins = (inPins, filter) => {
  let pins = inPins;

  if (filter.enabled) {
    pins = pins.filter(pin => pin.enabled);
  }

  if (filter.digital) {
    pins = pins.filter(pin => !pin.isAnalogPin);
  }

  if (filter.analog) {
    pins = pins.filter(pin => pin.isAnalogPin);
  }

  return pins;
};

function mapStateToProps(state) {
  return {
    pins: getVisiblePins(
              values(state.microcontroller.pins),
              state.microcontroller.visibilityFilter),
    connectionState: state.microcontroller.connectionState,
    mapping: state.microcontroller.mapping,
    name: state.microcontroller.name,
    visibilityFilter: state.microcontroller.visibilityFilter,
    replay: state.microcontroller.replay,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(MicrocontrollerActions, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Microcontroller);
<|endoftext|>


<commit_before>
function DynamicAudio(args) {
    if (this instanceof arguments.callee) {
        if (typeof this.init === "function") {
            this.init.apply(this, (args && args.callee) ? args : arguments);
        }
    }
    else {
        return new arguments.callee(arguments);
    }
}

DynamicAudio.VERSION = "<%= version %>";
DynamicAudio.nextId = 1;

DynamicAudio.prototype = {
    nextId: null,
    swf: 'dynamicaudio.swf',

    audioElement: null,
    flashWrapper: null,
    flashElement: null,

    init: function(opts) {
        var self = this;
        self.id = DynamicAudio.nextId++;

        if (opts && typeof opts['swf'] !== 'undefined') {
            self.swf = opts['swf'];
        }

        // Attempt to create an audio element
        if (typeof Audio !== 'undefined') {
            self.audioElement = new Audio();
            if (self.audioElement.mozSetup) {
                self.audioElement.mozSetup(2, 44100);
                return;
            }
        }

        // Fall back to creating flash player
        self.audioElement = null;
        self.flashWrapper = document.createElement('div');
        self.flashWrapper.id = 'dynamicaudio-flashwrapper-'+self.id;
        // Credit to SoundManager2 for this:
        var s = self.flashWrapper.style;
        s['position'] = 'fixed';
        s['width'] = s['height'] = '8px'; // must be at least 6px for flash to run fast
        s['bottom'] = s['left'] = '0px';
        s['overflow'] = 'hidden';
        self.flashElement = document.createElement('div');
        self.flashElement.id = 'dynamicaudio-flashelement-'+self.id;
        self.flashWrapper.appendChild(self.flashElement);

        document.body.appendChild(self.flashWrapper);

        swfobject.embedSWF(
            self.swf,
            self.flashElement.id,
            "8",
            "8",
            "9.0.0",
            null,
            null,
            {'allowScriptAccess': 'always'},
            null,
            function(e) {
                self.flashElement = e.ref;
            }
        );
    },

    write: function(samples) {
        if (this.audioElement !== null) {
            this.audioElement.mozWriteAudio(samples);
        }
        else if (this.flashElement !== null) {
            var out = new Array(samples.length);
            for (var i = samples.length-1; i !== 0; i--) {
                out[i] = Math.floor(samples[i] * 32768);
            }
            this.flashElement.write(out.join(' '));
        }
    },

    writeInt: function(samples) {
        if (this.audioElement !== null) {
            var out = new Array(samples.length);
            for (var i = samples.length-1; i !== 0; i--) {
                out[i] = samples[i] / 32768;
            }
            this.audioElement.mozWriteAudio(out);
        }
        else if (this.flashElement !== null) {
            this.flashElement.write(samples.join(' '));
        }
    }
};

<commit_msg>html5 web audio api added<commit_after>window.AudioContext = window.AudioContext || window.webkitAudioContext; // prefixed naming used in Safary 8-9

function DynamicAudio(args) {
    if (this instanceof arguments.callee) {
        if (typeof this.init === "function") {
            this.init.apply(this, (args && args.callee) ? args : arguments);
        }
    }
    else {
        return new arguments.callee(arguments);
    }
}

DynamicAudio.VERSION = "<%= version %>";
DynamicAudio.nextId = 1;

DynamicAudio.prototype = {
    nextId: null,
    swf: 'dynamicaudio.swf',

    audioContext: null,
    flashWrapper: null,
    flashElement: null,

    init: function(opts) {
        var self = this;
        self.id = DynamicAudio.nextId++;

        if (opts && typeof opts['swf'] !== 'undefined') {
            self.swf = opts['swf'];
        }

        // Attempt to create HTML5 Web audio context
        try {
            self.audioContext = new AudioContext();
        } catch(e) {
            // HTML5 Web Audio API not available
            // Fall back to creating flash player
            self.flashWrapper = document.createElement('div');
            self.flashWrapper.id = 'dynamicaudio-flashwrapper-'+self.id;
            // Credit to SoundManager2 for this:
            var s = self.flashWrapper.style;
            s['position'] = 'fixed';
            s['width'] = s['height'] = '8px'; // must be at least 6px for flash to run fast
            s['bottom'] = s['left'] = '0px';
            s['overflow'] = 'hidden';
            self.flashElement = document.createElement('div');
            self.flashElement.id = 'dynamicaudio-flashelement-'+self.id;
            self.flashWrapper.appendChild(self.flashElement);

            document.body.appendChild(self.flashWrapper);

            swfobject.embedSWF(
                self.swf,
                self.flashElement.id,
                "8",
                "8",
                "9.0.0",
                null,
                null,
                {'allowScriptAccess': 'always'},
                null,
                function(e) {
                    self.flashElement = e.ref;
                }
            );
        }
    },

    write: function(samples) {
        if (this.audioContext !== null) {
            this.output(samples);
        }
        else if (this.flashElement !== null) {
            var out = new Array(samples.length);
            for (var i = samples.length-1; i !== 0; i--) {
                out[i] = this.floatToIntSample(samples[i]);
            }
            this.flashElement.write(out.join(' '));
        }
    },

    writeInt: function(samples) {
        if (this.audioContext !== null) {
            this.output(samples, this.intToFloatSample);
        }
        else if (this.flashElement !== null) {
            this.flashElement.write(samples.join(' '));
        }
    },

    /**
     * Convert from interleaved buffer format to planar buffer
     * by writing right into appropriate channel buffers
     *
     * @param {Number[]} samples
     * @param {Function} converter - optional samples conversion function
     */
    output: function(samples, converter) {
        // Create output buffer (planar buffer format)
        var buffer = this.audioContext.createBuffer(2, samples.length, this.audioContext.sampleRate);
        var channelLeft = buffer.getChannelData(0);
        var channelRight = buffer.getChannelData(1);
        var j = 0;
        if (converter) { // for performance reasons we avoid conditions inside the for() cycle
            for (var i = 0; i < samples.length; i += 2) {
                channelLeft[j] = converter(samples[i]);
                channelRight[j] = converter(samples[i+1]);
                j++;
            }
        } else {
            for (var i = 0; i < samples.length; i += 2) {
                channelLeft[j] = samples[i];
                channelRight[j] = samples[i+1];
                j++;
            }
        }
        // Create sound source and play samples from buffer
        var source = this.audioContext.createBufferSource();
        source.buffer = buffer;
        source.connect(this.audioContext.destination); // Output to sound card
        source.start();
    },

    /**
     * helper function to convert Int output to Float
     * to return AudioBuffer/Float32Array output used in HTML5 WebAudio API
     *
     * @param {Number} value
     * @returns {number}
     */
    intToFloatSample: function(value) {
        return value / 32768; // from -32767..32768 to -1..1 range
    },

    /**
     * helper function to convert Float output to Int
     * to return Int Array used in SWF Audio player
     *
     * @param {Number} value
     * @returns {number}
     */
    floatToIntSample: function(value) {
        return Math.floor(value * 32768); // from -1..1 to -32767..32768 range
    }
};
<|endoftext|>


<commit_before>"use strict";

var width = 960,
    height = 500,
    cellSize = 25; // cell size

var no_months_in_a_row = Math.floor(width / (cellSize * 7 + 50));
var shift_up = cellSize * 3;

var day = function () {
  var func = d3.time.format("%w");
  return function (d) {
    var day = func(d) - 1;
    return day >= 0 ? day : 6;
  };
}(),
    // day of the week
day_of_month = d3.time.format("%e"); // day of the month
day_of_year = d3.time.format("%j");
week = function week(d) {
  return d3.time.mondayOfYear(d);
}, // week number of the year
month = d3.time.format("%m"), // month number
year = d3.time.format("%Y"), percent = d3.format(".1%"), format = d3.time.format("%Y-%m-%d");

var color = d3.scale.quantize().domain([-.05, .05]).range(d3.range(11).map(function (d) {
  return "q" + d + "-11";
}));

var svg = d3.select("#chart").selectAll("svg").data(d3.range(2016, 2017)).enter().append("svg").attr("width", width).attr("height", height).attr("class", "RdYlGn").append("g");

var rect = svg.selectAll(".day").data(function (d) {
  return d3.time.days(new Date(d, 5, 1), new Date(d + 1, 0, 1));
}).enter().append("rect").attr("class", "day").attr("width", cellSize).attr("height", cellSize).attr("x", function (d) {
  var month_padding = 1.2 * cellSize * 7 * ((month(d) - 6) % no_months_in_a_row);
  return day(d) * cellSize + month_padding;
}).attr("y", function (d) {
  var week_diff = week(d) - week(new Date(year(d), month(d) - 1, 1));
  var row_level = Math.ceil((month(d) - 5) / no_months_in_a_row);
  return week_diff * cellSize + row_level * cellSize * 8 - cellSize / 2 - shift_up;
}).attr('class', function (d, i) {
  return 'neighbour neighbour-' + (week(d) - 2) % 4;
}).datum(format);

var month_titles = svg.selectAll(".month-title") // Jan, Feb, Mar and the whatnot
.data(function (d) {
  return d3.time.months(new Date(d, 5, 1), new Date(d + 1, 0, 1));
}).enter().append("text").text(monthTitle).attr("x", function (d, i) {
  var month_padding = 1.2 * cellSize * 7 * ((month(d) - 6) % no_months_in_a_row);
  return month_padding + 60;
}).attr("y", function (d, i) {
  var week_diff = week(d) - week(new Date(year(d), month(d) - 1, 1));
  var row_level = Math.ceil((month(d) - 5) / no_months_in_a_row);
  return week_diff * cellSize + row_level * cellSize * 8 - cellSize - shift_up;
}).attr("class", "month-title").attr("d", monthTitle);

var mondayLabels = svg.selectAll('.monday-label').data(function (d) {
  return d3.time.months(new Date(d, 5, 1), new Date(d + 1, 0, 1));
}).enter().append('text').text('mon').attr('x', function (d) {
  return 1.2 * cellSize * 7 * ((month(d) - 6) % no_months_in_a_row);
}).attr('y', function (d) {
  var week_diff = week(d) - week(new Date(year(d), month(d) - 1, 1));
  var row_level = Math.ceil((month(d) - 5) / no_months_in_a_row);
  return week_diff * cellSize + row_level * cellSize * 8 - cellSize - shift_up;
}).attr("class", "monday-label");

var year_titles = svg.selectAll(".year-title") // Jan, Feb, Mar and the whatnot
.data(function (d) {
  return d3.time.years(new Date(d, 0, 1), new Date(d + 1, 0, 1));
}).enter().append("text").text(yearTitle).attr("x", function (d, i) {
  return width / 2 - 100;
}).attr("y", function (d, i) {
  return cellSize * 5.5 - shift_up;
}).attr("class", "year-title").attr("d", yearTitle);

var legend = svg.append('g');

legend.append('rect').attr('x', 715).attr('y', 300).attr('width', cellSize).attr('height', cellSize).attr('class', 'neighbour-0');

legend.append('text').text('40315').attr('x', 750).attr('y', 320);

legend.append('rect').attr('x', 715).attr('y', 340).attr('width', cellSize).attr('height', cellSize).attr('class', 'neighbour-1');

legend.append('text').text('40312').attr('x', 750).attr('y', 360);

legend.append('rect').attr('x', 715).attr('y', 380).attr('width', cellSize).attr('height', cellSize).attr('class', 'neighbour-2');

legend.append('text').text('41336').attr('x', 750).attr('y', 400);

legend.append('rect').attr('x', 715).attr('y', 420).attr('width', cellSize).attr('height', cellSize).attr('class', 'neighbour-3');

legend.append('text').text('40313').attr('x', 750).attr('y', 440);

//  Tooltip Object
var tooltip = d3.select("body").append("div").attr("id", "tooltip").style("position", "absolute").style("z-index", "10").style("visibility", "hidden").text("a simple tooltip");

function dayTitle(t0) {
  return t0.toString().split(" ")[2];
}
function monthTitle(t0) {
  return t0.toLocaleString("en-us", { month: "long" });
}
function yearTitle(t0) {
  return t0.toString().split(" ")[3];
}
<commit_msg>removed complied.js<commit_after><|endoftext|>


<commit_before>var _ = require("../utils")
  , containerless_nested;

function parseObject(object) {
  if (!_.isPlainObject(object)) {
    throw new TypeError(
      "containerless_nested:parseObject: expected object type but found `" + typeof(object) + "`."
    );
  }

  var result = {}
    , data = (result.data = {});

  _.each(object, function(value, key) {
    if (key[0] === '_') {
      if (key === "_type") {
        result.type = value;
      }
      else if (key === "_partial") {
        result.partial = value;
      }
      else {
        console.warn("containerless_nested:parseObject: ignoring unknown object property `" + key + "`");
      }

      return;
    }

    // while parsing an object, any array/object is considered embedded data
    // so we just treat it like the start of a unknown request
    if (_.isArray(value) || _.isPlainObject(value)) {
      value = parseObject(value);
    }

    data[key] = value;
  });

  return result;
}

function containerless_nested(data) {
  var result = null
    , discoveredType = null
    , discoveredPartial = null;

  // collection
  if (_.isArray(data)) {
    result = [];

    for (var i = 0; i < data.length; i++) {
      var object = data[i]
        , entry = parseObject(object);

      if (entry.type) {
        if (discoveredType && discoveredType !== entry.type) {
          throw new TypeError(
            "containerless_nested: Found conflicting types inside array of `" + discoveredType +
            "` and `" + entry.type + "`."
          );
        }
        discoveredType = entry.type;
      }

      if (entry.partial) {
        if (discoveredPartial && discoveredPartial !== entry.partial) {
          throw new TypeError(
            "containerless_nested: Found conflicting partials inside array of `" + discoveredPartial +
            "` and `" + entry.partial + "`."
          );
        }
        discoveredPartial = entry.partial;
      }

      result.push(entry.data);
    }
  }
  // non-collection
  else {
    var resource = parseObject(data);

    result = resource.data;
    discoveredType = resource.type;
    discoveredPartial = resource.partial;
  }

  return {
    type: discoveredType,
    partial: discoveredPartial,
    data: result
  };
}

module.exports = containerless_nested;
<commit_msg>Remove unused unnested check in nested<commit_after>var _ = require("../utils")
  , containerless_nested;

function parseObject(object) {
  if (!_.isPlainObject(object)) {
    throw new TypeError(
      "containerless_nested:parseObject: expected object type but found `" + typeof(object) + "`."
    );
  }

  var result = {}
    , data = (result.data = {});

  _.each(object, function(value, key) {
    if (key[0] === '_') {
      if (key === "_type") {
        result.type = value;
      }
      else if (key === "_partial") {
        result.partial = value;
      }
      else {
        console.warn("containerless_nested:parseObject: ignoring unknown object property `" + key + "`");
      }

      return;
    }

    data[key] = value;
  });

  return result;
}

function containerless_nested(data) {
  var result = null
    , discoveredType = null
    , discoveredPartial = null;

  // collection
  if (_.isArray(data)) {
    result = [];

    for (var i = 0; i < data.length; i++) {
      var object = data[i]
        , entry = parseObject(object);

      if (entry.type) {
        if (discoveredType && discoveredType !== entry.type) {
          throw new TypeError(
            "containerless_nested: Found conflicting types inside array of `" + discoveredType +
            "` and `" + entry.type + "`."
          );
        }
        discoveredType = entry.type;
      }

      if (entry.partial) {
        if (discoveredPartial && discoveredPartial !== entry.partial) {
          throw new TypeError(
            "containerless_nested: Found conflicting partials inside array of `" + discoveredPartial +
            "` and `" + entry.partial + "`."
          );
        }
        discoveredPartial = entry.partial;
      }

      result.push(entry.data);
    }
  }
  // non-collection
  else {
    var resource = parseObject(data);

    result = resource.data;
    discoveredType = resource.type;
    discoveredPartial = resource.partial;
  }

  return {
    type: discoveredType,
    partial: discoveredPartial,
    data: result
  };
}

module.exports = containerless_nested;
<|endoftext|>


<commit_before>var assert = require( 'assert' );
var fs = require( 'fs' );
var child_process = require( 'child_process' );
var info = require( '../../sherpa/info' );
var _ = require( '../../sherpa/lodash-2.0.0.min' );

/**
 * Grunt configuration file for simulations.
 * Requires a package.json file containing project settings.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
module.exports = function( grunt ) {
  function trimWhitespace( str ) {
    return str.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' );
  }

  function padString( str, n ) {
    while ( str.length < n ) {
      str += ' ';
    }
    return str;
  }

  function loadFileAsDataURI( filename ) {
    var mimeType = {
      'png': 'image/png',
      'svg': 'image/svg+xml',
      'jpg': 'image/jpeg',
      'cur': 'image/x-icon', // cursor files (used in build-a-molecule). x-win-bitmap gives off warnings in Chrome
      'mp3': 'audio/mpeg',
      'm4a': 'audio/mp4',
      'ogg': 'audio/ogg',
      'oga': 'audio/ogg',
      'bma': 'audio/webm', // webma is the full extension
      'wav': 'audio/wav'
    }[filename.slice( -3 )];
    assert( mimeType, 'Unknown mime type for filename: ' + filename );

    return 'data:' + mimeType + ';base64,' + Buffer( fs.readFileSync( filename ) ).toString( 'base64' );
  }

  function stringReplace( str, substring, replacement ) {
    var idx = str.indexOf( substring );
    if ( str.indexOf( substring ) !== -1 ) {
      return str.slice( 0, idx ) + replacement + str.slice( idx + substring.length );
    }
    else {
      return str;
    }
  }

  assert( fs.existsSync( 'package.json' ), 'We need to be in a sim directory with package.json' );

  var pkg = grunt.file.readJSON( 'package.json' );

  // TODO: eek, this is scary! we are importing from the sim dir. ideally we should just have uglify-js installed once in chipper?
  var uglify = require( '../../' + pkg.name + '/node_modules/uglify-js' );
  var requirejs = require( '../../' + pkg.name + '/node_modules/requirejs' ); // TODO: not currently used, figure out how to include almond correctly?
  var escodegen = require( '../../' + pkg.name + '/node_modules/escodegen' );
  var esprima = require( '../../' + pkg.name + '/node_modules/esprima' );

  var chipperRewrite = require( '../../chipper/ast/chipperRewrite.js' );
  var onBuildRead = function( name, path, contents ) {
    return chipperRewrite.chipperRewrite( contents, esprima, escodegen );
  };

  var preloadMapFilename = 'preload.js.map';

  // Project configuration.
  grunt.initConfig(
    {
      // Read in the project settings from the package.json file into the pkg property.
      // This allows us to refer to project settings from within this config file.
      pkg: pkg,

      // configure the RequireJS plugin
      requirejs: {

        // builds the minified script
        build: {
          options: {
            almond: true,
            mainConfigFile: 'js/<%= pkg.name %>-config.js',
            out: 'build/<%= pkg.name %>.min.js',
            name: '<%= pkg.name %>-config',
            optimize: 'uglify2',
            wrap: true,
            generateSourceMaps: true,
            preserveLicenseComments: false,
            uglify2: {
              output: {
                inline_script: true // escape </script
              },
              compress: {
                global_defs: {
                  // scenery assertions
                  sceneryAssert: false,
                  sceneryAssertExtra: false,
                  // scenery logging
                  sceneryLayerLog: false,
                  sceneryEventLog: false,
                  sceneryAccessibilityLog: false,
                  phetAllocation: false
                },
                dead_code: true
              }
            }
          }
        }
      },

      // configure the JSHint plugin
      jshint: {
        // source files that are specific to this simulation
        simFiles: [ 'js/**/*.js' ],
        // source files for common-code dependencies
        commonFiles: [
          '../assert/js/**/*.js',
          '../dot/js/**/*.js',
          '../joist/js/**/*.js',
          '../kite/js/**/*.js',
          '../phet-core/js/**/*.js',
          '../phetcommon/js/**/*.js',
          '../scenery/js/**/*.js',
          '../scenery-phet/js/**/*.js',
          '../sun/js/**/*.js'
        ],
        // reference external JSHint options in jshint-options.js
        options: require( './jshint-options' )
      }
    } );

  // Default task ('grunt')
  grunt.registerTask( 'default', [ 'generateLicenseInfo', 'lint-sim', 'lint-common', 'build' ] );

  // Other tasks ('grunt taskName')
  grunt.registerTask( 'lint-sim', [ 'jshint:simFiles' ] );
  grunt.registerTask( 'lint-common', [ 'jshint:commonFiles' ] );
  grunt.registerTask( 'lint', [ 'lint-sim', 'lint-common' ] );
  grunt.registerTask( 'build', [ 'simBeforeRequirejs', 'requirejs:build', 'simAfterRequirejs' ] );

  //Scoped variable to hold the result from the generateLicenseInfoTask.
  //TODO: A better way to store the return value?
  var licenseText;
  grunt.registerTask( 'generateLicenseInfo', 'Generate the license info', function() {

    //Prepare the license info
    //Run this first so that if something is missing from the license file you will find out before having to wait for jshint/requirejs build
    var licenseInfo = info();

    //Find all dependencies that have 'sherpa' in the path.
    //Please note, this requires all simulations to keep their dependencies in sherpa!
    var sherpaDependencyPaths = _.filter( pkg.preload.split( ' ' ), function( dependency ) { return dependency.indexOf( 'sherpa' ) >= 0; } );

    //Sort by name of the library, have to match cases to sort properly
    var sortedSherpaDependencyPaths = _.sortBy( sherpaDependencyPaths, function( path ) {return path.toUpperCase();} );

    //Map the paths to instances from the info.js file
    var licenses = _.uniq( _.map( sortedSherpaDependencyPaths, function( sherpaDependencyPath ) {
      var lastSlash = sherpaDependencyPath.lastIndexOf( '/' );
      var lastDot = sherpaDependencyPath.lastIndexOf( '.' );
      var dependencyName = sherpaDependencyPath.substring( lastSlash + 1, lastDot );
      //    console.log( 'found dependency: ' + sherpaDependencyPath + ', name = ' + dependencyName );

      //Make sure there is an entry in the info.js file, and return it
      assert( licenseInfo[dependencyName], 'no license entry for ' + dependencyName );
      return licenseInfo[dependencyName];
    } ) );

    //Get the text of each entry
    var separator = '=';

    //TODO: better way to return a value?
    licenseText = _.reduce( licenses,function( memo, license ) {
      var selectedLicenseText = license.selectedLicense ? '> Selected license: ' + license.selectedLicense + '\n' : '';
      return memo + license.text + '\n' +
             selectedLicenseText +
             separator +
             '\n';
    }, separator + '\n' ).trim();

    grunt.log.writeln( 'created license info for ' + licenses.length + ' dependencies' );
  } );

  // creates a performance snapshot for profiling changes
  grunt.registerTask( 'simBeforeRequirejs', 'Description', function() {
    grunt.log.writeln( 'Building simulation: ' + pkg.name + ' ' + pkg.version );

    assert( pkg.name, 'name required in package.json' );
    assert( pkg.version, 'version required in package.json' );
    assert( pkg.phetLibs, 'phetLibs required in package.json' );
    assert( pkg.preload, 'preload required in package.json' );

    if ( fs.existsSync( 'build' ) ) {
      grunt.log.writeln( 'Cleaning build directory' );
      grunt.file.delete( 'build' );
    }
    grunt.file.mkdir( 'build' );

    // grunt.log.writeln( 'Running Require.js optimizer' );
    // requirejs.optimize( {
    //   almond: true,
    //   mainConfigFile: 'js/' + pkg.name + '-config.js',
    //   out: 'build/' + pkg.name + '.min.js',
    //   name: pkg.name + '-config',
    //   optimize: 'uglify2',
    //   wrap: true,
    //   generateSourceMaps: true,
    //   preserveLicenseComments: false,
    //   uglify2: {
    //     output: {
    //       inline_script: true // escape </script
    //     },
    //     compress: {
    //       global_defs: {
    //         // scenery assertions
    //         sceneryAssert: false,
    //         sceneryAssertExtra: false,
    //         // scenery logging
    //         sceneryLayerLog: false,
    //         sceneryEventLog: false,
    //         sceneryAccessibilityLog: false
    //       },
    //       dead_code: true
    //     }
    //   }
    // }, function( response ) {
    // }
  } );

  grunt.registerTask( 'simAfterRequirejs', 'Description', function() {
    var done = this.async();

    grunt.log.writeln( 'Minifying preload scripts' );
    var preloadResult = uglify.minify( pkg.preload.split( ' ' ), {
      outSourceMap: preloadMapFilename,
      output: {
        inline_script: true // escape </script
      },
      compress: {
        // here for now, for when we want to include options for the preloaded code
        global_defs: {}
      }
    } );

    var preloadJS = preloadResult.code; // minified output
    var preloadMap = preloadResult.map; // map for minified output, use preloadMapFilename

    grunt.log.writeln( 'Copying preload source map' );
    grunt.file.write( 'build/' + preloadMapFilename, preloadMap );

    grunt.log.writeln( 'Copying changes.txt' );
    if ( fs.existsSync( 'changes.txt' ) ) {
      grunt.file.copy( 'changes.txt', 'build/changes.txt' );
    }
    else {
      grunt.log.error( 'WARNING: no changes.txt' );
    }

    var dependencies = pkg.phetLibs.split( ' ' );
    if ( dependencies.indexOf( 'chipper' ) === -1 ) {
      dependencies.push( 'chipper' );
    }

    var dependencyInfo = {};

    // git --git-dir ../scenery/.git rev-parse HEAD                 -- sha
    // git --git-dir ../scenery/.git rev-parse --abbrev-ref HEAD    -- branch
    function nextDependency() {
      if ( dependencies.length ) {
        var dependency = dependencies.pop();

        // get the SHA
        child_process.exec( 'git --git-dir ../' + dependency + '/.git rev-parse HEAD', function( error, stdout, stderr ) {
          assert( !error, error ? ( 'ERROR on git SHA attempt: code: ' + error.code + ', signal: ' + error.signal + ' with stderr:\n' + stderr ) : 'An error without an error? not good' );

          var sha = trimWhitespace( stdout );

          // get the branch
          child_process.exec( 'git --git-dir ../' + dependency + '/.git rev-parse --abbrev-ref HEAD', function( error, stdout, stderr ) {
            assert( !error, error ? ( 'ERROR on git branch attempt: code: ' + error.code + ', signal: ' + error.signal + ' with stderr:\n' + stderr ) : 'An error without an error? not good' );

            var branch = trimWhitespace( stdout );

            grunt.log.writeln( padString( dependency, 20 ) + branch + ' ' + sha );
            dependencyInfo[dependency] = { sha: sha, branch: branch };

            nextDependency();
          } );
        } );
      }
      else {
        // now continue on with the process! CALLBACK SOUP FOR YOU!

        // TODO: finish!
        grunt.log.writeln( 'Writing dependencies.txt' );
        grunt.file.write( 'build/dependencies.txt', '# ' + pkg.name + ' ' + pkg.version + ' ' + (new Date().toString()) + '\n' +
                                                    JSON.stringify( dependencyInfo, null, 2 ) + '\n' );

        var splashDataURI = loadFileAsDataURI( '../joist/images/phet-logo-loading.svg' );
        var mainInlineJavascript = grunt.file.read( 'build/' + pkg.name + '.min.js' );

        var imageFiles = pkg.images || [];
        var inlineImageHTML = "";
        while ( imageFiles.length ) {
          var imageFile = imageFiles.pop();
          grunt.log.writeln( 'Inserting image: ' + imageFile );

          var dataURI = loadFileAsDataURI( imageFile );
          // TODO: share this as a CSS class, helps file space if we have tons of images
          inlineImageHTML += '<img id="' + imageFile + '" style="visibility: hidden; overflow: hidden; position: absolute;" src="' + dataURI + '"/>';
        }
        var soundFiles = pkg.sounds || [];
        var inlineSoundHTML = "";
        while ( soundFiles.length ) {
          var soundFile = soundFiles.pop();
          grunt.log.writeln( 'Inserting sound: ' + soundFile );

          var dataURI = loadFileAsDataURI( soundFile );
          inlineSoundHTML += '<audio id="' + soundFile + '" src="' + dataURI + '"></audio>\n';
        }

        //Create the license header for this html and all the 3rd party dependencies
        //TODO: This puts the license for everything as GPLv3.  If we want to build other libraries with this, we should change the license to MIT
        var htmlHeader = pkg.name + '\n' +
                         'Copyright 2002-2013, University of Colorado Boulder\n' +
                         'PhET Interactive Simulations\n' +
                         'Licensed under GPLv3\n' +
                         'http://phet.colorado.edu/en/about/licensing\n' +
                         '\n' +
                         'Libraries:\n' + licenseText;

        grunt.log.writeln( 'Constructing HTML from template' );
        var html = grunt.file.read( '../chipper/templates/sim.html' );
        html = stringReplace( html, 'HTML_HEADER', htmlHeader );
        html = stringReplace( html, 'SPLASH_SCREEN_DATA_URI', splashDataURI );
        html = stringReplace( html, 'PRELOAD_INLINE_JAVASCRIPT', preloadJS + '\n//# sourceMappingURL=preload.js.map' );
        html = stringReplace( html, 'MAIN_INLINE_JAVASCRIPT', mainInlineJavascript );
        html = stringReplace( html, 'SIM_INLINE_IMAGES', inlineImageHTML );
        html = stringReplace( html, 'SIM_INLINE_SOUNDS', inlineSoundHTML );

        grunt.log.writeln( 'Writing HTML' );
        grunt.file.write( 'build/' + pkg.name + '_en.html', html );

        grunt.log.writeln( 'Cleaning temporary files' );
        grunt.file.delete( 'build/' + pkg.name + '.min.js' );

        done();
      }
    }

    grunt.log.writeln( 'Scanning dependencies from:\n' + dependencies.toString() );
    nextDependency();
  } );

  // Load tasks from grunt plugins that have been installed locally using npm.
  // Put these in package.json and run 'npm install' before running grunt.
  grunt.loadNpmTasks( 'grunt-requirejs' );
  grunt.loadNpmTasks( 'grunt-contrib-jshint' );
};
<commit_msg>Added licenses for dependencies that are not added through the package.json<commit_after>var assert = require( 'assert' );
var fs = require( 'fs' );
var child_process = require( 'child_process' );
var info = require( '../../sherpa/info' );
var _ = require( '../../sherpa/lodash-2.0.0.min' );

/**
 * Grunt configuration file for simulations.
 * Requires a package.json file containing project settings.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
module.exports = function( grunt ) {
  function trimWhitespace( str ) {
    return str.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' );
  }

  function padString( str, n ) {
    while ( str.length < n ) {
      str += ' ';
    }
    return str;
  }

  function loadFileAsDataURI( filename ) {
    var mimeType = {
      'png': 'image/png',
      'svg': 'image/svg+xml',
      'jpg': 'image/jpeg',
      'cur': 'image/x-icon', // cursor files (used in build-a-molecule). x-win-bitmap gives off warnings in Chrome
      'mp3': 'audio/mpeg',
      'm4a': 'audio/mp4',
      'ogg': 'audio/ogg',
      'oga': 'audio/ogg',
      'bma': 'audio/webm', // webma is the full extension
      'wav': 'audio/wav'
    }[filename.slice( -3 )];
    assert( mimeType, 'Unknown mime type for filename: ' + filename );

    return 'data:' + mimeType + ';base64,' + Buffer( fs.readFileSync( filename ) ).toString( 'base64' );
  }

  function stringReplace( str, substring, replacement ) {
    var idx = str.indexOf( substring );
    if ( str.indexOf( substring ) !== -1 ) {
      return str.slice( 0, idx ) + replacement + str.slice( idx + substring.length );
    }
    else {
      return str;
    }
  }

  assert( fs.existsSync( 'package.json' ), 'We need to be in a sim directory with package.json' );

  var pkg = grunt.file.readJSON( 'package.json' );

  // TODO: eek, this is scary! we are importing from the sim dir. ideally we should just have uglify-js installed once in chipper?
  var uglify = require( '../../' + pkg.name + '/node_modules/uglify-js' );
  var requirejs = require( '../../' + pkg.name + '/node_modules/requirejs' ); // TODO: not currently used, figure out how to include almond correctly?
  var escodegen = require( '../../' + pkg.name + '/node_modules/escodegen' );
  var esprima = require( '../../' + pkg.name + '/node_modules/esprima' );

  var chipperRewrite = require( '../../chipper/ast/chipperRewrite.js' );
  var onBuildRead = function( name, path, contents ) {
    return chipperRewrite.chipperRewrite( contents, esprima, escodegen );
  };

  var preloadMapFilename = 'preload.js.map';

  // Project configuration.
  grunt.initConfig(
    {
      // Read in the project settings from the package.json file into the pkg property.
      // This allows us to refer to project settings from within this config file.
      pkg: pkg,

      // configure the RequireJS plugin
      requirejs: {

        // builds the minified script
        build: {
          options: {
            almond: true,
            mainConfigFile: 'js/<%= pkg.name %>-config.js',
            out: 'build/<%= pkg.name %>.min.js',
            name: '<%= pkg.name %>-config',
            optimize: 'uglify2',
            wrap: true,
            generateSourceMaps: true,
            preserveLicenseComments: false,
            uglify2: {
              output: {
                inline_script: true // escape </script
              },
              compress: {
                global_defs: {
                  // scenery assertions
                  sceneryAssert: false,
                  sceneryAssertExtra: false,
                  // scenery logging
                  sceneryLayerLog: false,
                  sceneryEventLog: false,
                  sceneryAccessibilityLog: false,
                  phetAllocation: false
                },
                dead_code: true
              }
            }
          }
        }
      },

      // configure the JSHint plugin
      jshint: {
        // source files that are specific to this simulation
        simFiles: [ 'js/**/*.js' ],
        // source files for common-code dependencies
        commonFiles: [
          '../assert/js/**/*.js',
          '../dot/js/**/*.js',
          '../joist/js/**/*.js',
          '../kite/js/**/*.js',
          '../phet-core/js/**/*.js',
          '../phetcommon/js/**/*.js',
          '../scenery/js/**/*.js',
          '../scenery-phet/js/**/*.js',
          '../sun/js/**/*.js'
        ],
        // reference external JSHint options in jshint-options.js
        options: require( './jshint-options' )
      }
    } );

  // Default task ('grunt')
  grunt.registerTask( 'default', [ 'generateLicenseInfo', 'lint-sim', 'lint-common', 'build' ] );

  // Other tasks ('grunt taskName')
  grunt.registerTask( 'lint-sim', [ 'jshint:simFiles' ] );
  grunt.registerTask( 'lint-common', [ 'jshint:commonFiles' ] );
  grunt.registerTask( 'lint', [ 'lint-sim', 'lint-common' ] );
  grunt.registerTask( 'build', [ 'simBeforeRequirejs', 'requirejs:build', 'simAfterRequirejs' ] );

  //Scoped variable to hold the result from the generateLicenseInfoTask.
  //TODO: A better way to store the return value?
  var licenseText;
  grunt.registerTask( 'generateLicenseInfo', 'Generate the license info', function() {

    //Prepare the license info
    //Run this first so that if something is missing from the license file you will find out before having to wait for jshint/requirejs build
    var licenseInfo = info();

    //Find all dependencies that have 'sherpa' in the path.
    //Please note, this requires all simulations to keep their dependencies in sherpa!
    var sherpaDependencyPaths = _.filter( pkg.preload.split( ' ' ), function( dependency ) { return dependency.indexOf( 'sherpa' ) >= 0; } );

    //Add libraries that are not explicitly included by the sim
    //Note: must have a . character for the parsing below //TODO: Remove this restriction
    sherpaDependencyPaths.push( 'almond-0.2.6.' );
    sherpaDependencyPaths.push( 'pegjs.' );
    sherpaDependencyPaths.push( 'font-awesome.' );
    sherpaDependencyPaths.push( 'require-i18n.' );

    //Sort by name of the library, have to match cases to sort properly
    var sortedSherpaDependencyPaths = _.sortBy( sherpaDependencyPaths, function( path ) {return path.toUpperCase();} );

    //Map the paths to instances from the info.js file
    var licenses = _.uniq( _.map( sortedSherpaDependencyPaths, function( sherpaDependencyPath ) {
      var lastSlash = sherpaDependencyPath.lastIndexOf( '/' );
      var lastDot = sherpaDependencyPath.lastIndexOf( '.' );
      var dependencyName = sherpaDependencyPath.substring( lastSlash + 1, lastDot );
      //    console.log( 'found dependency: ' + sherpaDependencyPath + ', name = ' + dependencyName );

      //Make sure there is an entry in the info.js file, and return it
      assert( licenseInfo[dependencyName], 'no license entry for ' + dependencyName );
      return licenseInfo[dependencyName];
    } ) );

    //Get the text of each entry
    var separator = '=';

    //TODO: better way to return a value?
    licenseText = _.reduce( licenses,function( memo, license ) {
      var selectedLicenseText = license.selectedLicense ? '> Selected license: ' + license.selectedLicense + '\n' : '';
      return memo + license.text + '\n' +
             selectedLicenseText +
             separator +
             '\n';
    }, separator + '\n' ).trim();

    grunt.log.writeln( 'created license info for ' + licenses.length + ' dependencies' );
  } );

  // creates a performance snapshot for profiling changes
  grunt.registerTask( 'simBeforeRequirejs', 'Description', function() {
    grunt.log.writeln( 'Building simulation: ' + pkg.name + ' ' + pkg.version );

    assert( pkg.name, 'name required in package.json' );
    assert( pkg.version, 'version required in package.json' );
    assert( pkg.phetLibs, 'phetLibs required in package.json' );
    assert( pkg.preload, 'preload required in package.json' );

    if ( fs.existsSync( 'build' ) ) {
      grunt.log.writeln( 'Cleaning build directory' );
      grunt.file.delete( 'build' );
    }
    grunt.file.mkdir( 'build' );

    // grunt.log.writeln( 'Running Require.js optimizer' );
    // requirejs.optimize( {
    //   almond: true,
    //   mainConfigFile: 'js/' + pkg.name + '-config.js',
    //   out: 'build/' + pkg.name + '.min.js',
    //   name: pkg.name + '-config',
    //   optimize: 'uglify2',
    //   wrap: true,
    //   generateSourceMaps: true,
    //   preserveLicenseComments: false,
    //   uglify2: {
    //     output: {
    //       inline_script: true // escape </script
    //     },
    //     compress: {
    //       global_defs: {
    //         // scenery assertions
    //         sceneryAssert: false,
    //         sceneryAssertExtra: false,
    //         // scenery logging
    //         sceneryLayerLog: false,
    //         sceneryEventLog: false,
    //         sceneryAccessibilityLog: false
    //       },
    //       dead_code: true
    //     }
    //   }
    // }, function( response ) {
    // }
  } );

  grunt.registerTask( 'simAfterRequirejs', 'Description', function() {
    var done = this.async();

    grunt.log.writeln( 'Minifying preload scripts' );
    var preloadResult = uglify.minify( pkg.preload.split( ' ' ), {
      outSourceMap: preloadMapFilename,
      output: {
        inline_script: true // escape </script
      },
      compress: {
        // here for now, for when we want to include options for the preloaded code
        global_defs: {}
      }
    } );

    var preloadJS = preloadResult.code; // minified output
    var preloadMap = preloadResult.map; // map for minified output, use preloadMapFilename

    grunt.log.writeln( 'Copying preload source map' );
    grunt.file.write( 'build/' + preloadMapFilename, preloadMap );

    grunt.log.writeln( 'Copying changes.txt' );
    if ( fs.existsSync( 'changes.txt' ) ) {
      grunt.file.copy( 'changes.txt', 'build/changes.txt' );
    }
    else {
      grunt.log.error( 'WARNING: no changes.txt' );
    }

    var dependencies = pkg.phetLibs.split( ' ' );
    if ( dependencies.indexOf( 'chipper' ) === -1 ) {
      dependencies.push( 'chipper' );
    }

    var dependencyInfo = {};

    // git --git-dir ../scenery/.git rev-parse HEAD                 -- sha
    // git --git-dir ../scenery/.git rev-parse --abbrev-ref HEAD    -- branch
    function nextDependency() {
      if ( dependencies.length ) {
        var dependency = dependencies.pop();

        // get the SHA
        child_process.exec( 'git --git-dir ../' + dependency + '/.git rev-parse HEAD', function( error, stdout, stderr ) {
          assert( !error, error ? ( 'ERROR on git SHA attempt: code: ' + error.code + ', signal: ' + error.signal + ' with stderr:\n' + stderr ) : 'An error without an error? not good' );

          var sha = trimWhitespace( stdout );

          // get the branch
          child_process.exec( 'git --git-dir ../' + dependency + '/.git rev-parse --abbrev-ref HEAD', function( error, stdout, stderr ) {
            assert( !error, error ? ( 'ERROR on git branch attempt: code: ' + error.code + ', signal: ' + error.signal + ' with stderr:\n' + stderr ) : 'An error without an error? not good' );

            var branch = trimWhitespace( stdout );

            grunt.log.writeln( padString( dependency, 20 ) + branch + ' ' + sha );
            dependencyInfo[dependency] = { sha: sha, branch: branch };

            nextDependency();
          } );
        } );
      }
      else {
        // now continue on with the process! CALLBACK SOUP FOR YOU!

        // TODO: finish!
        grunt.log.writeln( 'Writing dependencies.txt' );
        grunt.file.write( 'build/dependencies.txt', '# ' + pkg.name + ' ' + pkg.version + ' ' + (new Date().toString()) + '\n' +
                                                    JSON.stringify( dependencyInfo, null, 2 ) + '\n' );

        var splashDataURI = loadFileAsDataURI( '../joist/images/phet-logo-loading.svg' );
        var mainInlineJavascript = grunt.file.read( 'build/' + pkg.name + '.min.js' );

        var imageFiles = pkg.images || [];
        var inlineImageHTML = "";
        while ( imageFiles.length ) {
          var imageFile = imageFiles.pop();
          grunt.log.writeln( 'Inserting image: ' + imageFile );

          var dataURI = loadFileAsDataURI( imageFile );
          // TODO: share this as a CSS class, helps file space if we have tons of images
          inlineImageHTML += '<img id="' + imageFile + '" style="visibility: hidden; overflow: hidden; position: absolute;" src="' + dataURI + '"/>';
        }
        var soundFiles = pkg.sounds || [];
        var inlineSoundHTML = "";
        while ( soundFiles.length ) {
          var soundFile = soundFiles.pop();
          grunt.log.writeln( 'Inserting sound: ' + soundFile );

          var dataURI = loadFileAsDataURI( soundFile );
          inlineSoundHTML += '<audio id="' + soundFile + '" src="' + dataURI + '"></audio>\n';
        }

        //Create the license header for this html and all the 3rd party dependencies
        //TODO: This puts the license for everything as GPLv3.  If we want to build other libraries with this, we should change the license to MIT
        var htmlHeader = pkg.name + '\n' +
                         'Copyright 2002-2013, University of Colorado Boulder\n' +
                         'PhET Interactive Simulations\n' +
                         'Licensed under GPLv3\n' +
                         'http://phet.colorado.edu/en/about/licensing\n' +
                         '\n' +
                         'Libraries:\n' + licenseText;

        grunt.log.writeln( 'Constructing HTML from template' );
        var html = grunt.file.read( '../chipper/templates/sim.html' );
        html = stringReplace( html, 'HTML_HEADER', htmlHeader );
        html = stringReplace( html, 'SPLASH_SCREEN_DATA_URI', splashDataURI );
        html = stringReplace( html, 'PRELOAD_INLINE_JAVASCRIPT', preloadJS + '\n//# sourceMappingURL=preload.js.map' );
        html = stringReplace( html, 'MAIN_INLINE_JAVASCRIPT', mainInlineJavascript );
        html = stringReplace( html, 'SIM_INLINE_IMAGES', inlineImageHTML );
        html = stringReplace( html, 'SIM_INLINE_SOUNDS', inlineSoundHTML );

        grunt.log.writeln( 'Writing HTML' );
        grunt.file.write( 'build/' + pkg.name + '_en.html', html );

        grunt.log.writeln( 'Cleaning temporary files' );
        grunt.file.delete( 'build/' + pkg.name + '.min.js' );

        done();
      }
    }

    grunt.log.writeln( 'Scanning dependencies from:\n' + dependencies.toString() );
    nextDependency();
  } );

  // Load tasks from grunt plugins that have been installed locally using npm.
  // Put these in package.json and run 'npm install' before running grunt.
  grunt.loadNpmTasks( 'grunt-requirejs' );
  grunt.loadNpmTasks( 'grunt-contrib-jshint' );
};
<|endoftext|>