<commit_before>/**
 * @file ScrollableTable component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component, createRef} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import BaseTable from '../_BaseTable';

import Theme from '../Theme';
import TableFragment from '../_statics/TableFragment';
import HorizontalAlign from '../_statics/HorizontalAlign';
import SelectMode from '../_statics/SelectMode';
import SelectAllMode from '../_statics/SelectAllMode';
import SortingType from '../_statics/SortingType';

import Util from '../_vendors/Util';

class ScrollableTable extends Component {

    static Fragment = TableFragment;
    static Align = HorizontalAlign;
    static Fixed = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.headScroller = createRef();
        this.bodyScroller = createRef();
        this.footScroller = createRef();

    }

    componentDidMount() {

        const {
            isHeadFixed, isFootFixed,
            onGetBodyScrollerEl, onGetHeadScrollerEl, onGetFootScrollerEl
        } = this.props;

        this.bodyScroller && onGetBodyScrollerEl && onGetBodyScrollerEl(this.bodyScroller.current);

        isHeadFixed && this.headScroller && onGetHeadScrollerEl
        && onGetHeadScrollerEl(this.headScroller.current);

        isFootFixed && this.footScroller && onGetFootScrollerEl
        && onGetFootScrollerEl(this.footScroller.current);

    }

    render() {

        const {

            className, style, bodyScrollerStyle, maskStyle, tableStyle, footStyle, columns, headColumns, bodyColumns,
            isHeadHidden, isFootHidden, isHeadFixed, isFootFixed, isFixedHeadHidden, isFixedFootHidden,
            hasFixedLeftColumn, hasFixedRightColumn, onScroll, onWheel,

            // not passing down these props
            onGetHeadScrollerEl, onGetBodyScrollerEl, onGetFootScrollerEl,

            ...restProps

        } = this.props;

        if (!columns && !headColumns && !bodyColumns) {
            return null;
        }

        return (

                {
                    !isHeadHidden && isHeadFixed && !isFixedHeadHidden ?

                                <BaseTable {...restProps}
                                           style={tableStyle}
                                           fragment={TableFragment.HEAD}
                                           columns={columns}
                                           headColumns={headColumns}
                                           bodyColumns={bodyColumns}/>

                        :
                        null
                }

                            <BaseTable {...restProps}
                                       style={tableStyle}
                                       columns={columns}
                                       headColumns={headColumns}
                                       bodyColumns={bodyColumns}
                                       isHeadFixed={isHeadFixed}
                                       isFootFixed={isFootFixed}
                                       isHeadHidden={isHeadHidden}
                                       isFootHidden={isFootHidden}/>

                {
                    !isFootHidden && isFootFixed && !isFixedFootHidden ?

                                <BaseTable {...restProps}
                                           style={tableStyle}
                                           fragment={TableFragment.FOOT}
                                           columns={columns}
                                           headColumns={headColumns}
                                           bodyColumns={bodyColumns}/>

                        :
                        null
                }

        );

    }
}

ScrollableTable.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    bodyScrollerStyle: PropTypes.object,
    maskStyle: PropTypes.object,
    tableStyle: PropTypes.object,
    footStyle: PropTypes.object,

    fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))]),

    /**
     * The theme of the table select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The select mode of table.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * The select all mode of table, all or current page.
     */
    selectAllMode: PropTypes.oneOf(Util.enumerateValue(SelectAllMode)),

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(PropTypes.shape({

        /**
         * fixed position of column ( true / 'left' / 'right' )
         */
        fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(Util.enumerateValue(HorizontalAlign))]),

        /**
         * width of column
         */
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * minimum width of column
         */
        minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

        /**
         * align of current column
         */
        align: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The class name of header.
         */
        headClassName: PropTypes.string,

        /**
         * Override the styles of header.
         */
        headStyle: PropTypes.object,

        /**
         * align of table header cell
         */
        headAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table head.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        headRenderer: PropTypes.any,

        /**
         * column span of table header
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        headSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * The class name of td.
         */
        bodyClassName: PropTypes.string,

        /**
         * Override the styles of td.
         */
        bodyStyle: PropTypes.object,

        /**
         * align of table body cell
         */
        bodyAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table body.
         *  (1) callback:
         *      function (rowData, rowIndex, colIndex) {
         *          return rowData.id;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        bodyRenderer: PropTypes.any,

        /**
         * column span of table body
         *  (1) function callback:
         *      function (rowData, colIndex, rowIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        bodySpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * The class name of footer.
         */
        footClassName: PropTypes.string,

        /**
         * Override the styles of footer.
         */
        footStyle: PropTypes.object,

        /**
         * align of table footer cell
         */
        footAlign: PropTypes.oneOf(Util.enumerateValue(HorizontalAlign)),

        /**
         * The render content in table foot.
         *  (1) callback:
         *      function (tableData, colIndex) {
         *          return colIndex;
         *      }
         *
         *  (2) others:
         *      render whatever you pass
         */
        footRenderer: PropTypes.any,

        /**
         * column span of table foot.
         *  (1) function callback:
         *      function (tableData, colIndex) {
         *          return null;
         *      }
         *
         *  (2) number:
         *      render whatever you pass
         */
        footSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),

        /**
         * If true,this column can be sorted.
         */
        sortable: PropTypes.bool,

        /**
         * Sorting property.
         */
        sortingProp: PropTypes.string,

        defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType))

    })),
    headColumns: PropTypes.array,
    bodyColumns: PropTypes.array,

    data: PropTypes.array,
    baseColIndex: PropTypes.number,
    expandRows: PropTypes.array,

    /**
     * sorting
     */
    sorting: PropTypes.shape({
        prop: PropTypes.string,
        type: PropTypes.oneOf(Util.enumerateValue(SortingType))
    }),
    defaultSortingType: PropTypes.oneOf(Util.enumerateValue(SortingType)),
    sortingAscIconCls: PropTypes.string,
    sortingDescIconCls: PropTypes.string,

    /**
     * fixed
     */
    isHeadFixed: PropTypes.bool,
    isFootFixed: PropTypes.bool,

    /**
     * hidden
     */
    isHeadHidden: PropTypes.bool,
    isFootHidden: PropTypes.bool,
    isFixedHeadHidden: PropTypes.bool,
    isFixedFootHidden: PropTypes.bool,

    /**
     * fixed column
     */
    hasFixedLeftColumn: PropTypes.bool,
    hasFixedRightColumn: PropTypes.bool,

    /**
     * callback
     */
    onGetBodyScrollerEl: PropTypes.func,
    onGetHeadScrollerEl: PropTypes.func,
    onGetFootScrollerEl: PropTypes.func,
    onExpandChange: PropTypes.func,
    onScroll: PropTypes.func,
    onWheel: PropTypes.func,
    onSortChange: PropTypes.func

};

ScrollableTable.defaultProps = {

    selectMode: SelectMode.SINGLE_SELECT,
    selectAllMode: SelectAllMode.CURRENT_PAGE,

    baseColIndex: 0,
    expandRows: [],

    /**
     * sorting
     */
    defaultSortingType: SortingType.ASC,
    sortingAscIconCls: 'fas fa-sort-up',
    sortingDescIconCls: 'fas fa-sort-down',

    /**
     * fixed
     */
    isHeadFixed: false,
    isFootFixed: false,

    /**
     * hidden
     */
    isHeadHidden: false,
    isFootHidden: false,
    isFixedHeadHidden: false,
    isFixedFootHidden: false,

    /**
     * fixed column
     */
    hasFixedLeftColumn: false,
    hasFixedRightColumn: false

};

export default ScrollableTable;
<commit_msg>update ScrollableTable<commit_after>/**
 * @file ScrollableTable component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

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

class ScrollableTable extends Component {

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    render() {

        const {children, scroll, ...restProps} = this.props;

        return scroll ?

                {children}

            :
            children;

    }
}

ScrollableTable.propTypes = {

    children: PropTypes.any,

    /**
     * scroll
     */
    scroll: PropTypes.shape({
        width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
        maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
    })

};

export default ScrollableTable;
<|endoftext|>


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

/**
 * @fileoverview A disposable implementation of a custom
 * listenable/event target. See also: documentation for
 * {@code goog.events.Listenable}.
 *
 * @author arv@google.com (Erik Arvidsson) [Original implementation]
 * @see ../demos/eventtarget.html
 * @see goog.events.Listenable
 */

goog.provide('goog.events.EventTarget');

goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.Listenable');
goog.require('goog.events.ListenerMap');
goog.require('goog.object');

/**
 * An implementation of {@code goog.events.Listenable} with full W3C
 * EventTarget-like support (capture/bubble mechanism, stopping event
 * propagation, preventing default actions).
 *
 * You may subclass this class to turn your class into a Listenable.
 *
 * Unless propagation is stopped, an event dispatched by an
 * EventTarget will bubble to the parent returned by
 * {@code getParentEventTarget}. To set the parent, call
 * {@code setParentEventTarget}. Subclasses that don't support
 * changing the parent can override the setter to throw an error.
 *
 * Example usage:
 *

 *   var source = new goog.events.EventTarget();
 *   function handleEvent(e) {
 *     alert('Type: ' + e.type + '; Target: ' + e.target);
 *   }
 *   source.listen('foo', handleEvent);
 *   // Or: goog.events.listen(source, 'foo', handleEvent);
 *   ...
 *   source.dispatchEvent('foo');  // will call handleEvent
 *   ...
 *   source.unlisten('foo', handleEvent);
 *   // Or: goog.events.unlisten(source, 'foo', handleEvent);
 *

 *
 * @constructor
 * @extends {goog.Disposable}
 * @implements {goog.events.Listenable}
 */
goog.events.EventTarget = function() {
  goog.Disposable.call(this);

  /**
   * Maps of event type to an array of listeners.
   * @private {!goog.events.ListenerMap}
   */
  this.eventTargetListeners_ = new goog.events.ListenerMap(this);

  /**
   * The object to use for event.target. Useful when mixing in an
   * EventTarget to another object.
   * @private {!Object}
   */
  this.actualEventTarget_ = this;

  /**
   * Parent event target, used during event bubbling.
   *
   * TODO(chrishenry): Change this to goog.events.Listenable. This
   * currently breaks people who expect getParentEventTarget to return
   * goog.events.EventTarget.
   *
   * @private {goog.events.EventTarget}
   */
  this.parentEventTarget_ = null;
};
goog.inherits(goog.events.EventTarget, goog.Disposable);
goog.events.Listenable.addImplementation(goog.events.EventTarget);

/**
 * An artificial cap on the number of ancestors you can have. This is mainly
 * for loop detection.
 * @const {number}
 * @private
 */
goog.events.EventTarget.MAX_ANCESTORS_ = 1000;

/**
 * Returns the parent of this event target to use for bubbling.
 *
 * @return {goog.events.EventTarget} The parent EventTarget or null if
 *     there is no parent.
 * @override
 */
goog.events.EventTarget.prototype.getParentEventTarget = function() {
  return this.parentEventTarget_;
};

/**
 * Sets the parent of this event target to use for capture/bubble
 * mechanism.
 * @param {goog.events.EventTarget} parent Parent listenable (null if none).
 */
goog.events.EventTarget.prototype.setParentEventTarget = function(parent) {
  this.parentEventTarget_ = parent;
};

/**
 * Adds an event listener to the event target. The same handler can only be
 * added once per the type. Even if you add the same handler multiple times
 * using the same type then it will only be called once when the event is
 * dispatched.
 *
 * @param {string} type The type of the event to listen for.
 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
 *     to handle the event. The handler can also be an object that implements
 *     the handleEvent method which takes the event object as argument.
 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
 *     whether the listener is fired during the capture or bubble phase
 *     of the event.
 * @param {Object=} opt_handlerScope Object in whose scope to call
 *     the listener.
 * @deprecated Use {@code #listen} instead, when possible. Otherwise, use
 *     {@code goog.events.listen} if you are passing Object
 *     (instead of Function) as handler.
 */
goog.events.EventTarget.prototype.addEventListener = function(
    type, handler, opt_capture, opt_handlerScope) {
  goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);
};

/**
 * Removes an event listener from the event target. The handler must be the
 * same object as the one added. If the handler has not been added then
 * nothing is done.
 *
 * @param {string} type The type of the event to listen for.
 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
 *     to handle the event. The handler can also be an object that implements
 *     the handleEvent method which takes the event object as argument.
 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
 *     whether the listener is fired during the capture or bubble phase
 *     of the event.
 * @param {Object=} opt_handlerScope Object in whose scope to call
 *     the listener.
 * @deprecated Use {@code #unlisten} instead, when possible. Otherwise, use
 *     {@code goog.events.unlisten} if you are passing Object
 *     (instead of Function) as handler.
 */
goog.events.EventTarget.prototype.removeEventListener = function(
    type, handler, opt_capture, opt_handlerScope) {
  goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);
};

/** @override */
goog.events.EventTarget.prototype.dispatchEvent = function(e) {
  this.assertInitialized_();

  var ancestorsTree, ancestor = this.getParentEventTarget();
  if (ancestor) {
    ancestorsTree = [];
    var ancestorCount = 1;
    for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
      ancestorsTree.push(ancestor);
      goog.asserts.assert(
          (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),
          'infinite loop');
    }
  }

  return goog.events.EventTarget.dispatchEventInternal_(
      this.actualEventTarget_, e, ancestorsTree);
};

/**
 * Removes listeners from this object.  Classes that extend EventTarget may
 * need to override this method in order to remove references to DOM Elements
 * and additional listeners.
 * @override
 */
goog.events.EventTarget.prototype.disposeInternal = function() {
  goog.events.EventTarget.superClass_.disposeInternal.call(this);

  this.removeAllListeners();
  this.parentEventTarget_ = null;
};

/** @override */
goog.events.EventTarget.prototype.listen = function(
    type, listener, opt_useCapture, opt_listenerScope) {
  this.assertInitialized_();
  return this.eventTargetListeners_.add(
      String(type), listener, false /* callOnce */, opt_useCapture,
      opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.listenOnce = function(
    type, listener, opt_useCapture, opt_listenerScope) {
  return this.eventTargetListeners_.add(
      String(type), listener, true /* callOnce */, opt_useCapture,
      opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.unlisten = function(
    type, listener, opt_useCapture, opt_listenerScope) {
  return this.eventTargetListeners_.remove(
      String(type), listener, opt_useCapture, opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.unlistenByKey = function(key) {
  return this.eventTargetListeners_.removeByKey(key);
};

/** @override */
goog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {
  // TODO(chrishenry): Previously, removeAllListeners can be called on
  // uninitialized EventTarget, so we preserve that behavior. We
  // should remove this when usages that rely on that fact are purged.
  if (!this.eventTargetListeners_) {
    return 0;
  }
  return this.eventTargetListeners_.removeAll(opt_type);
};

/** @override */
goog.events.EventTarget.prototype.fireListeners = function(
    type, capture, eventObject) {
  // TODO(chrishenry): Original code avoids array creation when there
  // is no listener, so we do the same. If this optimization turns
  // out to be not required, we can replace this with
  // getListeners(type, capture) instead, which is simpler.
  var listenerArray = this.eventTargetListeners_.listeners[String(type)];
  if (!listenerArray) {
    return true;
  }
  listenerArray = listenerArray.concat();

  var rv = true;
  for (var i = 0; i < listenerArray.length; ++i) {
    var listener = listenerArray[i];
    // We might not have a listener if the listener was removed.
    if (listener && !listener.removed && listener.capture == capture) {
      var listenerFn = listener.listener;
      var listenerHandler = listener.handler || listener.src;

      if (listener.callOnce) {
        this.unlistenByKey(listener);
      }
      rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
    }
  }

  return rv && eventObject.returnValue_ != false;
};

/** @override */
goog.events.EventTarget.prototype.getListeners = function(type, capture) {
  return this.eventTargetListeners_.getListeners(String(type), capture);
};

/** @override */
goog.events.EventTarget.prototype.getListener = function(
    type, listener, capture, opt_listenerScope) {
  return this.eventTargetListeners_.getListener(
      String(type), listener, capture, opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.hasListener = function(
    opt_type, opt_capture) {
  var id = goog.isDef(opt_type) ? String(opt_type) : undefined;
  return this.eventTargetListeners_.hasListener(id, opt_capture);
};

/**
 * Sets the target to be used for {@code event.target} when firing
 * event. Mainly used for testing. For example, see
 * {@code goog.testing.events.mixinListenable}.
 * @param {!Object} target The target.
 */
goog.events.EventTarget.prototype.setTargetForTesting = function(target) {
  this.actualEventTarget_ = target;
};

/**
 * Asserts that the event target instance is initialized properly.
 * @private
 */
goog.events.EventTarget.prototype.assertInitialized_ = function() {
  goog.asserts.assert(
      this.eventTargetListeners_,
      'Event target is not initialized. Did you call the superclass ' +
      '(goog.events.EventTarget) constructor?');
};

/**
 * Dispatches the given event on the ancestorsTree.
 *
 * @param {!Object} target The target to dispatch on.
 * @param {goog.events.Event|Object|string} e The event object.
 * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors
 *     tree of the target, in reverse order from the closest ancestor
 *     to the root event target. May be null if the target has no ancestor.
 * @return {boolean} If anyone called preventDefault on the event object (or
 *     if any of the listeners returns false) this will also return false.
 * @private
 */
goog.events.EventTarget.dispatchEventInternal_ = function(
    target, e, opt_ancestorsTree) {
  var type = e.type || /** @type {string} */ (e);

  // If accepting a string or object, create a custom event object so that
  // preventDefault and stopPropagation work with the event.
  if (goog.isString(e)) {
    e = new goog.events.Event(e, target);
  } else if (!(e instanceof goog.events.Event)) {
    var oldEvent = e;
    e = new goog.events.Event(type, target);
    goog.object.extend(e, oldEvent);
  } else {
    e.target = e.target || target;
  }

  var rv = true, currentTarget;

  // Executes all capture listeners on the ancestors, if any.
  if (opt_ancestorsTree) {
    for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
         i--) {
      currentTarget = e.currentTarget = opt_ancestorsTree[i];
      rv = currentTarget.fireListeners(type, true, e) && rv;
    }
  }

  // Executes capture and bubble listeners on the target.
  if (!e.propagationStopped_) {
    currentTarget = e.currentTarget = target;
    rv = currentTarget.fireListeners(type, true, e) && rv;
    if (!e.propagationStopped_) {
      rv = currentTarget.fireListeners(type, false, e) && rv;
    }
  }

  // Executes all bubble listeners on the ancestors, if any.
  if (opt_ancestorsTree) {
    for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
      currentTarget = e.currentTarget = opt_ancestorsTree[i];
      rv = currentTarget.fireListeners(type, false, e) && rv;
    }
  }

  return rv;
};
<commit_msg>Renamed goog.Disposabl.call to goog.base(<commit_after>// Copyright 2005 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview A disposable implementation of a custom
 * listenable/event target. See also: documentation for
 * {@code goog.events.Listenable}.
 *
 * @author arv@google.com (Erik Arvidsson) [Original implementation]
 * @see ../demos/eventtarget.html
 * @see goog.events.Listenable
 */

goog.provide('goog.events.EventTarget');

goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.Listenable');
goog.require('goog.events.ListenerMap');
goog.require('goog.object');

/**
 * An implementation of {@code goog.events.Listenable} with full W3C
 * EventTarget-like support (capture/bubble mechanism, stopping event
 * propagation, preventing default actions).
 *
 * You may subclass this class to turn your class into a Listenable.
 *
 * Unless propagation is stopped, an event dispatched by an
 * EventTarget will bubble to the parent returned by
 * {@code getParentEventTarget}. To set the parent, call
 * {@code setParentEventTarget}. Subclasses that don't support
 * changing the parent can override the setter to throw an error.
 *
 * Example usage:
 *

 *   var source = new goog.events.EventTarget();
 *   function handleEvent(e) {
 *     alert('Type: ' + e.type + '; Target: ' + e.target);
 *   }
 *   source.listen('foo', handleEvent);
 *   // Or: goog.events.listen(source, 'foo', handleEvent);
 *   ...
 *   source.dispatchEvent('foo');  // will call handleEvent
 *   ...
 *   source.unlisten('foo', handleEvent);
 *   // Or: goog.events.unlisten(source, 'foo', handleEvent);
 *

 *
 * @constructor
 * @extends {goog.Disposable}
 * @implements {goog.events.Listenable}
 */
goog.events.EventTarget = function() {
  goog.base(this);

  /**
   * Maps of event type to an array of listeners.
   * @private {!goog.events.ListenerMap}
   */
  this.eventTargetListeners_ = new goog.events.ListenerMap(this);

  /**
   * The object to use for event.target. Useful when mixing in an
   * EventTarget to another object.
   * @private {!Object}
   */
  this.actualEventTarget_ = this;

  /**
   * Parent event target, used during event bubbling.
   *
   * TODO(chrishenry): Change this to goog.events.Listenable. This
   * currently breaks people who expect getParentEventTarget to return
   * goog.events.EventTarget.
   *
   * @private {goog.events.EventTarget}
   */
  this.parentEventTarget_ = null;
};
goog.inherits(goog.events.EventTarget, goog.Disposable);
goog.events.Listenable.addImplementation(goog.events.EventTarget);

/**
 * An artificial cap on the number of ancestors you can have. This is mainly
 * for loop detection.
 * @const {number}
 * @private
 */
goog.events.EventTarget.MAX_ANCESTORS_ = 1000;

/**
 * Returns the parent of this event target to use for bubbling.
 *
 * @return {goog.events.EventTarget} The parent EventTarget or null if
 *     there is no parent.
 * @override
 */
goog.events.EventTarget.prototype.getParentEventTarget = function() {
  return this.parentEventTarget_;
};

/**
 * Sets the parent of this event target to use for capture/bubble
 * mechanism.
 * @param {goog.events.EventTarget} parent Parent listenable (null if none).
 */
goog.events.EventTarget.prototype.setParentEventTarget = function(parent) {
  this.parentEventTarget_ = parent;
};

/**
 * Adds an event listener to the event target. The same handler can only be
 * added once per the type. Even if you add the same handler multiple times
 * using the same type then it will only be called once when the event is
 * dispatched.
 *
 * @param {string} type The type of the event to listen for.
 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
 *     to handle the event. The handler can also be an object that implements
 *     the handleEvent method which takes the event object as argument.
 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
 *     whether the listener is fired during the capture or bubble phase
 *     of the event.
 * @param {Object=} opt_handlerScope Object in whose scope to call
 *     the listener.
 * @deprecated Use {@code #listen} instead, when possible. Otherwise, use
 *     {@code goog.events.listen} if you are passing Object
 *     (instead of Function) as handler.
 */
goog.events.EventTarget.prototype.addEventListener = function(
    type, handler, opt_capture, opt_handlerScope) {
  goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);
};

/**
 * Removes an event listener from the event target. The handler must be the
 * same object as the one added. If the handler has not been added then
 * nothing is done.
 *
 * @param {string} type The type of the event to listen for.
 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
 *     to handle the event. The handler can also be an object that implements
 *     the handleEvent method which takes the event object as argument.
 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
 *     whether the listener is fired during the capture or bubble phase
 *     of the event.
 * @param {Object=} opt_handlerScope Object in whose scope to call
 *     the listener.
 * @deprecated Use {@code #unlisten} instead, when possible. Otherwise, use
 *     {@code goog.events.unlisten} if you are passing Object
 *     (instead of Function) as handler.
 */
goog.events.EventTarget.prototype.removeEventListener = function(
    type, handler, opt_capture, opt_handlerScope) {
  goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);
};

/** @override */
goog.events.EventTarget.prototype.dispatchEvent = function(e) {
  this.assertInitialized_();

  var ancestorsTree, ancestor = this.getParentEventTarget();
  if (ancestor) {
    ancestorsTree = [];
    var ancestorCount = 1;
    for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
      ancestorsTree.push(ancestor);
      goog.asserts.assert(
          (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),
          'infinite loop');
    }
  }

  return goog.events.EventTarget.dispatchEventInternal_(
      this.actualEventTarget_, e, ancestorsTree);
};

/**
 * Removes listeners from this object.  Classes that extend EventTarget may
 * need to override this method in order to remove references to DOM Elements
 * and additional listeners.
 * @override
 */
goog.events.EventTarget.prototype.disposeInternal = function() {
  goog.events.EventTarget.superClass_.disposeInternal.call(this);

  this.removeAllListeners();
  this.parentEventTarget_ = null;
};

/** @override */
goog.events.EventTarget.prototype.listen = function(
    type, listener, opt_useCapture, opt_listenerScope) {
  this.assertInitialized_();
  return this.eventTargetListeners_.add(
      String(type), listener, false /* callOnce */, opt_useCapture,
      opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.listenOnce = function(
    type, listener, opt_useCapture, opt_listenerScope) {
  return this.eventTargetListeners_.add(
      String(type), listener, true /* callOnce */, opt_useCapture,
      opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.unlisten = function(
    type, listener, opt_useCapture, opt_listenerScope) {
  return this.eventTargetListeners_.remove(
      String(type), listener, opt_useCapture, opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.unlistenByKey = function(key) {
  return this.eventTargetListeners_.removeByKey(key);
};

/** @override */
goog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {
  // TODO(chrishenry): Previously, removeAllListeners can be called on
  // uninitialized EventTarget, so we preserve that behavior. We
  // should remove this when usages that rely on that fact are purged.
  if (!this.eventTargetListeners_) {
    return 0;
  }
  return this.eventTargetListeners_.removeAll(opt_type);
};

/** @override */
goog.events.EventTarget.prototype.fireListeners = function(
    type, capture, eventObject) {
  // TODO(chrishenry): Original code avoids array creation when there
  // is no listener, so we do the same. If this optimization turns
  // out to be not required, we can replace this with
  // getListeners(type, capture) instead, which is simpler.
  var listenerArray = this.eventTargetListeners_.listeners[String(type)];
  if (!listenerArray) {
    return true;
  }
  listenerArray = listenerArray.concat();

  var rv = true;
  for (var i = 0; i < listenerArray.length; ++i) {
    var listener = listenerArray[i];
    // We might not have a listener if the listener was removed.
    if (listener && !listener.removed && listener.capture == capture) {
      var listenerFn = listener.listener;
      var listenerHandler = listener.handler || listener.src;

      if (listener.callOnce) {
        this.unlistenByKey(listener);
      }
      rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
    }
  }

  return rv && eventObject.returnValue_ != false;
};

/** @override */
goog.events.EventTarget.prototype.getListeners = function(type, capture) {
  return this.eventTargetListeners_.getListeners(String(type), capture);
};

/** @override */
goog.events.EventTarget.prototype.getListener = function(
    type, listener, capture, opt_listenerScope) {
  return this.eventTargetListeners_.getListener(
      String(type), listener, capture, opt_listenerScope);
};

/** @override */
goog.events.EventTarget.prototype.hasListener = function(
    opt_type, opt_capture) {
  var id = goog.isDef(opt_type) ? String(opt_type) : undefined;
  return this.eventTargetListeners_.hasListener(id, opt_capture);
};

/**
 * Sets the target to be used for {@code event.target} when firing
 * event. Mainly used for testing. For example, see
 * {@code goog.testing.events.mixinListenable}.
 * @param {!Object} target The target.
 */
goog.events.EventTarget.prototype.setTargetForTesting = function(target) {
  this.actualEventTarget_ = target;
};

/**
 * Asserts that the event target instance is initialized properly.
 * @private
 */
goog.events.EventTarget.prototype.assertInitialized_ = function() {
  goog.asserts.assert(
      this.eventTargetListeners_,
      'Event target is not initialized. Did you call the superclass ' +
      '(goog.events.EventTarget) constructor?');
};

/**
 * Dispatches the given event on the ancestorsTree.
 *
 * @param {!Object} target The target to dispatch on.
 * @param {goog.events.Event|Object|string} e The event object.
 * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors
 *     tree of the target, in reverse order from the closest ancestor
 *     to the root event target. May be null if the target has no ancestor.
 * @return {boolean} If anyone called preventDefault on the event object (or
 *     if any of the listeners returns false) this will also return false.
 * @private
 */
goog.events.EventTarget.dispatchEventInternal_ = function(
    target, e, opt_ancestorsTree) {
  var type = e.type || /** @type {string} */ (e);

  // If accepting a string or object, create a custom event object so that
  // preventDefault and stopPropagation work with the event.
  if (goog.isString(e)) {
    e = new goog.events.Event(e, target);
  } else if (!(e instanceof goog.events.Event)) {
    var oldEvent = e;
    e = new goog.events.Event(type, target);
    goog.object.extend(e, oldEvent);
  } else {
    e.target = e.target || target;
  }

  var rv = true, currentTarget;

  // Executes all capture listeners on the ancestors, if any.
  if (opt_ancestorsTree) {
    for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
         i--) {
      currentTarget = e.currentTarget = opt_ancestorsTree[i];
      rv = currentTarget.fireListeners(type, true, e) && rv;
    }
  }

  // Executes capture and bubble listeners on the target.
  if (!e.propagationStopped_) {
    currentTarget = e.currentTarget = target;
    rv = currentTarget.fireListeners(type, true, e) && rv;
    if (!e.propagationStopped_) {
      rv = currentTarget.fireListeners(type, false, e) && rv;
    }
  }

  // Executes all bubble listeners on the ancestors, if any.
  if (opt_ancestorsTree) {
    for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
      currentTarget = e.currentTarget = opt_ancestorsTree[i];
      rv = currentTarget.fireListeners(type, false, e) && rv;
    }
  }

  return rv;
};
<|endoftext|>


<commit_before>(function(global) {
  "use strict";
  var GOVUK = global.GOVUK || {};
  var $ = global.jQuery;

  var selectProofOfAddress = {
    init: function (){
      selectProofOfAddress.$form = $('#new_select_proof_of_address_form');
      var errorMessage = selectProofOfAddress.$form.data('msg');
      if (selectProofOfAddress.$form.length === 1) {
        selectProofOfAddress.validator = selectProofOfAddress.$form.validate($.extend({}, GOVUK.validation.radiosValidation, {
          rules: {
            'select_proof_of_address_form[uk_bank_account_details]': 'required',
            'select_proof_of_address_form[debit_card]': 'required',
            'select_proof_of_address_form[credit_card]': 'required',
          },
            groups: {
                primary: 'select_proof_of_address_form[credit_card] select_proof_of_address_form[debit_card] select_proof_of_address_form[uk_bank_account_details]'
            },
          messages: {
            'select_proof_of_address_form[uk_bank_account_details]': errorMessage,
            'select_proof_of_address_form[debit_card]': errorMessage,
            'select_proof_of_address_form[credit_card]': errorMessage
          }
        }));
      }

      $('input[piwik_event_tracking]').change(function(event) {
          var target = $(event.target);
          var action = target.val() === 'true' ? 'yes' : 'no';
          var name = target.attr('piwik_event_tracking');
          _paq.push(['Evidence', action, name]);
      });
    }
  };

  GOVUK.selectProofOfAddress = selectProofOfAddress;

  global.GOVUK = GOVUK;
})(window);
<commit_msg>TT-681: Fix event tracking for analytics<commit_after>(function(global) {
  "use strict";
  var GOVUK = global.GOVUK || {};
  var $ = global.jQuery;

  var selectProofOfAddress = {
    init: function (){
      selectProofOfAddress.$form = $('#new_select_proof_of_address_form');
      var errorMessage = selectProofOfAddress.$form.data('msg');
      if (selectProofOfAddress.$form.length === 1) {
        selectProofOfAddress.validator = selectProofOfAddress.$form.validate($.extend({}, GOVUK.validation.radiosValidation, {
          rules: {
            'select_proof_of_address_form[uk_bank_account_details]': 'required',
            'select_proof_of_address_form[debit_card]': 'required',
            'select_proof_of_address_form[credit_card]': 'required',
          },
            groups: {
                primary: 'select_proof_of_address_form[credit_card] select_proof_of_address_form[debit_card] select_proof_of_address_form[uk_bank_account_details]'
            },
          messages: {
            'select_proof_of_address_form[uk_bank_account_details]': errorMessage,
            'select_proof_of_address_form[debit_card]': errorMessage,
            'select_proof_of_address_form[credit_card]': errorMessage
          }
        }));
      }

      $('input[piwik_event_tracking]').change(function(event) {
          var target = $(event.target);
          var action = target.val() === 'true' ? 'yes' : 'no';
          var name = target.attr('piwik_event_tracking');
          _paq.push(['trackEvent', 'Evidence', action, name]);
      });
    }
  };

  GOVUK.selectProofOfAddress = selectProofOfAddress;

  global.GOVUK = GOVUK;
})(window);
<|endoftext|>


<commit_before>import React from 'react'
import { A, IndexA } from './A'
import Icon from './Icon'
import { NonLinkAvatar } from './Avatar'
import Dropdown from './Dropdown'
import SearchInput from './SearchInput'
import { isAdmin } from '../models/currentUser'
import { filter, find, flow, get, map, sortBy } from 'lodash/fp'
import { throttle, merge } from 'lodash'
import { same } from '../models'
import { nextPath } from '../util/navigation'
import { MenuButton, leftNavWidth, leftNavEasing, menuButtonWidth } from './LeftNav'
import { editorUrl } from '../containers/StandalonePostEditor'
import { assetUrl } from '../util/assets'
const { array, object, func, string, bool } = React.PropTypes
import { viewportTop } from '../util/scrolling'
import { VelocityComponent } from 'velocity-react'
import cx from 'classnames'
import { navigate } from '../actions'

const getPostType = path => {
  if (path.endsWith('events')) return 'event'
  if (path.endsWith('projects')) return 'project'
}

const getLabel = path => {
  if (path.endsWith('events')) return 'Events'
  if (path.endsWith('projects')) return 'Projects'
  if (path.endsWith('people')) return 'Members'
  if (path.endsWith('about')) return 'About'
  if (path.endsWith('invite')) return 'Invite'
  if (path === '/' || path.match(/^\/c\/[^\/]+$/)) return 'Conversations'
  return 'Menu'
}

const allCommunities = () => ({
  id: null,
  avatar_url: assetUrl('/img/hylo-merkaba-300x300.png'),
  name: 'All Communities'
})

const getMenuItems = (currentUser, firstItem) =>
  [firstItem].concat(flow(
    get('memberships'),
    sortBy(m => -Date.parse(m.last_viewed_at || '2001-01-01')),
    map('community'),
    firstItem.isNetwork ? i => i : filter(c => !same('id', firstItem, c))
  )(currentUser))

const getCurrentMembership = (currentUser, community) =>
  flow(
    get('memberships'),
    find(m => m.community.id === get('id', community))
  )(currentUser)

export default class TopNav extends React.Component {
  static propTypes = {
    refresh: func,
    search: func,
    path: string,
    logout: func,
    openLeftNav: func,
    onChangeCommunity: func,
    opened: bool,
    community: object,
    network: object,
    leftNavIsOpen: bool,
    links: array
  }

  static contextTypes = {
    currentUser: object,
    isMobile: bool
  }

  constructor (props) {
    super(props)
    this.state = {isScrolling: false}
  }

  handleScrollEvents = throttle(event => {
    event.preventDefault()
    if (this.state.isScrolling) {
      if (viewportTop() === 0) {
        this.setState({isScrolling: false})
      }
    } else {
      if (viewportTop() > 0) {
        this.setState({isScrolling: true})
      }
    }
  }, 50)

  componentDidMount () {
    if (!this.context.isMobile) {
      this.setState({isScrolling: viewportTop() > 0})
      window.addEventListener('scroll', this.handleScrollEvents)
    }
  }

  componentWillUnmount () {
    if (!this.context.isMobile) {
      window.removeEventListener('scroll', this.handleScrollEvents)
    }
  }

  render () {
    const {
      search, logout, openLeftNav, leftNavIsOpen, path, onChangeCommunity,
      network, links
    } = this.props
    const { currentUser, isMobile } = this.context
    const label = getLabel(path)
    const community = this.props.community || allCommunities()
    const { slug } = community
    const firstItem = network ? merge(network, {isNetwork: true}) : community
    const menuItems = getMenuItems(currentUser, firstItem)
    const membership = getCurrentMembership(currentUser, community)
    const newCount = get('new_notification_count',
      community.id ? membership : currentUser)

    const moveWithMenu = isMobile
      ? {marginLeft: leftNavIsOpen ? leftNavWidth : 0}
      : {marginLeft: 0}

    const widenMenuButton = isMobile
      ? {width: leftNavWidth}
      : {width: leftNavIsOpen ? leftNavWidth : menuButtonWidth}

    return <VelocityComponent animation={moveWithMenu} easing={leftNavEasing}>

        <VelocityComponent animation={widenMenuButton} easing={leftNavEasing}>
          <MenuButton onClick={openLeftNav} label={isMobile ? label : null}/>
        </VelocityComponent>
        {currentUser
        ? <UserMenu {...{logout, currentUser, newCount, slug, search}}/>
        :

Sign up

Log in

}

        {currentUser && <CommunityMenu {...{menuItems, onChangeCommunity}}/>}
        {currentUser && !network && <TopMainMenu links={links}/>}
        {currentUser &&

            <Icon name='Compose'/>
          }

    </VelocityComponent>
  }
}

const TopMainMenu = ({ community, links }) => {
  const LinkItem = ({ link, className }) => {
    const { url, label, index } = link
    const AComponent = index ? IndexA : A
    return <AComponent to={url} className={className}>{label}</AComponent>
  }

  return

    {links.slice(0, 3).map(link =>
      <LinkItem className={`a-${link.label}`} link={link} key={link.label}/>)}
    <Dropdown triangle className='overflow-menu' openOnHover
      toggleChildren={<Icon name='More'/>}>
      {links.slice(1).map(link => <li key={link.label} className={`li-${link.label}`}>
        <LinkItem link={link}/>

)}
    </Dropdown>

}

const CommunityMenu = ({ menuItems, onChangeCommunity }, { isMobile, dispatch }) => {
  const currentItem = menuItems[0]
  const { isNetwork, id } = currentItem
  const jumpToConversations = event => {
    if (isMobile) return
    event.preventDefault()
    event.stopPropagation()
    dispatch(navigate(nextPath('', id ? currentItem : null, isNetwork)))
  }

  return <Dropdown className='communities' backdrop triangle toggleChildren={

          {currentItem.name}

        {isNetwork && Network}

    }>

           onChangeCommunity()}>
             All Communities

        {menuItems.slice(1).map(community =>

           onChangeCommunity(community)} title={community.name}>
             {community.name}

)}

        Join or start a community

  </Dropdown>
}
CommunityMenu.contextTypes = {isMobile: bool, dispatch: func}

const UserMenu = ({ slug, logout, newCount, currentUser, search }, { isMobile }) => {
  return

      <Icon name='Loupe'/>
      <SearchInput onChange={search}/>

      <A to={`${slug ? '/c/' + slug : ''}/notifications`}>
        <Icon name='Bell'/>
        {newCount > 0 &&
{newCount}
}

      <Dropdown className='user-menu' alignRight openOnHover triangle={isMobile}
        backdrop={isMobile} toggleChildren={

            <NonLinkAvatar person={currentUser}/>
            {newCount > 0 &&
}

        }>

          <A to={`/u/${currentUser.id}`}>
            <Icon name='User'/> My profile

          <A to={slug ? `/c/${slug}/notifications` : '/notifications'}>
            <Icon name='Bell'/> Notifications
            {newCount > 0 && {newCount}}

            <Icon name='Settings'/> Settings

        {isAdmin(currentUser) &&

            <Icon name='Keypad'/> Admin

}

            <Icon name='Fail'/> Log out

      </Dropdown>

}
UserMenu.contextTypes = {isMobile: bool}
<commit_msg>fix top nav layout on mobile<commit_after>import React from 'react'
import { A, IndexA } from './A'
import Icon from './Icon'
import { NonLinkAvatar } from './Avatar'
import Dropdown from './Dropdown'
import SearchInput from './SearchInput'
import { isAdmin } from '../models/currentUser'
import { filter, find, flow, get, map, sortBy } from 'lodash/fp'
import { throttle, merge } from 'lodash'
import { same } from '../models'
import { nextPath } from '../util/navigation'
import { MenuButton, leftNavWidth, leftNavEasing, menuButtonWidth } from './LeftNav'
import { editorUrl } from '../containers/StandalonePostEditor'
import { assetUrl } from '../util/assets'
const { array, object, func, string, bool } = React.PropTypes
import { viewportTop } from '../util/scrolling'
import { VelocityComponent } from 'velocity-react'
import cx from 'classnames'
import { navigate } from '../actions'

const getPostType = path => {
  if (path.endsWith('events')) return 'event'
  if (path.endsWith('projects')) return 'project'
}

const getLabel = path => {
  if (path.endsWith('events')) return 'Events'
  if (path.endsWith('projects')) return 'Projects'
  if (path.endsWith('people')) return 'Members'
  if (path.endsWith('about')) return 'About'
  if (path.endsWith('invite')) return 'Invite'
  if (path === '/app' || path.match(/^\/c\/[^\/]+$/)) return 'Conversations'
  return 'Menu'
}

const allCommunities = () => ({
  id: null,
  avatar_url: assetUrl('/img/hylo-merkaba-300x300.png'),
  name: 'All Communities'
})

const getMenuItems = (currentUser, firstItem) =>
  [firstItem].concat(flow(
    get('memberships'),
    sortBy(m => -Date.parse(m.last_viewed_at || '2001-01-01')),
    map('community'),
    firstItem.isNetwork ? i => i : filter(c => !same('id', firstItem, c))
  )(currentUser))

const getCurrentMembership = (currentUser, community) =>
  flow(
    get('memberships'),
    find(m => m.community.id === get('id', community))
  )(currentUser)

export default class TopNav extends React.Component {
  static propTypes = {
    refresh: func,
    search: func,
    path: string,
    logout: func,
    openLeftNav: func,
    onChangeCommunity: func,
    opened: bool,
    community: object,
    network: object,
    leftNavIsOpen: bool,
    links: array
  }

  static contextTypes = {
    currentUser: object,
    isMobile: bool
  }

  constructor (props) {
    super(props)
    this.state = {isScrolling: false}
  }

  handleScrollEvents = throttle(event => {
    event.preventDefault()
    if (this.state.isScrolling) {
      if (viewportTop() === 0) {
        this.setState({isScrolling: false})
      }
    } else {
      if (viewportTop() > 0) {
        this.setState({isScrolling: true})
      }
    }
  }, 50)

  componentDidMount () {
    if (!this.context.isMobile) {
      this.setState({isScrolling: viewportTop() > 0})
      window.addEventListener('scroll', this.handleScrollEvents)
    }
  }

  componentWillUnmount () {
    if (!this.context.isMobile) {
      window.removeEventListener('scroll', this.handleScrollEvents)
    }
  }

  render () {
    const {
      search, logout, openLeftNav, leftNavIsOpen, path, onChangeCommunity,
      network, links
    } = this.props
    const { currentUser, isMobile } = this.context
    const label = getLabel(path)
    const community = this.props.community || allCommunities()
    const { slug } = community
    const firstItem = network ? merge(network, {isNetwork: true}) : community
    const menuItems = getMenuItems(currentUser, firstItem)
    const membership = getCurrentMembership(currentUser, community)
    const newCount = get('new_notification_count',
      community.id ? membership : currentUser)

    const moveWithMenu = isMobile
      ? {marginLeft: leftNavIsOpen ? leftNavWidth : 0}
      : {marginLeft: 0}

    const widenMenuButton = {width: leftNavIsOpen ? leftNavWidth : menuButtonWidth}

    return <VelocityComponent animation={moveWithMenu} easing={leftNavEasing}>

        {isMobile
          ? <MenuButton onClick={openLeftNav} label={label}/>
          : <VelocityComponent animation={widenMenuButton} easing={leftNavEasing}>
              <MenuButton onClick={openLeftNav}/>
            </VelocityComponent>}
        {currentUser
        ? <UserMenu {...{logout, currentUser, newCount, slug, search}}/>
        :

Sign up

Log in

}

        {currentUser && <CommunityMenu {...{menuItems, onChangeCommunity}}/>}
        {currentUser && !network && <TopMainMenu links={links}/>}
        {currentUser &&

            <Icon name='Compose'/>
          }

    </VelocityComponent>
  }
}

const TopMainMenu = ({ community, links }) => {
  const LinkItem = ({ link, className }) => {
    const { url, label, index } = link
    const AComponent = index ? IndexA : A
    return <AComponent to={url} className={className}>{label}</AComponent>
  }

  return

    {links.slice(0, 3).map(link =>
      <LinkItem className={`a-${link.label}`} link={link} key={link.label}/>)}
    <Dropdown triangle className='overflow-menu' openOnHover
      toggleChildren={<Icon name='More'/>}>
      {links.slice(1).map(link => <li key={link.label} className={`li-${link.label}`}>
        <LinkItem link={link}/>

)}
    </Dropdown>

}

const CommunityMenu = ({ menuItems, onChangeCommunity }, { isMobile, dispatch }) => {
  const currentItem = menuItems[0]
  const { isNetwork, id } = currentItem
  const jumpToConversations = event => {
    if (isMobile) return
    event.preventDefault()
    event.stopPropagation()
    dispatch(navigate(nextPath('', id ? currentItem : null, isNetwork)))
  }

  return <Dropdown className='communities' backdrop triangle toggleChildren={

          {currentItem.name}

        {isNetwork && Network}

    }>

           onChangeCommunity()}>
             All Communities

        {menuItems.slice(1).map(community =>

           onChangeCommunity(community)} title={community.name}>
             {community.name}

)}

        Join or start a community

  </Dropdown>
}
CommunityMenu.contextTypes = {isMobile: bool, dispatch: func}

const UserMenu = ({ slug, logout, newCount, currentUser, search }, { isMobile }) => {
  return

      <Icon name='Loupe'/>
      <SearchInput onChange={search}/>

      <A to={`${slug ? '/c/' + slug : ''}/notifications`}>
        <Icon name='Bell'/>
        {newCount > 0 &&
{newCount}
}

      <Dropdown className='user-menu' alignRight openOnHover triangle={isMobile}
        backdrop={isMobile} toggleChildren={

            <NonLinkAvatar person={currentUser}/>
            {newCount > 0 &&
}

        }>

          <A to={`/u/${currentUser.id}`}>
            <Icon name='User'/> My profile

          <A to={slug ? `/c/${slug}/notifications` : '/notifications'}>
            <Icon name='Bell'/> Notifications
            {newCount > 0 && {newCount}}

            <Icon name='Settings'/> Settings

        {isAdmin(currentUser) &&

            <Icon name='Keypad'/> Admin

}

            <Icon name='Fail'/> Log out

      </Dropdown>

}
UserMenu.contextTypes = {isMobile: bool}
<|endoftext|>


<commit_before>
angular.module('os.administrative.user',
  [
    'os.administrative.user.dropdown',
    'os.administrative.user.list',
    'os.administrative.user.addedit',
    'os.administrative.user.detail',
    'os.administrative.user.roles',
    'os.administrative.user.password',
    'os.administrative.user.displayname',
    'os.common.import'
  ])

  .config(function($stateProvider) {
    $stateProvider
      .state('user-root', {
        abstract: true,
        template: '

',
        controller: function($scope) {
          // User Authorization Options
          $scope.userResource = {
            createOpts: {resource: 'User', operations: ['Create']},
            updateOpts: {resource: 'User', operations: ['Update']},
            deleteOpts: {resource: 'User', operations: ['Delete']},
            importOpts: {resource: 'User', operations: ['Bulk Import']}
          }
        },
        parent: 'signed-in'
      })
      .state('user-list', {
        url: '/users?filters',
        templateUrl: 'modules/administrative/user/list.html',
        controller: 'UserListCtrl',
        parent: 'user-root'
      })
      .state('user-addedit', {
        url: '/user-addedit/:userId',
        templateUrl: 'modules/administrative/user/addedit.html',
        resolve: {
          user: function($stateParams, User) {
            if ($stateParams.userId) {
              return User.getById($stateParams.userId);
            }

            return new User();
          },
          users: function() {
            return [];
          }
        },
        controller: 'UserAddEditCtrl',
        parent: 'user-root'
      })
      .state('user-bulk-edit', {
        url: '/bulk-edit-users',
        templateUrl: 'modules/administrative/user/bulk-edit.html',
        resolve: {
          user: function(User) {
            return new User();
          },
          users: function(ItemsHolder) {
            var users = ItemsHolder.getItems('users');
            ItemsHolder.setItems('users', undefined);
            return users;
          }
        },
        controller: 'UserAddEditCtrl',
        parent: 'user-root'
      })
      .state('user-import', {
        url: '/users-import?objectType',
        templateUrl: 'modules/common/import/add.html',
        controller: 'ImportObjectCtrl',
        resolve: {
          importDetail: function($stateParams) {
            var objectType = $stateParams.objectType;
            var title = undefined;
            if (objectType == 'user') {
              title = 'user.bulk_import_users';
            } else if (objectType == 'userRoles') {
              title = 'user.bulk_import_user_roles';
            }

            return {
              breadcrumbs: [{state: 'user-list', title: 'user.list'}],
              objectType: objectType,
              title: title,
              onSuccess: {state: 'user-list'}
            };
          }
        },
        parent: 'user-root'
      })
      .state('user-import-jobs', {
        url: '/users-import-jobs',
        templateUrl: 'modules/common/import/list.html',
        controller: 'ImportJobsListCtrl',
        resolve: {
          importDetail: function() {
            return {
              breadcrumbs: [{state: 'user-list', title: 'user.list'}],
              title: 'user.bulk_import_jobs',
              objectTypes: ['user', 'userRoles']
            }
          }
        },
        parent: 'user-root'
      })
      .state('user-detail', {
        url: '/users/:userId',
        templateUrl: 'modules/administrative/user/detail.html',
        resolve: {
          user: function($stateParams, User) {
            return User.getById($stateParams.userId);
          }
        },
        controller: 'UserDetailCtrl',
        parent: 'user-root'
      })
      .state('user-detail.overview', {
        url: '/overview',
        templateUrl: 'modules/administrative/user/overview.html',
        parent: 'user-detail'
      })
      .state('user-detail.roles', {
        url: '/roles',
        templateUrl: 'modules/administrative/user/roles.html',
        resolve: {
          userRoles: function(user) {
            return user.getRoles();
          },

          currentUserInstitute: function(currentUser) {
            return currentUser.getInstitute();
          }
        },
        controller: 'UserRolesCtrl',
        parent: 'user-detail'
      })
      .state('user-password', {
        url: '/user-password-change/:userId',
        templateUrl: 'modules/administrative/user/password.html',
        resolve: {
          user: function($stateParams, User) {
            return User.getById($stateParams.userId);
          }
        },
        controller: 'UserPasswordCtrl',
        parent: 'user-root'
      })
  })

  .run(function(UrlResolver) {
    UrlResolver.regUrlState('user-overview', 'user-detail.overview', 'userId');
    UrlResolver.regUrlState('user-roles', 'user-detail.roles', 'userId');
    UrlResolver.regUrlState('user-password-change', 'user-password', 'userId');
  });
<commit_msg>OPSMN-4435: Fixed the users list view to display import and export options if the user has rights to bulk import and export the user records.<commit_after>
angular.module('os.administrative.user',
  [
    'os.administrative.user.dropdown',
    'os.administrative.user.list',
    'os.administrative.user.addedit',
    'os.administrative.user.detail',
    'os.administrative.user.roles',
    'os.administrative.user.password',
    'os.administrative.user.displayname',
    'os.common.import'
  ])

  .config(function($stateProvider) {
    $stateProvider
      .state('user-root', {
        abstract: true,
        template: '

',
        controller: function($scope) {
          // User Authorization Options
          $scope.userResource = {
            createOpts: {resource: 'User', operations: ['Create']},
            updateOpts: {resource: 'User', operations: ['Update']},
            deleteOpts: {resource: 'User', operations: ['Delete']},
            importOpts: {resource: 'User', operations: ['Export Import']}
          }
        },
        parent: 'signed-in'
      })
      .state('user-list', {
        url: '/users?filters',
        templateUrl: 'modules/administrative/user/list.html',
        controller: 'UserListCtrl',
        parent: 'user-root'
      })
      .state('user-addedit', {
        url: '/user-addedit/:userId',
        templateUrl: 'modules/administrative/user/addedit.html',
        resolve: {
          user: function($stateParams, User) {
            if ($stateParams.userId) {
              return User.getById($stateParams.userId);
            }

            return new User();
          },
          users: function() {
            return [];
          }
        },
        controller: 'UserAddEditCtrl',
        parent: 'user-root'
      })
      .state('user-bulk-edit', {
        url: '/bulk-edit-users',
        templateUrl: 'modules/administrative/user/bulk-edit.html',
        resolve: {
          user: function(User) {
            return new User();
          },
          users: function(ItemsHolder) {
            var users = ItemsHolder.getItems('users');
            ItemsHolder.setItems('users', undefined);
            return users;
          }
        },
        controller: 'UserAddEditCtrl',
        parent: 'user-root'
      })
      .state('user-import', {
        url: '/users-import?objectType',
        templateUrl: 'modules/common/import/add.html',
        controller: 'ImportObjectCtrl',
        resolve: {
          importDetail: function($stateParams) {
            var objectType = $stateParams.objectType;
            var title = undefined;
            if (objectType == 'user') {
              title = 'user.bulk_import_users';
            } else if (objectType == 'userRoles') {
              title = 'user.bulk_import_user_roles';
            }

            return {
              breadcrumbs: [{state: 'user-list', title: 'user.list'}],
              objectType: objectType,
              title: title,
              onSuccess: {state: 'user-list'}
            };
          }
        },
        parent: 'user-root'
      })
      .state('user-import-jobs', {
        url: '/users-import-jobs',
        templateUrl: 'modules/common/import/list.html',
        controller: 'ImportJobsListCtrl',
        resolve: {
          importDetail: function() {
            return {
              breadcrumbs: [{state: 'user-list', title: 'user.list'}],
              title: 'user.bulk_import_jobs',
              objectTypes: ['user', 'userRoles']
            }
          }
        },
        parent: 'user-root'
      })
      .state('user-detail', {
        url: '/users/:userId',
        templateUrl: 'modules/administrative/user/detail.html',
        resolve: {
          user: function($stateParams, User) {
            return User.getById($stateParams.userId);
          }
        },
        controller: 'UserDetailCtrl',
        parent: 'user-root'
      })
      .state('user-detail.overview', {
        url: '/overview',
        templateUrl: 'modules/administrative/user/overview.html',
        parent: 'user-detail'
      })
      .state('user-detail.roles', {
        url: '/roles',
        templateUrl: 'modules/administrative/user/roles.html',
        resolve: {
          userRoles: function(user) {
            return user.getRoles();
          },

          currentUserInstitute: function(currentUser) {
            return currentUser.getInstitute();
          }
        },
        controller: 'UserRolesCtrl',
        parent: 'user-detail'
      })
      .state('user-password', {
        url: '/user-password-change/:userId',
        templateUrl: 'modules/administrative/user/password.html',
        resolve: {
          user: function($stateParams, User) {
            return User.getById($stateParams.userId);
          }
        },
        controller: 'UserPasswordCtrl',
        parent: 'user-root'
      })
  })

  .run(function(UrlResolver) {
    UrlResolver.regUrlState('user-overview', 'user-detail.overview', 'userId');
    UrlResolver.regUrlState('user-roles', 'user-detail.roles', 'userId');
    UrlResolver.regUrlState('user-password-change', 'user-password', 'userId');
  });
<|endoftext|>


<commit_before>'use strict'

const _Buffer = Buffer
const { assign, freeze } = Object

// TODO: (wip) this should not be injected by default into every page,
// instead should be lazy-loaded when .enable() method is called for the first time
const { createProxyClient } = require('ipfs-postmsg-proxy')

function createEnableCommand (proxyClient) {
  return {
    enable: async (opts) => {
      // Send message to proxy server for additional validation
      // eg. trigger user prompt if a list of requested capabilities is not empty
      // or fail fast and throw if IPFS Proxy is disabled globally
      await require('postmsg-rpc').call('proxy.enable', opts)
      // Create client
      const proxyClient = createProxyClient()
      // Additional client-side features
      if (opts && opts.experiments) {
        if (opts.experiments.ipfsx) {
          // Experiment: wrap API with  https://github.com/alanshaw/ipfsx
          return freeze(require('ipfsx')(proxyClient))
        }
      }
      return freeze(proxyClient)
    }
  }
}

function createWindowIpfs () {
  const proxyClient = createProxyClient()

  // Add deprecation warning to window.ipfs.<cmd>
  for (const cmd in proxyClient) {
    const fn = proxyClient[cmd]
    proxyClient[cmd] = function () {
      console.warn('Calling commands directly on window.ipfs is deprecated and will be removed on 2019-09-01. Use API instance returned by window.ipfs.enable() instead. More: https://github.com/ipfs-shipyard/ipfs-companion/blob/master/docs/window.ipfs.md')
      return fn.apply(this, arguments)
    }
  }

  // TODO: return thin object with lazy-init inside of window.ipfs.enable
  assign(proxyClient, createEnableCommand())

  return freeze(proxyClient)
}

// TODO: we should remove Buffer and add support for Uint8Array/ArrayBuffer natively
// See: https://github.com/ipfs/interface-ipfs-core/issues/404
window.Buffer = window.Buffer || _Buffer
window.ipfs = window.ipfs || createWindowIpfs()
<commit_msg>chore: updated deprecation of window.ipfs<commit_after>'use strict'

const _Buffer = Buffer
const { assign, freeze } = Object

// TODO: (wip) this should not be injected by default into every page,
// instead should be lazy-loaded when .enable() method is called for the first time
const { createProxyClient } = require('ipfs-postmsg-proxy')

function createEnableCommand (proxyClient) {
  return {
    enable: async (opts) => {
      // Send message to proxy server for additional validation
      // eg. trigger user prompt if a list of requested capabilities is not empty
      // or fail fast and throw if IPFS Proxy is disabled globally
      await require('postmsg-rpc').call('proxy.enable', opts)
      // Create client
      const proxyClient = createProxyClient()
      // Additional client-side features
      if (opts && opts.experiments) {
        if (opts.experiments.ipfsx) {
          // Experiment: wrap API with  https://github.com/alanshaw/ipfsx
          return freeze(require('ipfsx')(proxyClient))
        }
      }
      return freeze(proxyClient)
    }
  }
}

function createWindowIpfs () {
  const proxyClient = createProxyClient()

  // Add deprecation warning to window.ipfs.<cmd>
  for (const cmd in proxyClient) {
    const fn = proxyClient[cmd]
    proxyClient[cmd] = function () {
      console.warn('Calling commands directly on window.ipfs is deprecated and will be removed in the future. To future-proof your app use API instance returned by window.ipfs.enable() instead. Current best practices can be found at: https://github.com/ipfs-shipyard/ipfs-companion/blob/master/docs/window.ipfs.md')
      return fn.apply(this, arguments)
    }
  }

  // TODO: return thin object with lazy-init inside of window.ipfs.enable
  assign(proxyClient, createEnableCommand())

  return freeze(proxyClient)
}

// TODO: we should remove Buffer and add support for Uint8Array/ArrayBuffer natively
// See: https://github.com/ipfs/interface-ipfs-core/issues/404
window.Buffer = window.Buffer || _Buffer
window.ipfs = window.ipfs || createWindowIpfs()
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2014 YY Digital Pty Ltd. All Rights Reserved.
 * Please see the LICENSE file included with this distribution for details.
 */

var log = require('/api/Log');
var utils = require('/api/Utils');
var Compression = require("ti.compression");
var assert = require('/api/Assert');
var io = require('/lib/socket.io');
var osname = Ti.Platform.osname;
var platform = (osname === 'ipad' || osname === 'iphone') ? 'ios' : osname;
var socket, room;
var logs = [];

if (!Ti.App.Properties.hasProperty("tishadow:uuid")) {
  Ti.App.Properties.setString("tishadow:uuid",Ti.Platform.createUUID());
}

exports.currentApp;
exports.connect = function(o) {
  room = o.room;
  var version_property = "tishadow:" + room + ":version";
  if (socket) {
    exports.disconnect();
  }
  socket = io.connect((o.proto || "http") + "://" + o.host + ":"  + o.port, {'force new connection': true});

  socket.on("connect", function() {
    socket.emit("join", {
      name : o.name,
      uuid : Ti.App.Properties.getString("tishadow:uuid"),
      os_osname: Ti.Platform.osname,
      os_version: Ti.Platform.version,
      room : o.room,
      version: Ti.App.Properties.getString(version_property) || undefined
    });

    logs && logs.forEach(function(log) {
      socket.emit("log", log);
    });
    logs = false;

    if(o.callback) {
      o.callback();
    }
  });
  socket.on("error", function(e) {
    logs = false; // not sure if needed here
    if (o.onerror) {
      o.onerror(e);
    }
  });
  socket.on("connect_failed", function(e) {
    logs = false;
    if(o.onerror) {
      o.onerror(e);
    }
  });

  // REPL messages
  socket.on('message', function(data) {
    if (!isTarget(data)) {
      return;
    }
    require('/api/PlatformRequire').eval(data);
  });

  socket.on('bundle', function(data) {
    if (!isTarget(data) || (exports.Appify && exports.Appify !== data.name)) {
      return;
    }
    if(data.locale) {
      require("/api/Localisation").locale = data.locale;
    }
    loadRemoteZip(data.name, (o.proto || "http") + "://" + o.host + ":" + o.port + "/bundle", data, version_property);
  });

  socket.on('clear', function(data) {
    if (!isTarget(data)) {
      return;
    }
    exports.clearCache();
  });

  socket.on('close', function(data) {
    if (!isTarget(data)) {
      return;
    }
    exports.closeApp();
  });

  socket.on('screenshot', function(data) {
    if (!isTarget(data)) {
      return;
    }
    Ti.Media.takeScreenshot(function(o) {
      var image = o.media;
      if (data.scale) {
        var height = Ti.Platform.displayCaps.platformHeight * data.scale;
        var width = Ti.Platform.displayCaps.platformWidth * data.scale;
        image = image.imageAsResized(width, height);
      }
      var imgStr = Ti.Utils.base64encode(image).toString();
      socket.emit("screenshot_taken", {image: imgStr});
    });
  });

  socket.on('disconnect', function() {
    if(o.disconnected) {
      o.disconnected();
    }
  });

};

exports.emitLog = function(e) {
  if (socket && !logs) {
    socket.emit("log", e);
  } else {
    logs.push(e);
  }
};

exports.disconnect = function() {
  if (socket) {
    socket.disconnect();
  }
};

var bundle;
function restart() {
  require("/api/UI").closeAll();
  require("/api/App").clearAll();
  Ti.App.Properties.setBool("tishadow::reconnect",true );
  Ti.App.fireEvent('tishadow:close');
  exports.disconnect();
  Ti.App._restart();
}
exports.closeApp = function() {
  Ti.App.Properties.setString("tishadow::currentApp","" );
  restart();
};
exports.nextApp = function(name) {
  Ti.App.Properties.setString("tishadow::currentApp", name ? name.replace(/ /g,"_") : exports.currentApp);
  restart();
};
exports.launchApp = function(name) {
  try {
    var p = require('/api/PlatformRequire');
    // Custom Fonts
    if (osname === "ipad" || osname === "iphone") {
      require("/api/Fonts").loadCustomFonts(name);
    }
    // still requires cache clean on restart
    p.clearCache();
    require("/api/Localisation").clear();

    Ti.App.Properties.setString("tishadow::currentApp", "");
    Ti.App.Properties.setBool("tishadow::reconnect", false);

    exports.currentApp = name;
    bundle = p.include(null, "/app.js");
    log.info(exports.currentApp.replace(/_/g," ") + " launched.");
  } catch(e) {
    log.error(utils.extractExceptionData(e));
  }
};

exports.clearCache = function(no_restart) {
  Ti.App.Properties.listProperties().forEach(function(property) {
    if (!property.match("^tishadow:")) {
      Ti.App.Properties.removeProperty(property);
    }
  });

  var dirty_directories = [Ti.Filesystem.applicationDataDirectory];
  if (Ti.UI.iOS) {
    var applicationDatabaseDirectory = Ti.Filesystem.applicationDataDirectory.replace("Documents/","") + "Library/Private%20Documents/";
    if (Ti.Filesystem.getFile(applicationDatabaseDirectory).exists()) {
      dirty_directories.push(applicationDatabaseDirectory);
    }
  }

  try {
    dirty_directories.forEach(function(targetDirectory) {
      // Clear Applications
      var files = Ti.Filesystem.getFile(targetDirectory).getDirectoryListing();
      files.forEach(function(file_name) {
        var file = Ti.Filesystem.getFile(targetDirectory,file_name);
        if (Ti.Platform.osname === "android") {
          if (file.isFile()) {
            file.deleteFile();
          } else if (file.isDirectory()) {
            file.deleteDirectory(true);
          }
        } else {
          file.deleteFile();
          file.deleteDirectory(true);
        }
      });
    });
  } catch (e) {
    log.error(utils.extractExceptionData(e));
  }
  log.info("Cache cleared");
  if (!no_restart) {
    exports.closeApp();
  }
};

function loadRemoteZip(name, url, data, version_property) {
  var xhr = Ti.Network.createHTTPClient();
  xhr.setTimeout(10000);
  xhr.onload=function(e) {
    try {
      log.info("Unpacking new bundle: " + name);

      var path_name = name.replace(/ /g,"_");
      // SAVE ZIP
      var zip_file = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, path_name + '.zip');
      zip_file.write(this.responseData);
      // Prepare path
      var target = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, path_name);
      if (!target.exists()) {
        target.createDirectory();
      }
      // Extract
      var dataDir=Ti.Filesystem.applicationDataDirectory + "/";
      Compression.unzip(dataDir + path_name, dataDir + path_name + '.zip',true);
      if (data && data.version && version_property) {
        Ti.App.Properties.setString(version_property, data.version);
      } else {
        Ti.App.Properties.removeProperty(version_property);
      }
      // Launch
      if (data && data.spec && data.spec.run) {
        exports.currentApp = path_name;
        require("/api/Spec").run(path_name, data.spec.junitxml, data.spec.type);
      } else if (data && data.patch && data.patch.run) {
        require('/api/PlatformRequire').clearCache(data.patch.files);
      } else  {
        exports.nextApp(path_name);
      }
    } catch (e) {
      log.error(utils.extractExceptionData(e));
    }
  };
  xhr.onerror = function(e){
    Ti.UI.createAlertDialog({title:'XHR', message:'Error: ' + e.error}).show();
  };
  xhr.open('GET', url + "/" + room + "/" + Ti.App.Properties.getString("tishadow:uuid") );
  xhr.send();
}

function isTarget(data) {
  return (!data.platform || data.platform.indexOf(osname) !== -1 || data.platform.indexOf(platform) !== -1);
}

// FOR URL SCHEME - tishadow://
function loadRemoteBundle(url) {
  if (url.indexOf(".zip") === -1) {
    alert("Invalid Bundle");
  } else {
    var name_parts = url.split("/");
    var name = name_parts[name_parts.length -1].replace(".zip","");
    loadRemoteZip(name, url);
  }
}

function parseArguments() {
  cmd = Ti.App.getArguments();
  if ( (typeof cmd == 'object') && cmd.hasOwnProperty('url') ) {
    if ( cmd.url !== url ) {
      url = cmd.url;
      if (url.substring(0, 8) === 'tishadow') {
        loadRemoteBundle(url.replace("tishadow", "http"));
      }
    }
  }
}

var url = '';
if (osname !== "android") {
  parseArguments();
  Ti.App.addEventListener( 'resumed', parseArguments);
}
<commit_msg>Fixed dynamic localisation<commit_after>/*
 * Copyright (c) 2011-2014 YY Digital Pty Ltd. All Rights Reserved.
 * Please see the LICENSE file included with this distribution for details.
 */

var log = require('/api/Log');
var utils = require('/api/Utils');
var Compression = require("ti.compression");
var assert = require('/api/Assert');
var io = require('/lib/socket.io');
var osname = Ti.Platform.osname;
var platform = (osname === 'ipad' || osname === 'iphone') ? 'ios' : osname;
var socket, room;
var logs = [];

if (!Ti.App.Properties.hasProperty("tishadow:uuid")) {
  Ti.App.Properties.setString("tishadow:uuid",Ti.Platform.createUUID());
}

exports.currentApp;
exports.connect = function(o) {
  room = o.room;
  var version_property = "tishadow:" + room + ":version";
  if (socket) {
    exports.disconnect();
  }
  socket = io.connect((o.proto || "http") + "://" + o.host + ":"  + o.port, {'force new connection': true});

  socket.on("connect", function() {
    socket.emit("join", {
      name : o.name,
      uuid : Ti.App.Properties.getString("tishadow:uuid"),
      os_osname: Ti.Platform.osname,
      os_version: Ti.Platform.version,
      room : o.room,
      version: Ti.App.Properties.getString(version_property) || undefined
    });

    logs && logs.forEach(function(log) {
      socket.emit("log", log);
    });
    logs = false;

    if(o.callback) {
      o.callback();
    }
  });
  socket.on("error", function(e) {
    logs = false; // not sure if needed here
    if (o.onerror) {
      o.onerror(e);
    }
  });
  socket.on("connect_failed", function(e) {
    logs = false;
    if(o.onerror) {
      o.onerror(e);
    }
  });

  // REPL messages
  socket.on('message', function(data) {
    if (!isTarget(data)) {
      return;
    }
    require('/api/PlatformRequire').eval(data);
  });

  socket.on('bundle', function(data) {
    if (!isTarget(data) || (exports.Appify && exports.Appify !== data.name)) {
      return;
    }
    if(data.locale) {
      Ti.App.Properties.setString("tishadow::locale", data.locale);
    }
    loadRemoteZip(data.name, (o.proto || "http") + "://" + o.host + ":" + o.port + "/bundle", data, version_property);
  });

  socket.on('clear', function(data) {
    if (!isTarget(data)) {
      return;
    }
    exports.clearCache();
  });

  socket.on('close', function(data) {
    if (!isTarget(data)) {
      return;
    }
    exports.closeApp();
  });

  socket.on('screenshot', function(data) {
    if (!isTarget(data)) {
      return;
    }
    Ti.Media.takeScreenshot(function(o) {
      var image = o.media;
      if (data.scale) {
        var height = Ti.Platform.displayCaps.platformHeight * data.scale;
        var width = Ti.Platform.displayCaps.platformWidth * data.scale;
        image = image.imageAsResized(width, height);
      }
      var imgStr = Ti.Utils.base64encode(image).toString();
      socket.emit("screenshot_taken", {image: imgStr});
    });
  });

  socket.on('disconnect', function() {
    if(o.disconnected) {
      o.disconnected();
    }
  });

};

exports.emitLog = function(e) {
  if (socket && !logs) {
    socket.emit("log", e);
  } else {
    logs.push(e);
  }
};

exports.disconnect = function() {
  if (socket) {
    socket.disconnect();
  }
};

var bundle;
function restart() {
  require("/api/UI").closeAll();
  require("/api/App").clearAll();
  Ti.App.Properties.setBool("tishadow::reconnect",true );
  Ti.App.fireEvent('tishadow:close');
  exports.disconnect();
  Ti.App._restart();
}
exports.closeApp = function() {
  Ti.App.Properties.setString("tishadow::currentApp","" );
  restart();
};
exports.nextApp = function(name) {
  Ti.App.Properties.setString("tishadow::currentApp", name ? name.replace(/ /g,"_") : exports.currentApp);
  restart();
};
exports.launchApp = function(name) {
  try {
    var p = require('/api/PlatformRequire');
    // Custom Fonts
    if (osname === "ipad" || osname === "iphone") {
      require("/api/Fonts").loadCustomFonts(name);
    }
    // still requires cache clean on restart
    p.clearCache();
    require("/api/Localisation").clear();

    Ti.App.Properties.setString("tishadow::currentApp", "");
    Ti.App.Properties.setBool("tishadow::reconnect", false);

    //initialise custom localisation
    var locale =  Ti.App.Properties.getString("tishadow::locale","");
    if (locale) {
      require("/api/Localisation").locale = locale;
    }

    exports.currentApp = name;
    bundle = p.include(null, "/app.js");
    log.info(exports.currentApp.replace(/_/g," ") + " launched.");
  } catch(e) {
    log.error(utils.extractExceptionData(e));
  }
};

exports.clearCache = function(no_restart) {
  Ti.App.Properties.listProperties().forEach(function(property) {
    if (!property.match("^tishadow:") || property === "tishadow::locale") {
      Ti.App.Properties.removeProperty(property);
    }
  });

  var dirty_directories = [Ti.Filesystem.applicationDataDirectory];
  if (Ti.UI.iOS) {
    var applicationDatabaseDirectory = Ti.Filesystem.applicationDataDirectory.replace("Documents/","") + "Library/Private%20Documents/";
    if (Ti.Filesystem.getFile(applicationDatabaseDirectory).exists()) {
      dirty_directories.push(applicationDatabaseDirectory);
    }
  }

  try {
    dirty_directories.forEach(function(targetDirectory) {
      // Clear Applications
      var files = Ti.Filesystem.getFile(targetDirectory).getDirectoryListing();
      files.forEach(function(file_name) {
        var file = Ti.Filesystem.getFile(targetDirectory,file_name);
        if (Ti.Platform.osname === "android") {
          if (file.isFile()) {
            file.deleteFile();
          } else if (file.isDirectory()) {
            file.deleteDirectory(true);
          }
        } else {
          file.deleteFile();
          file.deleteDirectory(true);
        }
      });
    });
  } catch (e) {
    log.error(utils.extractExceptionData(e));
  }
  log.info("Cache cleared");
  if (!no_restart) {
    exports.closeApp();
  }
};

function loadRemoteZip(name, url, data, version_property) {
  var xhr = Ti.Network.createHTTPClient();
  xhr.setTimeout(10000);
  xhr.onload=function(e) {
    try {
      log.info("Unpacking new bundle: " + name);

      var path_name = name.replace(/ /g,"_");
      // SAVE ZIP
      var zip_file = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, path_name + '.zip');
      zip_file.write(this.responseData);
      // Prepare path
      var target = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, path_name);
      if (!target.exists()) {
        target.createDirectory();
      }
      // Extract
      var dataDir=Ti.Filesystem.applicationDataDirectory + "/";
      Compression.unzip(dataDir + path_name, dataDir + path_name + '.zip',true);
      if (data && data.version && version_property) {
        Ti.App.Properties.setString(version_property, data.version);
      } else {
        Ti.App.Properties.removeProperty(version_property);
      }
      // Launch
      if (data && data.spec && data.spec.run) {
        exports.currentApp = path_name;
        require("/api/Spec").run(path_name, data.spec.junitxml, data.spec.type);
      } else if (data && data.patch && data.patch.run) {
        require('/api/PlatformRequire').clearCache(data.patch.files);
      } else  {
        exports.nextApp(path_name);
      }
    } catch (e) {
      log.error(utils.extractExceptionData(e));
    }
  };
  xhr.onerror = function(e){
    Ti.UI.createAlertDialog({title:'XHR', message:'Error: ' + e.error}).show();
  };
  xhr.open('GET', url + "/" + room + "/" + Ti.App.Properties.getString("tishadow:uuid") );
  xhr.send();
}

function isTarget(data) {
  return (!data.platform || data.platform.indexOf(osname) !== -1 || data.platform.indexOf(platform) !== -1);
}

// FOR URL SCHEME - tishadow://
function loadRemoteBundle(url) {
  if (url.indexOf(".zip") === -1) {
    alert("Invalid Bundle");
  } else {
    var name_parts = url.split("/");
    var name = name_parts[name_parts.length -1].replace(".zip","");
    loadRemoteZip(name, url);
  }
}

function parseArguments() {
  cmd = Ti.App.getArguments();
  if ( (typeof cmd == 'object') && cmd.hasOwnProperty('url') ) {
    if ( cmd.url !== url ) {
      url = cmd.url;
      if (url.substring(0, 8) === 'tishadow') {
        loadRemoteBundle(url.replace("tishadow", "http"));
      }
    }
  }
}

var url = '';
if (osname !== "android") {
  parseArguments();
  Ti.App.addEventListener( 'resumed', parseArguments);
}
<|endoftext|>


<commit_before>'use strict';

/**
 * The controller for schema
 *
 * @memberof HashBrown.Server.Controller
 */
class SchemaController extends HashBrown.Controller.ResourceController {
    /**
     * Routes
     */
    static get routes() {
        return {
            '/api/${project}/${environment}/schemas/icons': {
                handler: this.icons,
                user: {
                    scope: 'schemas'
                }
            },
            ...super.routes
        };
    }

    /**
     * @example GET /api/${project}/${environment}/schemas/icons
     */
    static async icons(request, params, body, query, context) {
        let schemas = await HashBrown.Entity.Resource.SchemaBase.list(context);
        let icons = {};

        for(let schema of schemas) {
            icons[schema.id] = schema.icon;
        }

        return new HashBrown.Http.Response(icons);
    }

    /**
     * @example POST /api/${project}/${environment}/schemas/{$id}/pull
     *
     * @return {Object} The pulled resource
     */
    static async pull(request, params, body, query, context) {
        // Clear publication cache
        let publications = await HashBrown.Entity.Resource.Publication.list(context);

        for(let publication of publications) {
            await publication.clearCache();
        }

        return await super.pull(request, params, body, query, context);
    }

    /**
     * @example GET|POST|DELETE /api/${project}/${environment}/schemas/${id}?create=true|false
     */
    static async resource(request, params, body, query, context) {
        // Clear publication cache
        if(request.method === 'POST' || request.method === 'DELETE') {
            let publications = await HashBrown.Entity.Resource.Publication.list(context);

            for(let publication of publications) {
                await publication.clearCache();
            }
        }

        return await super.resource(request, params, body, query, context);
    }
}

module.exports = SchemaController;
<commit_msg>Schema icons route fix<commit_after>'use strict';

/**
 * The controller for schema
 *
 * @memberof HashBrown.Server.Controller
 */
class SchemaController extends HashBrown.Controller.ResourceController {
    /**
     * Routes
     */
    static get routes() {
        return {
            '/api/${project}/${environment}/schemas/icons': {
                handler: this.icons,
                user: true,
            },
            ...super.routes
        };
    }

    /**
     * @example GET /api/${project}/${environment}/schemas/icons
     */
    static async icons(request, params, body, query, context) {
        let schemas = await HashBrown.Entity.Resource.SchemaBase.list(context);
        let icons = {};

        for(let schema of schemas) {
            icons[schema.id] = schema.icon;
        }

        return new HashBrown.Http.Response(icons);
    }

    /**
     * @example POST /api/${project}/${environment}/schemas/{$id}/pull
     *
     * @return {Object} The pulled resource
     */
    static async pull(request, params, body, query, context) {
        // Clear publication cache
        let publications = await HashBrown.Entity.Resource.Publication.list(context);

        for(let publication of publications) {
            await publication.clearCache();
        }

        return await super.pull(request, params, body, query, context);
    }

    /**
     * @example GET|POST|DELETE /api/${project}/${environment}/schemas/${id}?create=true|false
     */
    static async resource(request, params, body, query, context) {
        // Clear publication cache
        if(request.method === 'POST' || request.method === 'DELETE') {
            let publications = await HashBrown.Entity.Resource.Publication.list(context);

            for(let publication of publications) {
                await publication.clearCache();
            }
        }

        return await super.resource(request, params, body, query, context);
    }
}

module.exports = SchemaController;
<|endoftext|>