<commit_before>/*
 * Copyright  1999-2005 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
/* $Id$ */

 cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/Form.js");
 cocoon.load("fallback://lenya/usecases/usecases-util.js");

/* Helper method to add all request parameters to a usecase */
function passRequestParameters(flowHelper, usecase) {
    var names = cocoon.request.getParameterNames();
    while (names.hasMoreElements()) {
        var name = names.nextElement();
        if (!name.equals("lenya.usecase")
            && !name.equals("lenya.continuation")
            && !name.equals("submit")) {

            var value = flowHelper.getRequest(cocoon).get(name);

            var string = new Packages.java.lang.String();
            if (string.getClass().isInstance(value)) {
                usecase.setParameter(name, value);
            }
            else {
                usecase.setPart(name, value);
            }

        }
    }
}

/*
 * Main function to execute a usecase.
 *
 * Uses request parameter "lenya.usecase" to determine what
 * usecase to execute.
 *
 */
function executeUsecase() {
    var usecaseName = cocoon.request.getParameter("lenya.usecase");
    var view;
    var proxy;
    var menu = "nomenu";

    var usecaseResolver;
    var usecase;
    var sourceUrl;

    if (cocoon.log.isDebugEnabled())
       cocoon.log.debug("usecases.js::executeUsecase() called, parameter lenya.usecase = [" + usecaseName + "]");

    try {

        var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
        var request = flowHelper.getRequest(cocoon);
        sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);

        usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
        usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
        usecase.setSourceURL(sourceUrl);
        usecase.setName(usecaseName);
        view = usecase.getView();
        if (view && view.showMenu()) {
            menu = "menu";
        }

        passRequestParameters(flowHelper, usecase);
        usecase.checkPreconditions();
        usecase.lockInvolvedObjects();
        proxy = new Packages.org.apache.lenya.cms.usecase.UsecaseProxy(usecase);
    }
    finally {
        /* done with usecase component, tell usecaseResolver to release it */
        if (usecaseResolver != null) {
            if (usecase != null) {
                usecaseResolver.release(usecase);
                usecase = undefined;
            }
            cocoon.releaseComponent(usecaseResolver);
        }
    }

    var success = false;
    var targetUrl;
    var form;
    var scriptString;
    var evalFunc;
    var generic;

    /*
     * If the usecase has a view, this means we want to display something
     * to the user before proceeding. This also means the usecase consists
     * several steps; repeated until the user chooses to submit or cancel.
     *
     * If the usecase does not have a view, it is simply executed.
     */

    if (view) {
        var ready = false;
        while (!ready) {

            try {
                var templateUri = view.getTemplateURI();
                if (templateUri) {
                    var viewUri = "view/" + menu + "/" + view.getTemplateURI();
                    if (cocoon.log.isDebugEnabled())
                        cocoon.log.debug("usecases.js::executeUsecase() in usecase " + usecaseName + ", creating view, calling Cocoon with viewUri = [" + viewUri + "]");
                    if (view.getViewType()=="cforms"){
                    /*
                     * var generic - Generic object that can be used in all custom flow code
                     * that is added by usecases. Right now it focus on document opertations
                     *  but the properties should be extended through user/dev feedback.
                     * The intention of this var is to provide a generic global flow object
                     * (like in lots of cocoon examples) that can be accessed through out the different flow stages.
                     * Alternatively one can use the invoking java class by providing bean properties methods
                     * (getter/setter methods) in this class and use them within the extensions (see usecase doc).
                     *
                     * FIXME: Add cforms integration howto to the documentation.
                     */
                      generic={proxy:proxy,uri:null,doc:null,generic:null};
                      var viewDef = "fallback://lenya/"+ view.getCformDefinition();
                      if (cocoon.log.isDebugEnabled())
                       cocoon.log.debug("usecases.js::executeUsecase()::cforms in usecase " + usecaseName + ", preparing formDefinition, calling Cocoon with viewUri = [" + viewDef + "]");

                       // custom flowscript
                       if (view.getCformIntro()!=null){
                          scriptString= view.getCformIntro();
                          evalFunc = new Function (scriptString);
                          evalFunc();
                       }

                       // form definition
                       form = new Form(viewDef);

                       // custom flowscript
                       if (view.getCformDefinitionBody()!=null){
                          scriptString= view.getCformDefinitionBody();
                          evalFunc = new Function ("form","generic",scriptString);
                          evalFunc(form,generic);
                       }

                       // form binding
                       if (view.getCformBinding() != null){
                          var viewBind = "fallback://lenya/"+ view.getCformBinding();
                          if (cocoon.log.isDebugEnabled())
                             cocoon.log.debug("usecases.js::executeUsecase()::cforms in usecase " + usecaseName + ", preparing formDefinition, calling Cocoon with viewUri = [" + viewBind + "]");

                          // form binding
                          form.createBinding(viewBind);

                          // custom flowscript
                          if (view.getCformBindingBody()!=null){
                              scriptString= view.getCformBindingBody();
                              evalFunc = new Function ("form","generic",scriptString);
                              evalFunc(form,generic);
                          }
                       }
                        // form template
                          form.showForm(viewUri, {"usecase" : proxy});
//DEBUG ajax="true"
// print("form.showForm after");
                    }
                    else{
                        cocoon.sendPageAndWait(viewUri, {
                            "usecase" : proxy
                        });
                    }
                }
                else {
                    var viewUri = view.getViewURI();
                    cocoon.sendPage(viewUri);
                    return;
                }
            }
            catch (exception) {
                /* if an exception was thrown by the view, allow the usecase to rollback the transition */
                try {
                    usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
                    usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
                    proxy.setup(usecase);
                    usecase.cancel();
                    throw exception;
                }
                finally {
                    if (usecaseResolver != null) {
                        if (usecase != null) {
                            usecaseResolver.release(usecase);
                            usecase = undefined;
                        }
                        cocoon.releaseComponent(usecaseResolver);
                    }
                }
            }

            if (cocoon.log.isDebugEnabled())
                cocoon.log.debug("usecases.js::executeUsecase() in usecase " + usecaseName + ", after view, now advancing in usecase");

            try {
                usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
                usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
                proxy.setup(usecase);

                passRequestParameters(flowHelper, usecase);
                usecase.advance();
                //HEADSUP: Cform do not allow id="submit" anymore. Use id="ok" for now (till it is settled on cocoon-dev).
                if (cocoon.request.getParameter("submit")||cocoon.request.getParameter("lenya.submit"!="cancel")) {
                    usecase.checkExecutionConditions();
                    if (! usecase.hasErrors()) {
                       if (view.getViewType()=="cforms"){
                       // custom flowscript
                         if (view.getCformOutro()!=null){
                            scriptString= view.getCformOutro();
                            evalFunc = new Function ("form","generic",scriptString);
                            evalFunc(form,generic);
                         }
                       }
                        usecase.execute();
                        if (! usecase.hasErrors()) {
                            usecase.checkPostconditions();
                            if (! usecase.hasErrors()) {
                                ready = true;
                                success = true;
                            }
                        }
                    }
                }
                else if (cocoon.request.getParameter("cancel")) {
                    usecase.cancel();
                    ready = true;
                }
                proxy = new Packages.org.apache.lenya.cms.usecase.UsecaseProxy(usecase);
                targetUrl = usecase.getTargetURL(success);
            }
            catch (exception) {
                /* allow usecase to rollback the transition */
                usecase.cancel();
                throw exception;
            }
            finally {
                if (usecaseResolver != null) {
                    if (usecase != null) {
                        usecaseResolver.release(usecase);
                        usecase = undefined;
                    }
                    cocoon.releaseComponent(usecaseResolver);
                }
            }
        }
    }
    else {
        try {
            usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
            usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
            proxy.setup(usecase);

            usecase.execute();
            if (! usecase.hasErrors()) {
                usecase.checkPostconditions();
                if (! usecase.hasErrors()) {
                    success = true;
                }
            }
            targetUrl = usecase.getTargetURL(success);
        }
        catch (exception) {
            /* allow usecase to rollback the transition */
            usecase.cancel();
            throw exception;
        }
        finally {
            usecaseResolver.release(usecase);
            usecase = undefined;
            cocoon.releaseComponent(usecaseResolver);
        }
    }

    var url = request.getContextPath() + targetUrl;

    if (cocoon.log.isDebugEnabled())
       cocoon.log.debug("usecases.js::executeUsecase() in usecase " + usecaseName + ", completed, redirecting to url = [" + url + "]");

    cocoon.redirectTo(url);

}
<commit_msg>Fixed typo in usecase.js. Spotted by Felix. Thx Felix.<commit_after>/*
 * Copyright  1999-2005 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
/* $Id$ */

 cocoon.load("resource://org/apache/cocoon/forms/flow/javascript/Form.js");
 cocoon.load("fallback://lenya/usecases/usecases-util.js");

/* Helper method to add all request parameters to a usecase */
function passRequestParameters(flowHelper, usecase) {
    var names = cocoon.request.getParameterNames();
    while (names.hasMoreElements()) {
        var name = names.nextElement();
        if (!name.equals("lenya.usecase")
            && !name.equals("lenya.continuation")
            && !name.equals("submit")) {

            var value = flowHelper.getRequest(cocoon).get(name);

            var string = new Packages.java.lang.String();
            if (string.getClass().isInstance(value)) {
                usecase.setParameter(name, value);
            }
            else {
                usecase.setPart(name, value);
            }

        }
    }
}

/*
 * Main function to execute a usecase.
 *
 * Uses request parameter "lenya.usecase" to determine what
 * usecase to execute.
 *
 */
function executeUsecase() {
    var usecaseName = cocoon.request.getParameter("lenya.usecase");
    var view;
    var proxy;
    var menu = "nomenu";

    var usecaseResolver;
    var usecase;
    var sourceUrl;

    if (cocoon.log.isDebugEnabled())
       cocoon.log.debug("usecases.js::executeUsecase() called, parameter lenya.usecase = [" + usecaseName + "]");

    try {

        var flowHelper = cocoon.getComponent("org.apache.lenya.cms.cocoon.flow.FlowHelper");
        var request = flowHelper.getRequest(cocoon);
        sourceUrl = Packages.org.apache.lenya.util.ServletHelper.getWebappURI(request);

        usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
        usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
        usecase.setSourceURL(sourceUrl);
        usecase.setName(usecaseName);
        view = usecase.getView();
        if (view && view.showMenu()) {
            menu = "menu";
        }

        passRequestParameters(flowHelper, usecase);
        usecase.checkPreconditions();
        usecase.lockInvolvedObjects();
        proxy = new Packages.org.apache.lenya.cms.usecase.UsecaseProxy(usecase);
    }
    finally {
        /* done with usecase component, tell usecaseResolver to release it */
        if (usecaseResolver != null) {
            if (usecase != null) {
                usecaseResolver.release(usecase);
                usecase = undefined;
            }
            cocoon.releaseComponent(usecaseResolver);
        }
    }

    var success = false;
    var targetUrl;
    var form;
    var scriptString;
    var evalFunc;
    var generic;

    /*
     * If the usecase has a view, this means we want to display something
     * to the user before proceeding. This also means the usecase consists
     * several steps; repeated until the user chooses to submit or cancel.
     *
     * If the usecase does not have a view, it is simply executed.
     */

    if (view) {
        var ready = false;
        while (!ready) {

            try {
                var templateUri = view.getTemplateURI();
                if (templateUri) {
                    var viewUri = "view/" + menu + "/" + view.getTemplateURI();
                    if (cocoon.log.isDebugEnabled())
                        cocoon.log.debug("usecases.js::executeUsecase() in usecase " + usecaseName + ", creating view, calling Cocoon with viewUri = [" + viewUri + "]");
                    if (view.getViewType()=="cforms"){
                    /*
                     * var generic - Generic object that can be used in all custom flow code
                     * that is added by usecases. Right now it focus on document opertations
                     *  but the properties should be extended through user/dev feedback.
                     * The intention of this var is to provide a generic global flow object
                     * (like in lots of cocoon examples) that can be accessed through out the different flow stages.
                     * Alternatively one can use the invoking java class by providing bean properties methods
                     * (getter/setter methods) in this class and use them within the extensions (see usecase doc).
                     *
                     * FIXME: Add cforms integration howto to the documentation.
                     */
                      generic={proxy:proxy,uri:null,doc:null,generic:null};
                      var viewDef = "fallback://lenya/"+ view.getCformDefinition();
                      if (cocoon.log.isDebugEnabled())
                       cocoon.log.debug("usecases.js::executeUsecase()::cforms in usecase " + usecaseName + ", preparing formDefinition, calling Cocoon with viewUri = [" + viewDef + "]");

                       // custom flowscript
                       if (view.getCformIntro()!=null){
                          scriptString= view.getCformIntro();
                          evalFunc = new Function (scriptString);
                          evalFunc();
                       }

                       // form definition
                       form = new Form(viewDef);

                       // custom flowscript
                       if (view.getCformDefinitionBody()!=null){
                          scriptString= view.getCformDefinitionBody();
                          evalFunc = new Function ("form","generic",scriptString);
                          evalFunc(form,generic);
                       }

                       // form binding
                       if (view.getCformBinding() != null){
                          var viewBind = "fallback://lenya/"+ view.getCformBinding();
                          if (cocoon.log.isDebugEnabled())
                             cocoon.log.debug("usecases.js::executeUsecase()::cforms in usecase " + usecaseName + ", preparing formDefinition, calling Cocoon with viewUri = [" + viewBind + "]");

                          // form binding
                          form.createBinding(viewBind);

                          // custom flowscript
                          if (view.getCformBindingBody()!=null){
                              scriptString= view.getCformBindingBody();
                              evalFunc = new Function ("form","generic",scriptString);
                              evalFunc(form,generic);
                          }
                       }
                        // form template
                          form.showForm(viewUri, {"usecase" : proxy});
//DEBUG ajax="true"
// print("form.showForm after");
                    }
                    else{
                        cocoon.sendPageAndWait(viewUri, {
                            "usecase" : proxy
                        });
                    }
                }
                else {
                    var viewUri = view.getViewURI();
                    cocoon.sendPage(viewUri);
                    return;
                }
            }
            catch (exception) {
                /* if an exception was thrown by the view, allow the usecase to rollback the transition */
                try {
                    usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
                    usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
                    proxy.setup(usecase);
                    usecase.cancel();
                    throw exception;
                }
                finally {
                    if (usecaseResolver != null) {
                        if (usecase != null) {
                            usecaseResolver.release(usecase);
                            usecase = undefined;
                        }
                        cocoon.releaseComponent(usecaseResolver);
                    }
                }
            }

            if (cocoon.log.isDebugEnabled())
                cocoon.log.debug("usecases.js::executeUsecase() in usecase " + usecaseName + ", after view, now advancing in usecase");

            try {
                usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
                usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
                proxy.setup(usecase);

                passRequestParameters(flowHelper, usecase);
                usecase.advance();
                //HEADSUP: Cform do not allow id="submit" anymore. Use id="ok" for now (till it is settled on cocoon-dev).
                if (cocoon.request.getParameter("submit")||cocoon.request.getParameter("lenya.submit")!="cancel") {
                    usecase.checkExecutionConditions();
                    if (! usecase.hasErrors()) {
                       if (view.getViewType()=="cforms"){
                       // custom flowscript
                         if (view.getCformOutro()!=null){
                            scriptString= view.getCformOutro();
                            evalFunc = new Function ("form","generic",scriptString);
                            evalFunc(form,generic);
                         }
                       }
                        usecase.execute();
                        if (! usecase.hasErrors()) {
                            usecase.checkPostconditions();
                            if (! usecase.hasErrors()) {
                                ready = true;
                                success = true;
                            }
                        }
                    }
                }
                else if (cocoon.request.getParameter("cancel")) {
                    usecase.cancel();
                    ready = true;
                }
                proxy = new Packages.org.apache.lenya.cms.usecase.UsecaseProxy(usecase);
                targetUrl = usecase.getTargetURL(success);
            }
            catch (exception) {
                /* allow usecase to rollback the transition */
                usecase.cancel();
                throw exception;
            }
            finally {
                if (usecaseResolver != null) {
                    if (usecase != null) {
                        usecaseResolver.release(usecase);
                        usecase = undefined;
                    }
                    cocoon.releaseComponent(usecaseResolver);
                }
            }
        }
    }
    else {
        try {
            usecaseResolver = cocoon.getComponent("org.apache.lenya.cms.usecase.UsecaseResolver");
            usecase = usecaseResolver.resolve(sourceUrl, usecaseName);
            proxy.setup(usecase);

            usecase.execute();
            if (! usecase.hasErrors()) {
                usecase.checkPostconditions();
                if (! usecase.hasErrors()) {
                    success = true;
                }
            }
            targetUrl = usecase.getTargetURL(success);
        }
        catch (exception) {
            /* allow usecase to rollback the transition */
            usecase.cancel();
            throw exception;
        }
        finally {
            usecaseResolver.release(usecase);
            usecase = undefined;
            cocoon.releaseComponent(usecaseResolver);
        }
    }

    var url = request.getContextPath() + targetUrl;

    if (cocoon.log.isDebugEnabled())
       cocoon.log.debug("usecases.js::executeUsecase() in usecase " + usecaseName + ", completed, redirecting to url = [" + url + "]");

    cocoon.redirectTo(url);

}
<|endoftext|>


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

import React, { useCallback } from 'react';
import PropTypes from 'prop-types';

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

import Row from './Row';
import EditableCell from './EditableCell';
import Cell from './Cell';
import CheckableCell from './CheckableCell';
import { NewExpiryDateInput } from '../NewExpiryDateInput';

import {
  CheckedComponent,
  UncheckedComponent,
  DisabledCheckedComponent,
  DisabledUncheckedComponent,
} from '../icons';

/**
 * Wrapper component for a mSupply DataTable page row.
 * Wraps Row component with a contained RenderCells, which
 * will generate the appropriate cell for a given columnKey.
 * Doesn't need to be used, but is a convenience component.
 *
 * @param {Object} rowData  Data object for a row i.e. ItemBatch object
 * @param {Object} rowState State object for a row, see: Row.js
 * @param {Object} style    Style object the be passed to inner Row
 * @param {String} rowKey   Unique key for a row
 * @param {Array} columns   Array of column objects, see: columns.js
 * @param {Bool} isFinalised    Boolean indicating if the DataTable page is finalised.
 * @param {Func} focusCellAction    Action for focusing a cell.
 * @param {Func} focusNextAction    Action for focusing the next cell.
 * @param {Func} dispatch   Dispatch function for containing reducer.
 * @param {Func} getAction  Function to return an action for a cell
 *                          (colKey, propName) => actionObject
 */
const DataTableRow = React.memo(
  ({
    rowData,
    rowState,
    rowKey,
    style,
    columns,
    isFinalised,
    dispatch,
    focusCellAction,
    focusNextAction,
    getAction,
    onPress,
  }) => {
    // Key of the current column focused.
    const focusedColumnKey = rowState && rowState.focusedColumn;

    // Callback for rendering a row of cells. A cell is
    const renderCells = useCallback(() => {
      const {
        cellText,
        cellContainer,
        touchableCellContainer,
        editableCellText,
        editableCellTextView,
        editableCellUnfocused,
      } = newDataTableStyles;

      // Map each column to an appropriate cell for a given row.
      return columns.map(({ key: columnKey, type, width, alignText }, index) => {
        const isLastCell = index === columns.length - 1;
        const isDisabled = isFinalised || (rowState && rowState.isDisabled);
        const isFocused = focusedColumnKey === columnKey;
        const cellAlignment = alignText || 'left';

        switch (type) {
          case 'editable':
            return (
              <EditableCell
                key={columnKey}
                value={rowData[columnKey]}
                rowKey={rowKey}
                columnKey={columnKey}
                editAction={getAction(columnKey)}
                isFocused={isFocused}
                isDisabled={isDisabled}
                focusAction={focusCellAction}
                focusNextAction={focusNextAction}
                dispatch={dispatch}
                width={width}
                viewStyle={cellContainer[cellAlignment]}
                textViewStyle={editableCellTextView}
                isLastCell={isLastCell}
                keyboardType="numeric"
                textInputStyle={cellText[cellAlignment]}
                textStyle={editableCellUnfocused[cellAlignment]}
                cellTextStyle={editableCellText}
              />
            );

          case 'checkable':
            return (
              <CheckableCell
                key={columnKey}
                rowKey={rowKey}
                columnKey={columnKey}
                isChecked={rowState && rowState.isSelected}
                isDisabled={isDisabled}
                CheckedComponent={CheckedComponent}
                UncheckedComponent={UncheckedComponent}
                DisabledCheckedComponent={DisabledCheckedComponent}
                DisabledUncheckedComponent={DisabledUncheckedComponent}
                onCheckAction={getAction(columnKey, 'onCheckAction')}
                onUncheckAction={getAction(columnKey, 'onUncheckAction')}
                dispatch={dispatch}
                containerStyle={touchableCellContainer}
                width={width}
                isLastCell={isLastCell}
              />
            );

          case 'date':
            return (
              <NewExpiryDateInput
                key={columnKey}
                value={rowData[columnKey]}
                rowKey={rowKey}
                columnKey={columnKey}
                editAction={getAction(columnKey)}
                isFocused={isFocused}
                isDisabled={isDisabled}
                focusAction={focusCellAction}
                focusNextAction={focusNextAction}
                dispatch={dispatch}
                width={width}
                isLastCell={isLastCell}
              />
            );

          default:
            return (
              <Cell
                key={columnKey}
                value={rowData[columnKey]}
                width={width}
                viewStyle={cellContainer[cellAlignment]}
                textStyle={cellText[cellAlignment]}
                isLastCell={isLastCell}
              />
            );
        }
      });
    }, [isFinalised, focusedColumnKey, rowState]);

    return (
      <Row
        onPress={onPress}
        style={style}
        renderCells={renderCells}
        debug
        rowKey={rowKey}
        rowData={rowData}
        rowState={rowState}
      />
    );
  }
);

DataTableRow.defaultProps = {
  isFinalised: false,
  focusCellAction: null,
  focusNextAction: null,
  getAction: null,
  onPress: null,
};
DataTableRow.propTypes = {
  onPress: PropTypes.func,
  rowData: PropTypes.object.isRequired,
  rowState: PropTypes.object.isRequired,
  rowKey: PropTypes.string.isRequired,
  style: PropTypes.object.isRequired,
  columns: PropTypes.array.isRequired,
  dispatch: PropTypes.func.isRequired,
  isFinalised: PropTypes.bool,
  focusCellAction: PropTypes.func,
  focusNextAction: PropTypes.func,
  getAction: PropTypes.func,
};

export default DataTableRow;
<commit_msg>Add corrected prop type in DataTableRow<commit_after>/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback } from 'react';
import PropTypes from 'prop-types';

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

import Row from './Row';
import EditableCell from './EditableCell';
import Cell from './Cell';
import CheckableCell from './CheckableCell';
import { NewExpiryDateInput } from '../NewExpiryDateInput';

import {
  CheckedComponent,
  UncheckedComponent,
  DisabledCheckedComponent,
  DisabledUncheckedComponent,
} from '../icons';

/**
 * Wrapper component for a mSupply DataTable page row.
 * Wraps Row component with a contained RenderCells, which
 * will generate the appropriate cell for a given columnKey.
 * Doesn't need to be used, but is a convenience component.
 *
 * @param {Object} rowData  Data object for a row i.e. ItemBatch object
 * @param {Object} rowState State object for a row, see: Row.js
 * @param {Object} style    Style object the be passed to inner Row
 * @param {String} rowKey   Unique key for a row
 * @param {Array} columns   Array of column objects, see: columns.js
 * @param {Bool} isFinalised    Boolean indicating if the DataTable page is finalised.
 * @param {Func} focusCellAction    Action for focusing a cell.
 * @param {Func} focusNextAction    Action for focusing the next cell.
 * @param {Func} dispatch   Dispatch function for containing reducer.
 * @param {Func} getAction  Function to return an action for a cell
 *                          (colKey, propName) => actionObject
 */
const DataTableRow = React.memo(
  ({
    rowData,
    rowState,
    rowKey,
    style,
    columns,
    isFinalised,
    dispatch,
    focusCellAction,
    focusNextAction,
    getAction,
    onPress,
  }) => {
    // Key of the current column focused.
    const focusedColumnKey = rowState && rowState.focusedColumn;

    // Callback for rendering a row of cells. A cell is
    const renderCells = useCallback(() => {
      const {
        cellText,
        cellContainer,
        touchableCellContainer,
        editableCellText,
        editableCellTextView,
        editableCellUnfocused,
      } = newDataTableStyles;

      // Map each column to an appropriate cell for a given row.
      return columns.map(({ key: columnKey, type, width, alignText }, index) => {
        const isLastCell = index === columns.length - 1;
        const isDisabled = isFinalised || (rowState && rowState.isDisabled);
        const isFocused = focusedColumnKey === columnKey;
        const cellAlignment = alignText || 'left';

        switch (type) {
          case 'editable':
            return (
              <EditableCell
                key={columnKey}
                value={rowData[columnKey]}
                rowKey={rowKey}
                columnKey={columnKey}
                editAction={getAction(columnKey)}
                isFocused={isFocused}
                isDisabled={isDisabled}
                focusAction={focusCellAction}
                focusNextAction={focusNextAction}
                dispatch={dispatch}
                width={width}
                viewStyle={cellContainer[cellAlignment]}
                textViewStyle={editableCellTextView}
                isLastCell={isLastCell}
                keyboardType="numeric"
                textInputStyle={cellText[cellAlignment]}
                textStyle={editableCellUnfocused[cellAlignment]}
                cellTextStyle={editableCellText}
              />
            );

          case 'checkable':
            return (
              <CheckableCell
                key={columnKey}
                rowKey={rowKey}
                columnKey={columnKey}
                isChecked={rowState && rowState.isSelected}
                isDisabled={isDisabled}
                CheckedComponent={CheckedComponent}
                UncheckedComponent={UncheckedComponent}
                DisabledCheckedComponent={DisabledCheckedComponent}
                DisabledUncheckedComponent={DisabledUncheckedComponent}
                onCheckAction={getAction(columnKey, 'onCheckAction')}
                onUncheckAction={getAction(columnKey, 'onUncheckAction')}
                dispatch={dispatch}
                containerStyle={touchableCellContainer}
                width={width}
                isLastCell={isLastCell}
              />
            );

          case 'date':
            return (
              <NewExpiryDateInput
                key={columnKey}
                value={rowData[columnKey]}
                rowKey={rowKey}
                columnKey={columnKey}
                editAction={getAction(columnKey)}
                isFocused={isFocused}
                isDisabled={isDisabled}
                focusAction={focusCellAction}
                focusNextAction={focusNextAction}
                dispatch={dispatch}
                width={width}
                isLastCell={isLastCell}
              />
            );

          default:
            return (
              <Cell
                key={columnKey}
                value={rowData[columnKey]}
                width={width}
                viewStyle={cellContainer[cellAlignment]}
                textStyle={cellText[cellAlignment]}
                isLastCell={isLastCell}
              />
            );
        }
      });
    }, [isFinalised, focusedColumnKey, rowState]);

    return (
      <Row
        onPress={onPress}
        style={style}
        renderCells={renderCells}
        debug
        rowKey={rowKey}
        rowData={rowData}
        rowState={rowState}
      />
    );
  }
);

DataTableRow.defaultProps = {
  isFinalised: false,
  focusCellAction: null,
  focusNextAction: null,
  getAction: null,
  onPress: null,
  rowState: null,
};
DataTableRow.propTypes = {
  onPress: PropTypes.func,
  rowData: PropTypes.object.isRequired,
  rowState: PropTypes.object,
  rowKey: PropTypes.string.isRequired,
  style: PropTypes.object.isRequired,
  columns: PropTypes.array.isRequired,
  dispatch: PropTypes.func.isRequired,
  isFinalised: PropTypes.bool,
  focusCellAction: PropTypes.func,
  focusNextAction: PropTypes.func,
  getAction: PropTypes.func,
};

export default DataTableRow;
<|endoftext|>


<commit_before>/**
* Point by Grant Skinner. Dec 5, 2010
* Visit http://easeljs.com/ for documentation, updates and examples.
*
*
* Copyright (c) 2010 Grant Skinner
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
**/

(function(window) {

/**
* Constructs a new Point instance.
* @param x X position. Default is 0.
* @param y Y position. Default is 0.
* @class Represents a point with x / y coordinates.
**/
Point = function(x, y) {
  this.initialize(x, y);
}
var p = Point.prototype;

// public properties:
	/** X position. */
	p.x = 0;
	/** Y position. */
	p.y = 0;

// constructor:
	/** @ignore */
	p.initialize = function(x, y) {
		this.x = (x == null ? 0 : x);
		this.y = (y == null ? 0 : y);
	}

// public methods:
	/**
	* Returns a clone of this Point.
	**/
	p.clone = function() {
		return new Point(this.x, this.y);
	}

	/**
	* Returns a string representation of this object.
	**/
	p.toString = function() {
		return "[Point (x="+this.x+" y="+this.y+")]";
	}

window.Point = Point;
}(window));<commit_msg>Tweaking / cleaning up docs. Converting to Yui Doc format.<commit_after>/**
* Point by Grant Skinner. Dec 5, 2010
* Visit http://easeljs.com/ for documentation, updates and examples.
*
*
* Copyright (c) 2010 Grant Skinner
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
**/

/**
 * The Easel Javascript library provides a retained graphics mode for canvas
 * including a full, hierarchical display list, a core interaction model, and
 * helper classes to make working with Canvas much easier.
 * @module EaselJS
 */

(function(window) {

/**
 * Represents a point on a 2 dimensional x / y coordinate system.
 * @class Point
 **/

/**
* Constructs a new Point instance.
* @constructor
* @param {Number} x X position. Default is 0.
* @param {Number} y Y position. Default is 0.
**/
Point = function(x, y) {
  this.initialize(x, y);
}
var p = Point.prototype;

// public properties:

	/**
	* X position.
	* @property x
	* @type {Number}
	**/
	p.x = 0;

	/**
	* Y position.
	* @property y
	* @type {Number}
	**/
	p.y = 0;

// constructor:
	/** @private */
	p.initialize = function(x, y) {
		this.x = (x == null ? 0 : x);
		this.y = (y == null ? 0 : y);
	}

// public methods:
	/**
	* Returns a clone of the Point instance.
	* @method clone
	* @return {Point} a clone of the Point instance.
	**/
	p.clone = function() {
		return new Point(this.x, this.y);
	}

	/**
	* Returns a string representation of this object.
	* @method toString
	* @return {String} a string representation of the instance.
	**/
	p.toString = function() {
		return "[Point (x="+this.x+" y="+this.y+")]";
	}

window.Point = Point;
}(window));<|endoftext|>


<commit_before>/* gameBuilder_dictSearch.js */
'use strict'

var wordScramble = wordScramble || {}
wordScramble.WordFinder = function (wordList) {
  function makeStatusUpdate (statusMessage) {
    console.log(statusMessage)
    send({
      update: 1,
      status: statusMessage
    })
  }

  function mapWordsToWordObjects (words) {
    var wordObjects = words.map(function (w) {
      return { 'word': w.word, 'solved': false, 'chars': w.length }
    })
    return wordObjects
  }

  function sortWordObjectsByLength (wordObjects) {
    return wordObjects.sort(function (a, b) {
      return a.chars - b.chars
    })
  }

  function removeShortAndLongWords (words, minLength, maxLength) {
    return words.filter(function (word) {
      return word.length <= maxLength && word.length >= minLength
    })
  }

  function removeWordsWithOtherLetters (words, letterSet) {
    var allLetters = 'abcdefghijklmnopqrstuvwxyz'
    var otherLetters = allLetters.split('').filter(function (allLettersElement) {
      return !letterSet.letterList.includes(allLettersElement)
    })
    for (var i = 0, len = otherLetters.length; i < len; i++) {
      words = words.filter(function (word) {
        return word.word.indexOf(otherLetters[i]) === -1
      })
    }
    return words
  }

  function removeWordsWithExtraLetters (words, letterSet) {
    words = words.filter(function (wordObject) {
      return wordObject.word.split('').every(function (letterInWord) {
        var letterObject = letterSet.letterCounts.find(function (letterObject) {
          return letterObject.letter === letterInWord
        })
        var countOfLetterInWordCandidate = wordObject.word.split(letterObject.letter).length - 1
        if (countOfLetterInWordCandidate <= letterObject.count) {
          return true
        }
      })
    })
    return words
  }

  function hasAcceptableWordCount (words, wordSetConfiguration) {
    if (words.length > wordSetConfiguration.maximumWords) {
      makeStatusUpdate('Too many words found (' + words.length + ')')
      return false
    }
    if (words.length < wordSetConfiguration.minimumWords) {
      makeStatusUpdate('Not enough words found (' + words.length + ')')
      return false
    }
    return true
  }

  function hasCorrectLetterCounts (words, letterSet) {
    return letterSet.letterCounts.every(function (letterObject) {
      var countOfLetterInList = letterObject.count
      return words.some(function (wordObject) {
        if (wordObject.word.split(letterObject.letter).length === countOfLetterInList) {
          return true
        }
      })
    })
  }

  function getUniqueLetters (letterList) {
    return letterList.filter(function (v, i) {
      return letterList.indexOf(v) === i
    })
  }

  this.makeLetterSet = function (letterList) {
    var letterCounts = []
    var letterString = letterList.join('')
    var uniqueLetters = getUniqueLetters(letterList)
    uniqueLetters.forEach(function (letter) {
      var letterObject = {
        'letter': letter,
        'count': 1
      }
      letterCounts.push(letterObject)
    })
    letterCounts.forEach(function (letterObject) {
      letterObject.count = letterString.split(letterObject.letter).length - 1
    })

    return {
      'letterList': letterList,
      'letterString': letterString,
      'letterCounts': letterCounts,
      'uniqueLetters': uniqueLetters
    }
  }

  this.findWords = function (wordSetConfiguration, letterSet) {
    var words = wordList

    makeStatusUpdate('Filtering ' + words.length + ' words (1 of 3)')
    words = removeShortAndLongWords(words, wordSetConfiguration.minimumWordLength, letterSet.letterList.length)

    makeStatusUpdate('Filtering ' + words.length + ' words (2 of 3)')
    words = removeWordsWithOtherLetters(words, letterSet)

    makeStatusUpdate('Filtering ' + words.length + ' words (3 of 3)')
    words = removeWordsWithExtraLetters(words, letterSet)

    if (!hasAcceptableWordCount(words, wordSetConfiguration)) return []

    makeStatusUpdate('Ensuring all letters are used')
    if (!hasCorrectLetterCounts(words, letterSet)) {
      makeStatusUpdate('Not all letters are used')
      return []
    }

    var wordObjects = mapWordsToWordObjects(words)
    wordObjects = sortWordObjectsByLength(wordObjects)

    return wordObjects
  }
}

var onmessage = function (evt) {
  if (evt == null) return
  var data = JSON.parse(evt.data)

  var configuration = data.configuration
  var wordList = data.dictionary
  var letterList = data.letters

  if (!wordList || wordList.length === 0) {
    throw new Error('No words provided to wordFinder')
  }

  var wordFinder = new wordScramble.WordFinder(wordList)
  var letterSet = wordFinder.makeLetterSet(letterList)
  var wordSetConfiguration = {
    'minimumWordLength': configuration.minWordLength,
    'maximumWords': configuration.maxWords,
    'minimumWords': configuration.minWords
  }
  var wordObjects = wordFinder.findWords(wordSetConfiguration, letterSet)

  send({
    complete: 1,
    words: wordObjects,
    letters: letterList
  })
}

var send = function (message) {
  postMessage(JSON.stringify(message))
}
onmessage(null)
<commit_msg>Fix bug where hasCorrectLetterCounts was off by one when counting occurrence of letter in word<commit_after>/* gameBuilder_dictSearch.js */
'use strict'

var wordScramble = wordScramble || {}
wordScramble.WordFinder = function (wordList) {
  function makeStatusUpdate (statusMessage) {
    console.log(statusMessage)
    send({
      update: 1,
      status: statusMessage
    })
  }

  function mapWordsToWordObjects (words) {
    var wordObjects = words.map(function (w) {
      return { 'word': w.word, 'solved': false, 'chars': w.length }
    })
    return wordObjects
  }

  function sortWordObjectsByLength (wordObjects) {
    return wordObjects.sort(function (a, b) {
      return a.chars - b.chars
    })
  }

  function removeShortAndLongWords (words, minLength, maxLength) {
    return words.filter(function (word) {
      return word.length <= maxLength && word.length >= minLength
    })
  }

  function removeWordsWithOtherLetters (words, letterSet) {
    var allLetters = 'abcdefghijklmnopqrstuvwxyz'
    var otherLetters = allLetters.split('').filter(function (allLettersElement) {
      return !letterSet.letterList.includes(allLettersElement)
    })
    for (var i = 0, len = otherLetters.length; i < len; i++) {
      words = words.filter(function (word) {
        return word.word.indexOf(otherLetters[i]) === -1
      })
    }
    return words
  }

  function removeWordsWithExtraLetters (words, letterSet) {
    words = words.filter(function (wordObject) {
      return wordObject.word.split('').every(function (letterInWord) {
        var letterObject = letterSet.letterCounts.find(function (letterObject) {
          return letterObject.letter === letterInWord
        })
        var countOfLetterInWordCandidate = wordObject.word.split(letterObject.letter).length - 1
        if (countOfLetterInWordCandidate <= letterObject.count) {
          return true
        }
      })
    })
    return words
  }

  function hasAcceptableWordCount (words, wordSetConfiguration) {
    if (words.length > wordSetConfiguration.maximumWords) {
      makeStatusUpdate('Too many words found (' + words.length + ')')
      return false
    }
    if (words.length < wordSetConfiguration.minimumWords) {
      makeStatusUpdate('Not enough words found (' + words.length + ')')
      return false
    }
    return true
  }

  function hasCorrectLetterCounts (words, letterSet) {
    return letterSet.letterCounts.every(function (letterObject) {
      return words.some(function (wordObject) {
        var countOfLetterInWord = wordObject.word.split(letterObject.letter).length - 1
        if (countOfLetterInWord === letterObject.count) {
          return true
        }
      })
    })
  }

  function getUniqueLetters (letterList) {
    return letterList.filter(function (v, i) {
      return letterList.indexOf(v) === i
    })
  }

  this.makeLetterSet = function (letterList) {
    var letterCounts = []
    var letterString = letterList.join('')
    var uniqueLetters = getUniqueLetters(letterList)
    uniqueLetters.forEach(function (letter) {
      var letterObject = {
        'letter': letter,
        'count': 1
      }
      letterCounts.push(letterObject)
    })
    letterCounts.forEach(function (letterObject) {
      letterObject.count = letterString.split(letterObject.letter).length - 1
    })

    return {
      'letterList': letterList,
      'letterString': letterString,
      'letterCounts': letterCounts,
      'uniqueLetters': uniqueLetters
    }
  }

  this.findWords = function (wordSetConfiguration, letterSet) {
    var words = wordList

    makeStatusUpdate('Filtering ' + words.length + ' words (1 of 3)')
    words = removeShortAndLongWords(words, wordSetConfiguration.minimumWordLength, letterSet.letterList.length)

    makeStatusUpdate('Filtering ' + words.length + ' words (2 of 3)')
    words = removeWordsWithOtherLetters(words, letterSet)

    makeStatusUpdate('Filtering ' + words.length + ' words (3 of 3)')
    words = removeWordsWithExtraLetters(words, letterSet)

    if (!hasAcceptableWordCount(words, wordSetConfiguration)) return []

    makeStatusUpdate('Ensuring all letters are used')
    if (!hasCorrectLetterCounts(words, letterSet)) {
      makeStatusUpdate('Not all letters are used')
      return []
    }

    var wordObjects = mapWordsToWordObjects(words)
    wordObjects = sortWordObjectsByLength(wordObjects)

    return wordObjects
  }
}

var onmessage = function (evt) {
  if (evt == null) return
  var data = JSON.parse(evt.data)

  var configuration = data.configuration
  var wordList = data.dictionary
  var letterList = data.letters

  if (!wordList || wordList.length === 0) {
    throw new Error('No words provided to wordFinder')
  }

  var wordFinder = new wordScramble.WordFinder(wordList)
  var letterSet = wordFinder.makeLetterSet(letterList)
  var wordSetConfiguration = {
    'minimumWordLength': configuration.minWordLength,
    'maximumWords': configuration.maxWords,
    'minimumWords': configuration.minWords
  }
  var wordObjects = wordFinder.findWords(wordSetConfiguration, letterSet)

  send({
    complete: 1,
    words: wordObjects,
    letters: letterList
  })
}

var send = function (message) {
  postMessage(JSON.stringify(message))
}
onmessage(null)
<|endoftext|>


<commit_before>export const insideHorizontal = (wrapper, card, marginLeft, offset) => {
  const isInsideLeft = insideLeft(wrapper, card, marginLeft, offset);
  const isInsideRight = insideRight(wrapper, card, marginLeft, offset);

  return isInsideLeft && isInsideRight;
};

export const insideRight = (wrapper, card, marginLeft, offset) =>
  card.right < wrapper.width - marginLeft;

export const insideLeft = (wrapper, card, marginLeft, offset) => {
  return card.left > -marginLeft;
};

export const isHorizontalVisible = (
  wrapper,
  card,
  marginLeft = 0,
  offset = 0
) => insideHorizontal(wrapper, card, marginLeft, offset);

export const insideVertical = (wrapper, card, marginTop, offset) =>
  wrapper.top - offset - marginTop <= card.top &&
  wrapper.down + offset - marginTop >= card.down;

export const insideTop = (wrapper, card, marginTop, offset) => {
  const elemPos = card.top + marginTop;
  return elemPos <= wrapper.height && elemPos > 0;
};

export const insideDown = (wrapper, card, marginTop, offset) => {
  return wrapper.height >= card.top + card.height + marginTop;
};

export const isVerticalVisible = (wrapper, card, marginTop = 0, offset = 0) => {
  return (
    insideVertical(wrapper, card, marginTop, offset) ||
    insideTop(wrapper, card, marginTop, offset) ||
    insideDown(wrapper, card, marginTop, offset)
  );
};

export const isVisible = (wrapper, card, marginLeft, marginTop, offset) => {
  return (
    isHorizontalVisible(wrapper, card, marginLeft, offset) &&
    isVerticalVisible(wrapper, card, marginTop, offset)
  );
};
<commit_msg>Binder offset #3<commit_after>export const insideHorizontal = (wrapper, card, marginLeft, offset) =>
  insideLeft(wrapper, card, marginLeft, offset) &&
  insideRight(wrapper, card, marginLeft, offset);

export const insideRight = (wrapper, card, marginLeft, offset) =>
  card.right + marginLeft < wrapper.width + offset;

export const insideLeft = (wrapper, card, marginLeft, offset) =>
  card.left + marginLeft > 0 - offset;

export const isHorizontalVisible = (
  wrapper,
  card,
  marginLeft = 0,
  offset = 0
) => insideHorizontal(wrapper, card, marginLeft, offset);

export const insideVertical = (wrapper, card, marginTop, offset) =>
  wrapper.top - offset - marginTop <= card.top &&
  wrapper.down + offset - marginTop >= card.down;

export const insideTop = (wrapper, card, marginTop, offset) => {
  const elemPos = card.top + marginTop;
  return elemPos <= wrapper.height && elemPos > 0;
};

export const insideDown = (wrapper, card, marginTop, offset) => {
  return wrapper.height >= card.top + card.height + marginTop;
};

export const isVerticalVisible = (wrapper, card, marginTop = 0, offset = 0) => {
  return (
    insideVertical(wrapper, card, marginTop, offset) ||
    insideTop(wrapper, card, marginTop, offset) ||
    insideDown(wrapper, card, marginTop, offset)
  );
};

export const isVisible = (wrapper, card, marginLeft, marginTop, offset) => {
  return (
    isHorizontalVisible(wrapper, card, marginLeft, offset) &&
    isVerticalVisible(wrapper, card, marginTop, offset)
  );
};
<|endoftext|>


<commit_before>import {d3, uuid, Baobab, WebFont} from "nbpresent-deps";
import _ from "underscore";

import Jupyter from "base/js/namespace";

import {FONTS, SYMB, PANGRAMS} from "./fonts";

import {BackgroundPicker} from "./background";
import {PaletteBuilder} from "./palette";

import {loadFonts} from "./fonts";

// pick a random pangram for this session. gotta catch'm all.
const pangram = d3.shuffle(PANGRAMS)[0];

export class ThemeOverlay{
  constructor(tree){
    this.tree = tree;

    this.theme = tree.select(["theme"]);
    this.rules = this.theme.select(["rules"]);
    this.textBase = this.theme.select(["text-base"]);
    this.palette = this.theme.select(["palette"]);

    this.themer = tree.select(".").root().select(["themer"]);
    this.focusContent = this.themer.select(["focuseContent"]);
    this.exampleText = this.themer.select("exampleText");

    this.backgroundUI = new BackgroundPicker(tree);
    this.paletteUI = new PaletteBuilder(tree);

    [
      this.rules,
      this.textBase,
      this.focusContent,
      this.exampleText,
      this.palette
    ]
      .map(({on})=> on("update", (e)=> this.update(e)));

    this.$body = d3.select("body");

    loadFonts(FONTS);

    this.textZoom = d3.behavior.zoom()
      .on("zoom", (d, i) => this.textZoomed(d, i));

    this.init();
  }

  selectorUsed(selector){
    return d3.select(`#notebook .cell ${selector}`)[0][0];
  }

  init(){
    let text = this.exampleText.get();

    this.$body.classed({"nbp-theming": 1});

    this.$ui = this.$body
      .append("div")
      .classed({
        "nbp-theme-overlay": 1,
        "container-fluid": 1
      })
      .each(function(){ Jupyter.keyboard_manager.register_events(this); });

    let row = this.$ui.append("div")
      .classed({row: 1});

    this.$background = row.append("div")
      .classed({
        "nbp-theme-overlay-backgrounds col-md-2 col-xs-6": 1,
      })
      .call((background) => this.backgroundUI.init(background));

    this.$palette = row.append("div")
      .classed({
        "nbp-theme-overlay-palette col-md-2 col-xs-6": 1
      })
      .call((palette) => this.paletteUI.init(palette));

    this.$rules = row.append("div")
      .classed({"theme-rules col-md-8 col-xs-12": 1});

    this.$toolbar = this.$rules.append("div")
      .classed({"nbp-theme-overlay-toolbar": 1});

    this.$ruleWrap = this.$rules.append("div")
      .classed({"theme-rule-wrap": 1});

    this.$toolbar.append("div").classed({row: 1})
      .call((row)=>{
        this.$baseText = row.append("div")
          .classed({"theme-base-font col-xs-6": 1})
          .datum({key: null, value: null})
          .call((font) => this.fontMenu(font));

        this.$focusContent = row.append("div")
          .classed({
            "focus-content col-xs-2 col-xs-offset-3": 1
          })
          .call((focus)=>{
            focus.append("input")
              .attr({type: "checkbox"})
              .on("change", ()=>{
                this.focusContent.set(d3.event.target.checked);
              });
            focus.append("label")
              .append("i")
              .classed({"fa fa-compress fa-2x": 1});
          });
      });

    this.$toolbar.append("div").classed({row: 1})
      .call((row)=>{
        this.$example = row.append("div")
          .classed({"col-xs-12": 1})
          .append("input")
          .classed({"theme-example-text": 1, "form-control": 1})
          .property({value: text})
          .attr("placeholder", "Custom Example Text")
          .on("input", () => {
            this.exampleText.set(this.$example.property("value"));
          });
      });

    this.update();
  }

  getExemplar(here, tag){
    let parent = d3.select(here.parentNode),
      found = null;

    while(true){
      found = parent.select(tag);
      if(found[0][0]){
        return found;
      }
      found = null;
      parent = d3.select(parent.node().parentNode);
    }
  }

  fontMenu(font){
    let overlay = this,
      row = font.append("div").classed({row: 1});

    row.append("div")
      .classed({"selector-label col-xs-3": 1})
      .text("∅");

    row.append("div")
      .classed({"selector-color dropdown col-xs-1": 1})
      .call(function(dropdown){
        dropdown.append("a")
          .classed({"btn btn-default dropdown-toggle": 1})
          .attr({"data-toggle": "dropdown"})
        dropdown.append("ul")
          .classed({"dropdown-menu": 1});
      });

    row.append("div").classed({"col-xs-4": 1})
      .append("input")
      .classed({"selector-font-size form-control": 1})
      .attr({
        type: "number"
      })
      .on("input", function({key, value}){
        var val = parseFloat(this.value);
        val = !isNaN(val) ? val : null;
        if(key){
          if(val){
            overlay.rules.set([key, "font-size"], parseFloat(this.value));
          }else{
            overlay.rules.unset([key, "font-size"]);
          }
        }else{
          if(val){
            overlay.textBase.set(["font-size"], parseFloat(this.value));
          }else{
            overlay.textBase.unset(["font-size"]);
          }
        }
      });

    row.append("div").classed({
        "selector-font-name dropdown col-xs-4": 1
      })
      .call(function(dropdown){
        dropdown.append("a")
          .classed({"btn btn-default dropdown-toggle": 1})
          .attr({"data-toggle": "dropdown"})
          .call((btn) => overlay.textZoom(btn));

        dropdown.append("ul")
          .classed({"dropdown-menu": 1});
      })

  }

  textZoomed({key, value}){
    let len = FONTS.length,
      ff = !key ? this.textBase.get(["font-family"]) || "Lato" :
        this.rules.get([key, "font-family"]),
      scaled = parseInt(20 * ((d3.event.scale && Math.log(d3.event.scale)) || 0)),
      nextFont = FONTS[Math.abs(scaled % len)];

    if(ff == nextFont){
      return;
    }

    if(!key){
      this.textBase.set(["font-family"], nextFont);
    }else{
      this.rules.set([key, "font-family"], nextFont)
    }
  }

  /** Draw all of the rules
  */
  update(){
    let overlay = this,
      rules = this.rules.get() || {},
      palette = this.palette.get() || {},

      textBase = this.textBase.get() || {},
      baseColor = textBase.color && palette[textBase.color],
      baseBg = baseColor && `rgb(${baseColor.rgb})`,

      rule = this.$ruleWrap.selectAll(".theme-rule")
        .data(SYMB.map((key)=> {
          return {key, value: rules[key], base: textBase};
        }), ({key}) => key),

      exampleText = this.exampleText.get() || "",
      focusContent = this.focusContent.get();

    this.$baseText
      .datum({key: null, value: textBase})
      .select(".selector-font-name")
      .call((dropdown) => this.updateFontMenu(dropdown))
      .select(".btn")
      .text(textBase["font-family"] || "sans-serif");

    this.$baseText.select(".selector-color")
      .call((dropdown) => this.updateColorMenu(dropdown))
      .style({"background-color": baseBg});

    this.$baseText.select(".selector-font-size")
      .attr({placeholder: "14px"})
      .property({
        value: ({value={}}) => value["font-size"],
      });

    rule.enter().append("div")
      .classed({"theme-rule": 1})
      .call((rule)=>{
        let settingsRow = rule.append("div")
          .classed({row: 1});

        settingsRow.append("div")
          .classed({"col-md-6 col-xs-12": 1})
          .call((font) => this.fontMenu(font));

        rule.append("div")
          .classed({
            "selector-exemplar": 1
          });

      });

    if(focusContent){
      rule.filter(({key}) => !this.selectorUsed(key)).remove();
    }

    rule.select(".selector-exemplar").each(function(d){
      let exemplar = d3.select(this),
        text = exampleText;

      if(!text && focusContent){
        let exEl = overlay.selectorUsed(d.key);
        if(exEl){
          text = d3.select(exEl).text();
        }
      }

      text = text || pangram;

      exemplar.selectAll(":not(" + d.key + ")").remove();

      let el = exemplar.selectAll(d.key)
        .data([d]);

      el.enter()
        .append(d.key)
        .attr({"contentEditable": "true"});

      el.text(text)
        .style({
          "font-family": ({value={}}) => {
            return value["font-family"] || textBase["font-family"];
          },
          "font-size": ({value={}}) => {
            let txt = value["font-size"] || textBase["font-size"];
            txt = txt ? `${txt}rem` : null;
            return txt;
          },
          "color": ({value={}}) => {
            let color = value.color && palette[value.color];
            if(color){
              return `rgba(${color.rgb}, ${color.a || 1.0})`;
            }
            return baseBg;
          }
        });

      el.exit().remove();
    });

    rule.select(".selector-label").text(({key}) => key);

    rule.select(".selector-font-size")
      .attr({
        placeholder: function({key}){
          return key ? overlay.getExemplar(this, key).style("font-size") :
            "14px";
        }
      })
      .property({
        value: ({value={}}) => value["font-size"],
      });

    rule.select(".selector-color")
      .call((dropdown) => this.updateColorMenu(dropdown))
      .style({
        "background-color": function({key}){
          return overlay.getExemplar(this, key).style("color");
        }
      });

    rule.select(".selector-font-name")
      .call((dropdown) => this.updateFontMenu(dropdown))
      .select(".btn")
      .text(function({key}){
        return key ? overlay.getExemplar(this, key).style("font-family") :
          "sans-serif";
      });
  }

  updateColorMenu(dropdown){
    let colors = d3.entries(this.palette.get() || {}),
      li = dropdown.select(".dropdown-menu")
        .selectAll("li")
        .data(({key, value})=> {
          return colors.map((color) => {
            return {key, value, color: color.value};
          });
        });

    li.enter()
      .append("li")
      .append("a")
      .text(" ")
      .on("click", ({key, color}) => {
        if(key){
          this.rules.set([key, "color"], color.id);
        }else{
          this.textBase.set(["color"], color.id);
        }
      });

    li.select("a").style({"background-color": ({color}) => {
      return `rgba(${color.rgb}, ${color.a || 1.0})`;
    }});
  }

  updateFontMenu(dropdown){
    let li = dropdown.select(".dropdown-menu")
      .selectAll("li")
      .data(({key, value}) => {
        return FONTS.map((font) => {
          return {font, key, value};
        });
      });

    li.enter()
      .append("li")
      .append("a")
      .on("mouseover", ({key, font})=> {
        if(key){
          this.rules.set([key, "font-family"], font);
        }else{
          this.textBase.set(["font-family"], font);
        }
      });

    li.select("a")
      .style({"font-family": ({font})=> font})
      .text(({font}) => font);
  }

  destroy(){
    this.$body.classed({"nbp-theming": 0});
    this.backgroundUI.destroy();
    this.$ui.remove();
  }
}
<commit_msg>refactored scroll, adding size<commit_after>import {d3, uuid, Baobab, WebFont} from "nbpresent-deps";
import _ from "underscore";

import Jupyter from "base/js/namespace";

import {FONTS, SYMB, PANGRAMS} from "./fonts";

import {BackgroundPicker} from "./background";
import {PaletteBuilder} from "./palette";

import {loadFonts} from "./fonts";

// pick a random pangram for this session. gotta catch'm all.
const pangram = d3.shuffle(PANGRAMS)[0];

export class ThemeOverlay{
  constructor(tree){
    this.tree = tree;

    this.theme = tree.select(["theme"]);
    this.rules = this.theme.select(["rules"]);
    this.textBase = this.theme.select(["text-base"]);
    this.palette = this.theme.select(["palette"]);

    this.themer = tree.select(".").root().select(["themer"]);
    this.focusContent = this.themer.select(["focuseContent"]);
    this.exampleText = this.themer.select("exampleText");

    this.backgroundUI = new BackgroundPicker(tree);
    this.paletteUI = new PaletteBuilder(tree);

    [
      this.rules,
      this.textBase,
      this.focusContent,
      this.exampleText,
      this.palette
    ]
      .map(({on})=> on("update", (e)=> this.update(e)));

    this.$body = d3.select("body");

    loadFonts(FONTS);

    this.fontZoom = d3.behavior.zoom()
      .on("zoom", this._zoomCycle("font-family", FONTS));

    this.sizeZoom = d3.behavior.zoom()
      .on("zoom", this._zoomCycle("font-size", d3.range(2, 30, 0.125)));

    this.init();
  }

  selectorUsed(selector){
    return d3.select(`#notebook .cell ${selector}`)[0][0];
  }

  init(){
    let text = this.exampleText.get();

    this.$body.classed({"nbp-theming": 1});

    this.$ui = this.$body
      .append("div")
      .classed({
        "nbp-theme-overlay": 1,
        "container-fluid": 1
      })
      .each(function(){ Jupyter.keyboard_manager.register_events(this); });

    let row = this.$ui.append("div")
      .classed({row: 1});

    this.$background = row.append("div")
      .classed({
        "nbp-theme-overlay-backgrounds col-md-2 col-xs-6": 1,
      })
      .call((background) => this.backgroundUI.init(background));

    this.$palette = row.append("div")
      .classed({
        "nbp-theme-overlay-palette col-md-2 col-xs-6": 1
      })
      .call((palette) => this.paletteUI.init(palette));

    this.$rules = row.append("div")
      .classed({"theme-rules col-md-8 col-xs-12": 1});

    this.$toolbar = this.$rules.append("div")
      .classed({"nbp-theme-overlay-toolbar": 1});

    this.$ruleWrap = this.$rules.append("div")
      .classed({"theme-rule-wrap": 1});

    this.$toolbar.append("div").classed({row: 1})
      .call((row)=>{
        this.$baseText = row.append("div")
          .classed({"theme-base-font col-xs-6": 1})
          .datum({key: null, value: null})
          .call((font) => this.fontMenu(font));

        this.$focusContent = row.append("div")
          .classed({
            "focus-content col-xs-2 col-xs-offset-3": 1
          })
          .call((focus)=>{
            focus.append("input")
              .attr({type: "checkbox"})
              .on("change", ()=>{
                this.focusContent.set(d3.event.target.checked);
              });
            focus.append("label")
              .append("i")
              .classed({"fa fa-compress fa-2x": 1});
          });
      });

    this.$toolbar.append("div").classed({row: 1})
      .call((row)=>{
        this.$example = row.append("div")
          .classed({"col-xs-12": 1})
          .append("input")
          .classed({"theme-example-text": 1, "form-control": 1})
          .property({value: text})
          .attr("placeholder", "Custom Example Text")
          .on("input", () => {
            this.exampleText.set(this.$example.property("value"));
          });
      });

    this.update();
  }

  getExemplar(here, tag){
    let parent = d3.select(here.parentNode),
      found = null;

    while(true){
      found = parent.select(tag);
      if(found[0][0]){
        return found;
      }
      found = null;
      parent = d3.select(parent.node().parentNode);
    }
  }

  fontMenu(font){
    let overlay = this,
      row = font.append("div").classed({row: 1});

    row.append("div")
      .classed({"selector-label col-xs-3": 1})
      .text("∅");

    row.append("div")
      .classed({"selector-color dropdown col-xs-1": 1})
      .call(function(dropdown){
        dropdown.append("a")
          .classed({"btn btn-default dropdown-toggle": 1})
          .attr({"data-toggle": "dropdown"})
        dropdown.append("ul")
          .classed({"dropdown-menu": 1});
      });

    row.append("div").classed({"col-xs-4": 1})
      .append("input")
      .classed({"selector-font-size form-control": 1})
      .attr({
        type: "number"
      })
      .call(this.sizeZoom)
      .on("input", function({key, value}){
        var val = parseFloat(this.value);
        val = !isNaN(val) ? val : null;
        if(key){
          if(val){
            overlay.rules.set([key, "font-size"], parseFloat(this.value));
          }else{
            overlay.rules.unset([key, "font-size"]);
          }
        }else{
          if(val){
            overlay.textBase.set(["font-size"], parseFloat(this.value));
          }else{
            overlay.textBase.unset(["font-size"]);
          }
        }
      });

    row.append("div").classed({
        "selector-font-name dropdown col-xs-4": 1
      })
      .call(function(dropdown){
        dropdown.append("a")
          .classed({"btn btn-default dropdown-toggle": 1})
          .attr({"data-toggle": "dropdown"})
          .call(overlay.fontZoom);

        dropdown.append("ul")
          .classed({"dropdown-menu": 1});
      })

  }

  fontZoomed({key, value}){
    let attr = "font-family",
      len = FONTS.length,
      val = !key ? this.textBase.get([attr]) || "Lato" :
        this.rules.get([key, attr]),
      scaled = parseInt(20 * ((d3.event.scale && Math.log(d3.event.scale)) || 0)),
      newVal = FONTS[Math.abs(scaled % len)];

    if(val === newVal){
      return;
    }

    if(!key){
      this.textBase.set([attr], nextFont);
    }else{
      this.rules.set([key, attr], nextFont);
    }
  }

  sizeZoomed({key, value}){
    let attr = "font-size",
      val = !key ? this.textBase.get([attr]) || "Lato" :
        this.rules.get([key, attr]);

    if(val === newVal){
      return;
    }

    if(!key){
      this.textBase.set([attr], nextFont);
    }else{
      this.rules.set([key, attr], nextFont)
    }
  }

  /** factory for d3.behavior.zoom
  */
  _zoomCycle(attr, choices, scale=20){
    const len = choices.length;

    return ({key, value}) => {
      let cursor = key ? this.rules : this.textBase,
        path = key ? [key, attr] : [attr],
        val = cursor.get(path),
        scaled = parseInt(
          scale * ((d3.event.scale && Math.log(d3.event.scale)) || 0)),
        newVal = choices[Math.abs(scaled % len)];

      return val === newVal ? null : cursor.set(path, newVal);
    }
  }

  /** Draw all of the rules
  */
  update(){
    let overlay = this,
      rules = this.rules.get() || {},
      palette = this.palette.get() || {},

      textBase = this.textBase.get() || {},
      baseColor = textBase.color && palette[textBase.color],
      baseBg = baseColor && `rgb(${baseColor.rgb})`,

      rule = this.$ruleWrap.selectAll(".theme-rule")
        .data(SYMB.map((key)=> {
          return {key, value: rules[key], base: textBase};
        }), ({key}) => key),

      exampleText = this.exampleText.get() || "",
      focusContent = this.focusContent.get();

    this.$baseText
      .datum({key: null, value: textBase})
      .select(".selector-font-name")
      .call((dropdown) => this.updateFontMenu(dropdown))
      .select(".btn")
      .text(textBase["font-family"] || "sans-serif");

    this.$baseText.select(".selector-color")
      .call((dropdown) => this.updateColorMenu(dropdown))
      .style({"background-color": baseBg});

    this.$baseText.select(".selector-font-size")
      .attr({placeholder: "14px"})
      .property({
        value: ({value={}}) => value["font-size"],
      });

    rule.enter().append("div")
      .classed({"theme-rule": 1})
      .call((rule)=>{
        let settingsRow = rule.append("div")
          .classed({row: 1});

        settingsRow.append("div")
          .classed({"col-md-6 col-xs-12": 1})
          .call((font) => this.fontMenu(font));

        rule.append("div")
          .classed({
            "selector-exemplar": 1
          });

      });

    if(focusContent){
      rule.filter(({key}) => !this.selectorUsed(key)).remove();
    }

    rule.select(".selector-exemplar").each(function(d){
      let exemplar = d3.select(this),
        text = exampleText;

      if(!text && focusContent){
        let exEl = overlay.selectorUsed(d.key);
        if(exEl){
          text = d3.select(exEl).text();
        }
      }

      text = text || pangram;

      exemplar.selectAll(":not(" + d.key + ")").remove();

      let el = exemplar.selectAll(d.key)
        .data([d]);

      el.enter()
        .append(d.key)
        .attr({"contentEditable": "true"});

      el.text(text)
        .style({
          "font-family": ({value={}}) => {
            return value["font-family"] || textBase["font-family"];
          },
          "font-size": ({value={}}) => {
            let txt = value["font-size"] || textBase["font-size"];
            txt = txt ? `${txt}rem` : null;
            return txt;
          },
          "color": ({value={}}) => {
            let color = value.color && palette[value.color];
            if(color){
              return `rgba(${color.rgb}, ${color.a || 1.0})`;
            }
            return baseBg;
          }
        });

      el.exit().remove();
    });

    rule.select(".selector-label").text(({key}) => key);

    rule.select(".selector-font-size")
      .attr({
        placeholder: function({key}){
          return key ? overlay.getExemplar(this, key).style("font-size") :
            "14px";
        }
      })
      .property({
        value: ({value={}}) => value["font-size"],
      });

    rule.select(".selector-color")
      .call((dropdown) => this.updateColorMenu(dropdown))
      .style({
        "background-color": function({key}){
          return overlay.getExemplar(this, key).style("color");
        }
      });

    rule.select(".selector-font-name")
      .call((dropdown) => this.updateFontMenu(dropdown))
      .select(".btn")
      .text(function({key}){
        return key ? overlay.getExemplar(this, key).style("font-family") :
          "sans-serif";
      });
  }

  updateColorMenu(dropdown){
    let colors = d3.entries(this.palette.get() || {}),
      li = dropdown.select(".dropdown-menu")
        .selectAll("li")
        .data(({key, value})=> {
          return colors.map((color) => {
            return {key, value, color: color.value};
          });
        });

    li.enter()
      .append("li")
      .append("a")
      .text(" ")
      .on("click", ({key, color}) => {
        if(key){
          this.rules.set([key, "color"], color.id);
        }else{
          this.textBase.set(["color"], color.id);
        }
      });

    li.select("a").style({"background-color": ({color}) => {
      return `rgba(${color.rgb}, ${color.a || 1.0})`;
    }});
  }

  updateFontMenu(dropdown){
    let li = dropdown.select(".dropdown-menu")
      .selectAll("li")
      .data(({key, value}) => {
        return FONTS.map((font) => {
          return {font, key, value};
        });
      });

    li.enter()
      .append("li")
      .append("a")
      .on("mouseover", ({key, font})=> {
        if(key){
          this.rules.set([key, "font-family"], font);
        }else{
          this.textBase.set(["font-family"], font);
        }
      });

    li.select("a")
      .style({"font-family": ({font})=> font})
      .text(({font}) => font);
  }

  destroy(){
    this.$body.classed({"nbp-theming": 0});
    this.backgroundUI.destroy();
    this.$ui.remove();
  }
}
<|endoftext|>


<commit_before>Contrasts = new Meteor.Collection('contrasts');

var sampleSchema = new SimpleSchema({
  study_label: { type: String },
  sample_label: { type: String },
  patient_label: { type: String, optional: true },
});

Contrasts.attachSchema(new SimpleSchema({
  user_id: { type: String },
  collaborations: { type: [String] },

  contrast_label: { type: String },
  version: { type: Number, min: 1 },
  description: { type: String },

  a_name: { type: String }, // group1
  a_samples: { type: [sampleSchema] }, // list1
  b_name: { type: String }, // group2
  b_samples: { type: [sampleSchema] } // list2
}));

// a_name
// b_name
// a_samples
// b_samples
//
// first_name      group1
// first_samples   list1
// second_name     group2
// second_samples  list2
<commit_msg>Contrasts.version ==> contrast_version<commit_after>Contrasts = new Meteor.Collection('contrasts');

var sampleSchema = new SimpleSchema({
  study_label: { type: String },
  sample_label: { type: String },
  patient_label: { type: String, optional: true },
});

Contrasts.attachSchema(new SimpleSchema({
  user_id: { type: String },
  collaborations: { type: [String] },

  contrast_label: { type: String },
  contrast_version: { type: Number, min: 1 },
  description: { type: String },

  a_name: { type: String }, // group1
  a_samples: { type: [sampleSchema] }, // list1
  b_name: { type: String }, // group2
  b_samples: { type: [sampleSchema] } // list2
}));

// a_name
// b_name
// a_samples
// b_samples
//
// first_name      group1
// first_samples   list1
// second_name     group2
// second_samples  list2
<|endoftext|>