<commit_before>import page  	  from 'page';
import React 	  from 'react';
import Immutable  from 'immutable';
import classNames from 'classnames';

import Action          from '../actions';
import UserAction      from '../actions/user';
import SettingsAction  from '../actions/settings';
import BroadcastAction from '../actions/broadcast';

import UserStore    from '../stores/user';
import Avatar       from '../components/avatar';
import Dropdown     from '../components/dropdown';
import MemberDialog from '../components/dialog/board-members';

import UserVoice   from '../components/user-voice';
import InfoView    from './dialog/view-info';
import AboutView   from './dialog/view-about';
import ProfileView from './dialog/edit-profile';

import Board from '../models/board';

import ActivityStore from '../stores/user-activity';

import listener    from '../mixins/listener';
import localeMixin from '../mixins/locale';

/**
 *
 */
export default React.createClass({
	mixins: [
		listener(ActivityStore),
		localeMixin()
	],

	propTypes: {
		title: React.PropTypes.string.isRequired,
		showHelp: React.PropTypes.bool,
		reviewActive: React.PropTypes.bool,
		killReview: React.PropTypes.func,
		board: (props) => {
			if(!props.board instanceof Board) throw new Error();
		}
	},

	componentDidMount() {
		//this is not good... but what is!
		let board = document.getElementById("board");
		board.addEventListener("click", (event) => {
				this.toggleDropdown();
				window.UserVoice.push([ 'hide' ]);
		}, false)
	},

	onChange() {
		this.setState({
			members: this.props.board
				? ActivityStore.getMembers(this.props.board.id)
				: Immutable.List()
		});
	},

	getInitialState() {
		return {
			members: this.props.board
				? ActivityStore.getMembers(this.props.board.id)
				: Immutable.List(),
			dropdown: false, localesDropdown: false,
			feedback: false, infoActive: false,
			aboutActive: false, membersActive: false,
			profileActive: false
		}
	},

	showWorkspace() {
		return page.show('/boards');
	},

	toggleMembersDialog() {
		this.setState({ membersActive: !this.state.membersActive });
	},

	toggleDropdown() {
		this.setState({ dropdown: !this.state.dropdown });
		if(this.state.localesDropdown) {
			this.setState({ localesDropdown: !this.state.localesDropdown });
		}
	},

	toggleInfoView() {
		this.setState({ infoActive: !this.state.infoActive });
	},

	toggleAboutView() {
		this.setState({ aboutActive: !this.state.aboutActive });
	},

	toggleLocaleDropdown() {
		this.setState({ localesDropdown: !this.state.localesDropdown });
	},

	toggleProfileView() {
		this.setState({ profileActive: !this.state.profileActive });
	},

	CancelReview(){
		return !this.props.reviewActive ? null : (

 {
				this.props.killReview(false)
			}}
			className="review active">

		);
	},

	boardMembersAmount() {
		if(!this.props.board) return null;

		return this.state.members.filter((member) => {
			return member.get('date') > new Date(new Date().getTime() - (5 * 60000));
		}).size;
   },

	render() {
		let infoDialog = null;
		let aboutDialog = null;
		let profileDialog = null;
		let infoIcon = null;

		if(!this.state.infoActive) {
			infoIcon = 'question';
			infoDialog = null;
		} else {
			infoIcon = 'times';
			infoDialog = <InfoView onDismiss = { this.toggleInfoView }  board={this.props.board}/>;
		}

		if(!this.state.aboutActive) {
			aboutDialog = null;
		} else {
			aboutDialog = <AboutView onDismiss = { this.toggleAboutView } />;
		}

		if(!this.state.profileActive) {
			profileDialog = null;
		} else {
			profileDialog = <ProfileView formProfile="profileSettings" onDismiss = { this.toggleProfileView } />;
		}

		let infoButtonClass =
			classNames(
				'infobutton',
				{ active: this.state.infoActive }
			);

		let userButtonClass =
			classNames(
				'avatar-wrapper',
				{ active: this.state.dropdown }
			);

		let membersButtonClass =
			classNames(
				'members',
				{ active: this.state.membersActive }
			);

		let boardMembersDialog = null;

		if (this.state.membersActive) {
			boardMembersDialog = <MemberDialog members={this.state.members} onDismiss={this.toggleMembersDialog}/>
		}

		let showBoardMembers = !this.props.showBoardMembers ? null : (

						{this.boardMembersAmount()}

		);

		let showInfo = !this.props.showHelp ? null : (

				<span className={`fa fa-fw fa-${infoIcon}`}>

			);

		// If userstore is empty then go back to login
		if(!UserStore.getUser()) {
			page.redirect('/login');
		}

		let isProfileDisabled = UserStore.getUser().type === 'standard';
		let items = [
			{
				disabled: true,
				customclass: 'profile-name',
				content: `${this.locale('DROPDOWN_HELLO')}, ${UserStore.getUser().username}`
			},
			{
				icon: 'user',
				content: this.locale('DROPDOWN_PROFILE'),
				disabled: !isProfileDisabled,
				onClick: () => {
					this.toggleProfileView();
					this.toggleDropdown();
				}
			},
			{
				icon: 'language',
				content: this.locale('DROPDOWN_LOCALE'),
				onClick: () => {
					this.toggleLocaleDropdown()
				}
			},
			{
				icon: 'bullhorn',
				nospan: true,
				content: (
					<UserVoice>
						{this.locale('DROPDOWN_FEEDBACK')}
					</UserVoice>
				)
			},
			{
				icon: 'info',
				content: this.locale('DROPDOWN_ABOUT'),
				onClick: () => {
					this.toggleAboutView();
				}
			},
			{
				icon: 'sign-out',
				content: this.locale('DROPDOWN_LOGOUT'),
				onClick: () => {
					UserAction.logout()
						.catch((err) => {
							BroadcastAction.add(err, Action.User.Logout);
						});
				}
			}
		];
		let locales = [
			{
				flag: 'gb',
				content: 'English',
				onClick: () => {
					SettingsAction.setSetting('locale', 'en');
					this.toggleDropdown();
				}
			},
			{
				flag: 'fi',
				content: 'Suomi',
				onClick: () => {
					SettingsAction.setSetting('locale', 'fi');
					this.toggleDropdown();
				}
			},
			{
				flag: 'se',
				content: 'Svenska',
				onClick: () => {
					SettingsAction.setSetting('locale', 'se');
					this.toggleDropdown();
				}
			},
			{
				flag: 'dk',
				content: 'Dansk',
				onClick: () => {
					SettingsAction.setSetting('locale', 'dk');
					this.toggleDropdown();
				}
			},
			{
				flag: 'jp',
				content: '日本語',
				onClick: () => {
					SettingsAction.setSetting('locale', 'jp');
					this.toggleDropdown();
				}
			}
		];

		let user      = UserStore.getUser();
		let name      = user.get('username');
		let avatarURL = user.get('avatar');
		let userType  = user.get('type');

		return (

{this.props.title}

				{this.CancelReview()}
				{showBoardMembers}
				{showInfo}

						<Avatar size={30} name={name}
								imageurl={avatarURL}
								isOnline={true}
								usertype={userType}>
						</Avatar>

				<Dropdown ref="dropdown" className="options" show={this.state.dropdown} items={items} />
				<Dropdown className="locales" show={this.state.localesDropdown} items={locales} />
				{infoDialog}
				{boardMembersDialog}
				{aboutDialog}
				{profileDialog}

		);
	}
});
<commit_msg>bug fix<commit_after>import page  	  from 'page';
import React 	  from 'react';
import Immutable  from 'immutable';
import classNames from 'classnames';

import Action          from '../actions';
import UserAction      from '../actions/user';
import SettingsAction  from '../actions/settings';
import BroadcastAction from '../actions/broadcast';

import UserStore    from '../stores/user';
import Avatar       from '../components/avatar';
import Dropdown     from '../components/dropdown';
import MemberDialog from '../components/dialog/board-members';

import UserVoice   from '../components/user-voice';
import InfoView    from './dialog/view-info';
import AboutView   from './dialog/view-about';
import ProfileView from './dialog/edit-profile';

import Board from '../models/board';

import ActivityStore from '../stores/user-activity';

import listener    from '../mixins/listener';
import localeMixin from '../mixins/locale';

/**
 *
 */
export default React.createClass({
	mixins: [
		listener(ActivityStore),
		localeMixin()
	],

	propTypes: {
		title: React.PropTypes.string.isRequired,
		showHelp: React.PropTypes.bool,
		reviewActive: React.PropTypes.bool,
		killReview: React.PropTypes.func,
		board: (props) => {
			if(!props.board instanceof Board) throw new Error();
		}
	},

	componentDidMount() {
		//this is not good... but what is!
		let contentArea = document.getElementById("board");
		contentArea.addEventListener("click", (event) => {
			if(this.state.dropdown) {
				this.toggleDropdown();
				window.UserVoice.push([ 'hide' ]);
			}
		});
	},

	onChange() {
		this.setState({
			members: this.props.board
				? ActivityStore.getMembers(this.props.board.id)
				: Immutable.List()
		});
	},

	getInitialState() {
		return {
			members: this.props.board
				? ActivityStore.getMembers(this.props.board.id)
				: Immutable.List(),
			dropdown: false, localesDropdown: false,
			feedback: false, infoActive: false,
			aboutActive: false, membersActive: false,
			profileActive: false
		}
	},

	showWorkspace() {
		return page.show('/boards');
	},

	toggleMembersDialog() {
		this.setState({ membersActive: !this.state.membersActive });
	},

	toggleDropdown() {
		this.setState({ dropdown: !this.state.dropdown });
		if(this.state.localesDropdown) {
			this.setState({ localesDropdown: !this.state.localesDropdown });
		}
	},

	toggleInfoView() {
		this.setState({ infoActive: !this.state.infoActive });
	},

	toggleAboutView() {
		this.setState({ aboutActive: !this.state.aboutActive });
	},

	toggleLocaleDropdown() {
		this.setState({ localesDropdown: !this.state.localesDropdown });
	},

	toggleProfileView() {
		this.setState({ profileActive: !this.state.profileActive });
	},

	CancelReview(){
		return !this.props.reviewActive ? null : (

 {
				this.props.killReview(false)
			}}
			className="review active">

		);
	},

	boardMembersAmount() {
		if(!this.props.board) return null;

		return this.state.members.filter((member) => {
			return member.get('date') > new Date(new Date().getTime() - (5 * 60000));
		}).size;
   },

	render() {
		let infoDialog = null;
		let aboutDialog = null;
		let profileDialog = null;
		let infoIcon = null;

		if(!this.state.infoActive) {
			infoIcon = 'question';
			infoDialog = null;
		} else {
			infoIcon = 'times';
			infoDialog = <InfoView onDismiss = { this.toggleInfoView }  board={this.props.board}/>;
		}

		if(!this.state.aboutActive) {
			aboutDialog = null;
		} else {
			aboutDialog = <AboutView onDismiss = { this.toggleAboutView } />;
		}

		if(!this.state.profileActive) {
			profileDialog = null;
		} else {
			profileDialog = <ProfileView formProfile="profileSettings" onDismiss = { this.toggleProfileView } />;
		}

		let infoButtonClass =
			classNames(
				'infobutton',
				{ active: this.state.infoActive }
			);

		let userButtonClass =
			classNames(
				'avatar-wrapper',
				{ active: this.state.dropdown }
			);

		let membersButtonClass =
			classNames(
				'members',
				{ active: this.state.membersActive }
			);

		let boardMembersDialog = null;

		if (this.state.membersActive) {
			boardMembersDialog = <MemberDialog members={this.state.members} onDismiss={this.toggleMembersDialog}/>
		}

		let showBoardMembers = !this.props.showBoardMembers ? null : (

						{this.boardMembersAmount()}

		);

		let showInfo = !this.props.showHelp ? null : (

				<span className={`fa fa-fw fa-${infoIcon}`}>

			);

		// If userstore is empty then go back to login
		if(!UserStore.getUser()) {
			page.redirect('/login');
		}

		let isProfileDisabled = UserStore.getUser().type === 'standard';
		let items = [
			{
				disabled: true,
				customclass: 'profile-name',
				content: `${this.locale('DROPDOWN_HELLO')}, ${UserStore.getUser().username}`
			},
			{
				icon: 'user',
				content: this.locale('DROPDOWN_PROFILE'),
				disabled: !isProfileDisabled,
				onClick: () => {
					this.toggleProfileView();
					this.toggleDropdown();
				}
			},
			{
				icon: 'language',
				content: this.locale('DROPDOWN_LOCALE'),
				onClick: () => {
					this.toggleLocaleDropdown()
				}
			},
			{
				icon: 'bullhorn',
				nospan: true,
				content: (
					<UserVoice>
						{this.locale('DROPDOWN_FEEDBACK')}
					</UserVoice>
				)
			},
			{
				icon: 'info',
				content: this.locale('DROPDOWN_ABOUT'),
				onClick: () => {
					this.toggleAboutView();
				}
			},
			{
				icon: 'sign-out',
				content: this.locale('DROPDOWN_LOGOUT'),
				onClick: () => {
					UserAction.logout()
						.catch((err) => {
							BroadcastAction.add(err, Action.User.Logout);
						});
				}
			}
		];
		let locales = [
			{
				flag: 'gb',
				content: 'English',
				onClick: () => {
					SettingsAction.setSetting('locale', 'en');
					this.toggleDropdown();
				}
			},
			{
				flag: 'fi',
				content: 'Suomi',
				onClick: () => {
					SettingsAction.setSetting('locale', 'fi');
					this.toggleDropdown();
				}
			},
			{
				flag: 'se',
				content: 'Svenska',
				onClick: () => {
					SettingsAction.setSetting('locale', 'se');
					this.toggleDropdown();
				}
			},
			{
				flag: 'dk',
				content: 'Dansk',
				onClick: () => {
					SettingsAction.setSetting('locale', 'dk');
					this.toggleDropdown();
				}
			},
			{
				flag: 'jp',
				content: '日本語',
				onClick: () => {
					SettingsAction.setSetting('locale', 'jp');
					this.toggleDropdown();
				}
			}
		];

		let user      = UserStore.getUser();
		let name      = user.get('username');
		let avatarURL = user.get('avatar');
		let userType  = user.get('type');

		return (

{this.props.title}

				{this.CancelReview()}
				{showBoardMembers}
				{showInfo}

						<Avatar size={30} name={name}
								imageurl={avatarURL}
								isOnline={true}
								usertype={userType}>
						</Avatar>

				<Dropdown ref="dropdown" className="options" show={this.state.dropdown} items={items} />
				<Dropdown className="locales" show={this.state.localesDropdown} items={locales} />
				{infoDialog}
				{boardMembersDialog}
				{aboutDialog}
				{profileDialog}

		);
	}
});
<|endoftext|>


<commit_before>
import * as th from './helpers/templates'

import { getIdName, getOtherPropertiesOfIdentifier, groupPropertiesByName, isImport } from './helpers/ast'
import { getClassInfo, convertClassToUI5Extend, isSAPUIDefineCallExpression } from './helpers/classes'
import { hasJsdocGlobalExportFlag } from './helpers/jsdoc'
import { wrap } from './helpers/wrapper'

/*
References:
Babel Plugin Handbook: https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md
AST Spec: https://github.com/babel/babylon/blob/master/ast/spec.md
Babel Types (t.*) https://github.com/babel/babel/tree/master/packages/babel-types
AST Explorer: https://astexplorer.net/
*/

const tempModuleName = (name) => `__${name}`

module.exports = ({ types: t }) => {

  const ProgramVisitor = {
    // Use a ProgramVisitor to efficiently avoid processing the same file twice if babel calls twice.
    // The UI5ModuleVisitor will only be used if it hasn't let ran.
    Program: {
      exit(path, { opts }) {
        if (this.ran) return
        this.ran = true
        let { node } = path

        this.defaultExport = null
        this.defaultExportNode = null
        this.exportGlobal = false
        this.imports = []
        this.namedExports = []
        this.requires = []
        this.injectDynamicImportHelper = false

        // Opts handling
        this.namespacePrefix = opts.namespacePrefix
        this.noImportInteropPrefixes = opts.noImportInteropPrefixes || ['sap/']
        this.noImportInteropPrefixesRegexp = new RegExp(this.noImportInteropPrefixes.map(p => `(^${p}.*)`).join('|'))

        this.programNode = node

        path.traverse(UI5ModuleVisitor, this)

        const needsWrap = this.defaultExport || this.imports.length || this.namedExports.length || this.injectDynamicImportHelper
        if (needsWrap) {
          wrap(this, node, opts)
        }
      }
    }
  }

  const UI5ModuleVisitor = {

    /*!
     * Removes the ES6 import and adds the details to the import array in our state.
     */
    ImportDeclaration(path) { // { opts = {} }
      const { node } = path

      const { specifiers, source } = node
      const src = source.value.replace(/\\/g, '/')

      const shouldInterop = !this.noImportInteropPrefixesRegexp.test(src)

      // NOTE: DO NOT REMOVE. This code is for future enhancements, not old logic.
      // const testLibs = opts.libs || ['^sap/']
      // const isLibRE = testLibs.length && new RegExp(`(${testLibs.join('|')})`)
      // const isLib = isLibRE.test(src)
      // const testSrc = (opts.libs || ['^sap/']).concat(opts.files || [])
      // const isUi5SrcRE = testSrc.length && new RegExp(`(${testSrc.join('|')})`)
      // const isUi5Src = isUi5SrcRE.test(src)

      // console.log(existingImport)

      const name = src.replace(/\//g, '_').replace(/\./g, '') // default to the src for import without named var

      const existingImport = this.imports.find(imp => imp.src === src)
      const imp = existingImport || {
        src, // url
        name,
        // isLib, // for future use separating UI5 imports from npm/webpack imports
        // isUi5Src, // not used yet
        tmpName: (shouldInterop ? tempModuleName(name) : name),
        destructors: [],
        default: false,
        interop: false,
        path: path,
        locked: false
      }

      const destructors = []

      for (const specifier of specifiers) {
        if (t.isImportDefaultSpecifier(specifier)) { // import X from 'X'
          imp.default = true
          imp.interop = shouldInterop

          // Shorten the imported-as name since it should be unique for default imports.
          // The default import should always come first,
          // so this new name will be used for destructuring the other too.
          if (!imp.locked) {
            imp.name = specifier.local.name
            imp.tmpName = shouldInterop ? tempModuleName(imp.name) : imp.name
            imp.locked = true
          }

          if (shouldInterop) {
            destructors.push(
              th.buildDefaultImportDestructor({
                MODULE: t.identifier(imp.tmpName),
                LOCAL: specifier.local,
              })
            )
          }
        }
        else if (t.isImportNamespaceSpecifier(specifier)) {
          // console.log(specifier)
          if (specifiers.length === 1 && !imp.locked) { // import * as X from 'X'
            // If the namespace specifier is the only import, we can avoid the temp name and the destructor
            imp.name = specifier.local.name
            imp.tmpName = specifier.local.name
            imp.locked = true
          }
          else { // import X, * as X2 from 'X'
            // Else it's probably combined with a default export. keep the tmpName and destruct it
            destructors.push(
              th.buildConstDeclaration({
                NAME: specifier.local,
                VALUE: t.identifier(imp.tmpName),
              })
            )
          }
        }
        else if (t.isImportSpecifier(specifier)) { // import { A } from 'X'
          destructors.push(
            th.buildNamedImportDestructor({
              MODULE: t.identifier(imp.tmpName),
              LOCAL: specifier.local,
              IMPORTED: t.stringLiteral(specifier.imported.name)
            })
          )
        }
        else {
          throw path.buildCodeFrameError(`Unknown ImportDeclaration specifier type ${specifier.type}`)
        }
      }

      path.replaceWithMultiple(destructors)
      if (destructors.length) {
        imp.locked = true
      }

      imp.destructors = imp.destructors.concat(destructors)

      // TODO now that we're saving destructors on the imp, dynamically determine firstImport if needed.
      if (!this.firstImport && imp.destructors[0]) {
        this.firstImport = imp.destructors[0]
      }

      if (!existingImport) {
        this.imports.push(imp)
      }
    },

    /**
     * Push all exports to an array.
     * The reason we don't export in place is to handle the situation
     * where a let or var can be defined, and the latest one should be exported.
     */
    ExportNamedDeclaration(path) {
      const { node } = path
      const { specifiers, declaration, source } = node

      let fromSource = ''
      if (source) { // export { one, two } from 'x'
        const src = source.value
        const name = src.replace(/\//g, '_').replace(/\./g, '')
        const tmpName = tempModuleName(name)
        this.imports.push({ src, name, tmpName })
        fromSource = tmpName + '.'
      }

      if (specifiers && specifiers.length) { // export { one, two }
        for (const specifier of path.node.specifiers) {
          this.namedExports.push({
            key: specifier.exported,
            value: t.identifier(`${fromSource}${specifier.local.name}`),
          })
        }
        path.remove()
      }
      else if (declaration) { // export const c = 1 | export function f() {}
        const name = getIdName(declaration)
        if (name) { // export function f() {}
          const id = t.identifier(declaration.id.name)
          this.namedExports.push({
            key: id,
            value: id,
          })
        }
        else if (declaration.declarations) { // export const c = 1
          for (const subDeclaration of declaration.declarations) {
            const id = t.identifier(subDeclaration.id.name)
            this.namedExports.push({
              value: id,
              key: id
            })
          }
        }
        else {
          throw path.buildCodeFrameError('Unknown ExportNamedDeclaration shape.')
        }
        path.replaceWith(declaration)
      }
      else {
        throw path.buildCodeFrameError('Unknown ExportNamedDeclaration shape.')
      }
    },

    /*!
     * Replaces the ES6 export with sap.ui.define by using the state.imports array built up when
     * visiting ImportDeclaration.
     * Only a single 'export default' is supported.
     */
    ExportDefaultDeclaration(path) {
      const { node } = path
      const { declaration } = node
      this.defaultExportDeclaration = declaration
      const declarationName = getIdName(declaration)
      if (hasGlobalExportFlag(node)) { // check for @export jsdoc
        this.exportGlobal = true
      }
      if (declarationName) {
        // ClassDeclaration or FunctionDeclaration with name.
        // Leave the declaration in-line and preserve the identifier for the return statement.
        path.replaceWith(declaration)
        this.defaultExport = t.identifier(declarationName)
      }
      else {
        // Identifier, ObjectExpression or anonymous FunctionDeclaration
        // Safe to move to the end and return directly
        if (t.isFunctionDeclaration(declaration)) {
          const { params, body, generator, async: isAsync } = declaration
          this.defaultExport = t.functionExpression(null, params, body, generator, isAsync)
        }
        else {
          this.defaultExport = declaration
        }
        path.remove()
      }
    },

    ExportAllDeclaration(path) {
      const src = path.node.source.value
      const name = src.replace(/\//g, '_').replace(/\./g, '')
      const tmpName = tempModuleName(name)

      this.imports.push({ src, name, tmpName })

      this.exportAllHelper = true

      this.namedExports.push({
        all: true,
        value: t.identifier(tmpName),
      })

      path.remove()
    },

    /**
     * ClassDeclaration visitor.
     * Use both enter() and exit() to track when the visitor is inside a UI5 class,
     * in order to convert the super calls.
     * No changes for non-UI5 classes.
     */
    Class: {
      enter(path, { opts = {} }) {
        const { node, parent, parentPath } = path

        if (opts.neverConvertClass) {
          return
        }
        if (doesClassExtendFromImport(node, [...this.imports, ...this.requires])) {
          const { name } = node.id
          // If the super class is one of the imports, we'll assume it's a UI5 managed class,
          // and therefore needs to be transformed to .extend() syntax.
          const classInfo = getClassInfo(path, node, path.parent, opts)
          if (classInfo.nonUI5 || (opts.onlyConvertNamedClass && !classInfo.name)) {
            return
          }
          this.superClassName = classInfo.superClassName

          // Find the Block scoped parent (Program or Function body) and search for assigned properties within that.
          const blockParent = path.findParent(path => path.isBlock()).node
          const staticProps = groupPropertiesByName(getOtherPropertiesOfIdentifier(blockParent, name))
          // TODO flag metadata and renderer for removal if applicable
          const ui5ExtendClass = convertClassToUI5Extend(node, classInfo, staticProps, opts)

          if (path.isClassDeclaration()) { // class X {}
            path.replaceWithMultiple(ui5ExtendClass)
          }
          else if (path.isClassExpression()) { // return class X {}
            if (t.isReturnStatement(parent)) {
              // Add the return statement back before calling replace
              ui5ExtendClass.push(th.buildReturn({
                ID: t.identifier(classInfo.localName)
              }))
            }
            parentPath.replaceWithMultiple(ui5ExtendClass)
          }
        }
      },
      exit() {
        this.superClassName = null
      }
    },

    /*!
     * Visits function calls.
     */
CallExpression(path) {
      const { node } = path
      const { callee } = node
      if (isImport(callee)) {
        this.injectDynamicImportHelper = true
        path.replaceWith({
          ...node,
          callee: t.identifier('__ui5_require_async')
        })
      }
      // If the file already has sap.ui.define, get the names of variables it creates to use for the class logic.
      else if (isSAPUIDefineCallExpression(node)) {
        this.requires = getRequiredParamsOfSAPUIDefine(path, node)
        return
      }
      else if (this.superClassName) {
        if (t.isSuper(callee)) {
          replaceConstructorSuperCall(path, node, this.superClassName)
        }
        else if (t.isSuper(callee.object)) {
          replaceObjectSuperCall(path, node, this.superClassName)
        }
      }
    },

    /**
     * Convert object method constructor() to constructor: function constructor(),
     * since a UI5 class is not a real class.
     */
    ObjectMethod(path) {
      const { node } = path
      const CONSTRUCTOR = 'constructor'
      if (node.key.name === CONSTRUCTOR) {
        // The keyword 'constructor' should not be used as a shorthand
        // method name in an object. It might(?) work on some objects,
        // but it doesn't work with X.extend(...) inheritance.
        path.replaceWith(
          t.objectProperty(
            t.identifier(CONSTRUCTOR),
            t.functionExpression(
              t.identifier(CONSTRUCTOR),
              node.params,
              node.body)))
      }
    }

  }

  function getRequiredParamsOfSAPUIDefine(path, node) {
    const defineArgs = node.arguments
    const callbackNode = defineArgs
      .find(argNode => (t.isFunction(argNode)))
    return callbackNode.params // Identifier
  }

  /**
   * Replace super() call
   */
  function replaceConstructorSuperCall(path, node, superClassName) {
    node.arguments.unshift(t.identifier('this'))
    path.replaceWith(
      t.callExpression(t.identifier(`${superClassName}.prototype.constructor.call`), node.arguments))
  }

  /**
   * Replace super.method() call
   */
  function replaceObjectSuperCall(path, node, superClassName) {
    node.arguments.unshift(t.identifier('this'))
    const callIdentifier = t.identifier(`${superClassName}.prototype.${node.callee.property.name}.call`)
    path.replaceWith(t.callExpression(callIdentifier, node.arguments))
  }

  function doesClassExtendFromImport(node, imports) {
    const superClass = node.superClass
    if (!superClass) {
      return false
    }
    const isImported = imports.some(imported => imported.name === superClass.name)
    return isImported
  }

  function hasGlobalExportFlag(node) {
    return hasJsdocGlobalExportFlag(node)
  }

  return {
    visitor: ProgramVisitor
  }
}
<commit_msg>Fix #1: Moved logic from 'Program.exit' to 'Program'<commit_after>
import * as th from './helpers/templates'

import { getIdName, getOtherPropertiesOfIdentifier, groupPropertiesByName, isImport } from './helpers/ast'
import { getClassInfo, convertClassToUI5Extend, isSAPUIDefineCallExpression } from './helpers/classes'
import { hasJsdocGlobalExportFlag } from './helpers/jsdoc'
import { wrap } from './helpers/wrapper'

/*
References:
Babel Plugin Handbook: https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md
AST Spec: https://github.com/babel/babylon/blob/master/ast/spec.md
Babel Types (t.*) https://github.com/babel/babel/tree/master/packages/babel-types
AST Explorer: https://astexplorer.net/
*/

const tempModuleName = (name) => `__${name}`

module.exports = ({ types: t }) => {

  const ProgramVisitor = {
    // Use a ProgramVisitor to efficiently avoid processing the same file twice if babel calls twice.
    // The UI5ModuleVisitor will only be used if it hasn't let ran.
    Program(path, { opts }) {
      if (this.ran) return
      this.ran = true
      let { node } = path

      this.defaultExport = null
      this.defaultExportNode = null
      this.exportGlobal = false
      this.imports = []
      this.namedExports = []
      this.requires = []
      this.injectDynamicImportHelper = false
      this.programNode = node

      // Opts handling
      this.namespacePrefix = opts.namespacePrefix
      this.noImportInteropPrefixes = opts.noImportInteropPrefixes || ['sap/']
      this.noImportInteropPrefixesRegexp = new RegExp(this.noImportInteropPrefixes.map(p => `(^${p}.*)`).join('|'))

      path.traverse(UI5ModuleVisitor, this)

      const needsWrap = !!(this.defaultExport || this.imports.length || this.namedExports.length || this.injectDynamicImportHelper)
      if (needsWrap) {
        wrap(this, node, opts)
      }
    }
  }

  const UI5ModuleVisitor = {

    /*!
     * Removes the ES6 import and adds the details to the import array in our state.
     */
    ImportDeclaration(path) { // { opts = {} }
      const { node } = path

      const { specifiers, source } = node
      const src = source.value.replace(/\\/g, '/')

      const shouldInterop = !this.noImportInteropPrefixesRegexp.test(src)

      // NOTE: DO NOT REMOVE. This code is for future enhancements, not old logic.
      // const testLibs = opts.libs || ['^sap/']
      // const isLibRE = testLibs.length && new RegExp(`(${testLibs.join('|')})`)
      // const isLib = isLibRE.test(src)
      // const testSrc = (opts.libs || ['^sap/']).concat(opts.files || [])
      // const isUi5SrcRE = testSrc.length && new RegExp(`(${testSrc.join('|')})`)
      // const isUi5Src = isUi5SrcRE.test(src)

      // console.log(existingImport)

      const name = src.replace(/\//g, '_').replace(/\./g, '') // default to the src for import without named var

      const existingImport = this.imports.find(imp => imp.src === src)
      const imp = existingImport || {
        src, // url
        name,
        // isLib, // for future use separating UI5 imports from npm/webpack imports
        // isUi5Src, // not used yet
        tmpName: (shouldInterop ? tempModuleName(name) : name),
        destructors: [],
        default: false,
        interop: false,
        path: path,
        locked: false
      }

      const destructors = []

      for (const specifier of specifiers) {
        if (t.isImportDefaultSpecifier(specifier)) { // import X from 'X'
          imp.default = true
          imp.interop = shouldInterop

          // Shorten the imported-as name since it should be unique for default imports.
          // The default import should always come first,
          // so this new name will be used for destructuring the other too.
          if (!imp.locked) {
            imp.name = specifier.local.name
            imp.tmpName = shouldInterop ? tempModuleName(imp.name) : imp.name
            imp.locked = true
          }

          if (shouldInterop) {
            destructors.push(
              th.buildDefaultImportDestructor({
                MODULE: t.identifier(imp.tmpName),
                LOCAL: specifier.local,
              })
            )
          }
        }
        else if (t.isImportNamespaceSpecifier(specifier)) {
          // console.log(specifier)
          if (specifiers.length === 1 && !imp.locked) { // import * as X from 'X'
            // If the namespace specifier is the only import, we can avoid the temp name and the destructor
            imp.name = specifier.local.name
            imp.tmpName = specifier.local.name
            imp.locked = true
          }
          else { // import X, * as X2 from 'X'
            // Else it's probably combined with a default export. keep the tmpName and destruct it
            destructors.push(
              th.buildConstDeclaration({
                NAME: specifier.local,
                VALUE: t.identifier(imp.tmpName),
              })
            )
          }
        }
        else if (t.isImportSpecifier(specifier)) { // import { A } from 'X'
          destructors.push(
            th.buildNamedImportDestructor({
              MODULE: t.identifier(imp.tmpName),
              LOCAL: specifier.local,
              IMPORTED: t.stringLiteral(specifier.imported.name)
            })
          )
        }
        else {
          throw path.buildCodeFrameError(`Unknown ImportDeclaration specifier type ${specifier.type}`)
        }
      }

      path.replaceWithMultiple(destructors)
      if (destructors.length) {
        imp.locked = true
      }

      imp.destructors = imp.destructors.concat(destructors)

      // TODO now that we're saving destructors on the imp, dynamically determine firstImport if needed.
      if (!this.firstImport && imp.destructors[0]) {
        this.firstImport = imp.destructors[0]
      }

      if (!existingImport) {
        this.imports.push(imp)
      }
    },

    /**
     * Push all exports to an array.
     * The reason we don't export in place is to handle the situation
     * where a let or var can be defined, and the latest one should be exported.
     */
    ExportNamedDeclaration(path) {
      const { node } = path
      const { specifiers, declaration, source } = node

      let fromSource = ''
      if (source) { // export { one, two } from 'x'
        const src = source.value
        const name = src.replace(/\//g, '_').replace(/\./g, '')
        const tmpName = tempModuleName(name)
        this.imports.push({ src, name, tmpName })
        fromSource = tmpName + '.'
      }

      if (specifiers && specifiers.length) { // export { one, two }
        for (const specifier of path.node.specifiers) {
          this.namedExports.push({
            key: specifier.exported,
            value: t.identifier(`${fromSource}${specifier.local.name}`),
          })
        }
        path.remove()
      }
      else if (declaration) { // export const c = 1 | export function f() {}
        const name = getIdName(declaration)
        if (name) { // export function f() {}
          const id = t.identifier(declaration.id.name)
          this.namedExports.push({
            key: id,
            value: id,
          })
        }
        else if (declaration.declarations) { // export const c = 1
          for (const subDeclaration of declaration.declarations) {
            const id = t.identifier(subDeclaration.id.name)
            this.namedExports.push({
              value: id,
              key: id
            })
          }
        }
        else {
          throw path.buildCodeFrameError('Unknown ExportNamedDeclaration shape.')
        }
        path.replaceWith(declaration)
      }
      else {
        throw path.buildCodeFrameError('Unknown ExportNamedDeclaration shape.')
      }
    },

    /*!
     * Replaces the ES6 export with sap.ui.define by using the state.imports array built up when
     * visiting ImportDeclaration.
     * Only a single 'export default' is supported.
     */
    ExportDefaultDeclaration(path) {
      const { node } = path
      const { declaration } = node
      this.defaultExportDeclaration = declaration
      const declarationName = getIdName(declaration)
      if (hasGlobalExportFlag(node)) { // check for @export jsdoc
        this.exportGlobal = true
      }
      if (declarationName) {
        // ClassDeclaration or FunctionDeclaration with name.
        // Leave the declaration in-line and preserve the identifier for the return statement.
        path.replaceWith(declaration)
        this.defaultExport = t.identifier(declarationName)
      }
      else {
        // Identifier, ObjectExpression or anonymous FunctionDeclaration
        // Safe to move to the end and return directly
        if (t.isFunctionDeclaration(declaration)) {
          const { params, body, generator, async: isAsync } = declaration
          this.defaultExport = t.functionExpression(null, params, body, generator, isAsync)
        }
        else {
          this.defaultExport = declaration
        }
        path.remove()
      }
    },

    ExportAllDeclaration(path) {
      const src = path.node.source.value
      const name = src.replace(/\//g, '_').replace(/\./g, '')
      const tmpName = tempModuleName(name)

      this.imports.push({ src, name, tmpName })

      this.exportAllHelper = true

      this.namedExports.push({
        all: true,
        value: t.identifier(tmpName),
      })

      path.remove()
    },

    /**
     * ClassDeclaration visitor.
     * Use both enter() and exit() to track when the visitor is inside a UI5 class,
     * in order to convert the super calls.
     * No changes for non-UI5 classes.
     */
    Class: {
      enter(path, { opts = {} }) {
        const { node, parent, parentPath } = path

        if (opts.neverConvertClass) {
          return
        }
        if (doesClassExtendFromImport(node, [...this.imports, ...this.requires])) {
          const { name } = node.id
          // If the super class is one of the imports, we'll assume it's a UI5 managed class,
          // and therefore needs to be transformed to .extend() syntax.
          const classInfo = getClassInfo(path, node, path.parent, opts)
          if (classInfo.nonUI5 || (opts.onlyConvertNamedClass && !classInfo.name)) {
            return
          }
          this.superClassName = classInfo.superClassName

          // Find the Block scoped parent (Program or Function body) and search for assigned properties within that.
          const blockParent = path.findParent(path => path.isBlock()).node
          const staticProps = groupPropertiesByName(getOtherPropertiesOfIdentifier(blockParent, name))
          // TODO flag metadata and renderer for removal if applicable
          const ui5ExtendClass = convertClassToUI5Extend(node, classInfo, staticProps, opts)

          if (path.isClassDeclaration()) { // class X {}
            path.replaceWithMultiple(ui5ExtendClass)
          }
          else if (path.isClassExpression()) { // return class X {}
            if (t.isReturnStatement(parent)) {
              // Add the return statement back before calling replace
              ui5ExtendClass.push(th.buildReturn({
                ID: t.identifier(classInfo.localName)
              }))
            }
            parentPath.replaceWithMultiple(ui5ExtendClass)
          }
        }
      },
      exit() {
        this.superClassName = null
      }
    },

    /*!
     * Visits function calls.
     */
    CallExpression(path) {
      const { node } = path
      const { callee } = node
      if (isImport(callee)) {
        this.injectDynamicImportHelper = true
        path.replaceWith({
          ...node,
          callee: t.identifier('__ui5_require_async')
        })
      }
      // If the file already has sap.ui.define, get the names of variables it creates to use for the class logic.
      else if (isSAPUIDefineCallExpression(node)) {
        this.requires = getRequiredParamsOfSAPUIDefine(path, node)
        return
      }
      else if (this.superClassName) {
        if (t.isSuper(callee)) {
          replaceConstructorSuperCall(path, node, this.superClassName)
        }
        else if (t.isSuper(callee.object)) {
          replaceObjectSuperCall(path, node, this.superClassName)
        }
      }
    },

    /**
     * Convert object method constructor() to constructor: function constructor(),
     * since a UI5 class is not a real class.
     */
    ObjectMethod(path) {
      const { node } = path
      const CONSTRUCTOR = 'constructor'
      if (node.key.name === CONSTRUCTOR) {
        // The keyword 'constructor' should not be used as a shorthand
        // method name in an object. It might(?) work on some objects,
        // but it doesn't work with X.extend(...) inheritance.
        path.replaceWith(
          t.objectProperty(
            t.identifier(CONSTRUCTOR),
            t.functionExpression(
              t.identifier(CONSTRUCTOR),
              node.params,
              node.body)))
      }
    }

  }

  function getRequiredParamsOfSAPUIDefine(path, node) {
    const defineArgs = node.arguments
    const callbackNode = defineArgs
      .find(argNode => (t.isFunction(argNode)))
    return callbackNode.params // Identifier
  }

  /**
   * Replace super() call
   */
  function replaceConstructorSuperCall(path, node, superClassName) {
    node.arguments.unshift(t.identifier('this'))
    path.replaceWith(
      t.callExpression(t.identifier(`${superClassName}.prototype.constructor.call`), node.arguments))
  }

  /**
   * Replace super.method() call
   */
  function replaceObjectSuperCall(path, node, superClassName) {
    node.arguments.unshift(t.identifier('this'))
    const callIdentifier = t.identifier(`${superClassName}.prototype.${node.callee.property.name}.call`)
    path.replaceWith(t.callExpression(callIdentifier, node.arguments))
  }

  function doesClassExtendFromImport(node, imports) {
    const superClass = node.superClass
    if (!superClass) {
      return false
    }
    const isImported = imports.some(imported => imported.name === superClass.name)
    return isImported
  }

  function hasGlobalExportFlag(node) {
    return hasJsdocGlobalExportFlag(node)
  }

  return {
    visitor: ProgramVisitor
  }
}
<|endoftext|>


<commit_before>// dependencies ----------------------------------------------------------------------

import Reflux    from 'reflux';
import Actions   from './dataset.actions.js';
import scitran   from '../utils/scitran';
import crn       from '../utils/crn';
import bids      from '../utils/bids';
import router    from '../utils/router-container';
import userStore from '../user/user.store';
import upload    from '../utils/upload';
import config    from '../config';
import files     from '../utils/files';

let datasetStore = Reflux.createStore({

// store setup -----------------------------------------------------------------------

	listenables: Actions,

	init: function () {
		this.setInitialState();
		this.loadApps();
	},

	getInitialState: function () {
		return this.data;
	},

// data ------------------------------------------------------------------------------

	data: {},

	update: function (data) {
		for (let prop in data) {this.data[prop] = data[prop];}
		this.trigger(this.data);
	},

	/**
	 * Set Initial State
	 *
	 * Sets the state to the data object defined
	 * inside the function. Also takes a diffs object
	 * which will set the state to the initial state
	 * with any differences passed.
	 */
	setInitialState: function (diffs) {
		let data = {
			apps: [],
			dataset: null,
			loading: false,
			loadingApps: false,
			loadingJobs: false,
			jobs: [],
			showJobsModal: false,
			showShareModal: false,
			snapshot: false,
			snapshots: [],
			status: null,
			users: []
		};
		for (let prop in diffs) {data[prop] = diffs[prop];}
		this.update(data);
	},

// Actions ---------------------------------------------------------------------------

	// Dataset -----------------------------------------------------------------------

	/**
	 * Load Dataset
	 *
	 * Takes a datasetId and loads the dataset.
	 */
	loadDataset(datasetId, options) {
		let snapshot = !!(options && options.snapshot)
		this.update({loading: true, dataset: null});
		bids.getDataset(datasetId, (res) => {
			if (res.status === 404 || res.status === 403) {
				this.update({status: res.status, loading: false, snapshot: snapshot});
			} else {
				this.update({dataset: res, loading: false, snapshot: snapshot});
			}
			let originalId = res.original ? res.original : datasetId;
			this.loadJobs(datasetId);
			this.loadSnapshots(originalId, datasetId);
		}, options);
	},

	/**
	 * Load Snapshot
	 *
	 * Takes a snapshot ID and loads the snapshot.
	 */
	loadSnapshot(isOriginal, snapshotId) {
		let datasetId = this.data.dataset.original ? this.data.dataset.original : this.data.dataset._id;
		if (isOriginal) {
			router.transitionTo('dataset', {datasetId: snapshotId});
		} else {
			router.transitionTo('snapshot', {datasetId, snapshotId});
		}
	},

	/**
	 * Reload Dataset
	 *
	 * Optionally takes a datasetId and only reloads
	 * the dataset if that ID matches the current ID.
	 * If no ID is passed it reloads the current ID.
	 */
	reloadDataset(datasetId) {
		if (this.data.dataset) {
			if (!datasetId) {
				this.loadDataset(this.data.dataset._id);
			}else if (this.data.dataset._id == datasetId) {
				this.loadDataset(datasetId);
			}
		}
	},

	/**
	 * Load Users
	 *
	 * Loads a list of all users.
	 */
	loadUsers() {
		scitran.getUsers((err, res) => {
			this.update({users: res.body});
		});
	},

	/**
	 * Load Jobs
	 */
	loadJobs(projectId) {
		this.update({loadingJobs: true});
		crn.getDatasetJobs(projectId, (err, res) => {
            this.update({jobs: res.body, loadingJobs: false});
        });
	},

	/**
	 * Load Apps
	 */
	loadApps() {
		this.update({loadingApps: true});
		crn.getApps((err, res) => {
			this.update({apps: res.body, loadingApps: false});
		});
	},

	/**
	 * Publish
	 *
	 * Takes a datasetId and sets the datset to public.
	 */
	publish(datasetId) {
		if (this.data.snapshot) {
			scitran.updateSnapshotPublic(datasetId, true, (err, res) => {
				if (!err) {
					let dataset = this.data.dataset;
					dataset.public = true;
				}
			});
		} else {
			/** this should be removed eventually as only snapshots can be publish **/
			scitran.updateProject(datasetId, {public: true}, (err, res) => {
				if (!err) {
					let dataset = this.data.dataset;
					dataset.public = true;
					this.update({dataset});
				}
			});
			/** this should be removed eventually as only snapshots can be publish **/
		}
	},

	/**
	 * Download Dataset
	 *
	 */
	downloadDataset(snapshot) {
		// open download window as synchronous action from click to avoid throwing popup blockers
		window.open('', 'bids-download');
		scitran.getBIDSDownloadTicket(this.data.dataset._id, (err, res) => {
			let ticket = res.body.ticket;
			let downloadWindow = window.open(res.req.url.split('?')[0] + '?ticket=' + ticket, 'bids-download');
			setTimeout(() => {downloadWindow.close();}, 1000);
			let dataset = this.data.dataset;
			dataset.downloads++;
			this.update({dataset});
		}, {snapshot: !!snapshot});
	},

	/**
	 * Delete Dataset
	 *
	 * Takes a datsetId, deletes the dataset, and returns the user
	 * to the my datasets page.
	 */
	deleteDataset(datasetId) {
		bids.deleteDataset(datasetId, () => {
            router.transitionTo('datasets');
		}, {snapshot: this.data.snapshot});
	},

	/**
	 * Toggle Modal
	 */
	toggleModal(name) {
		let updates = {};
		updates['show' + name + 'Modal'] = !this.data['show' + name + 'Modal'];
		this.update(updates);
	},

	// Metadata ----------------------------------------------------------------------

	updateName(value, callback) {
		scitran.updateProject(this.data.dataset._id, {name: value}, () => {
			this.updateDescription('Name', value, callback);
		});
	},

	/**
	 * Update Description
	 *
	 * Takes a key and a value and updates the dataset
	 * description JSON note accordingly.
	 */
	updateDescription(key, value, callback) {
		let dataset = this.data.dataset;
		let description = dataset.description;
		description[key] = value;
		if (key !== 'Authors'){description.Authors = dataset.authors;}
		this.saveDescription(description, callback);
		this.update({dataset: dataset});
	},

	/**
	 * Save Description
	 *
	 * Takes a description object and upserts
	 * the JSON description file.
	 */
	saveDescription(description, callback) {
		let datasetId = this.data.dataset._id;
		scitran.updateProject(datasetId, {metadata: {authors: description.Authors}}, (err, res) => {
			let authors = [];
			for (let author of description.Authors) {
				authors.push(author.name);
			}
			description.Authors = authors;
			scitran.updateFileFromString('projects', datasetId, 'dataset_description.json', JSON.stringify(description), 'application/json', ['project'], callback);
		});
	},

	/**
	 * Update README
	 */
	updateREADME(value, callback) {
		scitran.updateFileFromString('projects', this.data.dataset._id, 'README', value, '', [], callback);
	},

	// Attachments -------------------------------------------------------------------

	/**
	 * Upload Attachment
	 *
	 * Takes a file and a callback and uploads
	 * the file to the current dataset.
	 */
	uploadAttachment(file, callback) {
		let attachmentExists, fileExists;
		for (let attachment of this.data.dataset.attachments) {
			if (attachment.name === file.name) {
				attachmentExists = true;
			}
		}

		for (let existingFile of this.data.dataset.children) {
			if (existingFile.name === file.name) {
				fileExists = true;
			}
		}

		if (attachmentExists) {
			callback({error: '"' + file.name + '" has already been uploaded. Multiple attachments with the same name are not allowed.'});
		} else if (fileExists) {
			callback({error: 'You cannot upload a file named "' + file.name + '" as an attachment because it already exists in the dataset.'});
		} else {
			let request = {
				url: config.scitran.url + 'projects/' + this.data.dataset._id + '/files',
				file: file,
				tags: ['attachment'],
				progressStart: () => {},
				progressEnd: () => {
					bids.getDataset(this.data.dataset._id, (res) => {
						let dataset = this.data.dataset;
						dataset.attachments = res.attachments;
						this.update({dataset: dataset});
						callback();
					});
				},
				error: (err, req) => {
					callback({error: 'There was an error uploading your attachment. Please try again and contact the site administrator if the issue persists.'});
				}
			};
			upload.add(request);
		}
	},

	/**
	 * Delete Attachment
	 *
	 * Takes a filename and index and deletes
	 * the attachment from the current dataset.
	 */
	deleteAttachment(filename, index) {
		scitran.deleteFile('projects', this.data.dataset._id, filename, (err, res) => {
			let dataset = this.data.dataset;
			dataset.attachments.splice(index, 1);
			this.update({dataset});
		});
	},

	/**
	 * Download Attachment
	 *
	 * Takes a filename and starts a downloads
	 * for the file within the current dataset.
	 */
	downloadAttachment(filename, snapshot) {
		// open download window as synchronous action from click to avoid throwing popup blockers
		window.open('', 'attachment-download');
		scitran.getDownloadTicket('projects', this.data.dataset._id, filename, (err, res) => {
			let ticket = res.body.ticket;
			let downloadWindow = window.open(res.req.url.split('?')[0] + '?ticket=' + ticket, 'attachment-download');
			setTimeout(() => {downloadWindow.close();});
		}, {snapshot: !!snapshot});
	},

	// File Structure ----------------------------------------------------------------

	/**
	 * Add File
	 */
	addFile(container, file) {
		let exists;
		for (let existingFile of container.children) {
			if (existingFile.name === file.name) {
				exists = true;
			}
		}

		if (exists) {
			this.updateDirectoryState(container._id, {error: '"' + file.name + '" already exists in this directory.'});
		} else {
			this.updateDirectoryState(container._id, {loading: true});
			scitran.updateFile(container.containerType, container._id, file, () => {
				let children = container.children
				children.unshift({
					filename: file.name,
					name: file.name,
					parentContainer: container.containerType,
					parentId: container._id
				});
				this.updateDirectoryState(container._id, {children: children, loading: false});
			});
		}
	},

	/**
	 * Delete File
	 */
	deleteFile(file) {
		let dataset = this.data.dataset;
		scitran.deleteFile(file.parentContainer, file.parentId, file.name, (err, res) => {
			let match = files.findInTree([dataset], file.parentId);
			let children = [];
			for (let existingFile of match.children) {
				if (file.filename !== existingFile.filename) {
					children.push(existingFile);
				}
			}
			match.children = children;
			this.update({dataset});
		});
	},

	/**
	 * Dismiss Error
	 */
	dismissError(item) {
		if (item.children) {
			this.updateDirectoryState(item._id, {error: ''});
		} else {
			this.updateFileState(item, {error: ''});
		}
	},

	/**
	 * Update File
	 */
	updateFile(item, file) {
		let id       = item.parentId,
			level    = item.parentContainer,
			filename = item.name;

		if (filename !== file.name) {
			this.updateFileState(item, {
				error: 'You must replace a file with a file of the same name.'
			});
		} else {
			this.updateFileState(item, {error: null, loading: true});
			scitran.updateFile(level, id, file, (err, res) => {
				this.updateFileState(item, {loading: false});
			});
		}
	},

	/**
	 * Update Directory State
	 *
	 */
	updateDirectoryState(directoryId, changes) {
		let dataset = this.data.dataset;
		let match = files.findInTree([dataset], directoryId);
		if (match) {
			for (let key in changes) {
				match[key] = changes[key];
			}
		}
		this.update({dataset});
	},

	/**
	 * Update File State
	 *
	 * Take a file object and changes to be
	 * made and applies those changes by
	 * updating the state of the file tree
	 */
	updateFileState(file, changes) {
		let dataset = this.data.dataset;
		let parent = files.findInTree([dataset], file.parentId);
		let children = [];
		for (let existingFile of parent.children) {
			if (file.filename == existingFile.filename) {
				for (let key in changes) {
					existingFile[key] = changes[key];
				}
			}
		}
		this.update({dataset});
	},

	/**
	 * Toggle Folder
	 *
	 * Takes the id of a container in the
	 * current dataset and toggles its showChildren
	 * boolean which determines whether container
	 * children are shown in the tree hierarchy UI.
	 */
	toggleFolder(directory) {
		this.updateDirectoryState(directory._id, {showChildren: !directory.showChildren});
	},

	// Jobs --------------------------------------------------------------------------

	/**
	 * Start Job
	 */
	startJob(appName, appId, parameters, callback) {
		crn.createJob({
			name: appName,
			appId: appId,
			datasetId: this.data.dataset._id,
			userId: userStore.data.scitran._id,
			parameters: parameters
		}, (err, res) => {
			callback(err, res);
			// callback(err, {message: "Your analysis has been submitted. Periodically check the analysis section of this dataset to view the status and results."});
			this.loadJobs(this.data.dataset._id);
		});
	},

	/**
	 * Download Result
	 */
	downloadResult(jobId, fileName) {
		// open download window as synchronous action from click to avoid throwing popup blockers
		window.open('', 'bids-result');
		crn.getResultDownloadTicket(jobId, fileName, (err, res) => {
			let ticket = res.body._id;
			let downloadWindow = window.open(config.crn.url + 'jobs/' + jobId + '/results/' + fileName + '?ticket=' + ticket, 'bids-result');
			// close download window after 3 seconds
			setTimeout(() => {downloadWindow.close();}, 3000);
		});
	},

	// Snapshots ---------------------------------------------------------------------

	createSnapshot() {
		scitran.createSnapshot(this.data.dataset._id, (err, res) => {
			router.transitionTo('snapshot', {snapshotId: res.body._id});
		});
	},

	loadSnapshots(datasetId) {
		scitran.getProjectSnapshots(datasetId, (err, res) => {
			let snapshots = res.body;
			snapshots.unshift({
				isOriginal: true,
				_id: datasetId
			});
			this.update({snapshots: res.body});
		});
	}

});

export default datasetStore;<commit_msg>Fixed snapshot creation transition and added callback to publishing to close warn button<commit_after>// dependencies ----------------------------------------------------------------------

import Reflux    from 'reflux';
import Actions   from './dataset.actions.js';
import scitran   from '../utils/scitran';
import crn       from '../utils/crn';
import bids      from '../utils/bids';
import router    from '../utils/router-container';
import userStore from '../user/user.store';
import upload    from '../utils/upload';
import config    from '../config';
import files     from '../utils/files';

let datasetStore = Reflux.createStore({

// store setup -----------------------------------------------------------------------

	listenables: Actions,

	init: function () {
		this.setInitialState();
		this.loadApps();
	},

	getInitialState: function () {
		return this.data;
	},

// data ------------------------------------------------------------------------------

	data: {},

	update: function (data) {
		for (let prop in data) {this.data[prop] = data[prop];}
		this.trigger(this.data);
	},

	/**
	 * Set Initial State
	 *
	 * Sets the state to the data object defined
	 * inside the function. Also takes a diffs object
	 * which will set the state to the initial state
	 * with any differences passed.
	 */
	setInitialState: function (diffs) {
		let data = {
			apps: [],
			dataset: null,
			loading: false,
			loadingApps: false,
			loadingJobs: false,
			jobs: [],
			showJobsModal: false,
			showShareModal: false,
			snapshot: false,
			snapshots: [],
			status: null,
			users: []
		};
		for (let prop in diffs) {data[prop] = diffs[prop];}
		this.update(data);
	},

// Actions ---------------------------------------------------------------------------

	// Dataset -----------------------------------------------------------------------

	/**
	 * Load Dataset
	 *
	 * Takes a datasetId and loads the dataset.
	 */
	loadDataset(datasetId, options) {
		let snapshot = !!(options && options.snapshot)
		this.update({loading: true, dataset: null});
		bids.getDataset(datasetId, (res) => {
			if (res.status === 404 || res.status === 403) {
				this.update({status: res.status, loading: false, snapshot: snapshot});
			} else {
				this.update({dataset: res, loading: false, snapshot: snapshot});
			}
			let originalId = res.original ? res.original : datasetId;
			this.loadJobs(datasetId);
			this.loadSnapshots(originalId, datasetId);
		}, options);
	},

	/**
	 * Load Snapshot
	 *
	 * Takes a snapshot ID and loads the snapshot.
	 */
	loadSnapshot(isOriginal, snapshotId) {
		let datasetId = this.data.dataset.original ? this.data.dataset.original : this.data.dataset._id;
		if (isOriginal) {
			router.transitionTo('dataset', {datasetId: snapshotId});
		} else {
			router.transitionTo('snapshot', {datasetId, snapshotId});
		}
	},

	/**
	 * Reload Dataset
	 *
	 * Optionally takes a datasetId and only reloads
	 * the dataset if that ID matches the current ID.
	 * If no ID is passed it reloads the current ID.
	 */
	reloadDataset(datasetId) {
		if (this.data.dataset) {
			if (!datasetId) {
				this.loadDataset(this.data.dataset._id);
			}else if (this.data.dataset._id == datasetId) {
				this.loadDataset(datasetId);
			}
		}
	},

	/**
	 * Load Users
	 *
	 * Loads a list of all users.
	 */
	loadUsers() {
		scitran.getUsers((err, res) => {
			this.update({users: res.body});
		});
	},

	/**
	 * Load Jobs
	 */
	loadJobs(projectId) {
		this.update({loadingJobs: true});
		crn.getDatasetJobs(projectId, (err, res) => {
            this.update({jobs: res.body, loadingJobs: false});
        });
	},

	/**
	 * Load Apps
	 */
	loadApps() {
		this.update({loadingApps: true});
		crn.getApps((err, res) => {
			this.update({apps: res.body, loadingApps: false});
		});
	},

	/**
	 * Publish
	 *
	 * Takes a datasetId and sets the datset to public.
	 */
	publish(datasetId, callback) {
		if (this.data.snapshot) {
			scitran.updateSnapshotPublic(datasetId, true, (err, res) => {
				if (!err) {
					let dataset = this.data.dataset;
					dataset.public = true;
					callback();
				}
			});
		} else {
			/** this should be removed eventually as only snapshots can be publish **/
			scitran.updateProject(datasetId, {public: true}, (err, res) => {
				if (!err) {
					let dataset = this.data.dataset;
					dataset.public = true;
					this.update({dataset});
				}
			});
			/** this should be removed eventually as only snapshots can be publish **/
		}
	},

	/**
	 * Download Dataset
	 *
	 */
	downloadDataset(snapshot) {
		// open download window as synchronous action from click to avoid throwing popup blockers
		window.open('', 'bids-download');
		scitran.getBIDSDownloadTicket(this.data.dataset._id, (err, res) => {
			let ticket = res.body.ticket;
			let downloadWindow = window.open(res.req.url.split('?')[0] + '?ticket=' + ticket, 'bids-download');
			setTimeout(() => {downloadWindow.close();}, 1000);
			let dataset = this.data.dataset;
			dataset.downloads++;
			this.update({dataset});
		}, {snapshot: !!snapshot});
	},

	/**
	 * Delete Dataset
	 *
	 * Takes a datsetId, deletes the dataset, and returns the user
	 * to the my datasets page.
	 */
	deleteDataset(datasetId) {
		bids.deleteDataset(datasetId, () => {
            router.transitionTo('datasets');
		}, {snapshot: this.data.snapshot});
	},

	/**
	 * Toggle Modal
	 */
	toggleModal(name) {
		let updates = {};
		updates['show' + name + 'Modal'] = !this.data['show' + name + 'Modal'];
		this.update(updates);
	},

	// Metadata ----------------------------------------------------------------------

	updateName(value, callback) {
		scitran.updateProject(this.data.dataset._id, {name: value}, () => {
			this.updateDescription('Name', value, callback);
		});
	},

	/**
	 * Update Description
	 *
	 * Takes a key and a value and updates the dataset
	 * description JSON note accordingly.
	 */
	updateDescription(key, value, callback) {
		let dataset = this.data.dataset;
		let description = dataset.description;
		description[key] = value;
		if (key !== 'Authors'){description.Authors = dataset.authors;}
		this.saveDescription(description, callback);
		this.update({dataset: dataset});
	},

	/**
	 * Save Description
	 *
	 * Takes a description object and upserts
	 * the JSON description file.
	 */
	saveDescription(description, callback) {
		let datasetId = this.data.dataset._id;
		scitran.updateProject(datasetId, {metadata: {authors: description.Authors}}, (err, res) => {
			let authors = [];
			for (let author of description.Authors) {
				authors.push(author.name);
			}
			description.Authors = authors;
			scitran.updateFileFromString('projects', datasetId, 'dataset_description.json', JSON.stringify(description), 'application/json', ['project'], callback);
		});
	},

	/**
	 * Update README
	 */
	updateREADME(value, callback) {
		scitran.updateFileFromString('projects', this.data.dataset._id, 'README', value, '', [], callback);
	},

	// Attachments -------------------------------------------------------------------

	/**
	 * Upload Attachment
	 *
	 * Takes a file and a callback and uploads
	 * the file to the current dataset.
	 */
	uploadAttachment(file, callback) {
		let attachmentExists, fileExists;
		for (let attachment of this.data.dataset.attachments) {
			if (attachment.name === file.name) {
				attachmentExists = true;
			}
		}

		for (let existingFile of this.data.dataset.children) {
			if (existingFile.name === file.name) {
				fileExists = true;
			}
		}

		if (attachmentExists) {
			callback({error: '"' + file.name + '" has already been uploaded. Multiple attachments with the same name are not allowed.'});
		} else if (fileExists) {
			callback({error: 'You cannot upload a file named "' + file.name + '" as an attachment because it already exists in the dataset.'});
		} else {
			let request = {
				url: config.scitran.url + 'projects/' + this.data.dataset._id + '/files',
				file: file,
				tags: ['attachment'],
				progressStart: () => {},
				progressEnd: () => {
					bids.getDataset(this.data.dataset._id, (res) => {
						let dataset = this.data.dataset;
						dataset.attachments = res.attachments;
						this.update({dataset: dataset});
						callback();
					});
				},
				error: (err, req) => {
					callback({error: 'There was an error uploading your attachment. Please try again and contact the site administrator if the issue persists.'});
				}
			};
			upload.add(request);
		}
	},

	/**
	 * Delete Attachment
	 *
	 * Takes a filename and index and deletes
	 * the attachment from the current dataset.
	 */
	deleteAttachment(filename, index) {
		scitran.deleteFile('projects', this.data.dataset._id, filename, (err, res) => {
			let dataset = this.data.dataset;
			dataset.attachments.splice(index, 1);
			this.update({dataset});
		});
	},

	/**
	 * Download Attachment
	 *
	 * Takes a filename and starts a downloads
	 * for the file within the current dataset.
	 */
	downloadAttachment(filename, snapshot) {
		// open download window as synchronous action from click to avoid throwing popup blockers
		window.open('', 'attachment-download');
		scitran.getDownloadTicket('projects', this.data.dataset._id, filename, (err, res) => {
			let ticket = res.body.ticket;
			let downloadWindow = window.open(res.req.url.split('?')[0] + '?ticket=' + ticket, 'attachment-download');
			setTimeout(() => {downloadWindow.close();});
		}, {snapshot: !!snapshot});
	},

	// File Structure ----------------------------------------------------------------

	/**
	 * Add File
	 */
	addFile(container, file) {
		let exists;
		for (let existingFile of container.children) {
			if (existingFile.name === file.name) {
				exists = true;
			}
		}

		if (exists) {
			this.updateDirectoryState(container._id, {error: '"' + file.name + '" already exists in this directory.'});
		} else {
			this.updateDirectoryState(container._id, {loading: true});
			scitran.updateFile(container.containerType, container._id, file, () => {
				let children = container.children
				children.unshift({
					filename: file.name,
					name: file.name,
					parentContainer: container.containerType,
					parentId: container._id
				});
				this.updateDirectoryState(container._id, {children: children, loading: false});
			});
		}
	},

	/**
	 * Delete File
	 */
	deleteFile(file) {
		let dataset = this.data.dataset;
		scitran.deleteFile(file.parentContainer, file.parentId, file.name, (err, res) => {
			let match = files.findInTree([dataset], file.parentId);
			let children = [];
			for (let existingFile of match.children) {
				if (file.filename !== existingFile.filename) {
					children.push(existingFile);
				}
			}
			match.children = children;
			this.update({dataset});
		});
	},

	/**
	 * Dismiss Error
	 */
	dismissError(item) {
		if (item.children) {
			this.updateDirectoryState(item._id, {error: ''});
		} else {
			this.updateFileState(item, {error: ''});
		}
	},

	/**
	 * Update File
	 */
	updateFile(item, file) {
		let id       = item.parentId,
			level    = item.parentContainer,
			filename = item.name;

		if (filename !== file.name) {
			this.updateFileState(item, {
				error: 'You must replace a file with a file of the same name.'
			});
		} else {
			this.updateFileState(item, {error: null, loading: true});
			scitran.updateFile(level, id, file, (err, res) => {
				this.updateFileState(item, {loading: false});
			});
		}
	},

	/**
	 * Update Directory State
	 *
	 */
	updateDirectoryState(directoryId, changes) {
		let dataset = this.data.dataset;
		let match = files.findInTree([dataset], directoryId);
		if (match) {
			for (let key in changes) {
				match[key] = changes[key];
			}
		}
		this.update({dataset});
	},

	/**
	 * Update File State
	 *
	 * Take a file object and changes to be
	 * made and applies those changes by
	 * updating the state of the file tree
	 */
	updateFileState(file, changes) {
		let dataset = this.data.dataset;
		let parent = files.findInTree([dataset], file.parentId);
		let children = [];
		for (let existingFile of parent.children) {
			if (file.filename == existingFile.filename) {
				for (let key in changes) {
					existingFile[key] = changes[key];
				}
			}
		}
		this.update({dataset});
	},

	/**
	 * Toggle Folder
	 *
	 * Takes the id of a container in the
	 * current dataset and toggles its showChildren
	 * boolean which determines whether container
	 * children are shown in the tree hierarchy UI.
	 */
	toggleFolder(directory) {
		this.updateDirectoryState(directory._id, {showChildren: !directory.showChildren});
	},

	// Jobs --------------------------------------------------------------------------

	/**
	 * Start Job
	 */
	startJob(appName, appId, parameters, callback) {
		crn.createJob({
			name: appName,
			appId: appId,
			datasetId: this.data.dataset._id,
			userId: userStore.data.scitran._id,
			parameters: parameters
		}, (err, res) => {
			callback(err, res);
			// callback(err, {message: "Your analysis has been submitted. Periodically check the analysis section of this dataset to view the status and results."});
			this.loadJobs(this.data.dataset._id);
		});
	},

	/**
	 * Download Result
	 */
	downloadResult(jobId, fileName) {
		// open download window as synchronous action from click to avoid throwing popup blockers
		window.open('', 'bids-result');
		crn.getResultDownloadTicket(jobId, fileName, (err, res) => {
			let ticket = res.body._id;
			let downloadWindow = window.open(config.crn.url + 'jobs/' + jobId + '/results/' + fileName + '?ticket=' + ticket, 'bids-result');
			// close download window after 3 seconds
			setTimeout(() => {downloadWindow.close();}, 3000);
		});
	},

	// Snapshots ---------------------------------------------------------------------

	createSnapshot() {
		scitran.createSnapshot(this.data.dataset._id, (err, res) => {
			router.transitionTo('snapshot', {datasetId: this.data.dataset._id, snapshotId: res.body._id});
		});
	},

	loadSnapshots(datasetId) {
		scitran.getProjectSnapshots(datasetId, (err, res) => {
			let snapshots = res.body;
			snapshots.unshift({
				isOriginal: true,
				_id: datasetId
			});
			this.update({snapshots: res.body});
		});
	}

});

export default datasetStore;<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');

module.exports.parse = function (filename) {
  var contents = fs.readFileSync(filename);

  var title = contents.slice(37, 40).toString();
  var ignore = contents.slice(41, 42);
  var discipline = contents[43];
  var edition = contents[44];
  var last = contents[45];

  var bufInt = (contents.readUInt32BE(47) << 8) + contents.readUInt32BE(51);
  var section1_length = contents.readUInt32BE(55);
  var section1_title = contents.readUInt8(59);
  var section1_orig_center = contents.readUInt16BE(60);
  var section1_orig_subcenter = contents.readUInt16BE(62);
  var section1_year = contents.readUInt16BE(67);
  var section1_month = contents.readUInt8(68);
  var section1_day = contents.readUInt8(68);
  var section1_hour = contents.readUInt8(68);
  var section1_minute = contents.readUInt8(68);
  var section1_second = contents.readUInt8(68);

  console.log('title: '+title);
  console.log('discipline: '+discipline);
  console.log('edition: '+edition);
  console.log('bufInt: '+bufInt);
  console.log('length: '+section1_length);
  console.log('title: '+section1_title);
  console.log('orig_center: '+section1_orig_center);
  console.log('orig_subcenter: '+section1_orig_subcenter);
  console.log('year: '+section1_year);
  console.log('month: '+section1_month);
  console.log('day: '+section1_day);
  console.log('hour: '+section1_hour);
  console.log('minute: '+section1_minute);
  console.log('second: '+section1_second);

  for (var i = 37; i < 60; i++) {
    console.log(i+': '+contents[i]);
  }

  return 'test';
}

var test = 'test';
<commit_msg>full date from section 1<commit_after>'use strict';

var fs = require('fs');

module.exports.parse = function (filename) {
  var contents = fs.readFileSync(filename);

  var title = contents.slice(37, 40).toString();
  var ignore = contents.slice(41, 42);
  var discipline = contents[43];
  var edition = contents[44];
  var last = contents[45];

  var bufInt = (contents.readUInt32BE(47) << 8) + contents.readUInt32BE(51);
  var section1_length = contents.readUInt32BE(55);
  var section1_title = contents.readUInt8(59);
  var section1_orig_center = contents.readUInt16BE(60);
  var section1_orig_subcenter = contents.readUInt16BE(62);
  var section1_year = contents.readUInt16BE(67);
  var section1_month = contents.readUInt8(69);
  var section1_day = contents.readUInt8(70);
  var section1_hour = contents.readUInt8(71);
  var section1_minute = contents.readUInt8(72);
  var section1_second = contents.readUInt8(73);

  console.log('title: '+title);
  console.log('discipline: '+discipline);
  console.log('edition: '+edition);
  console.log('bufInt: '+bufInt);
  console.log('length: '+section1_length);
  console.log('title: '+section1_title);
  console.log('orig_center: '+section1_orig_center);
  console.log('orig_subcenter: '+section1_orig_subcenter);
  console.log('year: '+section1_year);
  console.log('month: '+section1_month);
  console.log('day: '+section1_day);
  console.log('hour: '+section1_hour);
  console.log('minute: '+section1_minute);
  console.log('second: '+section1_second);

  for (var i = 37; i < 60; i++) {
    console.log(i+': '+contents[i]);
  }

  return 'test';
}

var test = 'test';
<|endoftext|>