<commit_before>import sampleCombine from 'xstream/extra/sampleCombine'
import isolate from '@cycle/isolate'
import { ul, li, form, i, p, div, br, label, input, code, table, tr, td, b, h2, h5, button, textarea, a, select, option, span } from '@cycle/dom';
import { clone, merge, mergeAll } from 'ramda';
import xs from 'xstream';
import { ENTER_KEYCODE } from '../utils/keycodes.js'
import dropRepeats from 'xstream/extra/dropRepeats'
import { loggerFactory } from '~/../../src/js/utils/logger'
import { difference, keys, head, prop, assocPath, equals } from 'ramda'
import { initSettings } from '../configuration'

export const compoundFilterLens = {
    get: state => ({ core: state.filter, settings: state.settings.filter }),
    set: (state, childState) => ({ ...state, filter: childState.core })
}

/**
 * Provide a filter form. We inject (via input) a signature in order to hide the form when an empty signature is present.
 *
 * Please note that the filter state object contains the values for the filter that should be _included_.
 *
 * - input$: stream of signature updates
 * - output$: to be consumed by components that require filter functionality, object with filter values.
 */
function Filter(sources) {

    const logger = loggerFactory('filter', sources.onion.state$, 'settings.filter.debug')

    const state$ = sources.onion.state$

    const input$ = sources.input

    // When the component should not be shown, including empty signature
    const isEmptyState = (state) => {
        if (typeof state.core === 'undefined') {
            return true
        } else {
            if (typeof state.core.input === 'undefined') {
                return true
            } else {
                if (state.core.input === '') {
                    return true
                } else {
                    return false
                }
            }
        }
    }

    const emptyState$ = state$
        .filter(input => isEmptyState(input))

    const modifiedState$ = xs.combine(input$, state$)
        .filter(([_, state]) => !isEmptyState(state))
        .map(([i, state]) => ({ ...state, core: { ...state.core, input: i } }))
        .compose(dropRepeats(equals))

    // This for ghost mode, inject changes via external state updates...
    const ghostChanges$ = modifiedState$
        .filter(state => typeof state.core.ghost !== 'undefined')
        .compose(dropRepeats())

    const expandAnyGhost$ = ghostChanges$.map(state => state.core.ghost.expand).startWith(false)

    const expandConcentrationUI$ = sources.DOM
        .select('.concentration')
        .events('click')
        .fold((x, y) => !x, false)
        .startWith(false)

    const expandConcentration$ = xs.merge(expandConcentrationUI$, expandAnyGhost$).remember()

    const expandProtocolUI$ = sources.DOM
        .select('.protocol')
        .events('click')
        .fold((x, _) => !x, false)
        .startWith(false)

    const expandProtocol$ = xs.merge(expandProtocolUI$, expandAnyGhost$).remember()

    const expandTypeUI$ = sources.DOM
        .select('.type')
        .events('click')
        .fold((x, _) => !x, false)
        .startWith(false)

    const expandType$ = xs.merge(expandTypeUI$, expandAnyGhost$).remember()

    // Helper functions for options: all unset or all set
    const noFilter = (selectedOptions, possibleOptions) => (selectedOptions.length == possibleOptions.length)
    // const allFilter = (selectedOptions, possibleOptions) => (selectedOptions.length == 0)

    /**
     * Given an option from a list of options (x) and a selection, return the properties for a checkbox
     * @param {*} option The current option in the list
     * @param {*} selection Array with selection
     */
    const isSelectedProps = (option, selectedOptions) => {
        if (selectedOptions.includes(option)) {
            return { props: { type: 'checkbox', checked: true, id: option } }
        } else {
            return { props: { type: 'checkbox', checked: false, id: option } }
        }
    }

    /**
     * A line in a table with checkboxes for use with togglableFilter
     * @param {*} filter String representing the filter, to be used for selection later
     * @param {*} option The current option in the list
     * @param {*} selection Array with selection
     */
    const filterSwitch = (filter, option, selectedOptions) => div(
        '.collection-item ' + '.' + filter + '-options', { props: { id: option } }, [
            label({ props: { id: option } }, [
                input(isSelectedProps(option, selectedOptions), ''),
                // label('', { props: { id: option } },
                span({ props: { id: option } }, [option])
                // )
            ])
        ])

    /**
     * A table of check boxes that is only shown if needed
     * @param {*} toggle Visible or not?
     * @param {*} options Array of possible options
     * @param {*} selection Array of selections (multiple)
     */
    const togglableFilter = (filter, toggle, possibleOptions, selectedOptions) =>
        toggle ?
            div('.col .s10 .offset-s1', [ //{ style: { position: 'absolute', left: '0px', top: '50%', width: '50%', 'z-index': '1' } }, [
                div('.collection .selection ',
                    possibleOptions.map(option => filterSwitch(filter, option, selectedOptions))
                )

            ]) :
            div('.protocol .col .s10 .offset-s1', [''])

    const emptyVdom$ = emptyState$.mapTo(div())

    const loadedVdom$ = xs.combine(modifiedState$, expandConcentration$, expandProtocol$, expandType$)
        .map(([state, toggleConcentration, toggleProtocol, toggleType]) => {
            const selectedConcentrations = state.core.output.concentration
            const selectedProtocols = state.core.output.protocol
            const selectedTypes = state.core.output.type

            const possibleConcentrations = state.settings.values.concentration
            const possibleProtocols = state.settings.values.protocol
            const possibleTypes = state.settings.values.type

            return div([
                div('.col .s12 .l4', [
                    div('.chip .concentration .col .s12', [
                        span('.concentration .blue-grey-text', [
                            noFilter(selectedConcentrations, possibleConcentrations) ? 'No Concentration Filter' : 'Concentrations: ' + selectedConcentrations.join(', ')
                        ])
                    ]),
                    togglableFilter('concentration', toggleConcentration, possibleConcentrations, selectedConcentrations)
                ]),
                div('.col .s12 .l4', [
                    div('.chip .protocol .col .s12', [
                        span('.protocol .blue-grey-text', [
                            noFilter(selectedProtocols, possibleProtocols) ? 'No Protocol Filter' : 'Protocols: ' + selectedProtocols.join(', ')
                        ])
                    ]),
                    togglableFilter('protocol', toggleProtocol, possibleProtocols, selectedProtocols)

                ]),
                div('.col .s12 .l4', [
                    div('.chip .type .col .s12', [
                        span('.type .blue-grey-text', [
                            noFilter(selectedTypes, possibleTypes) ? 'No Type Filter' : 'Types: ' + selectedTypes.join(', ')
                        ])
                    ]),
                    togglableFilter('type', toggleType, possibleTypes, selectedTypes)
                ])
            ])
        })

    const vdom$ = xs.merge(
        emptyVdom$,
        loadedVdom$//.startWith(div())
    )

    // Trigger when all filter fields are collapsed
    const expandAny$ = xs.combine(expandConcentration$, expandProtocol$, expandType$)
        .filter(([concentrationExpanded, protocolExpanded, typeExpanded]) => !concentrationExpanded && !protocolExpanded && !typeExpanded)

    // Push filter through as output field ONLY when filter fields are collapsed
    // This is to avoid too frequent updates
    // merge with the first state update in order to have at least 1 cycle
    const filter$ = xs.merge(
      state$.take(1).map(s => s.settings.values),
      expandAny$.compose(sampleCombine(modifiedState$)).map(([_, state]) => state.core.output)
    ).remember()

    // Toggles for filter options
    const toggledGhost$ =
              ghostChanges$
                .filter(state => typeof state.core.ghost.deselect !== 'undefined')
                .map(state => state.core.ghost.deselect)
                .compose(dropRepeats(equals))

    const concentrationToggled$ =
              sources.DOM.select('.concentration-options')
                  .events('click')
                  .map(function (ev) { ev.preventDefault(); return ev; })
                  .map(ev => ev.ownerTarget.id)
                  .map(value => ({ concentration: value }))

    const protocolToggled$ = sources.DOM.select('.protocol-options')
        .events('click')
        .map(function (ev) { ev.preventDefault(); return ev; })
        .map(ev => ev.ownerTarget.id)
        .map(value => ({ protocol: value }))

    const typeToggled$ = sources.DOM.select('.type-options')
        .events('click')
        .map(function (ev) { ev.preventDefault(); return ev; })
        .map(ev => ev.ownerTarget.id)
        .map(value => ({ type: value }))

    /**
     * Reducers
     */
    // Add the filter values from the settings (and originally from deployments.json) to the current values
    const defaultReducer$ = xs.of(prevState =>
            ({...prevState, core: {...prevState.core, output: prevState.settings.values } })
        )

    const inputReducer$ = input$.map(i => prevState => ({ ...prevState, core: { ...prevState.core, input: i } }))

    const toggleReducer$ = xs.merge(
        concentrationToggled$, protocolToggled$, typeToggled$, toggledGhost$)
        .map(clickedConcentration => prevState => {
            // We want this function to work for 3 the different filters, so first get the appropriate one
            const filterKey = head(keys(clickedConcentration))
            const filterValue = prop(filterKey, clickedConcentration)
            // if already included, remove it from the list
            const currentArrayForFilterKey = prop(filterKey, prevState.core.output)
            const alreadyIncluded = currentArrayForFilterKey.includes(filterValue)
            const newArrayForFilterKey = alreadyIncluded ?
                currentArrayForFilterKey.filter(el => el != filterValue) : // the value has to be removed from the list
                currentArrayForFilterKey.concat(filterValue) // the value has to be added to the list
            // When all options are _excluded_, reset to all options _included_
            // const cleanArrayForFilterKey = (newArrayForFilterKey.length == 4) ? [] : newArrayForFilterKey
            const updatedState = assocPath(['core', 'output', filterKey], newArrayForFilterKey, prevState)
            return updatedState
        })

    const outputReducer$ = filter$.map(i => prevState => ({ ...prevState, core: { ...prevState.core, output: i } }))

    return {
        log: xs.merge(
            logger(state$, 'state$'),
        ),
        DOM: vdom$,
        output: filter$,
        onion: xs.merge(
            defaultReducer$,
            inputReducer$,
            outputReducer$,
            toggleReducer$
        )
    }

}

export { Filter }
<commit_msg>Add 'a' modifier key to toggle all filters<commit_after>import sampleCombine from 'xstream/extra/sampleCombine'
import isolate from '@cycle/isolate'
import { ul, li, form, i, p, div, br, label, input, code, table, tr, td, b, h2, h5, button, textarea, a, select, option, span } from '@cycle/dom';
import { clone, merge, mergeAll } from 'ramda';
import xs from 'xstream';
import { ENTER_KEYCODE } from '../utils/keycodes.js'
import dropRepeats from 'xstream/extra/dropRepeats'
import { loggerFactory } from '~/../../src/js/utils/logger'
import { difference, keys, head, prop, assocPath, equals } from 'ramda'
import { initSettings } from '../configuration'

export const compoundFilterLens = {
    get: state => ({ core: state.filter, settings: state.settings.filter }),
    set: (state, childState) => ({ ...state, filter: childState.core })
}

/**
 * Provide a filter form. We inject (via input) a signature in order to hide the form when an empty signature is present.
 *
 * Please note that the filter state object contains the values for the filter that should be _included_.
 *
 * - input$: stream of signature updates
 * - output$: to be consumed by components that require filter functionality, object with filter values.
 */
function Filter(sources) {

    const logger = loggerFactory('filter', sources.onion.state$, 'settings.filter.debug')

    const state$ = sources.onion.state$

    const input$ = sources.input

    // When the component should not be shown, including empty signature
    const isEmptyState = (state) => {
        if (typeof state.core === 'undefined') {
            return true
        } else {
            if (typeof state.core.input === 'undefined') {
                return true
            } else {
                if (state.core.input === '') {
                    return true
                } else {
                    return false
                }
            }
        }
    }

    const emptyState$ = state$
        .filter(input => isEmptyState(input))

    const modifiedState$ = xs.combine(input$, state$)
        .filter(([_, state]) => !isEmptyState(state))
        .map(([i, state]) => ({ ...state, core: { ...state.core, input: i } }))
        .compose(dropRepeats(equals))

    // This for ghost mode, inject changes via external state updates...
    const ghostChanges$ = modifiedState$
        .filter(state => typeof state.core.ghost !== 'undefined')
        .compose(dropRepeats())

    const expandAnyGhost$ = ghostChanges$.map(state => state.core.ghost.expand).startWith(false)

    const expandConcentrationUI$ = sources.DOM
        .select('.concentration')
        .events('click')
        .fold((x, y) => !x, false)
        .startWith(false)

    const expandConcentration$ = xs.merge(expandConcentrationUI$, expandAnyGhost$).remember()

    const expandProtocolUI$ = sources.DOM
        .select('.protocol')
        .events('click')
        .fold((x, _) => !x, false)
        .startWith(false)

    const expandProtocol$ = xs.merge(expandProtocolUI$, expandAnyGhost$).remember()

    const expandTypeUI$ = sources.DOM
        .select('.type')
        .events('click')
        .fold((x, _) => !x, false)
        .startWith(false)

    const expandType$ = xs.merge(expandTypeUI$, expandAnyGhost$).remember()

    // Helper functions for options: all unset or all set
    const noFilter = (selectedOptions, possibleOptions) => (selectedOptions.length == possibleOptions.length)
    // const allFilter = (selectedOptions, possibleOptions) => (selectedOptions.length == 0)

    /**
     * Given an option from a list of options (x) and a selection, return the properties for a checkbox
     * @param {*} option The current option in the list
     * @param {*} selection Array with selection
     */
    const isSelectedProps = (option, selectedOptions) => {
        if (selectedOptions.includes(option)) {
            return { props: { type: 'checkbox', checked: true, id: option } }
        } else {
            return { props: { type: 'checkbox', checked: false, id: option } }
        }
    }

    /**
     * A line in a table with checkboxes for use with togglableFilter
     * @param {*} filter String representing the filter, to be used for selection later
     * @param {*} option The current option in the list
     * @param {*} selection Array with selection
     */
    const filterSwitch = (filter, option, selectedOptions) => div(
        '.collection-item ' + '.' + filter + '-options', { props: { id: option } }, [
            label({ props: { id: option } }, [
                input(isSelectedProps(option, selectedOptions), ''),
                // label('', { props: { id: option } },
                span({ props: { id: option } }, [option])
                // )
            ])
        ])

    /**
     * A table of check boxes that is only shown if needed
     * @param {*} toggle Visible or not?
     * @param {*} options Array of possible options
     * @param {*} selection Array of selections (multiple)
     */
    const togglableFilter = (filter, toggle, possibleOptions, selectedOptions) =>
        toggle ?
            div('.col .s10 .offset-s1', [ //{ style: { position: 'absolute', left: '0px', top: '50%', width: '50%', 'z-index': '1' } }, [
                div('.collection .selection ',
                    possibleOptions.map(option => filterSwitch(filter, option, selectedOptions))
                )

            ]) :
            div('.protocol .col .s10 .offset-s1', [''])

    const emptyVdom$ = emptyState$.mapTo(div())

    const loadedVdom$ = xs.combine(modifiedState$, expandConcentration$, expandProtocol$, expandType$)
        .map(([state, toggleConcentration, toggleProtocol, toggleType]) => {
            const selectedConcentrations = state.core.output.concentration
            const selectedProtocols = state.core.output.protocol
            const selectedTypes = state.core.output.type

            const possibleConcentrations = state.settings.values.concentration
            const possibleProtocols = state.settings.values.protocol
            const possibleTypes = state.settings.values.type

            return div([
                div('.col .s12 .l4', [
                    div('.chip .concentration .col .s12', [
                        span('.concentration .blue-grey-text', [
                            noFilter(selectedConcentrations, possibleConcentrations) ? 'No Concentration Filter' : 'Concentrations: ' + selectedConcentrations.join(', ')
                        ])
                    ]),
                    togglableFilter('concentration', toggleConcentration, possibleConcentrations, selectedConcentrations)
                ]),
                div('.col .s12 .l4', [
                    div('.chip .protocol .col .s12', [
                        span('.protocol .blue-grey-text', [
                            noFilter(selectedProtocols, possibleProtocols) ? 'No Protocol Filter' : 'Protocols: ' + selectedProtocols.join(', ')
                        ])
                    ]),
                    togglableFilter('protocol', toggleProtocol, possibleProtocols, selectedProtocols)

                ]),
                div('.col .s12 .l4', [
                    div('.chip .type .col .s12', [
                        span('.type .blue-grey-text', [
                            noFilter(selectedTypes, possibleTypes) ? 'No Type Filter' : 'Types: ' + selectedTypes.join(', ')
                        ])
                    ]),
                    togglableFilter('type', toggleType, possibleTypes, selectedTypes)
                ])
            ])
        })

    const vdom$ = xs.merge(
        emptyVdom$,
        loadedVdom$//.startWith(div())
    )

    // Trigger when all filter fields are collapsed
    const expandAny$ = xs.combine(expandConcentration$, expandProtocol$, expandType$)
        .filter(([concentrationExpanded, protocolExpanded, typeExpanded]) => !concentrationExpanded && !protocolExpanded && !typeExpanded)

    // Push filter through as output field ONLY when filter fields are collapsed
    // This is to avoid too frequent updates
    // merge with the first state update in order to have at least 1 cycle
    const filter$ = xs.merge(
      state$.take(1).map(s => s.settings.values),
      expandAny$.compose(sampleCombine(modifiedState$)).map(([_, state]) => state.core.output)
    ).remember()

    // Toggles for filter options
    const toggledGhost$ =
              ghostChanges$
                .filter(state => typeof state.core.ghost.deselect !== 'undefined')
                .map(state => state.core.ghost.deselect)
                .compose(dropRepeats(equals))

    const concentrationToggled$ =
        sources.DOM.select('.concentration-options')
            .events('click')
            .map(function (ev) { ev.preventDefault(); return ev; })
            .map(ev => ev.ownerTarget.id)
            .map(value => ({ concentration: value }))

    const protocolToggled$ =
        sources.DOM.select('.protocol-options')
            .events('click')
            .map(function (ev) { ev.preventDefault(); return ev; })
            .map(ev => ev.ownerTarget.id)
            .map(value => ({ protocol: value }))

    const typeToggled$ =
        sources.DOM.select('.type-options')
            .events('click')
            .map(function (ev) { ev.preventDefault(); return ev; })
            .map(ev => ev.ownerTarget.id)
            .map(value => ({ type: value }))

    const aDown$ =
        sources.DOM.select('document')
            .events('keydown')
            .map(ev => ev.code)
            .filter(code => code == "KeyA")
            .mapTo(true)
            .startWith(false)

    // A modifier stream
    const aUp$ =
        sources.DOM.select('document')
            .events('keyup')
            .map(ev => ev.code)
            .filter(code => code == "KeyA")
            .mapTo(false)

    const a$ =
        xs.merge(aDown$, aUp$)
            .compose(dropRepeats(equals))
            .remember()
            .debug('a')

    /**
     * Reducers
     */
    // Add the filter values from the settings (and originally from deployments.json) to the current values
    const defaultReducer$ = xs.of(prevState =>
            ({...prevState, core: {...prevState.core, output: prevState.settings.values } })
        )

    const inputReducer$ = input$.map(i => prevState => ({ ...prevState, core: { ...prevState.core, input: i } }))

    const toggleReducer$ =
        xs.merge(concentrationToggled$, protocolToggled$, typeToggled$, toggledGhost$)
          .compose(sampleCombine(a$))
          .map(([clickedFilter, a]) => prevState => {
            // a is a modifier key. If it's not pressed it's the usual behaviour
            if (a == false) {
              // We want this function to work for 3 the different filters, so first get the appropriate one
              const filterKey = head(keys(clickedFilter))
              const filterValue = prop(filterKey, clickedFilter)
              // if already included, remove it from the list
              const currentArrayForFilterKey = prop(filterKey, prevState.core.output)
              const alreadyIncluded = currentArrayForFilterKey.includes(filterValue)
              const newArrayForFilterKey = alreadyIncluded ?
                  currentArrayForFilterKey.filter(el => el != filterValue) : // the value has to be removed from the list
                  currentArrayForFilterKey.concat(filterValue) // the value has to be added to the list
              // When all options are _excluded_, reset to all options _included_
              // const cleanArrayForFilterKey = (newArrayForFilterKey.length == 4) ? [] : newArrayForFilterKey
              const updatedState = assocPath(['core', 'output', filterKey], newArrayForFilterKey, prevState)
              return updatedState
            } else {
              // If a is pressed during the click, toggle ALL values
              console.log("not yet implemented")
              const filterKey = head(keys(clickedFilter))
              // values currently selected
              const currentValues = prop(filterKey, prevState.core.output)
              // possible values
              const allValues = prop(filterKey, prevState.settings.values)
              // possible - current values
              const newValues = allValues.filter(v => !currentValues.includes(v))
              const updatedState = assocPath(['core', 'output', filterKey], newValues, prevState)
              return updatedState
            }
          })

    const outputReducer$ = filter$.map(i => prevState => ({ ...prevState, core: { ...prevState.core, output: i } }))

    return {
        log: xs.merge(
            logger(state$, 'state$'),
        ),
        DOM: vdom$,
        output: filter$,
        onion: xs.merge(
            defaultReducer$,
            inputReducer$,
            outputReducer$,
            toggleReducer$
        )
    }

}

export { Filter }
<|endoftext|>


<commit_before>
openerp.web.formats = function(instance) {
var _t = instance.web._t;

/**
 * Intersperses ``separator`` in ``str`` at the positions indicated by
 * ``indices``.
 *
 * ``indices`` is an array of relative offsets (from the previous insertion
 * position, starting from the end of the string) at which to insert
 * ``separator``.
 *
 * There are two special values:
 *
 * ``-1``
 *   indicates the insertion should end now
 * ``0``
 *   indicates that the previous section pattern should be repeated (until all
 *   of ``str`` is consumed)
 *
 * @param {String} str
 * @param {Array<Number>} indices
 * @param {String} separator
 * @returns {String}
 */
instance.web.intersperse = function (str, indices, separator) {
    separator = separator || '';
    var result = [], last = str.length;

    for(var i=0; i<indices.length; ++i) {
        var section = indices[i];
        if (section === -1 || last <= 0) {
            // Done with string, or -1 (stops formatting string)
            break;
        } else if(section === 0 && i === 0) {
            // repeats previous section, which there is none => stop
            break;
        } else if (section === 0) {
            // repeat previous section forever
            //noinspection AssignmentToForLoopParameterJS
            section = indices[--i];
        }
        result.push(str.substring(last-section, last));
        last -= section;
    }

    var s = str.substring(0, last);
    if (s) { result.push(s); }
    return result.reverse().join(separator);
};
/**
 * Insert "thousands" separators in the provided number (which is actually
 * a string)
 *
 * @param {String} num
 * @returns {String}
 */
instance.web.insert_thousand_seps = function (num) {
    var negative = num[0] === '-';
    num = (negative ? num.slice(1) : num);
    return (negative ? '-' : '') + instance.web.intersperse(
        num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
};

/**
 * removes literal (non-format) text from a date or time pattern, as datejs can
 * not deal with literal text in format strings (whatever the format), whereas
 * strftime allows for literal characters
 *
 * @param {String} value original format
 */
instance.web.strip_raw_chars = function (value) {
    var isletter = /[a-zA-Z]/, output = [];
    for(var index=0; index < value.length; ++index) {
        var character = value[index];
        if(isletter.test(character) && (index === 0 || value[index-1] !== '%')) {
            continue;
        }
        output.push(character);
    }
    return output.join('');
};
var normalize_format = function (format) {
    return Date.normalizeFormat(instance.web.strip_raw_chars(format));
};

/**
 * Check with a scary heuristic if the value is a bin_size or not.
 * If not, compute an approximate size out of the base64 encoded string.
 *
 * @param {String} value original format
 */
instance.web.binary_to_binsize = function (value) {
    if (!value) {
        return instance.web.human_size(0);
    }
    if (value.substr(0, 10).indexOf(' ') == -1) {
        // Computing approximate size out of base64 encoded string
        // http://en.wikipedia.org/wiki/Base64#MIME
        return instance.web.human_size(value.length / 1.37);
    } else {
        // already bin_size
        return value;
    }
};

/**
 * Returns a human readable size
 *
 * @param {Number} numner of bytes
 */
instance.web.human_size = function(size) {
    var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(',');
    var i = 0;
    while (size >= 1024) {
        size /= 1024;
        ++i;
    }
    return size.toFixed(2) + ' ' + units[i];
};

/**
 * Formats a single atomic value based on a field descriptor
 *
 * @param {Object} value read from OpenERP
 * @param {Object} descriptor union of orm field and view field
 * @param {Object} [descriptor.widget] widget to use to display the value
 * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
 * @param {Object} [descriptor.digits] used for the formatting of floats
 * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
 */
instance.web.format_value = function (value, descriptor, value_if_empty) {
    // If NaN value, display as with a `false` (empty cell)
    if (typeof value === 'number' && isNaN(value)) {
        value = false;
    }
    //noinspection FallthroughInSwitchStatementJS
    switch (value) {
        case '':
            if (descriptor.type === 'char' || descriptor.type === 'text') {
                return '';
            }
            console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
        case false:
        case undefined:
        case Infinity:
        case -Infinity:
            return value_if_empty === undefined ?  '' : value_if_empty;
    }
    var l10n = _t.database.parameters;
    switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
        case 'id':
            return value.toString();
        case 'integer':
            return instance.web.insert_thousand_seps(
                _.str.sprintf('%d', value));
        case 'float':
            var digits = descriptor.digits ? descriptor.digits : [69,2];
            digits = typeof digits === "string" ? py.eval(digits) : digits;
            var precision = digits[1];
            var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
            formatted[0] = instance.web.insert_thousand_seps(formatted[0]);
            return formatted.join(l10n.decimal_point);
        case 'float_time':
            var pattern = '%02d:%02d';
            if (value < 0) {
                value = Math.abs(value);
                pattern = '-' + pattern;
            }
            var hour = Math.floor(value);
            var min = Math.round((value % 1) * 60);
            if (min == 60){
                min = 0;
                hour = hour + 1;
            }
            return _.str.sprintf(pattern, hour, min);
        case 'many2one':
            // name_get value format
            return value[1] ? value[1].split("\n")[0] : value[1];
        case 'one2many':
        case 'many2many':
            if (typeof value === 'string') {
                return value;
            }
            return _.str.sprintf(_t("(%d records)"), value.length);
        case 'datetime':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);

            return value.toString(normalize_format(l10n.date_format)
                        + ' ' + normalize_format(l10n.time_format));
        case 'date':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);
            return value.toString(normalize_format(l10n.date_format));
        case 'time':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);
            return value.toString(normalize_format(l10n.time_format));
        case 'selection': case 'statusbar':
            // Each choice is [value, label]
            if(_.isArray(value)) {
                 value = value[0]
            }
            var result = _(descriptor.selection).detect(function (choice) {
                return choice[0] === value;
            });
            if (result) { return result[1]; }
            return;
        default:
            return value;
    }
};

instance.web.parse_value = function (value, descriptor, value_if_empty) {
    var date_pattern = normalize_format(_t.database.parameters.date_format),
        time_pattern = normalize_format(_t.database.parameters.time_format);
    switch (value) {
        case false:
        case "":
            return value_if_empty === undefined ?  false : value_if_empty;
    }
    switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
        case 'integer':
            var tmp;
            do {
                tmp = value;
                value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== value);
            tmp = Number(value);
            if (isNaN(tmp))
                throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value));
            return tmp;
        case 'float':
            var tmp = Number(value);
            if (!isNaN(tmp))
                return tmp;

            var tmp2 = value;
            do {
                tmp = tmp2;
                tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== tmp2);
            var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, ".");
            var parsed = Number(reformatted_value);
            if (isNaN(parsed))
                throw new Error(_.str.sprintf(_t("'%s' is not a correct float"), value));
            return parsed;
        case 'float_time':
            var factor = 1;
            if (value[0] === '-') {
                value = value.slice(1);
                factor = -1;
            }
            var float_time_pair = value.split(":");
            if (float_time_pair.length != 2)
                return factor * instance.web.parse_value(value, {type: "float"});
            var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"});
            var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"});
            return factor * (hours + (minutes / 60));
        case 'progressbar':
            return instance.web.parse_value(value, {type: "float"});
        case 'datetime':
            var datetime = Date.parseExact(
                    value, (date_pattern + ' ' + time_pattern));
            if (datetime !== null)
                return instance.web.datetime_to_str(datetime);
            datetime = Date.parse(value);
            if (datetime !== null)
                return instance.web.datetime_to_str(datetime);
            throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value));
        case 'date':
            var date = Date.parseExact(value, date_pattern);
            if (date !== null)
                return instance.web.date_to_str(date);
            date = Date.parse(value);
            if (date !== null)
                return instance.web.date_to_str(date);
            throw new Error(_.str.sprintf(_t("'%s' is not a correct date"), value));
        case 'time':
            var time = Date.parseExact(value, time_pattern);
            if (time !== null)
                return instance.web.time_to_str(time);
            time = Date.parse(value);
            if (time !== null)
                return instance.web.time_to_str(time);
            throw new Error(_.str.sprintf(_t("'%s' is not a correct time"), value));
    }
    return value;
};

instance.web.auto_str_to_date = function(value, type) {
    try {
        return instance.web.str_to_datetime(value);
    } catch(e) {}
    try {
        return instance.web.str_to_date(value);
    } catch(e) {}
    try {
        return instance.web.str_to_time(value);
    } catch(e) {}
    throw new Error(_.str.sprintf(_t("'%s' is not a correct date, datetime nor time"), value));
};

instance.web.auto_date_to_str = function(value, type) {
    switch(type) {
        case 'datetime':
            return instance.web.datetime_to_str(value);
        case 'date':
            return instance.web.date_to_str(value);
        case 'time':
            return instance.web.time_to_str(value);
        default:
            throw new Error(_.str.sprintf(_t("'%s' is not convertible to date, datetime nor time"), type));
    }
};

/**
 * performs a half up rounding with arbitrary precision, correcting for float loss of precision
 * See the corresponding float_round() in server/tools/float_utils.py for more info
 * @param {Number} the value to be rounded
 * @param {Number} a non zero precision parameter. eg: 0.01 rounds to two digits.
 */
instance.web.round_precision = function(value, precision){
    if(!value){
        return 0;
    }else if(!precision){
        throw new Error('round_precision(...):  Cannot round value: '+value+' with a precision of zero (or undefined)');
    }
    var normalized_value = value / precision;
    var epsilon_magnitude = Math.log(Math.abs(normalized_value))/Math.log(2);
    var epsilon = Math.pow(2, epsilon_magnitude - 53);
    normalized_value += normalized_value >= 0 ? epsilon : -epsilon;
    var rounded_value = Math.round(normalized_value);
    return rounded_value * precision;
};

/**
 * performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision
 * See the corresponding float_round() in server/tools/float_utils.py for more info
 * @param {Number} the value to be rounded
 * @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14
 */
instance.web.round_decimals = function(value, decimals){
    return instance.web.round_precision(value, Math.pow(10,-decimals));
};

};
<commit_msg>[MERGE] [FIX] format_value: when formating to date, make sure not converting from a datetime string as would make timezones apply and lead to a possible change of day<commit_after>
openerp.web.formats = function(instance) {
var _t = instance.web._t;

/**
 * Intersperses ``separator`` in ``str`` at the positions indicated by
 * ``indices``.
 *
 * ``indices`` is an array of relative offsets (from the previous insertion
 * position, starting from the end of the string) at which to insert
 * ``separator``.
 *
 * There are two special values:
 *
 * ``-1``
 *   indicates the insertion should end now
 * ``0``
 *   indicates that the previous section pattern should be repeated (until all
 *   of ``str`` is consumed)
 *
 * @param {String} str
 * @param {Array<Number>} indices
 * @param {String} separator
 * @returns {String}
 */
instance.web.intersperse = function (str, indices, separator) {
    separator = separator || '';
    var result = [], last = str.length;

    for(var i=0; i<indices.length; ++i) {
        var section = indices[i];
        if (section === -1 || last <= 0) {
            // Done with string, or -1 (stops formatting string)
            break;
        } else if(section === 0 && i === 0) {
            // repeats previous section, which there is none => stop
            break;
        } else if (section === 0) {
            // repeat previous section forever
            //noinspection AssignmentToForLoopParameterJS
            section = indices[--i];
        }
        result.push(str.substring(last-section, last));
        last -= section;
    }

    var s = str.substring(0, last);
    if (s) { result.push(s); }
    return result.reverse().join(separator);
};
/**
 * Insert "thousands" separators in the provided number (which is actually
 * a string)
 *
 * @param {String} num
 * @returns {String}
 */
instance.web.insert_thousand_seps = function (num) {
    var negative = num[0] === '-';
    num = (negative ? num.slice(1) : num);
    return (negative ? '-' : '') + instance.web.intersperse(
        num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
};

/**
 * removes literal (non-format) text from a date or time pattern, as datejs can
 * not deal with literal text in format strings (whatever the format), whereas
 * strftime allows for literal characters
 *
 * @param {String} value original format
 */
instance.web.strip_raw_chars = function (value) {
    var isletter = /[a-zA-Z]/, output = [];
    for(var index=0; index < value.length; ++index) {
        var character = value[index];
        if(isletter.test(character) && (index === 0 || value[index-1] !== '%')) {
            continue;
        }
        output.push(character);
    }
    return output.join('');
};
var normalize_format = function (format) {
    return Date.normalizeFormat(instance.web.strip_raw_chars(format));
};

/**
 * Check with a scary heuristic if the value is a bin_size or not.
 * If not, compute an approximate size out of the base64 encoded string.
 *
 * @param {String} value original format
 */
instance.web.binary_to_binsize = function (value) {
    if (!value) {
        return instance.web.human_size(0);
    }
    if (value.substr(0, 10).indexOf(' ') == -1) {
        // Computing approximate size out of base64 encoded string
        // http://en.wikipedia.org/wiki/Base64#MIME
        return instance.web.human_size(value.length / 1.37);
    } else {
        // already bin_size
        return value;
    }
};

/**
 * Returns a human readable size
 *
 * @param {Number} numner of bytes
 */
instance.web.human_size = function(size) {
    var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(',');
    var i = 0;
    while (size >= 1024) {
        size /= 1024;
        ++i;
    }
    return size.toFixed(2) + ' ' + units[i];
};

/**
 * Formats a single atomic value based on a field descriptor
 *
 * @param {Object} value read from OpenERP
 * @param {Object} descriptor union of orm field and view field
 * @param {Object} [descriptor.widget] widget to use to display the value
 * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
 * @param {Object} [descriptor.digits] used for the formatting of floats
 * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
 */
instance.web.format_value = function (value, descriptor, value_if_empty) {
    // If NaN value, display as with a `false` (empty cell)
    if (typeof value === 'number' && isNaN(value)) {
        value = false;
    }
    //noinspection FallthroughInSwitchStatementJS
    switch (value) {
        case '':
            if (descriptor.type === 'char' || descriptor.type === 'text') {
                return '';
            }
            console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
        case false:
        case undefined:
        case Infinity:
        case -Infinity:
            return value_if_empty === undefined ?  '' : value_if_empty;
    }
    var l10n = _t.database.parameters;
    switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
        case 'id':
            return value.toString();
        case 'integer':
            return instance.web.insert_thousand_seps(
                _.str.sprintf('%d', value));
        case 'float':
            var digits = descriptor.digits ? descriptor.digits : [69,2];
            digits = typeof digits === "string" ? py.eval(digits) : digits;
            var precision = digits[1];
            var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
            formatted[0] = instance.web.insert_thousand_seps(formatted[0]);
            return formatted.join(l10n.decimal_point);
        case 'float_time':
            var pattern = '%02d:%02d';
            if (value < 0) {
                value = Math.abs(value);
                pattern = '-' + pattern;
            }
            var hour = Math.floor(value);
            var min = Math.round((value % 1) * 60);
            if (min == 60){
                min = 0;
                hour = hour + 1;
            }
            return _.str.sprintf(pattern, hour, min);
        case 'many2one':
            // name_get value format
            return value[1] ? value[1].split("\n")[0] : value[1];
        case 'one2many':
        case 'many2many':
            if (typeof value === 'string') {
                return value;
            }
            return _.str.sprintf(_t("(%d records)"), value.length);
        case 'datetime':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);

            return value.toString(normalize_format(l10n.date_format)
                        + ' ' + normalize_format(l10n.time_format));
        case 'date':
            if (typeof(value) == "string")
                value = instance.web.str_to_date(value.substring(0,10));
            return value.toString(normalize_format(l10n.date_format));
        case 'time':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);
            return value.toString(normalize_format(l10n.time_format));
        case 'selection': case 'statusbar':
            // Each choice is [value, label]
            if(_.isArray(value)) {
                 value = value[0]
            }
            var result = _(descriptor.selection).detect(function (choice) {
                return choice[0] === value;
            });
            if (result) { return result[1]; }
            return;
        default:
            return value;
    }
};

instance.web.parse_value = function (value, descriptor, value_if_empty) {
    var date_pattern = normalize_format(_t.database.parameters.date_format),
        time_pattern = normalize_format(_t.database.parameters.time_format);
    switch (value) {
        case false:
        case "":
            return value_if_empty === undefined ?  false : value_if_empty;
    }
    switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) {
        case 'integer':
            var tmp;
            do {
                tmp = value;
                value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== value);
            tmp = Number(value);
            if (isNaN(tmp))
                throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value));
            return tmp;
        case 'float':
            var tmp = Number(value);
            if (!isNaN(tmp))
                return tmp;

            var tmp2 = value;
            do {
                tmp = tmp2;
                tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== tmp2);
            var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, ".");
            var parsed = Number(reformatted_value);
            if (isNaN(parsed))
                throw new Error(_.str.sprintf(_t("'%s' is not a correct float"), value));
            return parsed;
        case 'float_time':
            var factor = 1;
            if (value[0] === '-') {
                value = value.slice(1);
                factor = -1;
            }
            var float_time_pair = value.split(":");
            if (float_time_pair.length != 2)
                return factor * instance.web.parse_value(value, {type: "float"});
            var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"});
            var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"});
            return factor * (hours + (minutes / 60));
        case 'progressbar':
            return instance.web.parse_value(value, {type: "float"});
        case 'datetime':
            var datetime = Date.parseExact(
                    value, (date_pattern + ' ' + time_pattern));
            if (datetime !== null)
                return instance.web.datetime_to_str(datetime);
            datetime = Date.parse(value);
            if (datetime !== null)
                return instance.web.datetime_to_str(datetime);
            throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value));
        case 'date':
            var date = Date.parseExact(value, date_pattern);
            if (date !== null)
                return instance.web.date_to_str(date);
            date = Date.parse(value);
            if (date !== null)
                return instance.web.date_to_str(date);
            throw new Error(_.str.sprintf(_t("'%s' is not a correct date"), value));
        case 'time':
            var time = Date.parseExact(value, time_pattern);
            if (time !== null)
                return instance.web.time_to_str(time);
            time = Date.parse(value);
            if (time !== null)
                return instance.web.time_to_str(time);
            throw new Error(_.str.sprintf(_t("'%s' is not a correct time"), value));
    }
    return value;
};

instance.web.auto_str_to_date = function(value, type) {
    try {
        return instance.web.str_to_datetime(value);
    } catch(e) {}
    try {
        return instance.web.str_to_date(value);
    } catch(e) {}
    try {
        return instance.web.str_to_time(value);
    } catch(e) {}
    throw new Error(_.str.sprintf(_t("'%s' is not a correct date, datetime nor time"), value));
};

instance.web.auto_date_to_str = function(value, type) {
    switch(type) {
        case 'datetime':
            return instance.web.datetime_to_str(value);
        case 'date':
            return instance.web.date_to_str(value);
        case 'time':
            return instance.web.time_to_str(value);
        default:
            throw new Error(_.str.sprintf(_t("'%s' is not convertible to date, datetime nor time"), type));
    }
};

/**
 * performs a half up rounding with arbitrary precision, correcting for float loss of precision
 * See the corresponding float_round() in server/tools/float_utils.py for more info
 * @param {Number} the value to be rounded
 * @param {Number} a non zero precision parameter. eg: 0.01 rounds to two digits.
 */
instance.web.round_precision = function(value, precision){
    if(!value){
        return 0;
    }else if(!precision){
        throw new Error('round_precision(...):  Cannot round value: '+value+' with a precision of zero (or undefined)');
    }
    var normalized_value = value / precision;
    var epsilon_magnitude = Math.log(Math.abs(normalized_value))/Math.log(2);
    var epsilon = Math.pow(2, epsilon_magnitude - 53);
    normalized_value += normalized_value >= 0 ? epsilon : -epsilon;
    var rounded_value = Math.round(normalized_value);
    return rounded_value * precision;
};

/**
 * performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision
 * See the corresponding float_round() in server/tools/float_utils.py for more info
 * @param {Number} the value to be rounded
 * @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14
 */
instance.web.round_decimals = function(value, decimals){
    return instance.web.round_precision(value, Math.pow(10,-decimals));
};

};
<|endoftext|>


<commit_before>/*global contract, config, it, web3*/
const assert = require('assert');
const AnotherStorage = require('Embark/contracts/AnotherStorage');
const SimpleStorage = require('Embark/contracts/SimpleStorage');

let accounts;

config({
  deployment: {
    "accounts": [
      {
        "mnemonic": "example exile argue silk regular smile grass bomb merge arm assist farm",
        balance: "5ether"
      }
    ],
    "host": "localhost",
    "port": 8545,
    "type": "rpc"
  },
  contracts: {
    "SimpleStorage": {
      args: [100]
    },
    "AnotherStorage": {
      args: ["$SimpleStorage"]
    }
  }
}, (err, theAccounts) => {
  accounts = theAccounts;
});

contract("AnotherStorage", function() {
  this.timeout(0);

  it("should have account with balance", async function() {
    let balance = await web3.eth.getBalance(accounts[0]);
    assert.ok(parseInt(balance, 10) > 4900000000000000000);
    assert.ok(parseInt(balance, 10) <= 5000000000000000000);
  });

  it("set SimpleStorage address", async function() {
    let result = await AnotherStorage.methods.simpleStorageAddress().call();
    assert.equal(result.toString(), SimpleStorage.options.address);
  });

  it("set SimpleStorage address with alternative syntax", async function() {
    let result = await AnotherStorage.simpleStorageAddress();
    assert.equal(result.toString(), SimpleStorage.options.address);
  });
});
<commit_msg>Remove test<commit_after>/*global contract, config, it, web3*/
const assert = require('assert');
const AnotherStorage = require('Embark/contracts/AnotherStorage');
const SimpleStorage = require('Embark/contracts/SimpleStorage');

let accounts;

config({
  deployment: {
    "accounts": [
      {
        "mnemonic": "example exile argue silk regular smile grass bomb merge arm assist farm",
        balance: "5ether"
      }
    ]
  },
  contracts: {
    "SimpleStorage": {
      args: [100]
    },
    "AnotherStorage": {
      args: ["$SimpleStorage"]
    }
  }
}, (err, theAccounts) => {
  accounts = theAccounts;
});

contract("AnotherStorage", function() {
  this.timeout(0);

  it("should have account with balance", async function() {
    let balance = await web3.eth.getBalance(accounts[0]);
    assert.ok(parseInt(balance, 10) > 4900000000000000000);
    assert.ok(parseInt(balance, 10) <= 5000000000000000000);
  });

  it("set SimpleStorage address", async function() {
    let result = await AnotherStorage.methods.simpleStorageAddress().call();
    assert.equal(result.toString(), SimpleStorage.options.address);
  });

  it("set SimpleStorage address with alternative syntax", async function() {
    let result = await AnotherStorage.simpleStorageAddress();
    assert.equal(result.toString(), SimpleStorage.options.address);
  });
});
<|endoftext|>


<commit_before>'use strict';

var DeployPlugin = require('ember-cli-deploy-plugin');
var path = require('path');

module.exports = DeployPlugin.extend({
  defaultConfig: {
    environment: 'production',
    outputPath: path.join('tmp', 'fastboot-dist')
  },

  build: function() {
    var outputPath = this.readConfig('outputPath');
    var self = this;

    return this.buildFastBoot(outputPath)
      .then(function(files) {
        return {
          fastbootDistDir: outputPath,
          fastbootDistFiles: files || []
        };
      })
      .catch(function(error) {
        self.log('build failed', { color: 'red' });
        return Promise.reject(error);
      });
  },

  didBuild: function(context) {
    var fs = require('fs');
    // Rewrite FastBoot index.html assets
    try {
      var browserAssetMap = JSON.parse(fs.readFileSync(context.distDir + '/assets/assetMap.json'));
      var fastBootAssetMap = JSON.parse(fs.readFileSync(context.fastbootDistDir + '/assets/assetMap.json'));
      var prepend = browserAssetMap.prepend;

      var indexHTML = fs.readFileSync(context.fastbootDistDir + '/index.html').toString();
      var newAssets = browserAssetMap.assets;
      var oldAssets = fastBootAssetMap.assets;

      for (var key in oldAssets) {
        var value = oldAssets[key];
        console.log(value);
        indexHTML = indexHTML.replace(prepend + value, prepend + newAssets[key]);
      }

      fs.writeFileSync(context.fastbootDistDir + '/index.html', indexHTML);
    } catch(e) {
      this.log('unable to rewrite assets: ' + e.stack, { verbose: true });
    }
  },

  buildFastBoot: function(outputPath) {
    var buildEnv   = this.readConfig('environment');

    this.log('building fastboot app to `' + outputPath + '` using buildEnv `' + buildEnv + '`...', { verbose: true });

    process.env.EMBER_CLI_FASTBOOT = true;

    var Builder  = this.project.require('ember-cli/lib/models/builder');

    var builder = new Builder({
      ui: this.ui,
      outputPath: outputPath,
      environment: buildEnv,
      project: this.project
    });

    return builder.build()
      .finally(function() {
        process.env.EMBER_CLI_FASTBOOT = false;
        return builder.cleanup();
      })
      .then(this._logSuccess.bind(this, outputPath));
  },

  _logSuccess: function(outputPath) {
    var glob = require('glob');
    var self = this;
    var files = glob.sync('**/**/*', { nonull: false, nodir: true, cwd: outputPath });
    var Promise = require('rsvp').Promise;

    if (files && files.length) {
      files.forEach(function(path) {
        self.log('✔  ' + path, { verbose: true });
      });
    }
    self.log('fastboot build ok', { verbose: true });

    return Promise.resolve(files);
  }
});

<commit_msg>allow ember-cli-fastboot-build to run without ember-cli-build<commit_after>'use strict';

var DeployPlugin = require('ember-cli-deploy-plugin');
var path = require('path');
var fs = require('fs');

module.exports = DeployPlugin.extend({
  defaultConfig: {
    environment: 'production',
    outputPath: path.join('tmp', 'fastboot-dist')
  },

  build: function() {
    var outputPath = this.readConfig('outputPath');
    var self = this;

    return this.buildFastBoot(outputPath)
      .then(function(files) {
        var distDir = self.readConfig('distDir');
        if (!fs.existsSync(distDir)) {
          //this is the scenario where there is only the fastboot build
          //and no traditional ember build step in the build pipeline
          //in that case the build files should go into the primary dist properties so downstream
          //steps don't have to look in a different place
          return {
            distDir: outputPath,
            distFiles: files || []
          };
        } else {
          return {
            fastbootDistDir: outputPath,
            fastbootDistFiles: files || []
          };
        }
      })
      .catch(function(error) {
        self.log('build failed', { color: 'red' });
        return Promise.reject(error);
      });
  },

  didBuild: function(context) {
    // For the scenario where users are opting to perform solely a fastboot build and no ember build step
    // lets not do rewriting

    if (!context.fastbootDistDir) { return; }

    var emberBuildAssetMapPath = path.join(context.distDir, 'assets/assetMap.json');
    var fastbootBuildAsssetMapPath = path.join(context.fastbootDistDir, 'assets/assetMap.json');

    // Rewrite FastBoot index.html assets
    try {
      var browserAssetMap = JSON.parse(fs.readFileSync(emberBuildAssetMapPath));
      var fastBootAssetMap = JSON.parse(fs.readFileSync(fastbootBuildAsssetMapPath));
      var fastbootIndexHtmlPath = path.join(context.fastbootDistDir, 'index.html');
      var prepend = browserAssetMap.prepend;

      var indexHTML = fs.readFileSync(fastbootIndexHtmlPath);
      var newAssets = browserAssetMap.assets;
      var oldAssets = fastBootAssetMap.assets;

      for (var key in oldAssets) {
        var value = oldAssets[key];
        console.log(value);
        indexHTML = indexHTML.replace(prepend + value, prepend + newAssets[key]);
      }

      fs.writeFileSync(fastbootIndexHtmlPath, indexHTML);
    } catch(e) {
      this.log('unable to rewrite assets: ' + e.stack, { verbose: true });
    }
  },

  buildFastBoot: function(outputPath) {
    var buildEnv   = this.readConfig('environment');

    this.log('building fastboot app to `' + outputPath + '` using buildEnv `' + buildEnv + '`...', { verbose: true });

    process.env.EMBER_CLI_FASTBOOT = true;

    var Builder  = this.project.require('ember-cli/lib/models/builder');

    var builder = new Builder({
      ui: this.ui,
      outputPath: outputPath,
      environment: buildEnv,
      project: this.project
    });

    return builder.build()
      .finally(function() {
        process.env.EMBER_CLI_FASTBOOT = false;
        return builder.cleanup();
      })
      .then(this._logSuccess.bind(this, outputPath));
  },

  _logSuccess: function(outputPath) {
    var glob = require('glob');
    var self = this;
    var files = glob.sync('**/**/*', { nonull: false, nodir: true, cwd: outputPath });
    var Promise = require('rsvp').Promise;

    if (files && files.length) {
      files.forEach(function(path) {
        self.log('✔  ' + path, { verbose: true });
      });
    }
    self.log('fastboot build ok', { verbose: true });

    return Promise.resolve(files);
  }
});

<|endoftext|>


<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.tags.link");
pimcore.document.tags.link = Class.create(pimcore.document.tag, {

    initialize: function(id, name, options, data, inherited) {

        if (!data) {
            data = {};
        }

        this.defaultData = {
            type: "internal",
            path: "",
            parameters: "",
            anchor: "",
            accesskey: "",
            rel: "",
            tabindex: "",
            target: "",
            "class": "",
            attributes: ""
        };

        this.data = mergeObject(this.defaultData, data);

        this.id = id;
        this.name = name;
        this.setupWrapper();
        this.options = this.parseOptions(options);

        Ext.get(id).setStyle({
            display:"inline"
        });
        Ext.get(id).insertHtml("beforeEnd",'' + this.getLinkContent() + '');

        var button = new Ext.Button({
            iconCls: "pimcore_icon_edit_link",
            cls: "pimcore_edit_link_button",
            listeners: {
                "click": this.openEditor.bind(this)
            }
        });
        button.render(id);
    },

    openEditor: function () {
        this.window = pimcore.helpers.editmode.openLinkEditPanel(this.data, {
            empty: this.empty.bind(this),
            cancel: this.cancel.bind(this),
            save: this.save.bind(this)
        });
    },

    getLinkContent: function () {

        var text = "[" + t("not_set") + "]";
        if (this.data.text) {
            text = this.data.text;
        }
        if (this.data.path) {
            return '' + text + '';
        }
        return text;
    },

    onNodeDrop: function (target, dd, e, data) {

        if(this.dndAllowed(data)){
            this.fieldPath.setValue(data.node.attributes.path);
            return true;
        } else {
            return false;
        }
    },

    onNodeOver: function(target, dd, e, data) {
        if (this.dndAllowed(data)) {
            return Ext.dd.DropZone.prototype.dropAllowed;
        }
        else {
            return Ext.dd.DropZone.prototype.dropNotAllowed;
        }
    },

    dndAllowed: function(data) {

        if (data.node.attributes.elementType == "asset" && data.node.attributes.type != "folder") {
            return true;
        } else if (data.node.attributes.elementType == "document"
                            && (data.node.attributes.type=="page" || data.node.attributes.type=="hardlink"
                                                                            || data.node.attributes.type=="link")){
            return true;
        }
        return false;

    },

    save: function () {

        // close window
        this.window.close();

        var values = this.window.getComponent("form").getForm().getFieldValues();
        this.data = values;

        // set text
        Ext.get(this.id).query(".pimcore_tag_link_text")[0].innerHTML = this.getLinkContent();

        this.reload();
    },

    reload : function () {
        if (this.options.reload) {
            this.reloadDocument();
        }
    },

    empty: function () {

        // close window
        this.window.close();

        this.data = this.defaultData;

        // set text
        Ext.get(this.id).query(".pimcore_tag_link_text")[0].innerHTML = this.getLinkContent();
    },

    cancel: function () {
        this.window.close();
    },

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

    getType: function () {
        return "link";
    }
});<commit_msg>link dialog, use window.close() instead of .hide();: bugfix<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.tags.link");
pimcore.document.tags.link = Class.create(pimcore.document.tag, {

    initialize: function(id, name, options, data, inherited) {

        if (!data) {
            data = {};
        }

        this.defaultData = {
            type: "internal",
            path: "",
            parameters: "",
            anchor: "",
            accesskey: "",
            rel: "",
            tabindex: "",
            target: "",
            "class": "",
            attributes: ""
        };

        this.data = mergeObject(this.defaultData, data);

        this.id = id;
        this.name = name;
        this.setupWrapper();
        this.options = this.parseOptions(options);

        Ext.get(id).setStyle({
            display:"inline"
        });
        Ext.get(id).insertHtml("beforeEnd",'' + this.getLinkContent() + '');

        var button = new Ext.Button({
            iconCls: "pimcore_icon_edit_link",
            cls: "pimcore_edit_link_button",
            listeners: {
                "click": this.openEditor.bind(this)
            }
        });
        button.render(id);
    },

    openEditor: function () {
        this.window = pimcore.helpers.editmode.openLinkEditPanel(this.data, {
            empty: this.empty.bind(this),
            cancel: this.cancel.bind(this),
            save: this.save.bind(this)
        });
    },

    getLinkContent: function () {

        var text = "[" + t("not_set") + "]";
        if (this.data.text) {
            text = this.data.text;
        }
        if (this.data.path) {
            return '' + text + '';
        }
        return text;
    },

    onNodeDrop: function (target, dd, e, data) {

        if(this.dndAllowed(data)){
            this.fieldPath.setValue(data.node.attributes.path);
            return true;
        } else {
            return false;
        }
    },

    onNodeOver: function(target, dd, e, data) {
        if (this.dndAllowed(data)) {
            return Ext.dd.DropZone.prototype.dropAllowed;
        }
        else {
            return Ext.dd.DropZone.prototype.dropNotAllowed;
        }
    },

    dndAllowed: function(data) {

        if (data.node.attributes.elementType == "asset" && data.node.attributes.type != "folder") {
            return true;
        } else if (data.node.attributes.elementType == "document"
                            && (data.node.attributes.type=="page" || data.node.attributes.type=="hardlink"
                                                                            || data.node.attributes.type=="link")){
            return true;
        }
        return false;

    },

    save: function () {

        var values = this.window.getComponent("form").getForm().getFieldValues();
        this.data = values;

        // close window
        this.window.close();

        // set text
        Ext.get(this.id).query(".pimcore_tag_link_text")[0].innerHTML = this.getLinkContent();

        this.reload();
    },

    reload : function () {
        if (this.options.reload) {
            this.reloadDocument();
        }
    },

    empty: function () {

        // close window
        this.window.close();

        this.data = this.defaultData;

        // set text
        Ext.get(this.id).query(".pimcore_tag_link_text")[0].innerHTML = this.getLinkContent();
    },

    cancel: function () {
        this.window.close();
    },

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

    getType: function () {
        return "link";
    }
});<|endoftext|>


<commit_before>/**
 *

 *  Constructs a ProAct.ObjectCore. ProAct.ObjectCore is a {@link ProAct.Core} that manages all the {@link ProAct.Property} instances for a reactive ProAct.js object.
 *

 *

 *  It is responsible for all the {@link ProAct.Property} instances as well initializing them and deciding which type of property corresponds to which field.
 *

 *

 *  ProAct.ObjectCore is part of the properties module of ProAct.js.
 *

 *
 * @class ProAct.ObjectCore
 * @extends ProAct.Core
 * @param {Object} object
 *      The shell objec arround this core. This should be plain JavaScript object.
 * @param {Object} meta
 *      Optional meta data to be used to define the observer-observable behavior of the object. For example transformations for its properties.
 * @see {@link ProAct.Property}
 */
ProAct.ObjectCore = P.OC = function (object, meta) {
  this.properties = {};

  P.C.call(this, object, meta); // Super!
};

ProAct.ObjectCore.prototype = P.U.ex(Object.create(P.C.prototype), {

  /**
   * Reference to the constructor of this object.
   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @constant
   * @default ProAct.ObjectCore
   */
  constructor: ProAct.ObjectCore,

  /**
   * A function to be set to the shell object's p field (if it is configured in @{link ProAct.Configuration}).
   *

   *  It uses its p argument if it is string to return the right {@link ProAct.Property} for passed field name.
   *

   *

   *  If the p argument is * or empty this ProAct.ObjectCore instance is returned.
   *

   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method value
   * @param {String} p
   *      The name of the managed {@link ProAct.Property} to retrieve. It can be set to * or skipped for this itself to be retrieved.
   * @return {Object}
   *      Managed {@link ProAct.Property} instance with field name equal to the passed p parameter or this.
   * @see {@link ProAct.Property}
   */
  value: function (p) {
    if (!p || p === '*') {
      return this;
    }

    return this.properties[p];
  },

  /**
   * Initializes all the {@link ProAct.Property} instances for the shellof this ProAct.ObjectCore.
   *

   *  Using the types of the fields of the shell object the right {@link ProAct.Property} instances are created and stored
   *  in this using {@link ProAct.ObjectCore#makeProp}.
   *

   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method setup
   * @see {@link ProAct.ObjectCore#makeProp}
   */
  setup: function () {
    var object = this.shell,
        property;

    for (property in object) {
      this.makeProp(property, null, this.meta[property]);
    }
  },

  /**
   * Creates a {@link ProAct.Property} instance for this's shell.
   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method makeProp
   * @param {String} property
   *      The name of the property, the name of the field in the shell.
   * @param {Array} listeners
   *      Initial listeners for 'change' of the property, can be skipped.
   * @param {String|Array} meta
   *      Meta information for the property to create, for example if the meta contains 'noprop', no property is created,
   *      and the initial value of the field is preserved. The meta is in format of the {@link ProAct.DSL}.
   * @return {ProAct.Property}
   *      The newly crated and stored in this property, or null if no property was created.
   * @throws {Error}
   *      If there is no field defined in the shell named as the passed property.
   * @see {@link ProAct.ObjectCore#setup}
   * @see {@link ProAct.DSL}
   */
  makeProp: function (property, listeners, meta) {
    var object = this.shell,
        conf = ProAct.Configuration,
        keyprops = conf.keyprops,
        keypropList = conf.keypropList,
        isF = P.U.isFunction,
        isA = P.U.isArrayObject,
        isO = P.U.isObject,
        result;

    if (meta && (meta === 'noprop' || (meta.indexOf && meta.indexOf('noprop') >= 0))) {
      return null;
    }

    if (keyprops && keypropList.indexOf(property) !== -1) {
      throw Error('The property name ' + property + ' is a key word for pro objects! Objects passed to ProAct.prob can not contain properties named as keyword properties.');
      return null;
    }

    if (object.hasOwnProperty(property)) {
      result = P.PP.provide(this.queueName, object, property, meta);
    }

    if (!result) {
      return null;
    }

    if (listeners) {
      this.properties[property].listeners.change = this.properties[property].listeners.change.concat(listeners);
    }

    if (meta && P.registry) {
      if (!P.U.isArray(meta)) {
        meta = [meta];
      }

      if (!(meta[0] instanceof ProAct.Property)) {
        P.registry.setup.apply(P.registry, [result].concat(meta));
      }
    }

    return result;
  },

  /**
   * Sets the value of a managed property. The interesting thing here is that
   * if the property does not exist this method creates it and stores a new field in the shell object
   * with the passed value.
   *

   *  The new field is reactive.
   *

   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method set
   * @param {String} property
   *      The name of the property to update/create.
   * @param {Object} value
   *      The value of the property to be set.
   * @see {@link ProAct.ObjectCore#makeProp}
   */
  set: function (property, value) {
    var object = this.shell;

    object[property] = value;
    if (this.properties[property]) {
      return;
    }

    this.makeProp(property);
  }
});
<commit_msg>ObjectCore updated to use the new def syntax.<commit_after>/**
 *

 *  Constructs a ProAct.ObjectCore. ProAct.ObjectCore is a {@link ProAct.Core} that manages all the {@link ProAct.Property} instances for a reactive ProAct.js object.
 *

 *

 *  It is responsible for all the {@link ProAct.Property} instances as well initializing them and deciding which type of property corresponds to which field.
 *

 *

 *  ProAct.ObjectCore is part of the properties module of ProAct.js.
 *

 *
 * @class ProAct.ObjectCore
 * @extends ProAct.Core
 * @param {Object} object
 *      The shell objec arround this core. This should be plain JavaScript object.
 * @param {Object} meta
 *      Optional meta data to be used to define the observer-observable behavior of the object. For example transformations for its properties.
 * @see {@link ProAct.Property}
 */
function ObjectCore (object, meta) {
  this.properties = {};

  P.C.call(this, object, meta); // Super!
};
ProAct.ObjectCore = P.OC = ObjectCore;

ProAct.ObjectCore.prototype = P.U.ex(Object.create(P.C.prototype), {

  /**
   * Reference to the constructor of this object.
   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @constant
   * @default ProAct.ObjectCore
   */
  constructor: ProAct.ObjectCore,

  /**
   * A function to be set to the shell object's p field (if it is configured in @{link ProAct.Configuration}).
   *

   *  It uses its p argument if it is string to return the right {@link ProAct.Property} for passed field name.
   *

   *

   *  If the p argument is * or empty this ProAct.ObjectCore instance is returned.
   *

   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method value
   * @param {String} p
   *      The name of the managed {@link ProAct.Property} to retrieve. It can be set to * or skipped for this itself to be retrieved.
   * @return {Object}
   *      Managed {@link ProAct.Property} instance with field name equal to the passed p parameter or this.
   * @see {@link ProAct.Property}
   */
  value: function (p) {
    if (!p || p === '*') {
      return this;
    }

    return this.properties[p];
  },

  /**
   * Initializes all the {@link ProAct.Property} instances for the shellof this ProAct.ObjectCore.
   *

   *  Using the types of the fields of the shell object the right {@link ProAct.Property} instances are created and stored
   *  in this using {@link ProAct.ObjectCore#makeProp}.
   *

   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method setup
   * @see {@link ProAct.ObjectCore#makeProp}
   */
  setup: function () {
    var object = this.shell,
        property;

    for (property in object) {
      this.makeProp(property, null, this.meta[property]);
    }
  },

  /**
   * Creates a {@link ProAct.Property} instance for this's shell.
   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method makeProp
   * @param {String} property
   *      The name of the property, the name of the field in the shell.
   * @param {Array} listeners
   *      Initial listeners for 'change' of the property, can be skipped.
   * @param {String|Array} meta
   *      Meta information for the property to create, for example if the meta contains 'noprop', no property is created,
   *      and the initial value of the field is preserved. The meta is in format of the {@link ProAct.DSL}.
   * @return {ProAct.Property}
   *      The newly crated and stored in this property, or null if no property was created.
   * @throws {Error}
   *      If there is no field defined in the shell named as the passed property.
   * @see {@link ProAct.ObjectCore#setup}
   * @see {@link ProAct.DSL}
   */
  makeProp: function (property, listeners, meta) {
    var object = this.shell,
        conf = ProAct.Configuration,
        keyprops = conf.keyprops,
        keypropList = conf.keypropList,
        isF = P.U.isFunction,
        isA = P.U.isArrayObject,
        isO = P.U.isObject,
        result;

    if (meta && (meta === 'noprop' || (meta.indexOf && meta.indexOf('noprop') >= 0))) {
      return null;
    }

    if (keyprops && keypropList.indexOf(property) !== -1) {
      throw Error('The property name ' + property + ' is a key word for pro objects! Objects passed to ProAct.prob can not contain properties named as keyword properties.');
      return null;
    }

    if (object.hasOwnProperty(property)) {
      result = P.PP.provide(this.queueName, object, property, meta);
    }

    if (!result) {
      return null;
    }

    if (listeners) {
      this.properties[property].listeners.change = this.properties[property].listeners.change.concat(listeners);
    }

    if (meta && P.registry) {
      if (!P.U.isArray(meta)) {
        meta = [meta];
      }

      if (!(meta[0] instanceof ProAct.Property)) {
        P.registry.setup.apply(P.registry, [result].concat(meta));
      }
    }

    return result;
  },

  /**
   * Sets the value of a managed property. The interesting thing here is that
   * if the property does not exist this method creates it and stores a new field in the shell object
   * with the passed value.
   *

   *  The new field is reactive.
   *

   *
   * @memberof ProAct.ObjectCore
   * @instance
   * @method set
   * @param {String} property
   *      The name of the property to update/create.
   * @param {Object} value
   *      The value of the property to be set.
   * @see {@link ProAct.ObjectCore#makeProp}
   */
  set: function (property, value) {
    var object = this.shell;

    object[property] = value;
    if (this.properties[property]) {
      return;
    }

    this.makeProp(property);
  }
});
<|endoftext|>


<commit_before>//  Copyright 2002-2014, University of Colorado Boulder

/**
 * A physical-looking probe with a handle and a semicircular sensor region, used in simulations like Bending Light and
 * Beer's Law Lab to show how much light is being received. It is typically connected to a body with readouts with a
 * curved wire. The origin is in the center of the circular part of the sensor (which is not vertically symmetrical).
 *
 * This code was generalized from Bending Light, see https://github.com/phetsims/bending-light/issues/165
 *
 * TODO: This is under development and not ready for review or usage in simulations other than Bending Light.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chandrashekar Bemagoni (Actual Concepts)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Shape = require( 'KITE/Shape' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Vector2 = require( 'DOT/Vector2' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var RadialGradient = require( 'SCENERY/util/RadialGradient' );
  var Color = require( 'SCENERY/util/Color' );
  var Line = require( 'SCENERY/nodes/Line' );

  var glass = function( options ) {
    var GLASS_DEFAULTS = {
      centerColor: 'white',
      middleColor: '#E6F5FF', // light blue
      edgeColor: '#C2E7FF'    // slightly darker blue, like glass
    };
    var options = _.extend( GLASS_DEFAULTS, options );
    return function( radius ) {
      return new Path( new Shape().ellipticalArc( 0, 0, radius * 0.35 * 2, radius * 0.35 * 2, Math.PI, 0, Math.PI * 2, false ), {
        fill: new RadialGradient( -radius * 0.15, -radius * 0.15, 0, -radius * 0.15, -radius * 0.20, radius * 0.60 )
          .addColorStop( 0, options.centerColor )
          .addColorStop( 0.4, options.middleColor ) // light blue
          .addColorStop( 1, options.edgeColor ), // slightly darker blue, like glass
        centerX: 0
      } );
    };
  };

  var crosshairs = function( options ) {
    var CROSSHAIRS_DEFAULTS = {
      stroke: 'black',
      lineWidth: 3,

      // The amount of blank space visible at the intersection of the 2 crosshairs lines
      intersectionRadius: 8
    };
    options = _.extend( CROSSHAIRS_DEFAULTS, options );
    return function( radius ) {

      var lineOptions = { stroke: options.stroke, lineWidth: options.lineWidth };
      return new Node( {
        children: [
          new Line( -radius, 0, -options.intersectionRadius, 0, lineOptions ),
          new Line( +radius, 0, +options.intersectionRadius, 0, lineOptions ),
          new Line( 0, -radius, 0, -options.intersectionRadius, lineOptions ),
          new Line( 0, +radius, 0, +options.intersectionRadius, lineOptions ) ]
      } );
    };
  };

  // constants
  var DEFAULT_OPTIONS = {
    radius: 50,
    innerRadius: 35,
    handleWidth: 50,
    handleHeight: 30,
    handleCornerRadius: 30,
    color: '#008541', // darkish green

    // The circular part of the ProbeNode is called the sensor, where it receives light or has crosshairs, etc.
    // or null for an empty region
    //sensorType: Glass()
    sensorType: crosshairs()
  };

  /**
   * Constructor for the ProbeNode
   * @param {Object} [options]
   * @constructor
   */
  function ProbeNode( options ) {

    options = _.extend( _.clone( DEFAULT_OPTIONS ), options );

    assert && assert( options.sensorType === 'glass' || options.sensorType === 'empty' || options.sensorType === 'crosshairs',
      'unsupported type for sensorType' );

    var color = new Color( options.color );

    // To improve readability
    var radius = options.radius;

    // the top of the handle, below the circle at the top of the sensor
    var handleBottom = radius + options.handleHeight;

    // The shape of the outer body, circular at top with a handle at the bottom
    var arcExtent = 0.8;
    var handleWidth = options.handleWidth;
    var innerRadius = Math.min( options.innerRadius, options.radius );
    var sensorShape = new Shape()

    // start in the bottom center
      .moveTo( 0, handleBottom )

      .lineTo( -handleWidth / 2, handleBottom )
      .lineTo( -handleWidth / 2, radius )

      // Top arc
      .ellipticalArc( 0, 0, radius, radius, 0, Math.PI * arcExtent, Math.PI * (1 - arcExtent), false )

      .lineTo( handleWidth / 2, radius )
      .lineTo( handleWidth / 2, handleBottom )

      .lineTo( 0, handleBottom )
      .moveTo( innerRadius, 0 )
      .arc( 0, 0, innerRadius, Math.PI * 2, 0, true )
      .close();

    var outerShapePath = new Path( sensorShape, {
      stroke: new LinearGradient( -radius / 2, -radius / 2, -radius / 2, handleBottom )
        .addColorStop( 0.0, color.colorUtilsBrightness( -0.1 ) ) // dark
        .addColorStop( 1.0, color.colorUtilsBrightness( -0.2 ) ), // darker
      fill: new LinearGradient( -radius / 2, -radius / 2, -radius / 2, handleBottom )
        .addColorStop( 0.0, color.colorUtilsBrightness( +0.4 ) ) // light
        .addColorStop( 0.3, color.colorUtilsBrightness( +0.2 ) ) // medium light
        .addColorStop( 1.0, color.colorUtilsBrightness( -0.1 ) ), // less light
      lineWidth: 2
    } );

    // the front flat "surface" of the sensor, makes it look 3d by putting a shiny glare on the top edge
    var innerPath = new Path( sensorShape, {
      fill: options.color,
      lineWidth: 2,
      scale: new Vector2( 0.9, 0.93 ),
      centerX: outerShapePath.centerX,

      // Shift it down a bit to make the face look a bit more 3d
      y: 2
    } );

    var children = [];

    if ( options.sensorType ) {
      children.push( options.sensorType( radius ) );
    }

    children.push( outerShapePath, innerPath );

    Node.call( this, {
      children: children
    } );

    this.mutate( options );
  }

  return inherit( Node, ProbeNode, {},

    // statics
    {

      // @public {read-only}, make the defaults publicly available to clients in case they need to make
      // customizations, such as 0.9 x the default width
      DEFAULT_OPTIONS: DEFAULT_OPTIONS,

      // Sensor types
      // @public
      crosshairs: crosshairs,
      glass: glass
    } );
} );<commit_msg>Use glass options and converted from Path to Circle, see https://github.com/phetsims/bending-light/issues/165<commit_after>//  Copyright 2002-2014, University of Colorado Boulder

/**
 * A physical-looking probe with a handle and a semicircular sensor region, used in simulations like Bending Light and
 * Beer's Law Lab to show how much light is being received. It is typically connected to a body with readouts with a
 * curved wire. The origin is in the center of the circular part of the sensor (which is not vertically symmetrical).
 *
 * This code was generalized from Bending Light, see https://github.com/phetsims/bending-light/issues/165
 *
 * TODO: This is under development and not ready for review or usage in simulations other than Bending Light.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chandrashekar Bemagoni (Actual Concepts)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Shape = require( 'KITE/Shape' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Vector2 = require( 'DOT/Vector2' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var RadialGradient = require( 'SCENERY/util/RadialGradient' );
  var Color = require( 'SCENERY/util/Color' );
  var Line = require( 'SCENERY/nodes/Line' );
  var Circle = require( 'SCENERY/nodes/Circle' );

  var glass = function( options ) {
    var GLASS_DEFAULTS = {
      centerColor: 'white',
      middleColor: '#E6F5FF', // light blue
      edgeColor: '#C2E7FF'    // slightly darker blue, like glass
    };
    options = _.extend( GLASS_DEFAULTS, options );
    return function( radius ) {
      return new Circle( radius, {
        fill: new RadialGradient( -radius * 0.15, -radius * 0.15, 0, -radius * 0.15, -radius * 0.20, radius * 0.60 )
          .addColorStop( 0, options.centerColor )
          .addColorStop( 0.4, options.middleColor ) // light blue
          .addColorStop( 1, options.edgeColor ) // slightly darker blue, like glass
      } );
    };
  };

  var crosshairs = function( options ) {
    var CROSSHAIRS_DEFAULTS = {
      stroke: 'black',
      lineWidth: 3,

      // The amount of blank space visible at the intersection of the 2 crosshairs lines
      intersectionRadius: 8
    };
    options = _.extend( CROSSHAIRS_DEFAULTS, options );
    return function( radius ) {

      var lineOptions = { stroke: options.stroke, lineWidth: options.lineWidth };
      return new Node( {
        children: [
          new Line( -radius, 0, -options.intersectionRadius, 0, lineOptions ),
          new Line( +radius, 0, +options.intersectionRadius, 0, lineOptions ),
          new Line( 0, -radius, 0, -options.intersectionRadius, lineOptions ),
          new Line( 0, +radius, 0, +options.intersectionRadius, lineOptions ) ]
      } );
    };
  };

  // constants
  var DEFAULT_OPTIONS = {
    radius: 50,
    innerRadius: 35,
    handleWidth: 50,
    handleHeight: 30,
    handleCornerRadius: 30,
    color: '#008541', // darkish green

    // The circular part of the ProbeNode is called the sensor, where it receives light or has crosshairs, etc.
    // or null for an empty region
    sensorType: glass()
    //sensorType: crosshairs()
  };

  /**
   * Constructor for the ProbeNode
   * @param {Object} [options]
   * @constructor
   */
  function ProbeNode( options ) {

    options = _.extend( _.clone( DEFAULT_OPTIONS ), options );

    assert && assert( options.sensorType === 'glass' || options.sensorType === 'empty' || options.sensorType === 'crosshairs',
      'unsupported type for sensorType' );

    var color = new Color( options.color );

    // To improve readability
    var radius = options.radius;

    // the top of the handle, below the circle at the top of the sensor
    var handleBottom = radius + options.handleHeight;

    // The shape of the outer body, circular at top with a handle at the bottom
    var arcExtent = 0.8;
    var handleWidth = options.handleWidth;
    var innerRadius = Math.min( options.innerRadius, options.radius );
    var sensorShape = new Shape()

    // start in the bottom center
      .moveTo( 0, handleBottom )

      .lineTo( -handleWidth / 2, handleBottom )
      .lineTo( -handleWidth / 2, radius )

      // Top arc
      .ellipticalArc( 0, 0, radius, radius, 0, Math.PI * arcExtent, Math.PI * (1 - arcExtent), false )

      .lineTo( handleWidth / 2, radius )
      .lineTo( handleWidth / 2, handleBottom )

      .lineTo( 0, handleBottom )
      .moveTo( innerRadius, 0 )
      .arc( 0, 0, innerRadius, Math.PI * 2, 0, true )
      .close();

    var outerShapePath = new Path( sensorShape, {
      stroke: new LinearGradient( -radius / 2, -radius / 2, -radius / 2, handleBottom )
        .addColorStop( 0.0, color.colorUtilsBrightness( -0.1 ) ) // dark
        .addColorStop( 1.0, color.colorUtilsBrightness( -0.2 ) ), // darker
      fill: new LinearGradient( -radius / 2, -radius / 2, -radius / 2, handleBottom )
        .addColorStop( 0.0, color.colorUtilsBrightness( +0.4 ) ) // light
        .addColorStop( 0.3, color.colorUtilsBrightness( +0.2 ) ) // medium light
        .addColorStop( 1.0, color.colorUtilsBrightness( -0.1 ) ), // less light
      lineWidth: 2
    } );

    // the front flat "surface" of the sensor, makes it look 3d by putting a shiny glare on the top edge
    var innerPath = new Path( sensorShape, {
      fill: options.color,
      lineWidth: 2,
      scale: new Vector2( 0.9, 0.93 ),
      centerX: outerShapePath.centerX,

      // Shift it down a bit to make the face look a bit more 3d
      y: 2
    } );

    var children = [];

    if ( options.sensorType ) {
      children.push( options.sensorType( radius ) );
    }

    children.push( outerShapePath, innerPath );

    Node.call( this, {
      children: children
    } );

    this.mutate( options );
  }

  return inherit( Node, ProbeNode, {},

    // statics
    {

      // @public {read-only}, make the defaults publicly available to clients in case they need to make
      // customizations, such as 0.9 x the default width
      DEFAULT_OPTIONS: DEFAULT_OPTIONS,

      // Sensor types
      // @public
      crosshairs: crosshairs,
      glass: glass
    } );
} );<|endoftext|>


<commit_before>import { View } from '../../core';
import Const from '../../const';
import Button from '../components/button';
import { isFunction } from 'lodash';

const IS_PLANTING_CLASS = 'plantingjs-is-planting';
const SUBMIT_BUTTON_INIT_VALUES = {
  modifier: 'accept',
  label: 'zrobione!',
  visible: false,
};
const START_BUTTON_INIT_VALUES = {
  modifier: 'accept',
  label: 'zacznij wysadzać!',
  visible: false,
};
const SELECT_PANO_INIT_VALUES = {
  modifier: 'accept',
  label: 'wybierz',
  visible: false,
};
const MODAL_CLASS = 'plantingjs-modal';

export default View.extend({
  toolbox: null,
  map: null,
  className: 'plantingjs-container',
  template: require('./main.hbs'),
  events: {
    'click .plantingjs-startbtn': 'startPlanting',
  },
  $proxy: null,

  initialize() {
    this.render();
    this.$proxy = this.$el.children();
    this.submit = new Button(SUBMIT_BUTTON_INIT_VALUES);
    this.submit.on('click', this.onClickSubmit, this);
    this.session().objects().on('add remove', (model, collection) => {
      if (this.app.getState() != Const.State.VIEWER) {
        const showSubmitButton = collection.length > 0;
        this.submit.model.set('visible', showSubmitButton);
      }
    });

    const { selectPanoMode } = this.app.options;

    if (selectPanoMode) {
      this.start = new Button(SELECT_PANO_INIT_VALUES);
      this.start.on('click', () => {
        const { onSelectPano } = this.app.options;
        const panoData = this.manifesto()
          .pick('lat', 'lng', 'pitch', 'heading', 'zoom');

        if (isFunction(onSelectPano)) {
          onSelectPano(panoData);
        } else {
          throw Error('onSelectPano must be a function');
        }
      });
    } else {
      this.start = new Button(START_BUTTON_INIT_VALUES);
      this.start.on('click', () => {
        this.app.trigger(Const.Event.START_PLANTING);
      });
    }

    this.$proxy.append(this.submit.$el, this.start.$el);
    this.app
      .on(Const.Event.VISIBLE_CHANGED, (visible) => {
        if (this.app.getState() !== Const.State.VIEWER) {
          this.start.model.set({ visible });
        }
      })
      .on(Const.Event.START_PLANTING, () => {
        this.$el.toggleClass(IS_PLANTING_CLASS, true);
        this.start.model.set('visible', false);
      })
      .on(Const.Event.STATE_CHANGED, (state) => {
        this.$el
          .children().attr('data-state', state);
      });
  },

  render() {
    this.$el.html(this.template());
  },

  onClickSubmit(event) {
    /**
     * @todo
     * Show submit popup. For now just save session.
     */
    event.preventDefault();
    this.session().save();
  },

  getModal() {
    return this.$el.find(`.${MODAL_CLASS}`);
  },
});
<commit_msg>button zrobione! is not initialized when the page is refreshing update<commit_after>import { View } from '../../core';
import Const from '../../const';
import Button from '../components/button';
import { isFunction } from 'lodash';

const IS_PLANTING_CLASS = 'plantingjs-is-planting';
const SUBMIT_BUTTON_INIT_VALUES = {
  modifier: 'accept',
  label: 'zrobione!',
  visible: false,
};
const START_BUTTON_INIT_VALUES = {
  modifier: 'accept',
  label: 'zacznij wysadzać!',
  visible: false,
};
const SELECT_PANO_INIT_VALUES = {
  modifier: 'accept',
  label: 'wybierz',
  visible: false,
};
const MODAL_CLASS = 'plantingjs-modal';

export default View.extend({
  toolbox: null,
  map: null,
  className: 'plantingjs-container',
  template: require('./main.hbs'),
  events: {
    'click .plantingjs-startbtn': 'startPlanting',
  },
  $proxy: null,

  initialize() {
    this.render();
    this.$proxy = this.$el.children();
    this.submit = new Button(SUBMIT_BUTTON_INIT_VALUES);
    this.submit.on('click', this.onClickSubmit, this);
    this.session().objects().on('add remove', (model, collection) => {
      if (this.app.getState() !== Const.State.VIEWER) {
        const showSubmitButton = collection.length > 0;
        this.submit.model.set('visible', showSubmitButton);
      }
    });

    const { selectPanoMode } = this.app.options;

    if (selectPanoMode) {
      this.start = new Button(SELECT_PANO_INIT_VALUES);
      this.start.on('click', () => {
        const { onSelectPano } = this.app.options;
        const panoData = this.manifesto()
          .pick('lat', 'lng', 'pitch', 'heading', 'zoom');

        if (isFunction(onSelectPano)) {
          onSelectPano(panoData);
        } else {
          throw Error('onSelectPano must be a function');
        }
      });
    } else {
      this.start = new Button(START_BUTTON_INIT_VALUES);
      this.start.on('click', () => {
        this.app.trigger(Const.Event.START_PLANTING);
      });
    }

    this.$proxy.append(this.submit.$el, this.start.$el);
    this.app
      .on(Const.Event.VISIBLE_CHANGED, (visible) => {
        if (this.app.getState() !== Const.State.VIEWER) {
          this.start.model.set({ visible });
        }
      })
      .on(Const.Event.START_PLANTING, () => {
        this.$el.toggleClass(IS_PLANTING_CLASS, true);
        this.start.model.set('visible', false);
      })
      .on(Const.Event.STATE_CHANGED, (state) => {
        this.$el
          .children().attr('data-state', state);
      });
  },

  render() {
    this.$el.html(this.template());
  },

  onClickSubmit(event) {
    /**
     * @todo
     * Show submit popup. For now just save session.
     */
    event.preventDefault();
    this.session().save();
  },

  getModal() {
    return this.$el.find(`.${MODAL_CLASS}`);
  },
});
<|endoftext|>