<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 New Vector Ltd

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.
*/

import dis from './dispatcher';
import Timer from './utils/Timer';

// important this is larger than the timeouts of timers
// used with UserActivity.timeWhileActive,
// such as READ_MARKER_INVIEW_THRESHOLD_MS,
// READ_MARKER_OUTOFVIEW_THRESHOLD_MS,
// READ_RECEIPT_INTERVAL_MS in TimelinePanel

// 'Under a few seconds'. Must be less than 'RECENTLY_ACTIVE_THRESHOLD_MS'
const CURRENTLY_ACTIVE_THRESHOLD_MS = 700;

// 'Under a few minutes'.
const RECENTLY_ACTIVE_THRESHOLD_MS = 2 * 60 * 1000;

/**
 * This class watches for user activity (moving the mouse or pressing a key)
 * and starts/stops attached timers while the user is active.
 *
 * There are two classes of 'active': 'active now' and 'active recently'
 * see doc on the userActive* functions for what these mean.
 */
export default class UserActivity {
    constructor(windowObj, documentObj) {
        this._window = windowObj;
        this._document = documentObj;

        this._attachedTimersActive = [];
        this._attachedTimersPassive = [];
        this._activeNowTimeout = new Timer(CURRENTLY_ACTIVE_THRESHOLD_MS);
        this._activeRecentlyTimeout = new Timer(RECENTLY_ACTIVE_THRESHOLD_MS);
        this._onUserActivity = this._onUserActivity.bind(this);
        this._onWindowBlurred = this._onWindowBlurred.bind(this);
        this._onPageVisibilityChanged = this._onPageVisibilityChanged.bind(this);
        this.lastScreenX = 0;
        this.lastScreenY = 0;
    }

    static sharedInstance() {
        if (global.mxUserActivity === undefined) {
            global.mxUserActivity = new UserActivity(window, document);
        }
        return global.mxUserActivity;
    }

    /**
     * Runs the given timer while the user is 'active now', aborting when the user is no longer
     * considered currently active.
     * See userActiveNow() for what it means for a user to be 'active'.
     * Can be called multiple times with the same already running timer, which is a NO-OP.
     * Can be called before the user becomes active, in which case it is only started
     * later on when the user does become active.
     * @param {Timer} timer the timer to use
     */
    timeWhileActiveNow(timer) {
        this._timeWhile(timer, this._attachedTimersActive);
        if (this.userActiveNow()) {
            timer.start();
        }
    }

    /**
     * Runs the given timer while the user is 'active' now or recently,
     * aborting when the user becomes inactive.
     * See userActiveRecently() for what it means for a user to be 'active recently'.
     * Can be called multiple times with the same already running timer, which is a NO-OP.
     * Can be called before the user becomes active, in which case it is only started
     * later on when the user does become active.
     * @param {Timer} timer the timer to use
     */
    timeWhileActiveRecently(timer) {
        this._timeWhile(timer, this._attachedTimersPassive);
        if (this.userActiveRecently()) {
            timer.start();
        }
    }

    _timeWhile(timer, attachedTimers) {
        // important this happens first
        const index = attachedTimers.indexOf(timer);
        if (index === -1) {
            attachedTimers.push(timer);
            // remove when done or aborted
            timer.finished().finally(() => {
                const index = attachedTimers.indexOf(timer);
                if (index !== -1) { // should never be -1
                    attachedTimers.splice(index, 1);
                }
            // as we fork the promise here,
            // avoid unhandled rejection warnings
            }).catch((err) => {});
        }
    }

    /**
     * Start listening to user activity
     */
    start() {
        this._document.addEventListener('mousedown', this._onUserActivity);
        this._document.addEventListener('mousemove', this._onUserActivity);
        this._document.addEventListener('keydown', this._onUserActivity);
        this._document.addEventListener("visibilitychange", this._onPageVisibilityChanged);
        this._window.addEventListener("blur", this._onWindowBlurred);
        this._window.addEventListener("focus", this._onUserActivity);
        // can't use document.scroll here because that's only the document
        // itself being scrolled. Need to use addEventListener's useCapture.
        // also this needs to be the wheel event, not scroll, as scroll is
        // fired when the view scrolls down for a new message.
        this._window.addEventListener('wheel', this._onUserActivity, {
            passive: true, capture: true,
        });
    }

    /**
     * Stop tracking user activity
     */
    stop() {
        this._document.removeEventListener('mousedown', this._onUserActivity);
        this._document.removeEventListener('mousemove', this._onUserActivity);
        this._document.removeEventListener('keydown', this._onUserActivity);
        this._window.removeEventListener('wheel', this._onUserActivity, {
            passive: true, capture: true,
        });

        this._document.removeEventListener("visibilitychange", this._onPageVisibilityChanged);
        this._window.removeEventListener("blur", this._onWindowBlurred);
        this._window.removeEventListener("focus", this._onUserActivity);
    }

    /**
     * Return true if the user is currently 'active'
     * A user is 'active' while they are interacting with the app and for a very short (<1s)
     * time after that. This is intended to give a strong indication that the app has the
     * user's attention at any given moment.
     * @returns {boolean} true if user is currently 'active'
     */
    userActiveNow() {
        return this._activeNowTimeout.isRunning();
    }

    /**
     * Return true if the user is currently active or has been recently
     * A user is 'active recently' for a longer period of time (~2 mins) after
     * they have been 'active' and while the app still has the focus. This is
     * intended to indicate when the app may still have the user's attention
     * (or they may have gone to make tea and left the window focused).
     * @returns {boolean} true if user has been active recently
     */
    userActiveRecently() {
        return this._activeRecentlyTimeout.isRunning();
    }

    _onPageVisibilityChanged(e) {
        if (this._document.visibilityState === "hidden") {
            this._activeNowTimeout.abort();
            this._activeRecentlyTimeout.abort();
        } else {
            this._onUserActivity(e);
        }
    }

    _onWindowBlurred() {
        this._activeNowTimeout.abort();
        this._activeRecentlyTimeout.abort();
    }

    _onUserActivity(event) {
        // ignore anything if the window isn't focused
        if (!this._document.hasFocus()) return;

        if (event.screenX && event.type === "mousemove") {
            if (event.screenX === this.lastScreenX && event.screenY === this.lastScreenY) {
                // mouse hasn't actually moved
                return;
            }
            this.lastScreenX = event.screenX;
            this.lastScreenY = event.screenY;
        }

        dis.dispatch({action: 'user_activity'});
        if (!this._activeNowTimeout.isRunning()) {
            this._activeNowTimeout.start();
            dis.dispatch({action: 'user_activity_start'});

            this._runTimersUntilTimeout(this._attachedTimersActive, this._activeNowTimeout);
        } else {
            this._activeNowTimeout.restart();
        }

        if (!this._activeRecentlyTimeout.isRunning()) {
            this._activeRecentlyTimeout.start();

            this._runTimersUntilTimeout(this._attachedTimersPassive, this._activeRecentlyTimeout);
        } else {
            this._activeRecentlyTimeout.restart();
        }
    }

    async _runTimersUntilTimeout(attachedTimers, timeout) {
        attachedTimers.forEach((t) => t.start());
        try {
            await timeout.finished();
        } catch (_e) { /* aborted */ }
        attachedTimers.forEach((t) => t.abort());
    }
}
<commit_msg>Fix comment<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 New Vector Ltd

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.
*/

import dis from './dispatcher';
import Timer from './utils/Timer';

// important these are larger than the timeouts of timers
// used with UserActivity.timeWhileActive*,
// such as READ_MARKER_INVIEW_THRESHOLD_MS (timeWhileActiveRecently),
// READ_MARKER_OUTOFVIEW_THRESHOLD_MS (timeWhileActiveRecently),
// READ_RECEIPT_INTERVAL_MS (timeWhileActiveNow) in TimelinePanel

// 'Under a few seconds'. Must be less than 'RECENTLY_ACTIVE_THRESHOLD_MS'
const CURRENTLY_ACTIVE_THRESHOLD_MS = 700;

// 'Under a few minutes'.
const RECENTLY_ACTIVE_THRESHOLD_MS = 2 * 60 * 1000;

/**
 * This class watches for user activity (moving the mouse or pressing a key)
 * and starts/stops attached timers while the user is active.
 *
 * There are two classes of 'active': 'active now' and 'active recently'
 * see doc on the userActive* functions for what these mean.
 */
export default class UserActivity {
    constructor(windowObj, documentObj) {
        this._window = windowObj;
        this._document = documentObj;

        this._attachedTimersActive = [];
        this._attachedTimersPassive = [];
        this._activeNowTimeout = new Timer(CURRENTLY_ACTIVE_THRESHOLD_MS);
        this._activeRecentlyTimeout = new Timer(RECENTLY_ACTIVE_THRESHOLD_MS);
        this._onUserActivity = this._onUserActivity.bind(this);
        this._onWindowBlurred = this._onWindowBlurred.bind(this);
        this._onPageVisibilityChanged = this._onPageVisibilityChanged.bind(this);
        this.lastScreenX = 0;
        this.lastScreenY = 0;
    }

    static sharedInstance() {
        if (global.mxUserActivity === undefined) {
            global.mxUserActivity = new UserActivity(window, document);
        }
        return global.mxUserActivity;
    }

    /**
     * Runs the given timer while the user is 'active now', aborting when the user is no longer
     * considered currently active.
     * See userActiveNow() for what it means for a user to be 'active'.
     * Can be called multiple times with the same already running timer, which is a NO-OP.
     * Can be called before the user becomes active, in which case it is only started
     * later on when the user does become active.
     * @param {Timer} timer the timer to use
     */
    timeWhileActiveNow(timer) {
        this._timeWhile(timer, this._attachedTimersActive);
        if (this.userActiveNow()) {
            timer.start();
        }
    }

    /**
     * Runs the given timer while the user is 'active' now or recently,
     * aborting when the user becomes inactive.
     * See userActiveRecently() for what it means for a user to be 'active recently'.
     * Can be called multiple times with the same already running timer, which is a NO-OP.
     * Can be called before the user becomes active, in which case it is only started
     * later on when the user does become active.
     * @param {Timer} timer the timer to use
     */
    timeWhileActiveRecently(timer) {
        this._timeWhile(timer, this._attachedTimersPassive);
        if (this.userActiveRecently()) {
            timer.start();
        }
    }

    _timeWhile(timer, attachedTimers) {
        // important this happens first
        const index = attachedTimers.indexOf(timer);
        if (index === -1) {
            attachedTimers.push(timer);
            // remove when done or aborted
            timer.finished().finally(() => {
                const index = attachedTimers.indexOf(timer);
                if (index !== -1) { // should never be -1
                    attachedTimers.splice(index, 1);
                }
            // as we fork the promise here,
            // avoid unhandled rejection warnings
            }).catch((err) => {});
        }
    }

    /**
     * Start listening to user activity
     */
    start() {
        this._document.addEventListener('mousedown', this._onUserActivity);
        this._document.addEventListener('mousemove', this._onUserActivity);
        this._document.addEventListener('keydown', this._onUserActivity);
        this._document.addEventListener("visibilitychange", this._onPageVisibilityChanged);
        this._window.addEventListener("blur", this._onWindowBlurred);
        this._window.addEventListener("focus", this._onUserActivity);
        // can't use document.scroll here because that's only the document
        // itself being scrolled. Need to use addEventListener's useCapture.
        // also this needs to be the wheel event, not scroll, as scroll is
        // fired when the view scrolls down for a new message.
        this._window.addEventListener('wheel', this._onUserActivity, {
            passive: true, capture: true,
        });
    }

    /**
     * Stop tracking user activity
     */
    stop() {
        this._document.removeEventListener('mousedown', this._onUserActivity);
        this._document.removeEventListener('mousemove', this._onUserActivity);
        this._document.removeEventListener('keydown', this._onUserActivity);
        this._window.removeEventListener('wheel', this._onUserActivity, {
            passive: true, capture: true,
        });

        this._document.removeEventListener("visibilitychange", this._onPageVisibilityChanged);
        this._window.removeEventListener("blur", this._onWindowBlurred);
        this._window.removeEventListener("focus", this._onUserActivity);
    }

    /**
     * Return true if the user is currently 'active'
     * A user is 'active' while they are interacting with the app and for a very short (<1s)
     * time after that. This is intended to give a strong indication that the app has the
     * user's attention at any given moment.
     * @returns {boolean} true if user is currently 'active'
     */
    userActiveNow() {
        return this._activeNowTimeout.isRunning();
    }

    /**
     * Return true if the user is currently active or has been recently
     * A user is 'active recently' for a longer period of time (~2 mins) after
     * they have been 'active' and while the app still has the focus. This is
     * intended to indicate when the app may still have the user's attention
     * (or they may have gone to make tea and left the window focused).
     * @returns {boolean} true if user has been active recently
     */
    userActiveRecently() {
        return this._activeRecentlyTimeout.isRunning();
    }

    _onPageVisibilityChanged(e) {
        if (this._document.visibilityState === "hidden") {
            this._activeNowTimeout.abort();
            this._activeRecentlyTimeout.abort();
        } else {
            this._onUserActivity(e);
        }
    }

    _onWindowBlurred() {
        this._activeNowTimeout.abort();
        this._activeRecentlyTimeout.abort();
    }

    _onUserActivity(event) {
        // ignore anything if the window isn't focused
        if (!this._document.hasFocus()) return;

        if (event.screenX && event.type === "mousemove") {
            if (event.screenX === this.lastScreenX && event.screenY === this.lastScreenY) {
                // mouse hasn't actually moved
                return;
            }
            this.lastScreenX = event.screenX;
            this.lastScreenY = event.screenY;
        }

        dis.dispatch({action: 'user_activity'});
        if (!this._activeNowTimeout.isRunning()) {
            this._activeNowTimeout.start();
            dis.dispatch({action: 'user_activity_start'});

            this._runTimersUntilTimeout(this._attachedTimersActive, this._activeNowTimeout);
        } else {
            this._activeNowTimeout.restart();
        }

        if (!this._activeRecentlyTimeout.isRunning()) {
            this._activeRecentlyTimeout.start();

            this._runTimersUntilTimeout(this._attachedTimersPassive, this._activeRecentlyTimeout);
        } else {
            this._activeRecentlyTimeout.restart();
        }
    }

    async _runTimersUntilTimeout(attachedTimers, timeout) {
        attachedTimers.forEach((t) => t.start());
        try {
            await timeout.finished();
        } catch (_e) { /* aborted */ }
        attachedTimers.forEach((t) => t.abort());
    }
}
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

var dis = require("./dispatcher");

var MIN_DISPATCH_INTERVAL = 1 * 1000;

/**
 * This class watches for user activity (moving the mouse or pressing a key)
 * and dispatches the user_activity action at times when the user is interacting
 * with the app (but at a much lower frequency than mouse move events)
 */
class UserActivity {

    /**
     * Start listening to user activity
     */
    start() {
        document.onmousemove = this._onUserActivity.bind(this);
        document.onkeypress = this._onUserActivity.bind(this);
        // can't use document.scroll here because that's only the document
        // itself being scrolled. Need to use addEventListener's useCapture.
        window.addEventListener('scroll', this._onUserActivity.bind(this), true);
        this.lastActivityAtTs = new Date().getTime();
        this.lastDispatchAtTs = 0;
    }

    /**
     * Stop tracking user activity
     */
    stop() {
        document.onmousemove = undefined;
        document.onkeypress = undefined;
        window.removeEventListener('scroll', this._onUserActivity.bind(this), true);
    }

    _onUserActivity(event) {
        if (event.screenX) {
            if (event.screenX === this.lastScreenX &&
                event.screenY === this.lastScreenY)
            {
                // mouse hasn't actually moved
                return;
            }
            this.lastScreenX = event.screenX;
            this.lastScreenY = event.screenY;
        }

        this.lastActivityAtTs = (new Date).getTime();
        if (this.lastDispatchAtTs < this.lastActivityAtTs - MIN_DISPATCH_INTERVAL) {
            this.lastDispatchAtTs = this.lastActivityAtTs;
            dis.dispatch({
                action: 'user_activity'
            });
        }
    }
}

module.exports = new UserActivity();
<commit_msg>Use wheel, not scroll as we get scroll events from auto scroll down. Also only do the cursor move check for mouse move events.<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

var dis = require("./dispatcher");

var MIN_DISPATCH_INTERVAL = 1 * 1000;

/**
 * This class watches for user activity (moving the mouse or pressing a key)
 * and dispatches the user_activity action at times when the user is interacting
 * with the app (but at a much lower frequency than mouse move events)
 */
class UserActivity {

    /**
     * Start listening to user activity
     */
    start() {
        document.onmousemove = this._onUserActivity.bind(this);
        document.onkeypress = this._onUserActivity.bind(this);
        // can't use document.scroll here because that's only the document
        // itself being scrolled. Need to use addEventListener's useCapture.
        // also this needs to be the wheel event, not scroll, as scroll is
        // fired when the view scrolls down for a new message.
        window.addEventListener('wheel', this._onUserActivity.bind(this), true);
        this.lastActivityAtTs = new Date().getTime();
        this.lastDispatchAtTs = 0;
    }

    /**
     * Stop tracking user activity
     */
    stop() {
        document.onmousemove = undefined;
        document.onkeypress = undefined;
        window.removeEventListener('wheel', this._onUserActivity.bind(this), true);
    }

    _onUserActivity(event) {
        if (event.screenX && event.type == "mousemove") {
            if (event.screenX === this.lastScreenX &&
                event.screenY === this.lastScreenY)
            {
                // mouse hasn't actually moved
                return;
            }
            this.lastScreenX = event.screenX;
            this.lastScreenY = event.screenY;
        }

        this.lastActivityAtTs = (new Date).getTime();
        if (this.lastDispatchAtTs < this.lastActivityAtTs - MIN_DISPATCH_INTERVAL) {
            this.lastDispatchAtTs = this.lastActivityAtTs;
            dis.dispatch({
                action: 'user_activity'
            });
        }
    }
}

module.exports = new UserActivity();
<|endoftext|>


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

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

import Th from '../_Th';

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';
import TableFragment from '../_statics/TableFragment';
import TableCalculation from '../_vendors/TableCalculation';

class Thead extends Component {

    static Align = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

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

    render() {

        const {
            className, style, columns, baseColIndex,
            sorting, defaultSortingType, sortingAscIconCls, sortingDescIconCls, onSortChange,
            ...restProps
        } = this.props;

        return (

                {
                    columns && columns.map((row, rowIndex) => row ?

                            {
                                TableCalculation.getColumnsWithSpan(TableFragment.HEAD, row).map(({column, span}, colIndex) => column ?
                                    <Th {...restProps}
                                        key={colIndex}
                                        className={column.headClassName}
                                        style={column.headStyle}
                                        renderer={column.headRenderer}
                                        align={column.headAlign || column.align}
                                        colIndex={baseColIndex + colIndex}
                                        rowSpan={column.rowSpan}
                                        colSpan={column.colSpan}
                                        sorting={sorting}
                                        defaultSortingType={column.defaultSortingType || defaultSortingType}
                                        sortingAscIconCls={sortingAscIconCls}
                                        sortingDescIconCls={sortingDescIconCls}
                                        sortable={column.sortable}
                                        sortingProp={column.sortingProp}
                                        onSortChange={onSortChange}/>
                                    :
                                    null
                                )
                            }

                        :
                        null
                    )
                }

        );

    }
}

Thead.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(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
         */
        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
         */
        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
         */
        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))

    }))).isRequired,

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

    /**
     * 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,

    onSortChange: PropTypes.func

};

Thead.defaultProps = {

    baseColIndex: 0,

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

};

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

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

import Th from '../_Th';

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';
import TableFragment from '../_statics/TableFragment';
import TableCalculation from '../_vendors/TableCalculation';

class Thead extends Component {

    static Align = HorizontalAlign;
    static SelectMode = SelectMode;
    static SelectAllMode = SelectAllMode;
    static SortingType = SortingType;

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

    render() {

        const {
            className, style, columns, baseColIndex,
            sorting, defaultSortingType, sortingAscIconCls, sortingDescIconCls, onSortChange,
            ...restProps
        } = this.props;

        return (

                {
                    columns && columns.map((row, rowIndex) => row ?

                            {
                                TableCalculation.getColumnsWithSpan(TableFragment.HEAD, row, data)
                                                .map(({column, span}, colIndex) => column ?
                                                    <Th {...restProps}
                                                        key={colIndex}
                                                        className={column.headClassName}
                                                        style={column.headStyle}
                                                        renderer={column.headRenderer}
                                                        align={column.headAlign || column.align}
                                                        colIndex={baseColIndex + colIndex}
                                                        rowSpan={column.rowSpan}
                                                        colSpan={column.colSpan}
                                                        sorting={sorting}
                                                        defaultSortingType={column.defaultSortingType || defaultSortingType}
                                                        sortingAscIconCls={sortingAscIconCls}
                                                        sortingDescIconCls={sortingDescIconCls}
                                                        sortable={column.sortable}
                                                        sortingProp={column.sortingProp}
                                                        onSortChange={onSortChange}/>
                                                    :
                                                    null
                                                )
                            }

                        :
                        null
                    )
                }

        );

    }
}

Thead.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    /**
     * Children passed into table header.
     */
    columns: PropTypes.arrayOf(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))

    }))).isRequired,

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

    /**
     * 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,

    onSortChange: PropTypes.func

};

Thead.defaultProps = {

    baseColIndex: 0,

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

};

export default Thead;
<|endoftext|>


<commit_before>const efs = require('../utils/fs');
const path = require('path');
const config = require('../config');
const log = require('npmlog');

const registerDependencyAndroid = require('../android/registerNativeModule');
const registerDependencyIOS = require('../ios/registerNativeModule');

/**
 * Returns an array of dependencies that should be linked/checked.
 */
const getProjectDependencies = () => {
  const pjson = require(path.join(process.cwd(), './package.json'));
  return Object.keys(pjson.dependencies).filter(name => name !== 'react-native');
};

/**
 * Updates project and linkes all dependencies to it
 *
 * If optional argument [packageName] is provided, it's the only one that's checked
 *
 * @todo add support for copying assets, investigate if it's still neded in RN 0.14+
 */
function link(packageName) {
  const project = config.getProjectConfig();

  if (!project) {
    log.error('ERRPACKAGEJSON', `No package found. Are you sure it's a React Native project?`);
    return;
  }

  const dependencies = packageName ? [packageName] : getProjectDependencies();

  if (!packageName) {
    log.info(`Found ${dependencies.length} native dependencies to link`);
  }

  dependencies
    .forEach(name => {
      const dependencyConfig = config.getDependencyConfig(name);

      if (!dependencyConfig) {
        return log.warn('ERRINVALIDPROJ', `Project ${name} is not a react-native library`);
      }
      //
      // if (project.android && dependencyConfig.android) {
      //   log.info(`Linking ${name} android dependency`);
      //   registerDependencyAndroid(name, dependencyConfig.android, project.android);
      // }

      console.log(dependencyConfig.ios);

      if (project.ios && dependencyConfig.ios) {
        log.info(`Linking ${name} ios dependency`);
        registerDependencyIOS(dependencyConfig.ios, project.ios);
      }
    });
}

module.exports = {
  description: 'This action updates your project and links all native dependencies',
  run: link,
  args: [{
    name: 'packageName',
  }],
};
<commit_msg>Revert android part<commit_after>const efs = require('../utils/fs');
const path = require('path');
const config = require('../config');
const log = require('npmlog');

const registerDependencyAndroid = require('../android/registerNativeModule');
const registerDependencyIOS = require('../ios/registerNativeModule');

/**
 * Returns an array of dependencies that should be linked/checked.
 */
const getProjectDependencies = () => {
  const pjson = require(path.join(process.cwd(), './package.json'));
  return Object.keys(pjson.dependencies).filter(name => name !== 'react-native');
};

/**
 * Updates project and linkes all dependencies to it
 *
 * If optional argument [packageName] is provided, it's the only one that's checked
 *
 * @todo add support for copying assets, investigate if it's still neded in RN 0.14+
 */
function link(packageName) {
  const project = config.getProjectConfig();

  if (!project) {
    log.error('ERRPACKAGEJSON', `No package found. Are you sure it's a React Native project?`);
    return;
  }

  const dependencies = packageName ? [packageName] : getProjectDependencies();

  if (!packageName) {
    log.info(`Found ${dependencies.length} native dependencies to link`);
  }

  dependencies
    .forEach(name => {
      const dependencyConfig = config.getDependencyConfig(name);

      if (!dependencyConfig) {
        return log.warn('ERRINVALIDPROJ', `Project ${name} is not a react-native library`);
      }

      if (project.android && dependencyConfig.android) {
        log.info(`Linking ${name} android dependency`);
        registerDependencyAndroid(name, dependencyConfig.android, project.android);
      }

      if (project.ios && dependencyConfig.ios) {
        log.info(`Linking ${name} ios dependency`);
        registerDependencyIOS(dependencyConfig.ios, project.ios);
      }
    });
}

module.exports = {
  description: 'This action updates your project and links all native dependencies',
  run: link,
  args: [{
    name: 'packageName',
  }],
};
<|endoftext|>


<commit_before>import Container from 'kenga/container';

class Anchors extends Container {
    constructor() {
        super();
        const self = this;

        this.element.classList.add('p-anchors');

        function is(a) {
            return typeof a !== 'undefined' && a !== null && a !== '';
        }

        function getAnchors(w) {
            return {
                left: w.element.style.left,
                top: w.element.style.top,
                right: w.element.style.right,
                bottom: w.element.style.bottom,
                width: w.element.style.width,
                height: w.element.style.height
            };
        }
        function setAnchors(w, anchors) {
            w.element.style.left = anchors.left;
            w.element.style.top = anchors.top;
            w.element.style.right = anchors.right;
            w.element.style.bottom = anchors.bottom;
            w.element.style.width = anchors.width;
            w.element.style.height = anchors.height;
        }

        function updatePlainValue(anchor, value, containerSize) {
            if (anchor.endsWith('px'))
                return `${value}px`;
            else if (anchor.endsWith('%'))
                return `${value / containerSize * 100}%`;
            else
                return `${value}px`;
        }

        function ajustWidth(w, aValue) {
            if (typeof aValue === 'string' && (aValue.endsWith('px') || aValue.endsWith('%'))) {
                w.element.style.width = aValue;
            } else {
                const anchors = getAnchors(w);
                const containerWidth = self.element.offsetWidth;
                if (is(anchors.width)) {
                    anchors.width = updatePlainValue(anchors.width, aValue, containerWidth);
                } else if (is(anchors.left) && is(anchors.right)) {
                    anchors.right = updatePlainValue(anchors.right, containerWidth - w.element.offsetLeft - aValue, containerWidth);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustWidth', {
            get: function () {
                return ajustWidth;
            }
        });

        function ajustHeight(w, aValue) {
            if (typeof aValue === 'string' && (aValue.endsWith('px') || aValue.endsWith('%'))) {
                w.element.style.height = aValue;
            } else {
                const anchors = getAnchors(w);
                const containerHeight = self.element.offsetHeight;
                if (is(anchors.height)) {
                    anchors.height = updatePlainValue(anchors.height, aValue, containerHeight);
                } else if (is(anchors.top) && is(anchors.bottom)) {
                    anchors.bottom = updatePlainValue(anchors.bottom, containerHeight - w.element.offsetTop - aValue, containerHeight);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustHeight', {
            get: function () {
                return ajustHeight;
            }
        });

        function ajustLeft(w, aValue) {
            if (typeof aValue === 'string' && (aValue.endsWith('px') || aValue.endsWith('%'))) {
                w.element.style.left = aValue;
            } else {
                const anchors = getAnchors(w);
                const containerWidth = self.element.offsetWidth;
                const childWidth = w.element.offsetWidth;
                if (is(anchors.left) && is(anchors.width)) {
                    anchors.left = updatePlainValue(anchors.left, aValue, containerWidth);
                } else if (is(anchors.width) && is(anchors.right)) {
                    anchors.right = updatePlainValue(anchors.right, containerWidth - aValue - childWidth, containerWidth);
                } else if (is(anchors.left) && is(anchors.right)) {
                    anchors.left = updatePlainValue(anchors.left, aValue, containerWidth);
                    anchors.right = updatePlainValue(anchors.right, containerWidth - aValue - childWidth, containerWidth);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustLeft', {
            get: function () {
                return ajustLeft;
            }
        });

        function ajustTop(w, aValue) {
            if (typeof aValue === 'string' && (aValue.endsWith('px') || aValue.endsWith('%'))) {
                w.element.style.top = aValue;
            } else {
                const anchors = getAnchors(w);
                const containerHeight = self.element.offsetHeight;
                const childHeight = w.element.offsetHeight;
                if (is(anchors.top) && is(anchors.height)) {
                    anchors.top = updatePlainValue(anchors.top, aValue, containerHeight);
                } else if (is(anchors.height) && is(anchors.bottom)) {
                    anchors.bottom = updatePlainValue(anchors.bottom, containerHeight - aValue - childHeight, containerHeight);
                } else if (is(anchors.top) && is(anchors.bottom)) {
                    anchors.top = updatePlainValue(anchors.top, aValue, containerHeight);
                    anchors.bottom = updatePlainValue(anchors.bottom, containerHeight - aValue - childHeight, containerHeight);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustTop', {
            get: function () {
                return ajustTop;
            }
        });
    }
}

export default Anchors;
<commit_msg>width height left top properties editing cleaning<commit_after>import Container from 'kenga/container';

class Anchors extends Container {
    constructor() {
        super();
        const self = this;

        this.element.classList.add('p-anchors');

        function is(a) {
            return typeof a !== 'undefined' && a !== null && a !== '';
        }

        function getAnchors(w) {
            return {
                left: w.element.style.left,
                top: w.element.style.top,
                right: w.element.style.right,
                bottom: w.element.style.bottom,
                width: w.element.style.width,
                height: w.element.style.height
            };
        }
        function setAnchors(w, anchors) {
            w.element.style.left = anchors.left;
            w.element.style.top = anchors.top;
            w.element.style.right = anchors.right;
            w.element.style.bottom = anchors.bottom;
            w.element.style.width = anchors.width;
            w.element.style.height = anchors.height;
        }

        function updatePlainValue(anchor, value, containerSize) {
            if (anchor.endsWith('px'))
                return `${value}px`;
            else if (anchor.endsWith('%'))
                return `${value / containerSize * 100}%`;
            else
                return `${value}px`;
        }

        function ajustWidth(w, aValue) {
            if (typeof aValue === 'string') {
                w.element.style.width = !aValue.endsWith('px') && !aValue.endsWith('%') && !aValue.endsWith('em') && aValue !== 'null' ?
                        aValue + 'px' :
                        (aValue === 'null' ? null : aValue);
            } else if (aValue == null){
                w.element.style.width = '';
            } else {
                const anchors = getAnchors(w);
                const containerWidth = self.element.offsetWidth;
                if (is(anchors.width)) {
                    anchors.width = updatePlainValue(anchors.width, aValue, containerWidth);
                } else if (is(anchors.left) && is(anchors.right)) {
                    anchors.right = updatePlainValue(anchors.right, containerWidth - w.element.offsetLeft - aValue, containerWidth);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustWidth', {
            get: function () {
                return ajustWidth;
            }
        });

        function ajustHeight(w, aValue) {
            if (typeof aValue === 'string') {
                w.element.style.height = !aValue.endsWith('px') && !aValue.endsWith('%') && !aValue.endsWith('em') && aValue !== 'null' ?
                        aValue + 'px' :
                        (aValue === 'null' ? null : aValue);
            } else if (aValue == null){
                w.element.style.height = '';
            } else {
                const anchors = getAnchors(w);
                const containerHeight = self.element.offsetHeight;
                if (is(anchors.height)) {
                    anchors.height = updatePlainValue(anchors.height, aValue, containerHeight);
                } else if (is(anchors.top) && is(anchors.bottom)) {
                    anchors.bottom = updatePlainValue(anchors.bottom, containerHeight - w.element.offsetTop - aValue, containerHeight);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustHeight', {
            get: function () {
                return ajustHeight;
            }
        });

        function ajustLeft(w, aValue) {
            if (typeof aValue === 'string') {
                w.element.style.left = !aValue.endsWith('px') && !aValue.endsWith('%') && !aValue.endsWith('em') && aValue !== 'null' ?
                        aValue + 'px' :
                        (aValue === 'null' ? null : aValue);
            } else if (aValue == null){
                w.element.style.left = '';
            } else {
                const anchors = getAnchors(w);
                const containerWidth = self.element.offsetWidth;
                const childWidth = w.element.offsetWidth;
                if (is(anchors.left) && is(anchors.width)) {
                    anchors.left = updatePlainValue(anchors.left, aValue, containerWidth);
                } else if (is(anchors.width) && is(anchors.right)) {
                    anchors.right = updatePlainValue(anchors.right, containerWidth - aValue - childWidth, containerWidth);
                } else if (is(anchors.left) && is(anchors.right)) {
                    anchors.left = updatePlainValue(anchors.left, aValue, containerWidth);
                    anchors.right = updatePlainValue(anchors.right, containerWidth - aValue - childWidth, containerWidth);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustLeft', {
            get: function () {
                return ajustLeft;
            }
        });

        function ajustTop(w, aValue) {
            if (typeof aValue === 'string') {
                w.element.style.top = !aValue.endsWith('px') && !aValue.endsWith('%') && !aValue.endsWith('em') && aValue !== 'null' ?
                        aValue + 'px' :
                        (aValue === 'null' ? null : aValue);
            } else if (aValue == null){
                w.element.style.top = '';
            } else {
                const anchors = getAnchors(w);
                const containerHeight = self.element.offsetHeight;
                const childHeight = w.element.offsetHeight;
                if (is(anchors.top) && is(anchors.height)) {
                    anchors.top = updatePlainValue(anchors.top, aValue, containerHeight);
                } else if (is(anchors.height) && is(anchors.bottom)) {
                    anchors.bottom = updatePlainValue(anchors.bottom, containerHeight - aValue - childHeight, containerHeight);
                } else if (is(anchors.top) && is(anchors.bottom)) {
                    anchors.top = updatePlainValue(anchors.top, aValue, containerHeight);
                    anchors.bottom = updatePlainValue(anchors.bottom, containerHeight - aValue - childHeight, containerHeight);
                }
                setAnchors(w, anchors);
            }
        }
        Object.defineProperty(this, 'ajustTop', {
            get: function () {
                return ajustTop;
            }
        });
    }
}

export default Anchors;
<|endoftext|>


<commit_before>/*global angular*/

angular.module('angular-jpeg', []);

angular.module('angular-jpeg').constant('ANGULAR_JPEG_ERRORS', {
  noFile: 'No file found',
  fileReadError: 'Unable to read file contents',
  unknown: 'Unknown',
  unrecognisedMarker: 'Unrecognised marker',
  unsupportedMarker: 'Unsupported marker',
  noSegments: 'No segments found',
  missingStartOfImageMarker: 'Missing start of image marker',
  missingEndOfImageMarker: 'Missing end of image marker'
});

angular.module('angular-jpeg').service('AngularJpeg', function($q, $window,
  ANGULAR_JPEG_SEGMENT_TYPES,
  ANGULAR_JPEG_SEGMENT_PREFIX,
  ANGULAR_JPEG_ERRORS) {
  'use strict';

  var ERRORS = ANGULAR_JPEG_ERRORS;
  var TYPES = ANGULAR_JPEG_SEGMENT_TYPES;
  var PREFIX = ANGULAR_JPEG_SEGMENT_PREFIX;
  var self = this;

  function readUInt16BigEndian(uInt8Array, offset) {
    /*jshint bitwise: false*/
    return (uInt8Array[offset] << 8) | uInt8Array[offset + 1];
  }

  // For convenience
  for (var name in TYPES) {
    /* istanbul ignore else */
    if (TYPES.hasOwnProperty(name)) {
      TYPES[name].name = name;
    }
  }

  function typeFromMarker(marker) {
    var type, key;
    for (key in TYPES) {
      /* istanbul ignore else */
      if (TYPES.hasOwnProperty(key)) {
        type = TYPES[key];
        if (marker === type.marker && !type.unsupported) {
          return TYPES[key];
        }
        if (marker === type.marker && type.unsupported) {
          throw ERRORS.unsupportedMarker + ': ' + marker;
        }
      }
    }
    throw ERRORS.unrecognisedMarker + ': ' + marker;
  }

  function getSegmentOffsets(uInt8Array) {
    var segments = [];
    var offset = 0;
    var hasData = false;
    var previousSegment;
    var segmentOffset, dataOffset;
    var possiblePrefix, possibleMarker, isMarker;
    var type, segmentSize;
    while (offset <= uInt8Array.length - 2) {
      possiblePrefix = uInt8Array[offset];
      possibleMarker = uInt8Array[offset + 1];
      isMarker = possiblePrefix === PREFIX && possibleMarker !== PREFIX && possibleMarker !== 0;
      if (isMarker) {
        if (hasData) {
          previousSegment = segments[segments.length - 1];
          previousSegment.dataSize = offset - previousSegment.dataOffset;
        }
        try {
          type = typeFromMarker(possibleMarker);
        } catch(e) {
          return $q.reject(e);
        }
        segmentSize = type.empty ? 0 : readUInt16BigEndian(uInt8Array, offset + 2) - 2;
        segmentOffset = offset + (type.empty ? 2 : 4);
        dataOffset = segmentOffset + segmentSize;
        hasData = !!type.hasData;
        segments.push({
          type: type,
          segmentOffset: segmentOffset,
          segmentSize: segmentSize,
          segmentContents: new $window.Uint8Array(0),
          dataOffset: dataOffset,
          dataSize: 0,
          dataContents: new $window.Uint8Array(0)
        });
        offset = dataOffset;
      } else {
        offset += 1;
      }
    }

    return validate(segments);
  }

  function validate(segments) {
    if (!segments.length) {
      return $q.reject(ERRORS.noSegments);
    }
    if (segments[0].type !== TYPES.startOfImage) {
      return $q.reject(ERRORS.missingStartOfImageMarker);
    }
    if (segments[segments.length - 1].type !== TYPES.endOfImage) {
      return $q.reject(ERRORS.missingEndOfImageMarker);
    }

    return $q.when(segments);
  }

  function attachContents(buffer, segments) {
    segments.forEach(function(segment) {
      if (segment.segmentSize) {
        segment.segmentContents = new $window.Uint8Array(buffer, segment.segmentOffset, segment.segmentSize);
      }
      if (segment.dataSize) {
        segment.dataContents = new $window.Uint8Array(buffer, segment.dataOffset, segment.dataSize);
      }
    });
    return segments;
  }

  function groupSegmentsByType(segments) {
    var grouped = {};
    segments.forEach(function(segment) {
      grouped[segment.type.name] = grouped[segment.type.name] || [];
      grouped[segment.type.name].push(segment);
    });
    return grouped;
  }

  self.loadSegmentsFromBuffer = function loadSegmentsFromBuffer(buffer) {
    return getSegmentOffsets(new $window.Uint8Array(buffer)).then(function(segments) {
      return validate(segments);
    }).then(function(segments) {
      return attachContents(buffer, segments);
    }).then(function(segments) {
      return groupSegmentsByType(segments);
    });
  };

  self.loadSegmentsFromFile = function loadFromFile(file) {
    if (!file) {
      return $q.reject(ERRORS.noFile);
    }
    var reader = new $window.FileReader();

    // Defer creation required to convert
    // onload to promise
    var deferred = $q.defer();

    reader.onload = function(e) {
      var buffer = e.target.result;
      self.loadSegmentsFromBuffer(buffer).then(function(segments) {
        deferred.resolve(segments);
      }, function(error) {
        deferred.reject(error);
      });
    };

    reader.onerror = function() {
      deferred.reject(ERRORS.fileReadError);
    };

    reader.readAsArrayBuffer(file);

    return deferred.promise;
  };

  function newNode(parent, bit) {
    return {
      children: {},
      parent: parent,
      full: false,
      codeLength: parent ? parent.codeLength + 1 : 0,
      bit: bit || null
    };
  }

  // Convert to simple structure
  function convertNode(node) {
      if (!angular.isObject(node)) {
        return node;
      }
      delete node.full;
      delete node.parent;
      delete node.bit;
      delete node.codeLength;
      if (node.children[0]) {
        node[0] = convertNode(node.children[0]);
      }
      if (node.children[1]) {
        node[1] = convertNode(node.children[1]);
      }
      delete node.children;
      return node;
    }

  self._huffmanTreeFromTable = function(table) {
    var root = newNode();
    var searchBelow = root;
    var node = root;
    var bit;

    table.forEach(function(values, codeLength) {
      values.forEach(function(value) {
        // Search for node of codeLength - 1
        node = searchBelow;
        while (node.codeLength < codeLength - 1) {
          if (angular.isObject(node.children[0]) && !node.children[0].full) {
            node = node.children[0];
          } else if (angular.isObject(node.children[1]) && !node.children[1].full) {
            node = node.children[1];
          } else {
            bit = Object.keys(node.children).length;
            node.children[bit] = newNode(node, bit);
            node = node.children[bit];
          }
        }

        // Set value in node child
        bit = Object.keys(node.children).length;
        node.children[bit] = value;

        // Mark this and parent nodes as full to avoid
        // looking down them in later iterations
        if (bit === 1) {
          node.full = true;
          while (node.parent && node.bit === 1) {
            node = node.parent;
            node.full = true;
          }
          searchBelow = node.parent;
        }
      });
    });

    convertNode(root);
    return root;
  };
});<commit_msg>Remove always true if<commit_after>/*global angular*/

angular.module('angular-jpeg', []);

angular.module('angular-jpeg').constant('ANGULAR_JPEG_ERRORS', {
  noFile: 'No file found',
  fileReadError: 'Unable to read file contents',
  unknown: 'Unknown',
  unrecognisedMarker: 'Unrecognised marker',
  unsupportedMarker: 'Unsupported marker',
  noSegments: 'No segments found',
  missingStartOfImageMarker: 'Missing start of image marker',
  missingEndOfImageMarker: 'Missing end of image marker'
});

angular.module('angular-jpeg').service('AngularJpeg', function($q, $window,
  ANGULAR_JPEG_SEGMENT_TYPES,
  ANGULAR_JPEG_SEGMENT_PREFIX,
  ANGULAR_JPEG_ERRORS) {
  'use strict';

  var ERRORS = ANGULAR_JPEG_ERRORS;
  var TYPES = ANGULAR_JPEG_SEGMENT_TYPES;
  var PREFIX = ANGULAR_JPEG_SEGMENT_PREFIX;
  var self = this;

  function readUInt16BigEndian(uInt8Array, offset) {
    /*jshint bitwise: false*/
    return (uInt8Array[offset] << 8) | uInt8Array[offset + 1];
  }

  // For convenience
  for (var name in TYPES) {
    /* istanbul ignore else */
    if (TYPES.hasOwnProperty(name)) {
      TYPES[name].name = name;
    }
  }

  function typeFromMarker(marker) {
    var type, key;
    for (key in TYPES) {
      /* istanbul ignore else */
      if (TYPES.hasOwnProperty(key)) {
        type = TYPES[key];
        if (marker === type.marker && !type.unsupported) {
          return TYPES[key];
        }
        if (marker === type.marker && type.unsupported) {
          throw ERRORS.unsupportedMarker + ': ' + marker;
        }
      }
    }
    throw ERRORS.unrecognisedMarker + ': ' + marker;
  }

  function getSegmentOffsets(uInt8Array) {
    var segments = [];
    var offset = 0;
    var hasData = false;
    var previousSegment;
    var segmentOffset, dataOffset;
    var possiblePrefix, possibleMarker, isMarker;
    var type, segmentSize;
    while (offset <= uInt8Array.length - 2) {
      possiblePrefix = uInt8Array[offset];
      possibleMarker = uInt8Array[offset + 1];
      isMarker = possiblePrefix === PREFIX && possibleMarker !== PREFIX && possibleMarker !== 0;
      if (isMarker) {
        if (hasData) {
          previousSegment = segments[segments.length - 1];
          previousSegment.dataSize = offset - previousSegment.dataOffset;
        }
        try {
          type = typeFromMarker(possibleMarker);
        } catch(e) {
          return $q.reject(e);
        }
        segmentSize = type.empty ? 0 : readUInt16BigEndian(uInt8Array, offset + 2) - 2;
        segmentOffset = offset + (type.empty ? 2 : 4);
        dataOffset = segmentOffset + segmentSize;
        hasData = !!type.hasData;
        segments.push({
          type: type,
          segmentOffset: segmentOffset,
          segmentSize: segmentSize,
          segmentContents: new $window.Uint8Array(0),
          dataOffset: dataOffset,
          dataSize: 0,
          dataContents: new $window.Uint8Array(0)
        });
        offset = dataOffset;
      } else {
        offset += 1;
      }
    }

    return validate(segments);
  }

  function validate(segments) {
    if (!segments.length) {
      return $q.reject(ERRORS.noSegments);
    }
    if (segments[0].type !== TYPES.startOfImage) {
      return $q.reject(ERRORS.missingStartOfImageMarker);
    }
    if (segments[segments.length - 1].type !== TYPES.endOfImage) {
      return $q.reject(ERRORS.missingEndOfImageMarker);
    }

    return $q.when(segments);
  }

  function attachContents(buffer, segments) {
    segments.forEach(function(segment) {
      if (segment.segmentSize) {
        segment.segmentContents = new $window.Uint8Array(buffer, segment.segmentOffset, segment.segmentSize);
      }
      if (segment.dataSize) {
        segment.dataContents = new $window.Uint8Array(buffer, segment.dataOffset, segment.dataSize);
      }
    });
    return segments;
  }

  function groupSegmentsByType(segments) {
    var grouped = {};
    segments.forEach(function(segment) {
      grouped[segment.type.name] = grouped[segment.type.name] || [];
      grouped[segment.type.name].push(segment);
    });
    return grouped;
  }

  self.loadSegmentsFromBuffer = function loadSegmentsFromBuffer(buffer) {
    return getSegmentOffsets(new $window.Uint8Array(buffer)).then(function(segments) {
      return validate(segments);
    }).then(function(segments) {
      return attachContents(buffer, segments);
    }).then(function(segments) {
      return groupSegmentsByType(segments);
    });
  };

  self.loadSegmentsFromFile = function loadFromFile(file) {
    if (!file) {
      return $q.reject(ERRORS.noFile);
    }
    var reader = new $window.FileReader();

    // Defer creation required to convert
    // onload to promise
    var deferred = $q.defer();

    reader.onload = function(e) {
      var buffer = e.target.result;
      self.loadSegmentsFromBuffer(buffer).then(function(segments) {
        deferred.resolve(segments);
      }, function(error) {
        deferred.reject(error);
      });
    };

    reader.onerror = function() {
      deferred.reject(ERRORS.fileReadError);
    };

    reader.readAsArrayBuffer(file);

    return deferred.promise;
  };

  function newNode(parent, bit) {
    return {
      children: {},
      parent: parent,
      full: false,
      codeLength: parent ? parent.codeLength + 1 : 0,
      bit: bit || null
    };
  }

  // Convert to simple structure
  function convertNode(node) {
      if (!angular.isObject(node)) {
        return node;
      }
      delete node.full;
      delete node.parent;
      delete node.bit;
      delete node.codeLength;
      node[0] = convertNode(node.children[0]);
      if (node.children[1]) {
        node[1] = convertNode(node.children[1]);
      }
      delete node.children;
      return node;
    }

  self._huffmanTreeFromTable = function(table) {
    var root = newNode();
    var searchBelow = root;
    var node = root;
    var bit;

    table.forEach(function(values, codeLength) {
      values.forEach(function(value) {
        // Search for node of codeLength - 1
        node = searchBelow;
        while (node.codeLength < codeLength - 1) {
          if (angular.isObject(node.children[0]) && !node.children[0].full) {
            node = node.children[0];
          } else if (angular.isObject(node.children[1]) && !node.children[1].full) {
            node = node.children[1];
          } else {
            bit = Object.keys(node.children).length;
            node.children[bit] = newNode(node, bit);
            node = node.children[bit];
          }
        }

        // Set value in node child
        bit = Object.keys(node.children).length;
        node.children[bit] = value;

        // Mark this and parent nodes as full to avoid
        // looking down them in later iterations
        if (bit === 1) {
          node.full = true;
          while (node.parent && node.bit === 1) {
            node = node.parent;
            node.full = true;
          }
          searchBelow = node.parent;
        }
      });
    });

    convertNode(root);
    return root;
  };
});<|endoftext|>


<commit_before>/* commonjs package manager support */
if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
    var autobahn = require('autobahn');
    module.exports = 'vxWamp';
}

(function () {
    'use strict';

    /**
     * @ngdoc module
     * @name vxWamp
     * @description
     *
     * # vxWamp
     *
     * The `vxWamp` module provides seamless WAMPv2 integration, by wrapping AutobahnJS, for angular apps.
     *
     * ## Example
     * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
     *
     *
     *

     */
    /* global -vxWampModule */
    var vxWampModule = angular.module('vxWamp', []).provider('$wamp', $WampProvider);

    /**
     * @ngdoc provider
     * @name $wampProvider
     *
     * @description
     *
     * Used for accessing a WAMP resource.
     *
     * ## Example
     * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
     *
     * ## Dependencies
     * Requires the {@link vxWamp `vxWamp`} module to be installed.
     */
    function $WampProvider() {
        var options;

        /**
         * @ngdoc method
         * @name $wampProvider#init
         *
         * @param {Object} initOptions autobahnJS connection options
         *
         *    Object properties:
         *
         *      Required options:
         *
         *    - `url`: `{string=}` - the WebSocket URL of the WAMP router to connect to, e.g. ws://myserver.com:8080/ws
         *    - `realm`: `{string=}` - The WAMP realm to join, e.g. realm1
         *
         *    Optional options:
         *
         *      Options that control what kind of Deferreds to use:
         *
         *      - `use_es6_promises`: `{boolean=}` - use deferreds based on ES6 promises
         *      - `use_deferred`: `{callable=}` - if provided, use this deferred constructor, e.g. jQuery.Deferred or Q.defer (default: $q.defer)
         *
         *      Options that control automatic reconnection:
         *
         *      - `max_retries`: `{integer=}` - Maximum number of reconnection attempts (default: 15)
         *      - `initial_retry_delay`: `{float=}` - Initial delay for reconnection attempt in seconds (default: 1.5).
         *      - `max_retry_delay`: `{float=}` - Maximum delay for reconnection attempts in seconds (default: 300).
         *      - `retry_delay_growth`: `{float=}` - The growth factor applied to the retry delay between reconnection attempts (default: 1.5).
         *      - `retry_delay_jitter`: `{float=}` - The standard deviation of a Gaussian to jitter the delay on each retry cycle as a fraction of the mean (default: 0.1).
         *
         * @description
         * Configures the AutobhanJS Service
         */
        this.init = function (initOptions) {
            options = initOptions || {};
        };

        /**
         * @ngdoc property
         * @name $wampProvider#interceptors
         * @description
         *
         * Array containing service factories for all synchronous or asynchronous
         * postprocessing of responses.
         *
         * {@link $wamp#interceptors Interceptors detailed info}
         **/
        var interceptors = this.interceptors = [];

        this.$get = function ($rootScope, $q, $log, $injector) {

            /**
             * @ngdoc service
             * @name $wamp
             *
             * @description
             * `$wamp` give you access to the autobahnJS methods, call, register, subscribe, unsubscribe and publish.
             *
             * Requires the {@link vxWamp `vxWamp`} module to be installed.
             *
             * You can configure the WAMP connection through {@link $wampProvider}'s API.
             *
             * @example
             *
             * app.config(function ($wampProvider) {
             *      $wampProvider.init({
             *          url: 'ws://127.0.0.1:9000/',
             *          realm: 'realm1'
             *      });
             * })
             *
             *  app.controller("MyCtrl", function($scope, $wamp) {
             *
             *      // 1) subscribe to a topic
             *      function onevent(args) {
             *          $scope.hello = args[0];
             *      }
             *      $wamp.subscribe('com.myapp.hello', onevent);
             *
             *      // 2) publish an event
             *      $wamp.publish('com.myapp.hello', ['Hello, world!']);
             *
             *      // 3) register a procedure for remoting
             *      function add2(args) {
             *          return args[0] + args[1];
             *      }
             *      $wamp.register('com.myapp.add2', add2);
             *
             *      // 4) call a remote procedure
             *      $wamp.call('com.myapp.add2', [2, 3]).then(
             *          function (res) {
             *          $scope.add2 = res;
             *      });
             * });
             *
             *  Events
             *
             *  There are four events that $wamp can broadcast:
             *      1)  $wamp.open - is sent when the WAMP connection opens.
             *
             *              $scope.$on("$wamp.open", function (event, info) {
             *                  // info.session
             *                  // info.details
             *                  // Do something
             *              });
             *
             *      2)  $wamp.close - is sent when the WAMP connection closes.
             *
             *              $scope.$on("$wamp.close", function (event, info) {
             *                  // info.reason: wamp close reason
             *                  // info.details: wamp close details
             *              });
             *
             *      3)  $wamp.error - is sent when an error occurs while trying to make a call, publish or register
             *
             *              $scope.$on("$wamp.error", function (event, error) {
             *                  // error: Autobahn.Error object
             *              });
             *
             *      4)  $wamp.onchallenge
             *
             *              $scope.$on("$wamp.onchallenge", function (event, info) {
             *                  // info.promise: promise to return to wamp,
             *                  // info.session: wamp session,
             *                  // info.method: auth method,
             *                  // info.extra: extra
             *
             *                  //ie. wamp-cra
             *                  var key =  autobahn.auth_cra.derive_key("da_password", info.extra.salt);
             *                  return info.promise.resolve(autobahn.auth_cra.sign(key, info.extra.challenge));
             *              });
             *
             */

            var connection;
            var sessionDeferred = $q.defer();
            var sessionPromise = sessionDeferred.promise;

            /**
             * @param session
             * @param method
             * @param extra
             * @returns {*}
             *
             * @description
             * Gets called when a Challenge Message is sent by the router
             */
            var onchallenge = function (session, method, extra) {

                var onChallengeDeferred = $q.defer();

                $rootScope.$broadcast("$wamp.onchallenge", {
                    promise: onChallengeDeferred,
                    session: session,
                    method: method,
                    extra: extra
                });

                return onChallengeDeferred.promise;
            };

            /**
             * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
             */
            var reversedInterceptors = [];

            angular.forEach(interceptors, function (interceptor) {
                reversedInterceptors.unshift(
                    angular.isString(interceptor) ? $injector.get(interceptor) :  $injector.invoke(interceptor));
            });

            /**
             * @param func
             * @returns {Function}
             *
             * @description
             * Wraps a callback with a function that calls scope.$apply(), so that the callback is added to the digest
             */
            function digestWrapper(func) {
                return function () {
                    var cb = func.apply(this, arguments);
                    $rootScope.$apply();
                    return cb;
                };
            }

            options = angular.extend({onchallenge: digestWrapper(onchallenge), use_deferred: $q.defer}, options);

            connection = new autobahn.Connection(options);
            connection.onopen = digestWrapper(function (session, details) {
                $log.debug("Congrats!  You're connected to the WAMP server!");
                $rootScope.$broadcast("$wamp.open", {session: session, details: details});
                sessionDeferred.resolve();
            });

            connection.onclose = digestWrapper(function (reason, details) {
                $log.debug("Connection Closed: ", reason, details);
                sessionDeferred = $q.defer();
                sessionPromise = sessionDeferred.promise;
                $rootScope.$broadcast("$wamp.close", {reason: reason, details: details});
            });

            /**
             * Subscription object which self manages reconnections
             * @param topic
             * @param handler
             * @param options
             * @param subscribedCallback
             * @returns {{}}
             * @constructor
             */
            var Subscription = function (topic, handler, options, subscribedCallback) {

                var subscription = {}, unregister, onOpen, deferred = $q.defer();

                handler = digestWrapper(handler);

                onOpen = function () {
                    var p = connection.session.subscribe(topic, handler, options).then(
                        function (s) {
                            subscription = angular.extend(s, subscription);
                            deferred.resolve(subscription);
                            return s;
                        }
                    );
                    if (subscribedCallback) {
                        subscribedCallback(p);
                    }

                };

                if (connection.isOpen) {
                    onOpen();
                }

                unregister = $rootScope.$on("$wamp.open", onOpen);

                subscription.promise = deferred.promise;
                subscription.unsubscribe = function () {
                    unregister(); //Remove the event listener, so this object can get cleaned up by gc
                    return connection.session.unsubscribe(subscription);
                };

                return subscription.promise;
            };

            /**
             * Wraps WAMP actions, so that when they're called, the defined interceptors get called before the result is returned
             *
             * @param type
             * @param args
             * @param callback
             * @returns {*}
             */
            var interceptorWrapper = function (type, args, callback) {

                /**
                 * Default result
                 *
                 * @param result
                 * @returns {{result: *, type: *, args: *}}
                 */
                var result = function (result) {
                    return {result: result, type: type, args: args};
                };

                /**
                 * Default Error
                 *
                 * @param error
                 * @returns {{error: *, type: *, args: *}}
                 */
                var error = function (error) {
                    $log.error("$wamp error", {type: type, arguments: args, error: error});
                    return $q.reject({error: error, type: type, args: args});
                };

                // Only execute the action callback once we have an established session
                var action = sessionPromise.then(function () {
                    return callback();
                });

                var chain = [result, error];

                // Apply interceptors
                angular.forEach(reversedInterceptors, function (interceptor) {
                    if (interceptor[type + 'Response'] || interceptor[type + 'ResponseError']) {
                        chain.push(interceptor[type + 'Response'], interceptor[type + 'ResponseError']);
                    }
                });

                // We only want to return the actually result or error, not the entire information object
                chain.push(
                    function (response) {
                        return response.result;
                    }, function (response) {
                        return $q.reject(response.error);
                    }
                );

                while (chain.length) {
                    var resolved = chain.shift();
                    var rejected = chain.shift();

                    action = action.then(resolved, rejected);
                }

                return action;
            };

            return {
                connection: connection,
                open: function () {
                    //If using WAMP CRA we need to get the authid before the connection can be opened.
                    if (options.authmethods && options.authmethods.indexOf('wampcra') !== -1 && !options.authid) {
                        $log.debug("You're using WAMP CRA.  The authid must be set on $wamp before the connection can be opened, ie: $wamp.setAuthId('john.doe')");
                    } else {
                        connection.open();
                    }
                },
                setAuthId: function (authid, open) {
                    options.authid = authid;
                    if (open) {
                        connection.open();
                    }
                },
                close: function () {
                    connection.close();
                },
                subscribe: function (topic, handler, options, subscribedCallback) {
                    return interceptorWrapper('subscribe', arguments, function () {
                        return Subscription(topic, handler, options, subscribedCallback);
                    });
                },
                unsubscribe: function (subscription) {
                    return interceptorWrapper('unsubscribe', arguments, function () {
                        return subscription.unsubscribe();
                    });
                },
                publish: function (topic, args, kwargs, options) {
                    return interceptorWrapper('publish', arguments, function () {
                        return connection.session.publish(topic, args, kwargs, options);
                    });
                },
                register: function (procedure, endpoint, options) {
                    endpoint = digestWrapper(endpoint);

                    return interceptorWrapper('register', arguments, function () {
                        return connection.session.register(procedure, endpoint, options);
                    });
                },
                call: function (procedure, args, kwargs, options) {
                    return interceptorWrapper('call', arguments, function () {
                        return connection.session.call(procedure, args, kwargs, options);
                    });
                }
            };
        };

        return this;

    }
})();<commit_msg>fix: removes old id if present on. Resolves #33<commit_after>/* commonjs package manager support */
if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
    var autobahn = require('autobahn');
    module.exports = 'vxWamp';
}

(function () {
    'use strict';

    /**
     * @ngdoc module
     * @name vxWamp
     * @description
     *
     * # vxWamp
     *
     * The `vxWamp` module provides seamless WAMPv2 integration, by wrapping AutobahnJS, for angular apps.
     *
     * ## Example
     * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
     *
     *
     *

     */
    /* global -vxWampModule */
    var vxWampModule = angular.module('vxWamp', []).provider('$wamp', $WampProvider);

    /**
     * @ngdoc provider
     * @name $wampProvider
     *
     * @description
     *
     * Used for accessing a WAMP resource.
     *
     * ## Example
     * See {@link $wamp#example $wamp} for an example of configuring and using `vxWamp`.
     *
     * ## Dependencies
     * Requires the {@link vxWamp `vxWamp`} module to be installed.
     */
    function $WampProvider() {
        var options;

        /**
         * @ngdoc method
         * @name $wampProvider#init
         *
         * @param {Object} initOptions autobahnJS connection options
         *
         *    Object properties:
         *
         *      Required options:
         *
         *    - `url`: `{string=}` - the WebSocket URL of the WAMP router to connect to, e.g. ws://myserver.com:8080/ws
         *    - `realm`: `{string=}` - The WAMP realm to join, e.g. realm1
         *
         *    Optional options:
         *
         *      Options that control what kind of Deferreds to use:
         *
         *      - `use_es6_promises`: `{boolean=}` - use deferreds based on ES6 promises
         *      - `use_deferred`: `{callable=}` - if provided, use this deferred constructor, e.g. jQuery.Deferred or Q.defer (default: $q.defer)
         *
         *      Options that control automatic reconnection:
         *
         *      - `max_retries`: `{integer=}` - Maximum number of reconnection attempts (default: 15)
         *      - `initial_retry_delay`: `{float=}` - Initial delay for reconnection attempt in seconds (default: 1.5).
         *      - `max_retry_delay`: `{float=}` - Maximum delay for reconnection attempts in seconds (default: 300).
         *      - `retry_delay_growth`: `{float=}` - The growth factor applied to the retry delay between reconnection attempts (default: 1.5).
         *      - `retry_delay_jitter`: `{float=}` - The standard deviation of a Gaussian to jitter the delay on each retry cycle as a fraction of the mean (default: 0.1).
         *
         * @description
         * Configures the AutobhanJS Service
         */
        this.init = function (initOptions) {
            options = initOptions || {};
        };

        /**
         * @ngdoc property
         * @name $wampProvider#interceptors
         * @description
         *
         * Array containing service factories for all synchronous or asynchronous
         * postprocessing of responses.
         *
         * {@link $wamp#interceptors Interceptors detailed info}
         **/
        var interceptors = this.interceptors = [];

        this.$get = function ($rootScope, $q, $log, $injector) {

            /**
             * @ngdoc service
             * @name $wamp
             *
             * @description
             * `$wamp` give you access to the autobahnJS methods, call, register, subscribe, unsubscribe and publish.
             *
             * Requires the {@link vxWamp `vxWamp`} module to be installed.
             *
             * You can configure the WAMP connection through {@link $wampProvider}'s API.
             *
             * @example
             *
             * app.config(function ($wampProvider) {
             *      $wampProvider.init({
             *          url: 'ws://127.0.0.1:9000/',
             *          realm: 'realm1'
             *      });
             * })
             *
             *  app.controller("MyCtrl", function($scope, $wamp) {
             *
             *      // 1) subscribe to a topic
             *      function onevent(args) {
             *          $scope.hello = args[0];
             *      }
             *      $wamp.subscribe('com.myapp.hello', onevent);
             *
             *      // 2) publish an event
             *      $wamp.publish('com.myapp.hello', ['Hello, world!']);
             *
             *      // 3) register a procedure for remoting
             *      function add2(args) {
             *          return args[0] + args[1];
             *      }
             *      $wamp.register('com.myapp.add2', add2);
             *
             *      // 4) call a remote procedure
             *      $wamp.call('com.myapp.add2', [2, 3]).then(
             *          function (res) {
             *          $scope.add2 = res;
             *      });
             * });
             *
             *  Events
             *
             *  There are four events that $wamp can broadcast:
             *      1)  $wamp.open - is sent when the WAMP connection opens.
             *
             *              $scope.$on("$wamp.open", function (event, info) {
             *                  // info.session
             *                  // info.details
             *                  // Do something
             *              });
             *
             *      2)  $wamp.close - is sent when the WAMP connection closes.
             *
             *              $scope.$on("$wamp.close", function (event, info) {
             *                  // info.reason: wamp close reason
             *                  // info.details: wamp close details
             *              });
             *
             *      3)  $wamp.error - is sent when an error occurs while trying to make a call, publish or register
             *
             *              $scope.$on("$wamp.error", function (event, error) {
             *                  // error: Autobahn.Error object
             *              });
             *
             *      4)  $wamp.onchallenge
             *
             *              $scope.$on("$wamp.onchallenge", function (event, info) {
             *                  // info.promise: promise to return to wamp,
             *                  // info.session: wamp session,
             *                  // info.method: auth method,
             *                  // info.extra: extra
             *
             *                  //ie. wamp-cra
             *                  var key =  autobahn.auth_cra.derive_key("da_password", info.extra.salt);
             *                  return info.promise.resolve(autobahn.auth_cra.sign(key, info.extra.challenge));
             *              });
             *
             */

            var connection;
            var sessionDeferred = $q.defer();
            var sessionPromise = sessionDeferred.promise;

            /**
             * @param session
             * @param method
             * @param extra
             * @returns {*}
             *
             * @description
             * Gets called when a Challenge Message is sent by the router
             */
            var onchallenge = function (session, method, extra) {

                var onChallengeDeferred = $q.defer();

                $rootScope.$broadcast("$wamp.onchallenge", {
                    promise: onChallengeDeferred,
                    session: session,
                    method: method,
                    extra: extra
                });

                return onChallengeDeferred.promise;
            };

            /**
             * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
             */
            var reversedInterceptors = [];

            angular.forEach(interceptors, function (interceptor) {
                reversedInterceptors.unshift(
                    angular.isString(interceptor) ? $injector.get(interceptor) :  $injector.invoke(interceptor));
            });

            /**
             * @param func
             * @returns {Function}
             *
             * @description
             * Wraps a callback with a function that calls scope.$apply(), so that the callback is added to the digest
             */
            function digestWrapper(func) {
                return function () {
                    var cb = func.apply(this, arguments);
                    $rootScope.$apply();
                    return cb;
                };
            }

            options = angular.extend({onchallenge: digestWrapper(onchallenge), use_deferred: $q.defer}, options);

            connection = new autobahn.Connection(options);
            connection.onopen = digestWrapper(function (session, details) {
                $log.debug("Congrats!  You're connected to the WAMP server!");
                $rootScope.$broadcast("$wamp.open", {session: session, details: details});
                sessionDeferred.resolve();
            });

            connection.onclose = digestWrapper(function (reason, details) {
                $log.debug("Connection Closed: ", reason, details);
                sessionDeferred = $q.defer();
                sessionPromise = sessionDeferred.promise;
                $rootScope.$broadcast("$wamp.close", {reason: reason, details: details});
            });

            /**
             * Subscription object which self manages reconnections
             * @param topic
             * @param handler
             * @param options
             * @param subscribedCallback
             * @returns {{}}
             * @constructor
             */
            var Subscription = function (topic, handler, options, subscribedCallback) {

                var subscription = {}, unregister, onOpen, deferred = $q.defer();

                handler = digestWrapper(handler);

                onOpen = function () {
                    var p = connection.session.subscribe(topic, handler, options).then(
                        function (s) {
                            if (subscription.hasOwnProperty('id')) {
                                delete subscription.id;
                            }

                            subscription = angular.extend(s, subscription);
                            deferred.resolve(subscription);
                            return s;
                        }
                    );
                    if (subscribedCallback) {
                        subscribedCallback(p);
                    }

                };

                if (connection.isOpen) {
                    onOpen();
                }

                unregister = $rootScope.$on("$wamp.open", onOpen);

                subscription.promise = deferred.promise;
                subscription.unsubscribe = function () {
                    unregister(); //Remove the event listener, so this object can get cleaned up by gc
                    return connection.session.unsubscribe(subscription);
                };

                return subscription.promise;
            };

            /**
             * Wraps WAMP actions, so that when they're called, the defined interceptors get called before the result is returned
             *
             * @param type
             * @param args
             * @param callback
             * @returns {*}
             */
            var interceptorWrapper = function (type, args, callback) {

                /**
                 * Default result
                 *
                 * @param result
                 * @returns {{result: *, type: *, args: *}}
                 */
                var result = function (result) {
                    return {result: result, type: type, args: args};
                };

                /**
                 * Default Error
                 *
                 * @param error
                 * @returns {{error: *, type: *, args: *}}
                 */
                var error = function (error) {
                    $log.error("$wamp error", {type: type, arguments: args, error: error});
                    return $q.reject({error: error, type: type, args: args});
                };

                // Only execute the action callback once we have an established session
                var action = sessionPromise.then(function () {
                    return callback();
                });

                var chain = [result, error];

                // Apply interceptors
                angular.forEach(reversedInterceptors, function (interceptor) {
                    if (interceptor[type + 'Response'] || interceptor[type + 'ResponseError']) {
                        chain.push(interceptor[type + 'Response'], interceptor[type + 'ResponseError']);
                    }
                });

                // We only want to return the actually result or error, not the entire information object
                chain.push(
                    function (response) {
                        return response.result;
                    }, function (response) {
                        return $q.reject(response.error);
                    }
                );

                while (chain.length) {
                    var resolved = chain.shift();
                    var rejected = chain.shift();

                    action = action.then(resolved, rejected);
                }

                return action;
            };

            return {
                connection: connection,
                open: function () {
                    //If using WAMP CRA we need to get the authid before the connection can be opened.
                    if (options.authmethods && options.authmethods.indexOf('wampcra') !== -1 && !options.authid) {
                        $log.debug("You're using WAMP CRA.  The authid must be set on $wamp before the connection can be opened, ie: $wamp.setAuthId('john.doe')");
                    } else {
                        connection.open();
                    }
                },
                setAuthId: function (authid, open) {
                    options.authid = authid;
                    if (open) {
                        connection.open();
                    }
                },
                close: function () {
                    connection.close();
                },
                subscribe: function (topic, handler, options, subscribedCallback) {
                    return interceptorWrapper('subscribe', arguments, function () {
                        return Subscription(topic, handler, options, subscribedCallback);
                    });
                },
                unsubscribe: function (subscription) {
                    return interceptorWrapper('unsubscribe', arguments, function () {
                        return subscription.unsubscribe();
                    });
                },
                publish: function (topic, args, kwargs, options) {
                    return interceptorWrapper('publish', arguments, function () {
                        return connection.session.publish(topic, args, kwargs, options);
                    });
                },
                register: function (procedure, endpoint, options) {
                    endpoint = digestWrapper(endpoint);

                    return interceptorWrapper('register', arguments, function () {
                        return connection.session.register(procedure, endpoint, options);
                    });
                },
                call: function (procedure, args, kwargs, options) {
                    return interceptorWrapper('call', arguments, function () {
                        return connection.session.call(procedure, args, kwargs, options);
                    });
                }
            };
        };

        return this;

    }
})();<|endoftext|>