<commit_before>//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Sets a method's value.
//------------------------------------------------------------------------------
d3plus.method.set = function( vars , method , object , key , value ) {

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Create reference text for console statements.
  //----------------------------------------------------------------------------
  if (key == "value" || !key) {

    var text = "\."+method+"()"

  }
  else {

    var of = vars.format.locale.value.dev.of
      , text = "\""+key+"\" "+of+" \."+method+"()"

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Find appropriate "accepted" list.
  //----------------------------------------------------------------------------
  if (key === "value" && object.accepted) {

    var accepted = object.accepted

  }
  else if ( d3plus.object.validate( object[key] ) && object[key].accepted ) {

    var accepted = object[key].accepted

  }
  else {

    var accepted = false

  }

  if ( typeof accepted === "function" ) {
    accepted = accepted( vars )
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Check to see if the given value is allowed.
  //----------------------------------------------------------------------------
  var allowed = true
  if (accepted instanceof Array) {

    var constructor = [ null , undefined ].indexOf(value) >= 0
                    ? value : value.constructor

    allowed = accepted.indexOf(value) >= 0
              || accepted.indexOf(constructor) >= 0

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // If value is not allowed, show an error message in the console.
  //----------------------------------------------------------------------------
  if (allowed === false) {

    if ( value !== undefined ) {

      var str = vars.format.locale.value.warning.accepted
        , recs = []
        , val = "\""+JSON.stringify(value)+"\""

      accepted.forEach(function(a){

        if (typeof a == "string") {
          recs.push("\""+a+"\"")
        }
        else {
          recs.push("\""+a.toString().split("()")[0].substring(9)+"\"")
        }

      })

      d3plus.console.warning(d3plus.util.format(str,val,text,recs.join(", ")))

    }

  }
  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Otherwise, set the value!
  //----------------------------------------------------------------------------
  else {

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // If the method we are setting has a nested "value" key, change the
    // reference object and key to reflect that.
    //--------------------------------------------------------------------------
    if ( d3plus.object.validate( object[key] ) && "value" in object[key] ) {

      var parentKey = key
      object = object[key]
      key = "value"

    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // If there is a callback function, run it.
    //--------------------------------------------------------------------------
    if ( key === "value" ) {

      if ( "process" in object ) {

        value = d3plus.method.process( object , value )

      }

      if ( typeof object.callback === "function" && !object.url ) {

        var returnedValue = object.callback(value)
        if (returnedValue !== undefined) {
          value = returnedValue
        }

      }

    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // If value has not changed, show a comment in the console.
    //--------------------------------------------------------------------------
    if ( ( !(object[key] instanceof Array) && object[key] === value
         || ( object[key] && object[key] === value ) ) && value !== undefined ) {

      var str = vars.format.locale.value.dev.noChange
      if (vars.dev.value) d3plus.console.comment(d3plus.util.format(str,text))

    }
    else {

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Mark the method as being changed.
      //------------------------------------------------------------------------
      object.changed = true

      if ( "history" in vars && method !== "draw" ) {
        var copy = d3plus.util.copy(object)
        copy.method = method
        vars.history.chain.push(copy)
      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Before updating the value, store the previous one for reference.
      //------------------------------------------------------------------------
      object.previous = object[key]

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Set the variable!
      //------------------------------------------------------------------------
      if ( key === "value" && "nesting" in object ) {

        if ( method !== "id" ) {

          if ( typeof object.nesting !== "object" ) {
            object.nesting = {}
          }

          if ( typeof value == "string" ) {
            object.nesting[vars.id.value] = [value]
          }
          else if (value instanceof Array) {
            object.nesting[vars.id.value] = value
          }
          else {

            for (var id in value) {

              if ( typeof value[id] === "string" ) {
                value[id] = [value[id]]
              }

            }

            object.nesting = d3plus.util.merge( object.nesting , value )

            if ( !(vars.id.value in object.nesting) ) {
              object.nesting[vars.id.value] = value[d3.keys(value)[0]]
            }

          }

          object[key] = object.nesting[vars.id.value][0]

        }
        else {

          if ( value instanceof Array ) {

            object.nesting = value

            if ("depth" in vars && vars.depth.value < value.length) {
              object[key] = value[vars.depth.value]
            }
            else {
              object[key] = value[0]
              if ("depth" in vars) {
                vars.depth.value = 0
              }
            }

          }
          else {

            object[key] = value
            object.nesting = [value]
            if ("depth" in vars) {
              vars.depth.value = 0
            }

          }

        }

      }
      else if ( method === "depth" ) {

        if (value >= vars.id.nesting.length) {
          vars.depth.value = vars.id.nesting.length-1
        }
        else if (value < 0) {
          vars.depth.value = 0
        }
        else {
          vars.depth.value = value
        }

        vars.id.value = vars.id.nesting[vars.depth.value]

        if ( typeof vars.text.nesting === "object" ) {

          var n = vars.text.nesting[vars.id.value]
          if ( n ) {
            vars.text.nesting[vars.id.value] = typeof n == "string" ? [n] : n
            vars.text.value = n instanceof Array ? n[0] : n
          }

        }
      }
      else if ( d3plus.object.validate(object[key]) && d3plus.object.validate(value) ) {

        object[key] = d3plus.util.merge( object[key] , value )

      }
      else {

        object[key] = value

      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Add method to global array if applicable.
      //------------------------------------------------------------------------
      if ( key === "value" && object.global === true ) {

        var hasValue = object[key].length > 0
          , k = parentKey || key

        if ( k in vars && ( ( hasValue && vars[k].indexOf(method) < 0 )
        || ( !hasValue && vars[k].indexOf(method) >= 0 ) ) ) {

          vars[k] = d3plus.array.update(vars[k],method)

        }

      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Reset associated variables given if "value" is changed.
      //------------------------------------------------------------------------
      if (key == "value" && object.reset) {

        var reset = typeof object.reset == "string"
                  ? [ object.reset ] : object.reset

        reset.forEach(function(r){
          object[r] = false
        })

      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Display console message, if applicable.
      //------------------------------------------------------------------------
      if ( ( vars.dev.value || key === "dev" )
      && ( object.changed || ("key" in object && object[key].changed) ) ) {

        var newVal = JSON.stringify(object[key])

        if (typeof object[key] !== "function" && newVal.length < 260) {

          var str = vars.format.locale.value.dev.setLong
          d3plus.console.log(d3plus.util.format(str,text,newVal))

        }
        else {

          var str = vars.format.locale.value.dev.set
          d3plus.console.log(d3plus.util.format(str,text))

        }

      }

    }

  }

}
<commit_msg>fixed errors with dev statements<commit_after>//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Sets a method's value.
//------------------------------------------------------------------------------
d3plus.method.set = function( vars , method , object , key , value ) {

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Create reference text for console statements.
  //----------------------------------------------------------------------------
  if (key == "value" || !key) {

    var text = "\."+method+"()"

  }
  else {

    var of = vars.format.locale.value.dev.of
      , text = "\""+key+"\" "+of+" \."+method+"()"

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Find appropriate "accepted" list.
  //----------------------------------------------------------------------------
  if (key === "value" && object.accepted) {

    var accepted = object.accepted

  }
  else if ( d3plus.object.validate( object[key] ) && object[key].accepted ) {

    var accepted = object[key].accepted

  }
  else {

    var accepted = false

  }

  if ( typeof accepted === "function" ) {
    accepted = accepted( vars )
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Check to see if the given value is allowed.
  //----------------------------------------------------------------------------
  var allowed = true
  if (accepted instanceof Array) {

    var constructor = [ null , undefined ].indexOf(value) >= 0
                    ? value : value.constructor

    allowed = accepted.indexOf(value) >= 0
              || accepted.indexOf(constructor) >= 0

  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // If value is not allowed, show an error message in the console.
  //----------------------------------------------------------------------------
  if (allowed === false) {

    if ( value !== undefined ) {

      var str = vars.format.locale.value.warning.accepted
        , recs = []
        , val = "\""+JSON.stringify(value)+"\""

      accepted.forEach(function(a){

        if (typeof a == "string") {
          recs.push("\""+a+"\"")
        }
        else {
          recs.push("\""+a.toString().split("()")[0].substring(9)+"\"")
        }

      })

      d3plus.console.warning(d3plus.util.format(str,val,text,recs.join(", ")))

    }

  }
  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Otherwise, set the value!
  //----------------------------------------------------------------------------
  else {

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // If the method we are setting has a nested "value" key, change the
    // reference object and key to reflect that.
    //--------------------------------------------------------------------------
    if ( d3plus.object.validate( object[key] ) && "value" in object[key] ) {

      var parentKey = key
      object = object[key]
      key = "value"

    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // If there is a callback function, run it.
    //--------------------------------------------------------------------------
    if ( key === "value" ) {

      if ( "process" in object ) {

        value = d3plus.method.process( object , value )

      }

      if ( typeof object.callback === "function" && !object.url ) {

        var returnedValue = object.callback(value)
        if (returnedValue !== undefined) {
          value = returnedValue
        }

      }

    }

    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // If value has not changed, show a comment in the console.
    //--------------------------------------------------------------------------
    if ( ( !(object[key] instanceof Array) && object[key] === value
         || ( object[key] && object[key] === value ) ) && value !== undefined ) {

      var str = vars.format.locale.value.dev.noChange
      if (vars.dev.value) d3plus.console.comment(d3plus.util.format(str,text))

    }
    else {

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Mark the method as being changed.
      //------------------------------------------------------------------------
      object.changed = true

      if ( "history" in vars && method !== "draw" ) {
        var copy = d3plus.util.copy(object)
        copy.method = method
        vars.history.chain.push(copy)
      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Before updating the value, store the previous one for reference.
      //------------------------------------------------------------------------
      object.previous = object[key]

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Set the variable!
      //------------------------------------------------------------------------
      if ( key === "value" && "nesting" in object ) {

        if ( method !== "id" ) {

          if ( typeof object.nesting !== "object" ) {
            object.nesting = {}
          }

          if ( typeof value == "string" ) {
            object.nesting[vars.id.value] = [value]
          }
          else if (value instanceof Array) {
            object.nesting[vars.id.value] = value
          }
          else {

            for (var id in value) {

              if ( typeof value[id] === "string" ) {
                value[id] = [value[id]]
              }

            }

            object.nesting = d3plus.util.merge( object.nesting , value )

            if ( !(vars.id.value in object.nesting) ) {
              object.nesting[vars.id.value] = value[d3.keys(value)[0]]
            }

          }

          object[key] = object.nesting[vars.id.value][0]

        }
        else {

          if ( value instanceof Array ) {

            object.nesting = value

            if ("depth" in vars && vars.depth.value < value.length) {
              object[key] = value[vars.depth.value]
            }
            else {
              object[key] = value[0]
              if ("depth" in vars) {
                vars.depth.value = 0
              }
            }

          }
          else {

            object[key] = value
            object.nesting = [value]
            if ("depth" in vars) {
              vars.depth.value = 0
            }

          }

        }

      }
      else if ( method === "depth" ) {

        if (value >= vars.id.nesting.length) {
          vars.depth.value = vars.id.nesting.length-1
        }
        else if (value < 0) {
          vars.depth.value = 0
        }
        else {
          vars.depth.value = value
        }

        vars.id.value = vars.id.nesting[vars.depth.value]

        if ( typeof vars.text.nesting === "object" ) {

          var n = vars.text.nesting[vars.id.value]
          if ( n ) {
            vars.text.nesting[vars.id.value] = typeof n == "string" ? [n] : n
            vars.text.value = n instanceof Array ? n[0] : n
          }

        }
      }
      else if ( d3plus.object.validate(object[key]) && d3plus.object.validate(value) ) {

        object[key] = d3plus.util.merge( object[key] , value )

      }
      else {

        object[key] = value

      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Add method to global array if applicable.
      //------------------------------------------------------------------------
      if ( key === "value" && object.global === true ) {

        var hasValue = object[key].length > 0
          , k = parentKey || key

        if ( k in vars && ( ( hasValue && vars[k].indexOf(method) < 0 )
        || ( !hasValue && vars[k].indexOf(method) >= 0 ) ) ) {

          vars[k] = d3plus.array.update(vars[k],method)

        }

      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Reset associated variables given if "value" is changed.
      //------------------------------------------------------------------------
      if (key == "value" && object.reset) {

        var reset = typeof object.reset == "string"
                  ? [ object.reset ] : object.reset

        reset.forEach(function(r){
          object[r] = false
        })

      }

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Display console message, if applicable.
      //------------------------------------------------------------------------
      if ( ( vars.dev.value || key === "dev" )
      && ( object.changed || ("key" in object && object[key].changed) ) ) {

        var longArray = object[key] instanceof Array && object[key].length > 10
          , d3selection = d3plus.util.d3selection(object[key])
          , typeFunction = typeof object[key] === "function"

        var valString = object[key] === undefined ? "\"undefined\""
                      : !longArray && !d3selection && !typeFunction
                      ? JSON.stringify(object[key]) : null

        if (valString !== null && valString.length < 260) {

          var str = vars.format.locale.value.dev.setLong
          d3plus.console.log(d3plus.util.format(str,text,valString))

        }
        else {

          var str = vars.format.locale.value.dev.set
          d3plus.console.log(d3plus.util.format(str,text))

        }

      }

    }

  }

}
<|endoftext|>


<commit_before>/*---------------------------------------------------------
 * OpenERP base_hello (Example module)
 *---------------------------------------------------------*/

openerp.base_hello = function(openerp) {

openerp.base.SearchView = openerp.base.SearchView.extend({
    init:function() {
        this._super.apply(this,arguments);
        this.on_search.add(function(){alert('hello')});
    },
});

// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them

}

// vim:et fdc=0 fdl=0:
<commit_msg>[FIX] remove annoying alert in base_hello, remove trailing commas and add missing semicolons<commit_after>/*---------------------------------------------------------
 * OpenERP base_hello (Example module)
 *---------------------------------------------------------*/

openerp.base_hello = function(openerp) {

openerp.base.SearchView = openerp.base.SearchView.extend({
    init:function() {
        this._super.apply(this,arguments);
        this.on_search.add(function(){console.log('hello');});
    }
});

// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them

};

// vim:et fdc=0 fdl=0:
<|endoftext|>


<commit_before>// @flow

import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { connect } from 'react-redux';

import { translate } from '../../i18n';
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
import {
    MEDIA_TYPE,
    shouldRenderVideoTrack,
    VideoTrack
} from '../../media';
import { prefetch } from '../../../mobile/image-cache';
import { Container, TintedView } from '../../react';
import { TestHint } from '../../testing/components';
import { getTrackByMediaTypeAndParticipant } from '../../tracks';

import Avatar from './Avatar';
import {
    getAvatarURL,
    getParticipantById,
    getParticipantDisplayName
} from '../functions';
import styles from './styles';

/**
 * The type of the React {@link Component} props of {@link ParticipantView}.
 */
type Props = {

    /**
     * The indicator which determines whether conferencing is in audio-only
     * mode.
     *
     * @private
     */
    _audioOnly: boolean,

    /**
     * The source (e.g. URI, URL) of the avatar image of the participant with
     * {@link #participantId}.
     *
     * @private
     */
    _avatar: string,

    /**
     * The connection status of the participant. Her video will only be rendered
     * if the connection status is 'active'; otherwise, the avatar will be
     * rendered. If undefined, 'active' is presumed.
     *
     * @private
     */
    _connectionStatus: string,

    /**
     * The name of the participant which this component represents.
     *
     * @private
     */
    _participantName: string,

    /**
     * The video Track of the participant with {@link #participantId}.
     */
    _videoTrack: Object,

    /**
     * The avatar size.
     */
    avatarSize: number,

    /**
     * Callback to invoke when the {@code ParticipantView} is clicked/pressed.
     */
    onPress: Function,

    /**
     * The ID of the participant (to be) depicted by {@link ParticipantView}.
     *
     * @public
     */
    participantId: string,

    /**
     * True if the avatar of the depicted participant is to be shown should the
     * avatar be available and the video of the participant is not to be shown;
     * otherwise, false. If undefined, defaults to true.
     */
    showAvatar: boolean,

    /**
     * True if the video of the depicted participant is to be shown should the
     * video be available. If undefined, defaults to true.
     */
    showVideo: boolean,

    /**
     * The style, if any, to apply to {@link ParticipantView} in addition to its
     * default style.
     */
    style: Object,

    /**
     * The function to translate human-readable text.
     */
    t: Function,

    /**
     * The test hint id which can be used to locate the {@code ParticipantView}
     * on the jitsi-meet-torture side. If not provided, the
     * {@code participantId} with the following format will be used:
     * {@code `org.jitsi.meet.Participant#${participantId}`}
     */
    testHintId: ?string,

    /**
     * Indicates if the connectivity info label should be shown, if appropriate.
     * It will be shown in case the connection is interrupted.
     */
    useConnectivityInfoLabel: boolean,

    /**
     * The z-order of the {@link Video} of {@link ParticipantView} in the
     * stacking space of all {@code Video}s. For more details, refer to the
     * {@code zOrder} property of the {@code Video} class for React Native.
     */
    zOrder: number,

    /**
     * Indicates whether zooming (pinch to zoom and/or drag) is enabled.
     */
    zoomEnabled: boolean
};

/**
 * Implements a React Component which depicts a specific participant's avatar
 * and video.
 *
 * @extends Component
 */
class ParticipantView extends Component<Props> {

    /**
     * Renders the connection status label, if appropriate.
     *
     * @param {string} connectionStatus - The status of the participant's
     * connection.
     * @private
     * @returns {ReactElement|null}
     */
    _renderConnectionInfo(connectionStatus) {
        let messageKey;

        switch (connectionStatus) {
        case JitsiParticipantConnectionStatus.INACTIVE:
            messageKey = 'connection.LOW_BANDWIDTH';
            break;
        case JitsiParticipantConnectionStatus.INTERRUPTED:
            messageKey = 'connection.USER_CONNECTION_INTERRUPTED';
            break;
        default:
            return null;
        }

        const {
            avatarSize,
            _participantName: displayName,
            t
        } = this.props;

        // XXX Consider splitting this component into 2: one for the large view
        // and one for the thumbnail. Some of these don't apply to both.
        const containerStyle = {
            ...styles.connectionInfoContainer,
            width: avatarSize * 1.5
        };

        return (
            <View
                pointerEvents = 'box-none'
                style = { containerStyle }>
                <Text style = { styles.connectionInfoText }>
                    { t(messageKey, { displayName }) }
                </Text>
            </View>
        );
    }

    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        const {
            onPress,
            _avatar: avatar,
            _connectionStatus: connectionStatus,
            _videoTrack: videoTrack
        } = this.props;

        // Is the video to be rendered?
        // FIXME It's currently impossible to have true as the value of
        // waitForVideoStarted because videoTrack's state videoStarted will be
        // updated only after videoTrack is rendered.
        // XXX Note that, unlike on web, we don't render video when the
        // connection status is interrupted, this is because the renderer
        // doesn't retain the last frame forever, so we would end up with a
        // black screen.
        const waitForVideoStarted = false;
        let renderVideo
            = !this.props._audioOnly
                && (connectionStatus
                    === JitsiParticipantConnectionStatus.ACTIVE)
                && shouldRenderVideoTrack(videoTrack, waitForVideoStarted);

        // Is the avatar to be rendered?
        let renderAvatar = Boolean(!renderVideo && avatar);

        // The consumer of this ParticipantView is allowed to forbid showing the
        // video if the private logic of this ParticipantView determines that
        // the video could be rendered.
        renderVideo = renderVideo && _toBoolean(this.props.showVideo, true);

        // The consumer of this ParticipantView is allowed to forbid showing the
        // avatar if the private logic of this ParticipantView determines that
        // the avatar could be rendered.
        renderAvatar = renderAvatar && _toBoolean(this.props.showAvatar, true);

        // If the connection has problems, we will "tint" the video / avatar.
        const useTint
            = connectionStatus === JitsiParticipantConnectionStatus.INACTIVE
                || connectionStatus
                    === JitsiParticipantConnectionStatus.INTERRUPTED;

        const testHintId
            = this.props.testHintId
                ? this.props.testHintId
                : `org.jitsi.meet.Participant#${this.props.participantId}`;

        return (
            <Container
                onClick = { renderVideo ? undefined : onPress }
                style = {{
                    ...styles.participantView,
                    ...this.props.style
                }}
                touchFeedback = { false }>

                <TestHint
                    id = { testHintId }
                    onPress = { onPress }
                    value = '' />

                { renderVideo
                    && <VideoTrack
                        onPress = { renderVideo ? onPress : undefined }
                        videoTrack = { videoTrack }
                        waitForVideoStarted = { waitForVideoStarted }
                        zOrder = { this.props.zOrder }
                        zoomEnabled = { this.props.zoomEnabled } /> }

                { renderAvatar
                    && <Avatar
                        size = { this.props.avatarSize }
                        uri = { avatar } /> }

                { useTint

                    // If the connection has problems, tint the video / avatar.
                    && <TintedView /> }

                { this.props.useConnectivityInfoLabel
                    && this._renderConnectionInfo(connectionStatus) }
            </Container>
        );
    }
}

/**
 * Converts the specified value to a boolean value. If the specified value is
 * undefined, returns the boolean value of undefinedValue.
 *
 * @param {any} value - The value to convert to a boolean value should it not be
 * undefined.
 * @param {any} undefinedValue - The value to convert to a boolean value should
 * the specified value be undefined.
 * @private
 * @returns {boolean}
 */
function _toBoolean(value, undefinedValue) {
    return Boolean(typeof value === 'undefined' ? undefinedValue : value);
}

/**
 * Maps (parts of) the redux state to the associated {@link ParticipantView}'s
 * props.
 *
 * @param {Object} state - The redux state.
 * @param {Object} ownProps - The React {@code Component} props passed to the
 * associated (instance of) {@code ParticipantView}.
 * @private
 * @returns {{
 *     _audioOnly: boolean,
 *     _avatar: string,
 *     _connectionStatus: string,
 *     _participantName: string,
 *     _videoTrack: Track
 * }}
 */
function _mapStateToProps(state, ownProps) {
    const { participantId } = ownProps;
    const participant = getParticipantById(state, participantId);
    let avatar;
    let connectionStatus;
    let participantName;

    if (participant) {
        avatar = getAvatarURL(participant);
        connectionStatus = participant.connectionStatus;
        participantName = getParticipantDisplayName(state, participant.id);

        // Avatar (on React Native) now has the ability to generate an
        // automatically-colored default image when no URI/URL is specified or
        // when it fails to load. In order to make the coloring permanent(ish)
        // per participant, Avatar will need something permanent(ish) per
        // perticipant, obviously. A participant's ID is such a piece of data.
        // But the local participant changes her ID as she joins, leaves.
        // TODO @lyubomir: The participants may change their avatar URLs at
        // runtime which means that, if their old and new avatar URLs fail to
        // download, Avatar will change their automatically-generated colors.
        avatar || participant.local || (avatar = `#${participant.id}`);

        // ParticipantView knows before Avatar that an avatar URL will be used
        // so it's advisable to prefetch here.
        avatar && prefetch({ uri: avatar });
    }

    return {
        _audioOnly: state['features/base/conference'].audioOnly,
        _avatar: avatar,
        _connectionStatus:
            connectionStatus
                || JitsiParticipantConnectionStatus.ACTIVE,
        _participantName: participantName,
        _videoTrack:
            getTrackByMediaTypeAndParticipant(
                state['features/base/tracks'],
                MEDIA_TYPE.VIDEO,
                participantId)
    };
}

export default translate(connect(_mapStateToProps)(ParticipantView));
<commit_msg>[RN] Simplify logic for using tinted view in ParticipantView<commit_after>// @flow

import React, { Component } from 'react';
import { Text, View } from 'react-native';
import { connect } from 'react-redux';

import { translate } from '../../i18n';
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
import {
    MEDIA_TYPE,
    shouldRenderVideoTrack,
    VideoTrack
} from '../../media';
import { prefetch } from '../../../mobile/image-cache';
import { Container, TintedView } from '../../react';
import { TestHint } from '../../testing/components';
import { getTrackByMediaTypeAndParticipant } from '../../tracks';

import Avatar from './Avatar';
import {
    getAvatarURL,
    getParticipantById,
    getParticipantDisplayName
} from '../functions';
import styles from './styles';

/**
 * The type of the React {@link Component} props of {@link ParticipantView}.
 */
type Props = {

    /**
     * The indicator which determines whether conferencing is in audio-only
     * mode.
     *
     * @private
     */
    _audioOnly: boolean,

    /**
     * The source (e.g. URI, URL) of the avatar image of the participant with
     * {@link #participantId}.
     *
     * @private
     */
    _avatar: string,

    /**
     * The connection status of the participant. Her video will only be rendered
     * if the connection status is 'active'; otherwise, the avatar will be
     * rendered. If undefined, 'active' is presumed.
     *
     * @private
     */
    _connectionStatus: string,

    /**
     * The name of the participant which this component represents.
     *
     * @private
     */
    _participantName: string,

    /**
     * The video Track of the participant with {@link #participantId}.
     */
    _videoTrack: Object,

    /**
     * The avatar size.
     */
    avatarSize: number,

    /**
     * Callback to invoke when the {@code ParticipantView} is clicked/pressed.
     */
    onPress: Function,

    /**
     * The ID of the participant (to be) depicted by {@link ParticipantView}.
     *
     * @public
     */
    participantId: string,

    /**
     * True if the avatar of the depicted participant is to be shown should the
     * avatar be available and the video of the participant is not to be shown;
     * otherwise, false. If undefined, defaults to true.
     */
    showAvatar: boolean,

    /**
     * True if the video of the depicted participant is to be shown should the
     * video be available. If undefined, defaults to true.
     */
    showVideo: boolean,

    /**
     * The style, if any, to apply to {@link ParticipantView} in addition to its
     * default style.
     */
    style: Object,

    /**
     * The function to translate human-readable text.
     */
    t: Function,

    /**
     * The test hint id which can be used to locate the {@code ParticipantView}
     * on the jitsi-meet-torture side. If not provided, the
     * {@code participantId} with the following format will be used:
     * {@code `org.jitsi.meet.Participant#${participantId}`}
     */
    testHintId: ?string,

    /**
     * Indicates if the connectivity info label should be shown, if appropriate.
     * It will be shown in case the connection is interrupted.
     */
    useConnectivityInfoLabel: boolean,

    /**
     * The z-order of the {@link Video} of {@link ParticipantView} in the
     * stacking space of all {@code Video}s. For more details, refer to the
     * {@code zOrder} property of the {@code Video} class for React Native.
     */
    zOrder: number,

    /**
     * Indicates whether zooming (pinch to zoom and/or drag) is enabled.
     */
    zoomEnabled: boolean
};

/**
 * Implements a React Component which depicts a specific participant's avatar
 * and video.
 *
 * @extends Component
 */
class ParticipantView extends Component<Props> {

    /**
     * Renders the connection status label, if appropriate.
     *
     * @param {string} connectionStatus - The status of the participant's
     * connection.
     * @private
     * @returns {ReactElement|null}
     */
    _renderConnectionInfo(connectionStatus) {
        let messageKey;

        switch (connectionStatus) {
        case JitsiParticipantConnectionStatus.INACTIVE:
            messageKey = 'connection.LOW_BANDWIDTH';
            break;
        case JitsiParticipantConnectionStatus.INTERRUPTED:
            messageKey = 'connection.USER_CONNECTION_INTERRUPTED';
            break;
        default:
            return null;
        }

        const {
            avatarSize,
            _participantName: displayName,
            t
        } = this.props;

        // XXX Consider splitting this component into 2: one for the large view
        // and one for the thumbnail. Some of these don't apply to both.
        const containerStyle = {
            ...styles.connectionInfoContainer,
            width: avatarSize * 1.5
        };

        return (
            <View
                pointerEvents = 'box-none'
                style = { containerStyle }>
                <Text style = { styles.connectionInfoText }>
                    { t(messageKey, { displayName }) }
                </Text>
            </View>
        );
    }

    /**
     * Implements React's {@link Component#render()}.
     *
     * @inheritdoc
     * @returns {ReactElement}
     */
    render() {
        const {
            onPress,
            _avatar: avatar,
            _connectionStatus: connectionStatus,
            _videoTrack: videoTrack
        } = this.props;

        // Is the video to be rendered?
        // FIXME It's currently impossible to have true as the value of
        // waitForVideoStarted because videoTrack's state videoStarted will be
        // updated only after videoTrack is rendered.
        // XXX Note that, unlike on web, we don't render video when the
        // connection status is interrupted, this is because the renderer
        // doesn't retain the last frame forever, so we would end up with a
        // black screen.
        const waitForVideoStarted = false;
        let renderVideo
            = !this.props._audioOnly
                && (connectionStatus
                    === JitsiParticipantConnectionStatus.ACTIVE)
                && shouldRenderVideoTrack(videoTrack, waitForVideoStarted);

        // Is the avatar to be rendered?
        let renderAvatar = Boolean(!renderVideo && avatar);

        // The consumer of this ParticipantView is allowed to forbid showing the
        // video if the private logic of this ParticipantView determines that
        // the video could be rendered.
        renderVideo = renderVideo && _toBoolean(this.props.showVideo, true);

        // The consumer of this ParticipantView is allowed to forbid showing the
        // avatar if the private logic of this ParticipantView determines that
        // the avatar could be rendered.
        renderAvatar = renderAvatar && _toBoolean(this.props.showAvatar, true);

        // If the connection has problems, we will "tint" the video / avatar.
        const useTint
            = connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE;

        const testHintId
            = this.props.testHintId
                ? this.props.testHintId
                : `org.jitsi.meet.Participant#${this.props.participantId}`;

        return (
            <Container
                onClick = { renderVideo ? undefined : onPress }
                style = {{
                    ...styles.participantView,
                    ...this.props.style
                }}
                touchFeedback = { false }>

                <TestHint
                    id = { testHintId }
                    onPress = { onPress }
                    value = '' />

                { renderVideo
                    && <VideoTrack
                        onPress = { renderVideo ? onPress : undefined }
                        videoTrack = { videoTrack }
                        waitForVideoStarted = { waitForVideoStarted }
                        zOrder = { this.props.zOrder }
                        zoomEnabled = { this.props.zoomEnabled } /> }

                { renderAvatar
                    && <Avatar
                        size = { this.props.avatarSize }
                        uri = { avatar } /> }

                { useTint

                    // If the connection has problems, tint the video / avatar.
                    && <TintedView /> }

                { this.props.useConnectivityInfoLabel
                    && this._renderConnectionInfo(connectionStatus) }
            </Container>
        );
    }
}

/**
 * Converts the specified value to a boolean value. If the specified value is
 * undefined, returns the boolean value of undefinedValue.
 *
 * @param {any} value - The value to convert to a boolean value should it not be
 * undefined.
 * @param {any} undefinedValue - The value to convert to a boolean value should
 * the specified value be undefined.
 * @private
 * @returns {boolean}
 */
function _toBoolean(value, undefinedValue) {
    return Boolean(typeof value === 'undefined' ? undefinedValue : value);
}

/**
 * Maps (parts of) the redux state to the associated {@link ParticipantView}'s
 * props.
 *
 * @param {Object} state - The redux state.
 * @param {Object} ownProps - The React {@code Component} props passed to the
 * associated (instance of) {@code ParticipantView}.
 * @private
 * @returns {{
 *     _audioOnly: boolean,
 *     _avatar: string,
 *     _connectionStatus: string,
 *     _participantName: string,
 *     _videoTrack: Track
 * }}
 */
function _mapStateToProps(state, ownProps) {
    const { participantId } = ownProps;
    const participant = getParticipantById(state, participantId);
    let avatar;
    let connectionStatus;
    let participantName;

    if (participant) {
        avatar = getAvatarURL(participant);
        connectionStatus = participant.connectionStatus;
        participantName = getParticipantDisplayName(state, participant.id);

        // Avatar (on React Native) now has the ability to generate an
        // automatically-colored default image when no URI/URL is specified or
        // when it fails to load. In order to make the coloring permanent(ish)
        // per participant, Avatar will need something permanent(ish) per
        // perticipant, obviously. A participant's ID is such a piece of data.
        // But the local participant changes her ID as she joins, leaves.
        // TODO @lyubomir: The participants may change their avatar URLs at
        // runtime which means that, if their old and new avatar URLs fail to
        // download, Avatar will change their automatically-generated colors.
        avatar || participant.local || (avatar = `#${participant.id}`);

        // ParticipantView knows before Avatar that an avatar URL will be used
        // so it's advisable to prefetch here.
        avatar && prefetch({ uri: avatar });
    }

    return {
        _audioOnly: state['features/base/conference'].audioOnly,
        _avatar: avatar,
        _connectionStatus:
            connectionStatus
                || JitsiParticipantConnectionStatus.ACTIVE,
        _participantName: participantName,
        _videoTrack:
            getTrackByMediaTypeAndParticipant(
                state['features/base/tracks'],
                MEDIA_TYPE.VIDEO,
                participantId)
    };
}

export default translate(connect(_mapStateToProps)(ParticipantView));
<|endoftext|>


<commit_before>
L.Map.include({
	openPopup: function(popup) {
		this.closePopup();
		this._popup = popup;
		this.addLayer(popup);
		this.fire('popupopen', { popup: this._popup });

		return this;
	},

	closePopup: function(popup) {
		if (this._popup) {
			this.removeLayer(this._popup);
			this.fire('popupclose', { popup: this._popup });
			this._popup = null;
		}
		return this;
	}
});
<commit_msg>Removed unnecessary parameter.<commit_after>
L.Map.include({
	openPopup: function(popup) {
		this.closePopup();
		this._popup = popup;
		this.addLayer(popup);
		this.fire('popupopen', { popup: this._popup });

		return this;
	},

	closePopup: function() {
		if (this._popup) {
			this.removeLayer(this._popup);
			this.fire('popupclose', { popup: this._popup });
			this._popup = null;
		}
		return this;
	}
});
<|endoftext|>


<commit_before>/* global videojs */
/* eslint-disable max-len */

import './videojs-resumecard.scss';

let resumeCard = null;

/**
 * Video.js plugin for resuming playback.
 *
 * @example
 * videojs('player_id', {
 *   plugins: {
 *     resumecard: {
 *       time: 42,
 *
 *       template: function template() {
 *         // ...
 *       }
 *     },
 *   },
 * });
 *
 * videojs('player_id').resumecard({
 *   time: 42,
 * });
 *
 * @param {Object} player VideoJS player
 * @param {Object} options={}
 * @param {string} [options.className=vjs-resumecard] HTMLElement class.
 * @param {string} [options.id=vjs_resumecard] HTMLElement id.
 * @param {boolean} [options.preload] Have the player *immediately* seek to time specified.
 * @param {function(event)} [options.restartCallback] Custom restart button callback.
 * @param {function(event)} [options.resumeCallback] Custom resume button callback.
 * @param {function(restartCallback, resumeCallback):HTMLElement} options.template Callback to return custom template HTMLElement.
 * @param {number} options.time Start time, in seconds, for the contented to be resumed.
 */
class ResumeCard {
  constructor(player, options = {}) {
    if (!options.time) {
      throw new Error('videojs-resumecard requires a valid time.');
    }

    if (!options.template) {
      throw new Error('videojs-resumecard requires a valid template.');
    }

    // hide big play button since resume card will take over initial play experience
    if (player.bigPlayButton) {
      player.bigPlayButton.hide();
    }

    // seek immediately on preload
    if (options.preload === true) {
      player.currentTime(options.time);
    }

    this.player = player;
    this.settings = videojs.mergeOptions({
      className: 'vjs-resumecard',
      id: 'vjs_resumecard',
    }, options);

    resumeCard = null;
  }

  fade() {
    if (resumeCard instanceof HTMLElement === false) {
      return;
    }

    resumeCard.style.opacity = 0;
  }

  hide() {
    if (resumeCard instanceof HTMLElement === false) {
      return;
    }

    resumeCard.style.display = 'none';
  }

  render() {
    const {
      className,
      id,
      template,
    } = this.settings;

    resumeCard = document.createElement('div');
    resumeCard.className = className;
    resumeCard.id = id;

    if (typeof template !== 'function') {
      this.player.el().appendChild(resumeCard);
    }

    const resumeCardTemplate = template(
      this.restartCallback.bind(this),
      this.resumeCallback.bind(this)
    );

    if (resumeCardTemplate instanceof HTMLElement === false) {
      throw new Error('videojs-resumecard requires template to be a valid HTMLElement.');
    }

    resumeCard.appendChild(resumeCardTemplate);

    this.player.el().appendChild(resumeCard);

    this.show();
  }

  /**
   * Executes the callback on restartButton interaction.
   *
   * @param {Object} event
   */
  restartCallback(event) {
    const {
      restartCallback,
    } = this.settings;

    if (typeof restartCallback === 'function') {
      return restartCallback.call(this, event);
    }

    this.player.ready(() => {
      this.player.one('seeked', () => {
        this.hide();

        this.player.play();
      });

      this.player.currentTime(0);

      // visually hide resumecard, but prevent player interaction while video loads.
      this.fade();
    });

    return undefined;
  }

  /**
   * Executes the callback on resumeButton interaction.
   *
   * @param {Object} event
   */
  resumeCallback(event) {
    const {
      resumeCallback,
      time,
    } = this.settings;

    if (typeof resumeCallback === 'function') {
      return resumeCallback.call(this, event);
    }

    this.player.ready(() => {
      this.player.one('seeked', () => {
        this.hide();

        this.player.play();
      });

      this.player.currentTime(time);

      // visually hide resumecard, but prevent player interaction while video loads.
      this.fade();
    });

    return undefined;
  }

  show() {
    if (resumeCard instanceof HTMLElement === false) {
      return;
    }

    resumeCard.style.display = 'block';
  }
}

function resumecard(options) {
  (new ResumeCard(this, options)).render();
}

videojs.plugin('resumecard', resumecard);

export default resumecard;
<commit_msg>chore(videojs-resumecard): Bind callback functions inside constructor.<commit_after>/* global videojs */
/* eslint-disable max-len */

import './videojs-resumecard.scss';

let resumeCard = null;

/**
 * Video.js plugin for resuming playback.
 *
 * @example
 * videojs('player_id', {
 *   plugins: {
 *     resumecard: {
 *       time: 42,
 *
 *       template: function template() {
 *         // ...
 *       }
 *     },
 *   },
 * });
 *
 * videojs('player_id').resumecard({
 *   time: 42,
 * });
 *
 * @param {Object} player VideoJS player
 * @param {Object} options={}
 * @param {string} [options.className=vjs-resumecard] HTMLElement class.
 * @param {string} [options.id=vjs_resumecard] HTMLElement id.
 * @param {boolean} [options.preload] Have the player *immediately* seek to time specified.
 * @param {function(event)} [options.restartCallback] Custom restart button callback.
 * @param {function(event)} [options.resumeCallback] Custom resume button callback.
 * @param {function(restartCallback, resumeCallback):HTMLElement} options.template Callback to return custom template HTMLElement.
 * @param {number} options.time Start time, in seconds, for the contented to be resumed.
 */
class ResumeCard {
  constructor(player, options = {}) {
    if (!options.time) {
      throw new Error('videojs-resumecard requires a valid time.');
    }

    if (!options.template) {
      throw new Error('videojs-resumecard requires a valid template.');
    }

    // hide big play button since resume card will take over initial play experience
    if (player.bigPlayButton) {
      player.bigPlayButton.hide();
    }

    // seek immediately on preload
    if (options.preload === true) {
      player.currentTime(options.time);
    }

    this.player = player;
    this.settings = videojs.mergeOptions({
      className: 'vjs-resumecard',
      id: 'vjs_resumecard',
    }, options);

    this.restartCallback = this.restartCallback.bind(this);
    this.resumeCallback = this.resumeCallback.bind(this);

    resumeCard = null;
  }

  fade() {
    if (resumeCard instanceof HTMLElement === false) {
      return;
    }

    resumeCard.style.opacity = 0;
  }

  hide() {
    if (resumeCard instanceof HTMLElement === false) {
      return;
    }

    resumeCard.style.display = 'none';
  }

  render() {
    const {
      className,
      id,
      template,
    } = this.settings;

    resumeCard = document.createElement('div');
    resumeCard.className = className;
    resumeCard.id = id;

    if (typeof template !== 'function') {
      this.player.el().appendChild(resumeCard);
    }

    const resumeCardTemplate = template(
      this.restartCallback,
      this.resumeCallback
    );

    if (resumeCardTemplate instanceof HTMLElement === false) {
      throw new Error('videojs-resumecard requires template to be a valid HTMLElement.');
    }

    resumeCard.appendChild(resumeCardTemplate);

    this.player.el().appendChild(resumeCard);

    this.show();
  }

  /**
   * Executes the callback on restartButton interaction.
   *
   * @param {Object} event
   */
  restartCallback(event) {
    const {
      restartCallback,
    } = this.settings;

    if (typeof restartCallback === 'function') {
      return restartCallback.call(this, event);
    }

    this.player.ready(() => {
      this.player.one('seeked', () => {
        this.hide();

        this.player.play();
      });

      this.player.currentTime(0);

      // visually hide resumecard, but prevent player interaction while video loads.
      this.fade();
    });

    return undefined;
  }

  /**
   * Executes the callback on resumeButton interaction.
   *
   * @param {Object} event
   */
  resumeCallback(event) {
    const {
      resumeCallback,
      time,
    } = this.settings;

    if (typeof resumeCallback === 'function') {
      return resumeCallback.call(this, event);
    }

    this.player.ready(() => {
      this.player.one('seeked', () => {
        this.hide();

        this.player.play();
      });

      this.player.currentTime(time);

      // visually hide resumecard, but prevent player interaction while video loads.
      this.fade();
    });

    return undefined;
  }

  show() {
    if (resumeCard instanceof HTMLElement === false) {
      return;
    }

    resumeCard.style.display = 'block';
  }
}

function resumecard(options) {
  (new ResumeCard(this, options)).render();
}

videojs.plugin('resumecard', resumecard);

export default resumecard;
<|endoftext|>


<commit_before>define(['text!template-form-controls.mustache',
  'model-application',
  'feature!api',
  'enum-user-actions'],
  function (Template, app, API, USER_ACTIONS) {
    'use strict';

    var isHTML = function (string) {
      var html$;
      if (!string || typeof string !== 'string' || !string.trim()) {
        return false;
      }
      if (typeof Node === 'undefined' || !Node.TEXT_NODE) {
        return true; // the string _might_ be HTML, we just can't tell
      }
      html$ = $.parseHTML(string);
      return !html$.every(function (el) {
        return el.nodeType === Node.TEXT_NODE;
      });
    };

    var checkForFormErrors = function(view, userAction){
      // helper function for better control over what happens when
      // an item is added to the queue
      return function(){
        if (_.isEmpty(BlinkForms.current.getErrors())) {
          view.formLeave(userAction);
        } else {
          $(window).trigger('pagechange');
        }
      };
    };

    var FormControlView = Backbone.View.extend({

      events: {
        'click #FormControls #submit': 'formSubmit',
        'click #FormControls #close': 'formClose',
        'click #FormControls #save': 'formSave2',
        'click #nextFormPage': 'nextFormPage',
        'click #previousFormPage': 'previousFormPage'
      },

      render: function () {
        var view, options;

        view = this;
        options = {
          hasStorage: app.hasStorage()
        };

        if (BlinkForms.current.get('pages').length > 1) {
          options.pages = {
            current: BlinkForms.current.get('pages').current.index() + 1,
            total: BlinkForms.current.get('pages').length
          };

          if (BlinkForms.current.get('pages').current.index() !== 0) {
            options.pages.previous = true;
          }

          if (BlinkForms.current.get('pages').current.index() !== BlinkForms.current.get('pages').length - 1) {
            options.pages.next = true;
          }
        }

        view.$el.html(Mustache.render(Template, options));
        $.mobile.activePage.trigger('pagecreate');

        return view;
      },

      nextFormPage: function () {
        var view, index;

        view = this;
        index = BlinkForms.current.get('pages').current.index();

        if (index < BlinkForms.current.get('pages').length - 1) {
          BlinkForms.current.get('pages').goto(index + 1);
        }

        view.render();
      },

      previousFormPage: function () {
        var view, index;

        view = this;
        index = BlinkForms.current.get('pages').current.index();

        if (index > 0) {
          BlinkForms.current.get('pages').goto(index - 1);
        }

        view.render();
      },

      formLeave: function (userAction) {
        var onLeave = BlinkForms.current.get('onFormLeaveInteraction');

        if ( onLeave && onLeave[this.model.get('blinkFormAction')] ){
          return onLeave[this.model.get('blinkFormAction')]({ model: BlinkForms.current, userAction: userAction});
        }

        if (window.BMP.BIC3.history.length === 0) {
          return window.BMP.BIC3.view.home();
        }

        history.back();
      },

      formSubmit: function () {
        var me = this;
        var model = this.model;
        if (app.hasStorage()) {
          if (_.isEmpty(BlinkForms.current.getErrors())) {
            this.addToQueue('Pending')
                .then(checkForFormErrors(this, USER_ACTIONS.SUBMIT));
          } else {
            this.addToQueue('Draft')
                .then(checkForFormErrors(this, USER_ACTIONS.SUBMIT));
          }
        } else {
          $.mobile.loading('show');
          BlinkForms.current.data()
            .then(function (data) {
              return API.setPendingItem(
                model.get('blinkFormObjectName'),
                model.get('blinkFormAction'),
                data
              );
            })
            .then(function (data) {
              if (!isHTML(data)) {
                data = '
' + data + '
';
              }
              app.view.popup(data);
              $('#popup').one('popupafterclose', function () {
                me.formLeave(USER_ACTIONS.SUBMIT);
              });
            }, function (jqXHR) {
              var status = jqXHR.status;
              var json;
              var html = '';
              try {
                json = JSON.parse(jqXHR.responseText);
              } catch (ignore) {
                json = { message: 'error ' + status };
              }
              if (json.message) {
                html += json.message;
              }
              if (status === 470 || status === 471 || json.errors) {
                if (typeof json.errors === 'object') {
                  html += '
';
                  Object.keys(json.errors).forEach(function (key) {
                    html += '
' + key + ': ' + json.errors[key] + '
';
                  });
                  html += '
';
                }
              }
              if (!isHTML(html)) {
                html = '
' + html + '
';
              }
              app.view.popup(html);
            }).then(function () {
              $.mobile.loading('hide');
            });
        }
      },

      formClose: function () {
        var that = this;
        $('#closePopup').popup({
          afteropen: function (event) {
            $(event.target).on('click', '#save', {view: that}, that.formSave.bind(that));
            $(event.target).on('click', '#discard', {view: that}, that.formDiscard.bind(that));
          },
          afterclose: function (event) {
            $(event.target).off('click', '#save');
            $(event.target).off('click', '#discard');
          }
        });
        $('#closePopup').popup('open');
      },

      formSave: function (e) {
        var me = this;
        e.data.view.addToQueue('Draft')
         .then(function(){
          $('#closePopup').one('popupafterclose', function () { me.formLeave(USER_ACTIONS.SAVE); });
          $('#closePopup').popup('close');
         });
      },

      formSave2: function () {
        this.addToQueue("Draft")
            .then(checkForFormErrors(this, USER_ACTIONS.SAVE));
      },

      formDiscard: function () {
        var me = this;
        $('#closePopup').one('popupafterclose', function () {
          me.formLeave(USER_ACTIONS.DISCARD);
        });
        $('#closePopup').popup('close');
      },

      addToQueue: function (status, supressQueue) {
        var view = this;
        var model;
        supressQueue = supressQueue || false;

        return new Promise(function (resolve, reject) {
          BlinkForms.current.data().then(function (data) {
            var modelAttrs;
            var options = {};

            options.success = function (updatedModel) {
              if (!supressQueue) {
                $(window).one('pagechange', function () {
                  if (!navigator.onLine || model.get('status') === 'Draft') {
                    app.view.pendingQueue();
                  } else {
                    model.once('processed', function () {
                      var result = model.get('result');
                      if (model.get('status') === 'Submitted') {
                        if (!isHTML(result)) {
                          data = '
' + result + '
';
                        }
                        app.view.popup(result);
                        model.destroy();
                      } else {
                        app.view.pendingQueue();
                      }
                    });
                    app.pending.processQueue();
                  }
                });
              }
              resolve(updatedModel);
            };

            options.error = reject;

            data._action = view.model.get('blinkFormAction');
            modelAttrs = {
              type: 'Form',
              status: status,
              name: view.model.get('blinkFormObjectName'),
              label: view.model.get('displayName'),
              action: view.model.get('blinkFormAction'),
              answerspaceid: view.model.get('dbid'),
              data: data
            };
            if (view.model.get('args')['args[pid]']) {
              model = app.pending.get(view.model.get('args')['args[pid]']);
              model.save(modelAttrs, options);
            } else {
              model = app.pending.create(modelAttrs, options);
            }
          });
        });
      }
    });

    return FormControlView;
  }
);
<commit_msg>BIC-145 # fixed form submission going back too far when there is no url history<commit_after>define(['text!template-form-controls.mustache',
  'model-application',
  'feature!api',
  'enum-user-actions'],
  function (Template, app, API, USER_ACTIONS) {
    'use strict';

    var isHTML = function (string) {
      var html$;
      if (!string || typeof string !== 'string' || !string.trim()) {
        return false;
      }
      if (typeof Node === 'undefined' || !Node.TEXT_NODE) {
        return true; // the string _might_ be HTML, we just can't tell
      }
      html$ = $.parseHTML(string);
      return !html$.every(function (el) {
        return el.nodeType === Node.TEXT_NODE;
      });
    };

    var checkForFormErrors = function(view, userAction){
      // helper function for better control over what happens when
      // an item is added to the queue
      return function(){
        if (_.isEmpty(BlinkForms.current.getErrors())) {
          view.formLeave(userAction);
        } else {
          $(window).trigger('pagechange');
        }
      };
    };

    var FormControlView = Backbone.View.extend({

      events: {
        'click #FormControls #submit': 'formSubmit',
        'click #FormControls #close': 'formClose',
        'click #FormControls #save': 'formSave2',
        'click #nextFormPage': 'nextFormPage',
        'click #previousFormPage': 'previousFormPage'
      },

      render: function () {
        var view, options;

        view = this;
        options = {
          hasStorage: app.hasStorage()
        };

        if (BlinkForms.current.get('pages').length > 1) {
          options.pages = {
            current: BlinkForms.current.get('pages').current.index() + 1,
            total: BlinkForms.current.get('pages').length
          };

          if (BlinkForms.current.get('pages').current.index() !== 0) {
            options.pages.previous = true;
          }

          if (BlinkForms.current.get('pages').current.index() !== BlinkForms.current.get('pages').length - 1) {
            options.pages.next = true;
          }
        }

        view.$el.html(Mustache.render(Template, options));
        $.mobile.activePage.trigger('pagecreate');

        return view;
      },

      nextFormPage: function () {
        var view, index;

        view = this;
        index = BlinkForms.current.get('pages').current.index();

        if (index < BlinkForms.current.get('pages').length - 1) {
          BlinkForms.current.get('pages').goto(index + 1);
        }

        view.render();
      },

      previousFormPage: function () {
        var view, index;

        view = this;
        index = BlinkForms.current.get('pages').current.index();

        if (index > 0) {
          BlinkForms.current.get('pages').goto(index - 1);
        }

        view.render();
      },

      formLeave: function (userAction) {
        var onLeave = BlinkForms.current.get('onFormLeaveInteraction');

        if ( onLeave && onLeave[this.model.get('blinkFormAction')] ){
          return onLeave[this.model.get('blinkFormAction')]({ model: BlinkForms.current, userAction: userAction});
        }

        if (window.BMP.BIC3.history.length <= 1) {
          return window.BMP.BIC3.view.home();
        }

        history.back();
      },

      formSubmit: function () {
        var me = this;
        var model = this.model;
        if (app.hasStorage()) {
          if (_.isEmpty(BlinkForms.current.getErrors())) {
            this.addToQueue('Pending')
                .then(checkForFormErrors(this, USER_ACTIONS.SUBMIT));
          } else {
            this.addToQueue('Draft')
                .then(checkForFormErrors(this, USER_ACTIONS.SUBMIT));
          }
        } else {
          $.mobile.loading('show');
          BlinkForms.current.data()
            .then(function (data) {
              return API.setPendingItem(
                model.get('blinkFormObjectName'),
                model.get('blinkFormAction'),
                data
              );
            })
            .then(function (data) {
              if (!isHTML(data)) {
                data = '
' + data + '
';
              }
              app.view.popup(data);
              $('#popup').one('popupafterclose', function () {
                me.formLeave(USER_ACTIONS.SUBMIT);
              });
            }, function (jqXHR) {
              var status = jqXHR.status;
              var json;
              var html = '';
              try {
                json = JSON.parse(jqXHR.responseText);
              } catch (ignore) {
                json = { message: 'error ' + status };
              }
              if (json.message) {
                html += json.message;
              }
              if (status === 470 || status === 471 || json.errors) {
                if (typeof json.errors === 'object') {
                  html += '
';
                  Object.keys(json.errors).forEach(function (key) {
                    html += '
' + key + ': ' + json.errors[key] + '
';
                  });
                  html += '
';
                }
              }
              if (!isHTML(html)) {
                html = '
' + html + '
';
              }
              app.view.popup(html);
            }).then(function () {
              $.mobile.loading('hide');
            });
        }
      },

      formClose: function () {
        var that = this;
        $('#closePopup').popup({
          afteropen: function (event) {
            $(event.target).on('click', '#save', {view: that}, that.formSave.bind(that));
            $(event.target).on('click', '#discard', {view: that}, that.formDiscard.bind(that));
          },
          afterclose: function (event) {
            $(event.target).off('click', '#save');
            $(event.target).off('click', '#discard');
          }
        });
        $('#closePopup').popup('open');
      },

      formSave: function (e) {
        var me = this;
        e.data.view.addToQueue('Draft')
         .then(function(){
          $('#closePopup').one('popupafterclose', function () { me.formLeave(USER_ACTIONS.SAVE); });
          $('#closePopup').popup('close');
         });
      },

      formSave2: function () {
        this.addToQueue("Draft")
            .then(checkForFormErrors(this, USER_ACTIONS.SAVE));
      },

      formDiscard: function () {
        var me = this;
        $('#closePopup').one('popupafterclose', function () {
          me.formLeave(USER_ACTIONS.DISCARD);
        });
        $('#closePopup').popup('close');
      },

      addToQueue: function (status, supressQueue) {
        var view = this;
        var model;
        supressQueue = supressQueue || false;

        return new Promise(function (resolve, reject) {
          BlinkForms.current.data().then(function (data) {
            var modelAttrs;
            var options = {};

            options.success = function (updatedModel) {
              if (!supressQueue) {
                $(window).one('pagechange', function () {
                  if (!navigator.onLine || model.get('status') === 'Draft') {
                    app.view.pendingQueue();
                  } else {
                    model.once('processed', function () {
                      var result = model.get('result');
                      if (model.get('status') === 'Submitted') {
                        if (!isHTML(result)) {
                          data = '
' + result + '
';
                        }
                        app.view.popup(result);
                        model.destroy();
                      } else {
                        app.view.pendingQueue();
                      }
                    });
                    app.pending.processQueue();
                  }
                });
              }
              resolve(updatedModel);
            };

            options.error = reject;

            data._action = view.model.get('blinkFormAction');
            modelAttrs = {
              type: 'Form',
              status: status,
              name: view.model.get('blinkFormObjectName'),
              label: view.model.get('displayName'),
              action: view.model.get('blinkFormAction'),
              answerspaceid: view.model.get('dbid'),
              data: data
            };
            if (view.model.get('args')['args[pid]']) {
              model = app.pending.get(view.model.get('args')['args[pid]']);
              model.save(modelAttrs, options);
            } else {
              model = app.pending.create(modelAttrs, options);
            }
          });
        });
      }
    });

    return FormControlView;
  }
);
<|endoftext|>


<commit_before>/**
 * Brackets Themes Copyright (c) 2014 Miguel Castillo.
 *
 * Licensed under MIT
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global $, define, require, Mustache */

define(function(require, exports, module) {
    "use strict";

    var _themes = {};

    var defaults = {
        "fontSize": 12,
        "lineHeight": '1.3em',
        "fontType": "'SourceCodePro-Medium', ＭＳ ゴシック, 'MS Gothic', monospace",
        "customScrollbars": true,
        "themes": ["default"]
    };

    var templates = {
        "settings": require("text!htmlContent/themes-settings.html")
    };

    var _                  = require("thirdparty/lodash"),
        Dialogs            = require("widgets/Dialogs"),
        Strings            = require("strings"),
        PreferencesManager = require("preferences/PreferencesManager"),
        prefs              = PreferencesManager.getExtensionPrefs("brackets-themes");

    // Setup all the templates so that we can easily render them with Mustache
    var $settings = $(templates.settings).addClass("themeSettings");

    function showDialog() {
        var currentSettings = getValues("themes", "fontSize", "fontType", "lineHeight", "customScrollbars");
        var newSettings     = {};
        var themes          = _.map(_themes, function(theme) {return theme;});
        var template        = $("<div>").append($settings).html();
        var $template       = $(Mustache.render(template, {"settings": currentSettings, "themes": themes, "Strings": Strings}));

        // Select the correct theme.
        _.each(currentSettings.themes, function(item) {
            $template
                .find("[value='" + item + "']")
                .attr("selected", "selected");
        });

        $template
            .find("[data-toggle=tab].default")
            .tab("show");

        $template
            .on("change", "[data-target]:checkbox", function() {
                var $target = $(this);
                var attr = $target.attr("data-target");
                newSettings[attr] = $target.is(":checked");
            })
            .on("change", "[data-target]:text", function() {
                var $target = $(this);
                var attr = $target.attr("data-target");
                newSettings[attr] = $target.val();
            })
            .on("change", function() {
                var $target = $(":selected", this);
                var attr = $target.attr("data-target");
                if (attr) {
                    var items = $target.map(function(i, item) {return $(item).val();}).toArray();
                    prefs.set(attr, items);
                }
            });

        Dialogs.showModalDialogUsingTemplate($template).done(function (id) {
            if (id === "save") {
                for (var i in newSettings) {
                    if (currentSettings.hasOwnProperty(i)) {
                        prefs.set(i, newSettings[i]);
                    }
                }
            }
        });
    }

    function setThemes(themes) {
        _themes = themes;
    }

    function getValues() {
        return _.transform(arguments, function(result, value, key) {
            result[value] = prefs.get(value);
        });
    }

    function reset() {
        prefs.set("themes",           defaults.themes);
        prefs.set("fontSize",         defaults.fontSize + "px");
        prefs.set("lineHeight",       defaults.lineHeight);
        prefs.set("fontType",         defaults.fontType);
        prefs.set("customScrollbars", defaults.customScrollbars);
    }

    prefs.definePreference("themes", "array", defaults.themes);
    prefs.definePreference("fontSize", "string", defaults.fontSize + "px");
    prefs.definePreference("lineHeight", "string", defaults.lineHeight);
    prefs.definePreference("fontType", "string", defaults.fontType);
    prefs.definePreference("customScrollbars", "boolean", defaults.customScrollbars);

    // Exposed API
    exports.defaults   = defaults;
    exports.setThemes  = setThemes;
    exports.showDialog = showDialog;
});
<commit_msg>Export reset settings interface and set the default theme to thor-light-theme<commit_after>/**
 * Brackets Themes Copyright (c) 2014 Miguel Castillo.
 *
 * Licensed under MIT
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global $, define, require, Mustache */

define(function(require, exports, module) {
    "use strict";

    var _themes = {};

    var defaults = {
        "fontSize": 12,
        "lineHeight": '1.3em',
        "fontType": "'SourceCodePro-Medium', ＭＳ ゴシック, 'MS Gothic', monospace",
        "customScrollbars": true,
        "themes": ["thor-light-theme"]
    };

    var templates = {
        "settings": require("text!htmlContent/themes-settings.html")
    };

    var _                  = require("thirdparty/lodash"),
        Dialogs            = require("widgets/Dialogs"),
        Strings            = require("strings"),
        PreferencesManager = require("preferences/PreferencesManager"),
        prefs              = PreferencesManager.getExtensionPrefs("brackets-themes");

    // Setup all the templates so that we can easily render them with Mustache
    var $settings = $(templates.settings).addClass("themeSettings");

    function showDialog() {
        var currentSettings = getValues("themes", "fontSize", "fontType", "lineHeight", "customScrollbars");
        var newSettings     = {};
        var themes          = _.map(_themes, function(theme) {return theme;});
        var template        = $("<div>").append($settings).html();
        var $template       = $(Mustache.render(template, {"settings": currentSettings, "themes": themes, "Strings": Strings}));

        // Select the correct theme.
        _.each(currentSettings.themes, function(item) {
            $template
                .find("[value='" + item + "']")
                .attr("selected", "selected");
        });

        $template
            .find("[data-toggle=tab].default")
            .tab("show");

        $template
            .on("change", "[data-target]:checkbox", function() {
                var $target = $(this);
                var attr = $target.attr("data-target");
                newSettings[attr] = $target.is(":checked");
            })
            .on("change", "[data-target]:text", function() {
                var $target = $(this);
                var attr = $target.attr("data-target");
                newSettings[attr] = $target.val();
            })
            .on("change", function() {
                var $target = $(":selected", this);
                var attr = $target.attr("data-target");
                if (attr) {
                    var items = $target.map(function(i, item) {return $(item).val();}).toArray();
                    prefs.set(attr, items);
                }
            });

        Dialogs.showModalDialogUsingTemplate($template).done(function (id) {
            if (id === "save") {
                for (var i in newSettings) {
                    if (currentSettings.hasOwnProperty(i)) {
                        prefs.set(i, newSettings[i]);
                    }
                }
            }
        });
    }

    function setThemes(themes) {
        _themes = themes;
    }

    function getValues() {
        return _.transform(arguments, function(result, value) {
            result[value] = prefs.get(value);
        });
    }

    function reset() {
        prefs.set("themes",           defaults.themes);
        prefs.set("fontSize",         defaults.fontSize + "px");
        prefs.set("lineHeight",       defaults.lineHeight);
        prefs.set("fontType",         defaults.fontType);
        prefs.set("customScrollbars", defaults.customScrollbars);
    }

    prefs.definePreference("themes", "array", defaults.themes);
    prefs.definePreference("fontSize", "string", defaults.fontSize + "px");
    prefs.definePreference("lineHeight", "string", defaults.lineHeight);
    prefs.definePreference("fontType", "string", defaults.fontType);
    prefs.definePreference("customScrollbars", "boolean", defaults.customScrollbars);

    // Exposed API
    exports.defaults   = defaults;
    exports.reset      = reset;
    exports.setThemes  = setThemes;
    exports.showDialog = showDialog;
});
<|endoftext|>


<commit_before>var components = [];

////
// This is the "Application" controller and selection model
//
Viewer = {
  loc: '',
  apps: {},
  current_app: { state: { app: 'mobilize' }},
  prev_agents: null,
  rendered: false,

  open: function(tag) {
    $('#welcome').remove();
    unreveal();
    if (!Viewer.selected_city) {
      var city_id = tag.resource().city_id;
      Viewer.go('/organize/your_personal_squad/City__' + city_id + '/' + tag);
    } else {
      [Viewer.current_app, Viewer].dispatch(
        'marker_clicked', tag, Viewer.current_app.state);
    }
  },

  marker_clicked: function(tag) {
    Viewer.go('/organize/your_personal_squad/:city/' + tag);
  },

  breadcrumb_change: function(new_url, state) {
    Viewer.go(new_url);
  },

  dispatch: function(method, args) {
    var args = $.makeArray(arguments);
    var method = args.shift();
    return [Viewer.current_app, Viewer].dispatch(method, args[0], args[1], args[2], args[3]);
  },

  parse_url: function(url) {
    if (url == '..')   return Viewer.loc.slice(0, Viewer.loc.lastIndexOf('/'));
    if (url[0] != '/') return Viewer.loc + '/' + url;
    return url.replace(/\/:(\w+)/g, function(x, attr){
      if (Viewer.current_app.state[attr]) return '/' + Viewer.current_app.state[attr];
      return '';
    });
  },

  go: function(url) {
    // adjust url
    if (url[0] == '#') {
      url = url.slice(1);
      var parts = url.split('/');
      return Viewer.dispatch(parts[0], Viewer.current_app.state, parts[1]);
    }

    url = Viewer.loc = Viewer.parse_url(url);
    var parts = url.slice(1).split('/');
    var app_name = parts.shift();

    // switch apps if necessary
    if (!Viewer.apps[app_name]) { alert('invalid url: ' + url); return; }
    if (app_name != Viewer.current_app_name) {
      $('body').removeClass(Viewer.current_app_name + "_app").addClass(app_name + "_app");
      Viewer.apps[app_name].state = { first: true, app: app_name };
      Viewer.current_app_name = app_name;
    }
    var app = Viewer.current_app = Viewer.apps[app_name];

    // extract url part info into state
    var state = app.state;
    var renderer = "show_root";
    $.each(app.url_part_labels, function(i, label){
      var x = parts[i];
      if (state[label] != x || state.first) {
        state[label] = x;
        Viewer.dispatch("set_" + label, x, state);
      }
      if (x) renderer = "show_" + label;
    });

    // update agents, facebar, and map city
    if (Viewer.prev_agents != state.agents || !Viewer.prev_agents) {
      if (!state.agents)
        state.agents = state.city ? Agents.in_city(state.city) : Agents.all;
      MapMarkers.display(state.city, state.agents);
      Frame.populate_flexbar_agents(state.agents);
      Viewer.prev_agents = state.agents;
    }

    // run renderer
    Viewer.rendered = false;
    app[renderer] && app[renderer](state);
    if (!Viewer.rendered) Viewer.render(renderer);

    // clean up
    $('#breadcrumbs').html(Viewer.breadcrumbs()).val(url);
    delete state.first;
  },

  breadcrumbs: function() {
    var app_name = Viewer.current_app_name;
    var app = Viewer.current_app;
    var state = app.state;
    var breadcrumbs = [];
    var breadcrumb_url = '/' + app_name;

    // push World
    breadcrumbs.push(tag('option', {value:'/hero', content:'World'}));

    $.each(app.url_part_labels, function(i, label){
      var x = state[label];
      if (x) {
        breadcrumb_url += "/" + x;
        var breadcrumb_label = state[label + "_label"] || x;
        breadcrumbs.push(tag('option', {value:breadcrumb_url, content:breadcrumb_label}));
      }
    });
    return breadcrumbs.join(' ');
  },

  render: function(renderer) {
    var app_name = Viewer.current_app_name;
    var app = Viewer.current_app;
    var state = app.state;

    // Map.Gmap && Map.Gmap.closeInfoWindow();
    if (Viewer.prev_renderer) $('body').removeClass(Viewer.prev_renderer);

    Viewer.prev_renderer = renderer;
    $('body').addClass(renderer);
    $('.palette').app_paint();
    $('#' + app_name + "_" + renderer).app_paint();
    Viewer.rendered = true;
  },

  set_city: function(city, state) {
    delete state.agents;
    if (!city) CityChooser.update();
    if (city) state.city_label = cities[city.split('__')[1]];
    Viewer.selected_city = city && city.resource_id();
    components.trigger('city_changed', city);
  },

  set_item: function(item, state) {
    Viewer.item = item;
    if (!item) return state.agents = null;
    state.item_r = item && item.resource();
    if (!state.item_r) alert("no item lookup for: " + item);
    state.item_label = state.item_r.title;
  },

  // functions

  zoom_out: function(){ Viewer.go('/'); return false; },

  // dyn fills

  blank:       function(){ return ''; }

};

$.fn.app_paint = function(){
  var data = {};
  this.find('[fill]').each(function(){
    var obj = $(this);
    var method = obj.attr('fill');
    var attr = false;
    if (method.contains(" ")) {
      var parts = method.split(' ');
      method = parts[0];
      attr = parts[1];
    }
    if (!data[method] && Viewer.current_app.state[method])
      data[method] = Viewer.current_app.state[method];
    if (!data[method]) {
      var f = Viewer.current_app[method] || Viewer[method];
      if (f) data[method] = f(Viewer.current_app.state);
      else   alert('missing fill method: ' + method);
    }
    if (data[method]) {
      if (attr) obj.attr(attr, data[method]);
      else      obj.html(data[method]);
    }
  });
  this.find('form').enable().unbind('submit').submit(function(){
    try {
      $(this).disable();
      var method = this.id + "_submitted";
      if (!Viewer.current_app[method]) alert('unusual form submit!');
      Viewer.current_app[method]($(this).form_values(), Viewer.current_app.state, this);
    } catch (err) {
      alert('form error!');
      console.log(err);
      return false;
    }
    return false;
  });
  return this.feature_paint();
};
<commit_msg>limit maximum length of breadcrumb labels<commit_after>var components = [];

////
// This is the "Application" controller and selection model
//
Viewer = {
  loc: '',
  apps: {},
  current_app: { state: { app: 'mobilize' }},
  prev_agents: null,
  rendered: false,

  open: function(tag) {
    $('#welcome').remove();
    unreveal();
    if (!Viewer.selected_city) {
      var city_id = tag.resource().city_id;
      Viewer.go('/organize/your_personal_squad/City__' + city_id + '/' + tag);
    } else {
      [Viewer.current_app, Viewer].dispatch(
        'marker_clicked', tag, Viewer.current_app.state);
    }
  },

  marker_clicked: function(tag) {
    Viewer.go('/organize/your_personal_squad/:city/' + tag);
  },

  breadcrumb_change: function(new_url, state) {
    Viewer.go(new_url);
  },

  dispatch: function(method, args) {
    var args = $.makeArray(arguments);
    var method = args.shift();
    return [Viewer.current_app, Viewer].dispatch(method, args[0], args[1], args[2], args[3]);
  },

  parse_url: function(url) {
    if (url == '..')   return Viewer.loc.slice(0, Viewer.loc.lastIndexOf('/'));
    if (url[0] != '/') return Viewer.loc + '/' + url;
    return url.replace(/\/:(\w+)/g, function(x, attr){
      if (Viewer.current_app.state[attr]) return '/' + Viewer.current_app.state[attr];
      return '';
    });
  },

  go: function(url) {
    // adjust url
    if (url[0] == '#') {
      url = url.slice(1);
      var parts = url.split('/');
      return Viewer.dispatch(parts[0], Viewer.current_app.state, parts[1]);
    }

    url = Viewer.loc = Viewer.parse_url(url);
    var parts = url.slice(1).split('/');
    var app_name = parts.shift();

    // switch apps if necessary
    if (!Viewer.apps[app_name]) { alert('invalid url: ' + url); return; }
    if (app_name != Viewer.current_app_name) {
      $('body').removeClass(Viewer.current_app_name + "_app").addClass(app_name + "_app");
      Viewer.apps[app_name].state = { first: true, app: app_name };
      Viewer.current_app_name = app_name;
    }
    var app = Viewer.current_app = Viewer.apps[app_name];

    // extract url part info into state
    var state = app.state;
    var renderer = "show_root";
    $.each(app.url_part_labels, function(i, label){
      var x = parts[i];
      if (state[label] != x || state.first) {
        state[label] = x;
        Viewer.dispatch("set_" + label, x, state);
      }
      if (x) renderer = "show_" + label;
    });

    // update agents, facebar, and map city
    if (Viewer.prev_agents != state.agents || !Viewer.prev_agents) {
      if (!state.agents)
        state.agents = state.city ? Agents.in_city(state.city) : Agents.all;
      MapMarkers.display(state.city, state.agents);
      Frame.populate_flexbar_agents(state.agents);
      Viewer.prev_agents = state.agents;
    }

    // run renderer
    Viewer.rendered = false;
    app[renderer] && app[renderer](state);
    if (!Viewer.rendered) Viewer.render(renderer);

    // clean up
    $('#breadcrumbs').html(Viewer.breadcrumbs()).val(url);
    delete state.first;
  },

  breadcrumbs: function() {
    var app_name = Viewer.current_app_name;
    var app = Viewer.current_app;
    var state = app.state;
    var breadcrumbs = [];
    var breadcrumb_url = '/' + app_name;

    // push World
    breadcrumbs.push(tag('option', {value:'/hero', content:'World'}));

    $.each(app.url_part_labels, function(i, label){
      var x = state[label];
      if (x) {
        breadcrumb_url += "/" + x;
        var breadcrumb_label = state[label + "_label"] || x;
        var label_max = 25;
        if (breadcrumb_label.length > label_max) {
          breadcrumb_label = breadcrumb_label.slice(0, label_max) + ' ...';
        }
        breadcrumbs.push(tag('option', {value:breadcrumb_url, content:breadcrumb_label}));
      }
    });
    return breadcrumbs.join(' ');
  },

  render: function(renderer) {
    var app_name = Viewer.current_app_name;
    var app = Viewer.current_app;
    var state = app.state;

    // Map.Gmap && Map.Gmap.closeInfoWindow();
    if (Viewer.prev_renderer) $('body').removeClass(Viewer.prev_renderer);

    Viewer.prev_renderer = renderer;
    $('body').addClass(renderer);
    $('.palette').app_paint();
    $('#' + app_name + "_" + renderer).app_paint();
    Viewer.rendered = true;
  },

  set_city: function(city, state) {
    delete state.agents;
    if (!city) CityChooser.update();
    if (city) state.city_label = cities[city.split('__')[1]];
    Viewer.selected_city = city && city.resource_id();
    components.trigger('city_changed', city);
  },

  set_item: function(item, state) {
    Viewer.item = item;
    if (!item) return state.agents = null;
    state.item_r = item && item.resource();
    if (!state.item_r) alert("no item lookup for: " + item);
    state.item_label = state.item_r.title;
  },

  // functions

  zoom_out: function(){ Viewer.go('/'); return false; },

  // dyn fills

  blank:       function(){ return ''; }

};

$.fn.app_paint = function(){
  var data = {};
  this.find('[fill]').each(function(){
    var obj = $(this);
    var method = obj.attr('fill');
    var attr = false;
    if (method.contains(" ")) {
      var parts = method.split(' ');
      method = parts[0];
      attr = parts[1];
    }
    if (!data[method] && Viewer.current_app.state[method])
      data[method] = Viewer.current_app.state[method];
    if (!data[method]) {
      var f = Viewer.current_app[method] || Viewer[method];
      if (f) data[method] = f(Viewer.current_app.state);
      else   alert('missing fill method: ' + method);
    }
    if (data[method]) {
      if (attr) obj.attr(attr, data[method]);
      else      obj.html(data[method]);
    }
  });
  this.find('form').enable().unbind('submit').submit(function(){
    try {
      $(this).disable();
      var method = this.id + "_submitted";
      if (!Viewer.current_app[method]) alert('unusual form submit!');
      Viewer.current_app[method]($(this).form_values(), Viewer.current_app.state, this);
    } catch (err) {
      alert('form error!');
      console.log(err);
      return false;
    }
    return false;
  });
  return this.feature_paint();
};
<|endoftext|>