<commit_before>// @flow

import { CONFERENCE_LEFT, getCurrentConference } from '../base/conference';
import {
    PARTICIPANT_LEFT,
    getLocalParticipant,
    participantJoined,
    participantLeft,
    pinParticipant
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';

import { TOGGLE_SHARED_VIDEO, SET_SHARED_VIDEO_STATUS } from './actionTypes';
import { setSharedVideoStatus, showEnterVideoLinkPrompt } from './actions';

const SHARED_VIDEO = 'shared-video';

/**
 * Middleware that captures actions related to YouTube video sharing and updates
 * components not hooked into redux.
 *
 * @param {Store} store - The redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    const { dispatch, getState } = store;
    const state = getState();
    const conference = getCurrentConference(state);
    const localParticipantId = getLocalParticipant(state)?.id;
    const { videoId, status, ownerId, time } = action;

    switch (action.type) {
    case TOGGLE_SHARED_VIDEO:
        _toggleSharedVideo(store, next, action);
        break;
    case CONFERENCE_LEFT:
        dispatch(setSharedVideoStatus('', 'stop', 0, ''));
        break;
    case PARTICIPANT_LEFT:
        if (action.participant.id === action.ownerId) {
            dispatch(setSharedVideoStatus('', 'stop', 0, ''));
        }
        break;
    case SET_SHARED_VIDEO_STATUS:
        if (localParticipantId === ownerId) {
            sendShareVideoCommand(videoId, status, conference, localParticipantId, time);
        }
        break;
    }

    return next(action);
});

/**
 * Set up state change listener to perform maintenance tasks when the conference
 * is left or failed, e.g. clear messages or close the chat modal if it's left
 * open.
 */
StateListenerRegistry.register(
    state => getCurrentConference(state),
    (conference, store, previousConference) => {
        if (conference && conference !== previousConference) {
            conference.addCommandListener(SHARED_VIDEO,
                ({ value, attributes }) => {

                    const { dispatch, getState } = store;
                    const { from } = attributes;
                    const localParticipantId = getLocalParticipant(getState()).id;
                    const status = attributes.state;

                    if ([ 'playing', 'pause', 'start' ].includes(status)) {
                        handleSharingVideoStatus(store, value, attributes, conference);
                    } else if (status === 'stop') {
                        dispatch(participantLeft(value, conference));
                        if (localParticipantId !== from) {
                            dispatch(setSharedVideoStatus(value, 'stop', 0, from));
                        }
                    }
                }
            );
        }
    });

/**
 * Handles the playing, pause and start statuses for the shared video.
 * Dispatches participantJoined event and, if necessary, pins it.
 * Sets the SharedVideoStatus if the event was triggered by the local user.
 *
 * @param {Store} store - The redux store.
 * @param {string} videoId - The YoutubeId of the video to the shared.
 * @param {Object} attributes - The attributes received from the share video command.
 * @param {JitsiConference} conference - The current conference.
 * @returns {void}
 */
function handleSharingVideoStatus(store, videoId, { state, time, from }, conference) {
    const { dispatch, getState } = store;
    const localParticipantId = getLocalParticipant(getState()).id;
    const oldStatus = getState()['features/youtube-player']?.status;

    if (state === 'start' || ![ 'playing', 'pause', 'start' ].includes(oldStatus)) {
        dispatch(participantJoined({
            conference,
            id: videoId,
            isFakeParticipant: true,
            avatarURL: `https://img.youtube.com/vi/${videoId}/0.jpg`,
            name: 'YouTube'
        }));

        dispatch(pinParticipant(videoId));
    }

    if (localParticipantId !== from) {
        dispatch(setSharedVideoStatus(videoId, state, time, from));
    }
}

/**
 * Dispatches shared video status.
 *
 * @param {Store} store - The redux store.
 * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
 * specified {@code action} in the specified {@code store}.
 * @param {Action} action - The redux action which is
 * being dispatched in the specified {@code store}.
 * @returns {Function}
 */
function _toggleSharedVideo(store, next, action) {
    const { dispatch, getState } = store;
    const state = getState();
    const { videoId, ownerId, status } = state['features/youtube-player'];
    const localParticipant = getLocalParticipant(state);

    if (status === 'playing' || status === 'start' || status === 'pause') {
        if (ownerId === localParticipant.id) {
            dispatch(setSharedVideoStatus(videoId, 'stop', 0, localParticipant.id));
        }
    } else {
        dispatch(showEnterVideoLinkPrompt(id => _onVideoLinkEntered(store, id)));
    }

    return next(action);
}

/**
 * Sends SHARED_VIDEO start command.
 *
 * @param {Store} store - The redux store.
 * @param {string} id - The youtube id of the video to be shared.
 * @returns {void}
 */
function _onVideoLinkEntered(store, id) {
    const { dispatch, getState } = store;
    const conference = getCurrentConference(getState());

    if (conference) {
        const localParticipant = getLocalParticipant(getState());

        dispatch(setSharedVideoStatus(id, 'start', 0, localParticipant.id));
    }
}

/* eslint-disable max-params */

/**
 * Sends SHARED_VIDEO command.
 *
 * @param {string} id - The youtube id of the video.
 * @param {string} status - The status of the shared video.
 * @param {JitsiConference} conference - The current conference.
 * @param {string} localParticipantId - The id of the local participant.
 * @param {string} time - The seek position of the video.
 * @returns {void}
 */
function sendShareVideoCommand(id, status, conference, localParticipantId, time) {
    conference.sendCommandOnce(SHARED_VIDEO, {
        value: id,
        attributes: {
            from: localParticipantId,
            state: status,
            time
        }
    });
}
<commit_msg>rn: fixes the bug on shared video not stopping when the user leaves the conferences<commit_after>// @flow

import { CONFERENCE_LEFT, getCurrentConference } from '../base/conference';
import {
    PARTICIPANT_LEFT,
    getLocalParticipant,
    participantJoined,
    participantLeft,
    pinParticipant
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';

import { TOGGLE_SHARED_VIDEO, SET_SHARED_VIDEO_STATUS } from './actionTypes';
import { setSharedVideoStatus, showEnterVideoLinkPrompt } from './actions';

const SHARED_VIDEO = 'shared-video';

/**
 * Middleware that captures actions related to YouTube video sharing and updates
 * components not hooked into redux.
 *
 * @param {Store} store - The redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    const { dispatch, getState } = store;
    const state = getState();
    const conference = getCurrentConference(state);
    const localParticipantId = getLocalParticipant(state)?.id;
    const { videoId, status, ownerId, time } = action;
    const { ownerId: stateOwnerId, videoId: stateVideoId } = state['features/youtube-player'];

    switch (action.type) {
    case TOGGLE_SHARED_VIDEO:
        _toggleSharedVideo(store, next, action);
        break;
    case CONFERENCE_LEFT:
        dispatch(setSharedVideoStatus('', 'stop', 0, ''));
        break;
    case PARTICIPANT_LEFT:
        if (action.participant.id === stateOwnerId) {
            dispatch(setSharedVideoStatus('', 'stop', 0, ''));
            dispatch(participantLeft(stateVideoId, conference));
        }
        break;
    case SET_SHARED_VIDEO_STATUS:
        if (localParticipantId === ownerId) {
            sendShareVideoCommand(videoId, status, conference, localParticipantId, time);
        }
        break;
    }

    return next(action);
});

/**
 * Set up state change listener to perform maintenance tasks when the conference
 * is left or failed, e.g. clear messages or close the chat modal if it's left
 * open.
 */
StateListenerRegistry.register(
    state => getCurrentConference(state),
    (conference, store, previousConference) => {
        if (conference && conference !== previousConference) {
            conference.addCommandListener(SHARED_VIDEO,
                ({ value, attributes }) => {

                    const { dispatch, getState } = store;
                    const { from } = attributes;
                    const localParticipantId = getLocalParticipant(getState()).id;
                    const status = attributes.state;

                    if ([ 'playing', 'pause', 'start' ].includes(status)) {
                        handleSharingVideoStatus(store, value, attributes, conference);
                    } else if (status === 'stop') {
                        dispatch(participantLeft(value, conference));
                        if (localParticipantId !== from) {
                            dispatch(setSharedVideoStatus(value, 'stop', 0, from));
                        }
                    }
                }
            );
        }
    });

/**
 * Handles the playing, pause and start statuses for the shared video.
 * Dispatches participantJoined event and, if necessary, pins it.
 * Sets the SharedVideoStatus if the event was triggered by the local user.
 *
 * @param {Store} store - The redux store.
 * @param {string} videoId - The YoutubeId of the video to the shared.
 * @param {Object} attributes - The attributes received from the share video command.
 * @param {JitsiConference} conference - The current conference.
 * @returns {void}
 */
function handleSharingVideoStatus(store, videoId, { state, time, from }, conference) {
    const { dispatch, getState } = store;
    const localParticipantId = getLocalParticipant(getState()).id;
    const oldStatus = getState()['features/youtube-player']?.status;

    if (state === 'start' || ![ 'playing', 'pause', 'start' ].includes(oldStatus)) {
        dispatch(participantJoined({
            conference,
            id: videoId,
            isFakeParticipant: true,
            avatarURL: `https://img.youtube.com/vi/${videoId}/0.jpg`,
            name: 'YouTube'
        }));

        dispatch(pinParticipant(videoId));
    }

    if (localParticipantId !== from) {
        dispatch(setSharedVideoStatus(videoId, state, time, from));
    }
}

/**
 * Dispatches shared video status.
 *
 * @param {Store} store - The redux store.
 * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
 * specified {@code action} in the specified {@code store}.
 * @param {Action} action - The redux action which is
 * being dispatched in the specified {@code store}.
 * @returns {Function}
 */
function _toggleSharedVideo(store, next, action) {
    const { dispatch, getState } = store;
    const state = getState();
    const { videoId, ownerId, status } = state['features/youtube-player'];
    const localParticipant = getLocalParticipant(state);

    if (status === 'playing' || status === 'start' || status === 'pause') {
        if (ownerId === localParticipant.id) {
            dispatch(setSharedVideoStatus(videoId, 'stop', 0, localParticipant.id));
        }
    } else {
        dispatch(showEnterVideoLinkPrompt(id => _onVideoLinkEntered(store, id)));
    }

    return next(action);
}

/**
 * Sends SHARED_VIDEO start command.
 *
 * @param {Store} store - The redux store.
 * @param {string} id - The youtube id of the video to be shared.
 * @returns {void}
 */
function _onVideoLinkEntered(store, id) {
    const { dispatch, getState } = store;
    const conference = getCurrentConference(getState());

    if (conference) {
        const localParticipant = getLocalParticipant(getState());

        dispatch(setSharedVideoStatus(id, 'start', 0, localParticipant.id));
    }
}

/* eslint-disable max-params */

/**
 * Sends SHARED_VIDEO command.
 *
 * @param {string} id - The youtube id of the video.
 * @param {string} status - The status of the shared video.
 * @param {JitsiConference} conference - The current conference.
 * @param {string} localParticipantId - The id of the local participant.
 * @param {string} time - The seek position of the video.
 * @returns {void}
 */
function sendShareVideoCommand(id, status, conference, localParticipantId, time) {
    conference.sendCommandOnce(SHARED_VIDEO, {
        value: id,
        attributes: {
            from: localParticipantId,
            state: status,
            time
        }
    });
}
<|endoftext|>


<commit_before>var applyProperties = require("./apply-properties")

var isWidget = require("../vnode/is-widget.js")
var VPatch = require("../vnode/vpatch.js")

var render = require("./create-element")
var updateWidget = require("./update-widget")

module.exports = applyPatch

function applyPatch(vpatch, domNode, renderOptions) {
    var type = vpatch.type
    var vNode = vpatch.vNode
    var patch = vpatch.patch

    switch (type) {
        case VPatch.REMOVE:
            return removeNode(domNode, vNode)
        case VPatch.INSERT:
            return insertNode(domNode, patch, renderOptions)
        case VPatch.VTEXT:
            return stringPatch(domNode, vNode, patch, renderOptions)
        case VPatch.WIDGET:
            return widgetPatch(domNode, vNode, patch, renderOptions)
        case VPatch.VNODE:
            return vNodePatch(domNode, vNode, patch, renderOptions)
        case VPatch.ORDER:
            reorderChildren(domNode, patch)
            return domNode
        case VPatch.PROPS:
            applyProperties(domNode, patch, vNode.properties)
            return domNode
        case VPatch.THUNK:
            return replaceRoot(domNode,
                renderOptions.patch(domNode, patch, renderOptions))
        default:
            return domNode
    }
}

function removeNode(domNode, vNode) {
    var parentNode = domNode.parentNode

    if (parentNode) {
        parentNode.removeChild(domNode)
    }

    destroyWidget(domNode, vNode);

    return null
}

function insertNode(parentNode, vNode, renderOptions) {
    var newNode = render(vNode, renderOptions)

    if (parentNode) {
        parentNode.appendChild(newNode)
    }

    return parentNode
}

function stringPatch(domNode, leftVNode, vText, renderOptions) {
    var newNode

    if (domNode.nodeType === 3) {
        domNode.replaceData(0, domNode.length, vText.text)
        newNode = domNode
    } else {
        var parentNode = domNode.parentNode
        newNode = render(vText, renderOptions)

        if (parentNode) {
            parentNode.replaceChild(newNode, domNode)
        }
    }

    return newNode
}

function widgetPatch(domNode, leftVNode, widget, renderOptions) {
    var updating = updateWidget(leftVNode, widget)
    var newNode

    if (updating) {
        newNode = widget.update(leftVNode, domNode) || domNode
    } else {
        newNode = render(widget, renderOptions)
    }

    var parentNode = domNode.parentNode

    if (parentNode && newNode !== domNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    if (!updating) {
        destroyWidget(domNode, leftVNode)
    }

    return newNode
}

function vNodePatch(domNode, leftVNode, vNode, renderOptions) {
    var parentNode = domNode.parentNode
    var newNode = render(vNode, renderOptions)

    if (parentNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    return newNode
}

function destroyWidget(domNode, w) {
    if (typeof w.destroy === "function" && isWidget(w)) {
        w.destroy(domNode)
    }
}

function reorderChildren(domNode, bIndex) {
    var children = []
    var childNodes = domNode.childNodes
    var len = childNodes.length
    var i
    var reverseIndex = bIndex.reverse

    for (i = 0; i < len; i++) {
        children.push(domNode.childNodes[i])
    }

    var insertOffset = 0
    var move
    var node
    var insertNode
    var chainLength
    var insertedLength
    var nextSibling
    for (i = 0; i < len;) {
        move = bIndex[i]
        chainLength = 1
        if (move !== undefined && move !== i) {
            // try to bring forward as long of a chain as possible
            while (bIndex[i + chainLength] === move + chainLength) {
                chainLength++;
            }

            // the element currently at this index will be moved later so increase the insert offset
            if (reverseIndex[i] > i + chainLength) {
                insertOffset++
            }

            node = children[move]
            insertNode = childNodes[i + insertOffset] || null
            insertedLength = 0
            while (node !== insertNode && insertedLength++ < chainLength) {
                nextSibling = node.nextSibling;
                domNode.insertBefore(node, insertNode);
                node = nextSibling;
            }

            // the moved element came from the front of the array so reduce the insert offset
            if (move + chainLength < i) {
                insertOffset--
            }
        }

        // element at this index is scheduled to be removed so increase insert offset
        if (i in bIndex.removes) {
            insertOffset++
        }

        i += chainLength
    }
}

function replaceRoot(oldRoot, newRoot) {
    if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) {
        console.log(oldRoot)
        oldRoot.parentNode.replaceChild(newRoot, oldRoot)
    }

    return newRoot;
}
<commit_msg>avoid using nextSibling<commit_after>var applyProperties = require("./apply-properties")

var isWidget = require("../vnode/is-widget.js")
var VPatch = require("../vnode/vpatch.js")

var render = require("./create-element")
var updateWidget = require("./update-widget")

module.exports = applyPatch

function applyPatch(vpatch, domNode, renderOptions) {
    var type = vpatch.type
    var vNode = vpatch.vNode
    var patch = vpatch.patch

    switch (type) {
        case VPatch.REMOVE:
            return removeNode(domNode, vNode)
        case VPatch.INSERT:
            return insertNode(domNode, patch, renderOptions)
        case VPatch.VTEXT:
            return stringPatch(domNode, vNode, patch, renderOptions)
        case VPatch.WIDGET:
            return widgetPatch(domNode, vNode, patch, renderOptions)
        case VPatch.VNODE:
            return vNodePatch(domNode, vNode, patch, renderOptions)
        case VPatch.ORDER:
            reorderChildren(domNode, patch)
            return domNode
        case VPatch.PROPS:
            applyProperties(domNode, patch, vNode.properties)
            return domNode
        case VPatch.THUNK:
            return replaceRoot(domNode,
                renderOptions.patch(domNode, patch, renderOptions))
        default:
            return domNode
    }
}

function removeNode(domNode, vNode) {
    var parentNode = domNode.parentNode

    if (parentNode) {
        parentNode.removeChild(domNode)
    }

    destroyWidget(domNode, vNode);

    return null
}

function insertNode(parentNode, vNode, renderOptions) {
    var newNode = render(vNode, renderOptions)

    if (parentNode) {
        parentNode.appendChild(newNode)
    }

    return parentNode
}

function stringPatch(domNode, leftVNode, vText, renderOptions) {
    var newNode

    if (domNode.nodeType === 3) {
        domNode.replaceData(0, domNode.length, vText.text)
        newNode = domNode
    } else {
        var parentNode = domNode.parentNode
        newNode = render(vText, renderOptions)

        if (parentNode) {
            parentNode.replaceChild(newNode, domNode)
        }
    }

    return newNode
}

function widgetPatch(domNode, leftVNode, widget, renderOptions) {
    var updating = updateWidget(leftVNode, widget)
    var newNode

    if (updating) {
        newNode = widget.update(leftVNode, domNode) || domNode
    } else {
        newNode = render(widget, renderOptions)
    }

    var parentNode = domNode.parentNode

    if (parentNode && newNode !== domNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    if (!updating) {
        destroyWidget(domNode, leftVNode)
    }

    return newNode
}

function vNodePatch(domNode, leftVNode, vNode, renderOptions) {
    var parentNode = domNode.parentNode
    var newNode = render(vNode, renderOptions)

    if (parentNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    return newNode
}

function destroyWidget(domNode, w) {
    if (typeof w.destroy === "function" && isWidget(w)) {
        w.destroy(domNode)
    }
}

function reorderChildren(domNode, bIndex) {
    var children = []
    var childNodes = domNode.childNodes
    var len = childNodes.length
    var i
    var reverseIndex = bIndex.reverse

    for (i = 0; i < len; i++) {
        children.push(domNode.childNodes[i])
    }

    var insertOffset = 0
    var move
    var node
    var insertNode
    var chainLength
    var insertedLength
    var nextSibling
    for (i = 0; i < len;) {
        move = bIndex[i]
        chainLength = 1
        if (move !== undefined && move !== i) {
            // try to bring forward as long of a chain as possible
            while (bIndex[i + chainLength] === move + chainLength) {
                chainLength++;
            }

            // the element currently at this index will be moved later so increase the insert offset
            if (reverseIndex[i] > i + chainLength) {
                insertOffset++
            }

            node = children[move]
            insertNode = childNodes[i + insertOffset] || null
            insertedLength = 0
            while (node !== insertNode && insertedLength++ < chainLength) {
                domNode.insertBefore(node, insertNode);
                node = children[move + insertedLength];
            }

            // the moved element came from the front of the array so reduce the insert offset
            if (move + chainLength < i) {
                insertOffset--
            }
        }

        // element at this index is scheduled to be removed so increase insert offset
        if (i in bIndex.removes) {
            insertOffset++
        }

        i += chainLength
    }
}

function replaceRoot(oldRoot, newRoot) {
    if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) {
        console.log(oldRoot)
        oldRoot.parentNode.replaceChild(newRoot, oldRoot)
    }

    return newRoot;
}
<|endoftext|>


<commit_before>import React from 'react';
import { render } from 'react-dom';
import Button from '../blocks/Button';
import Link from '../blocks/Link';
import Popup from '../blocks/Popup';
import TextInput from '../blocks/TextInput';
import Radio from '../blocks/Radio';
import RadioGroup from '../blocks/RadioGroup';
import Checkbox from '../blocks/Checkbox';
import CheckboxGroup from '../blocks/CheckboxGroup';

class RadioGroupExample extends React.Component {
    constructor( props ) {
        super( props );

        this.state = {
            value: '30'
        };

        this.onChange = this.onChange.bind( this );
    }

    render() {
        return (

                <RadioGroup name="phones" value={ this.state.value } theme="islands" size="l" type="button" onChange={ this.onChange }>
                    <Radio value="10">10</Radio>
                    <Radio value="20" disabled>20</Radio>
                    <Radio value="30" onClick={ (e) => console.log('CLICK') }>30</Radio>
                    <Radio value="40">40</Radio>
                    <Radio value="50">50</Radio>
                </RadioGroup>

Вы выбрали: { this.state.value }

        );
    }

    onChange( value ) {
        this.setState( { value } );
    }

}

class CheckboxGroupExample extends React.Component {
    constructor( props ) {
        super( props );

        this.state = {
            value: [ '30', '40' ]
        };

        this.onChange = this.onChange.bind( this );
    }

    render() {
        return (

                <CheckboxGroup name="phones" value={ this.state.value } theme="islands" type="button" size="l" onChange={ this.onChange }>
                    <Checkbox value="10">10</Checkbox>
                    <Checkbox value="20" disabled>20</Checkbox>
                    <Checkbox value="30" focused>30</Checkbox>
                    <Checkbox value="40">40</Checkbox>
                    <Checkbox value="50">50</Checkbox>
                </CheckboxGroup>

Вы выбрали: { this.state.value.join( ', ' ) }

        );
    }

    onChange( value ) {
        this.setState( { value } );
    }

}

class TextInputExample extends React.Component {
    constructor(...args) {
        super(...args);

        this.state = {
            value: 'islands'
        };

        this.onInputChange = this.onInputChange.bind(this);
    }

    onInputChange(value) {
        console.log('textinput changed!');
        this.setState({ value });
    }

    render() {
        return (

                    <TextInput theme="islands" size="s" value={this.state.value} placeholder="Fill me!" hasClear onChange={this.onInputChange}/>

                    <TextInput theme="islands" size="s" value={this.state.value.toUpperCase()} hasClear/>

                    <TextInput theme="islands" size="s" value={this.state.value} disabled placeholder="Fill me!" hasClear/>

                    <TextInput theme="islands" size="s" type="password" placeholder="Fill me!"/>

        )
    }
}

class Example extends React.Component {
    constructor(...args) {
        super(...args);
        this.state = {
            popup1Visible: false,
            popup2Visible: false,
            popup3Visible: false,
        };
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        console.log('click!');
    }

    handlePopup1AnchorClick() {
        this.setState({ popup1Visible: !this.state.popup1Visible });
    }

    handlePopup2AnchorClick() {
        this.setState({ popup2Visible: !this.state.popup2Visible });
    }

    handlePopup3AnchorClick() {
        this.setState({ popup3Visible: !this.state.popup3Visible });
    }

    handlePopup3VisibilityChange(val) {
        this.setState({ popup3Visible: val });
    }

    render() {
        return (

                {this.renderButton()}
                {this.renderLink()}
                {this.renderTextInput()}
                {this.renderPopup()}
                {this.renderRadioGroup()}
                {this.renderCheckboxGroup()}

        );
    }

    renderButton() {
        return (

                <Button name="btn1" theme="islands" size="s" onClick={this.handleClick}>Click me!</Button>

                <Button name="btn2-disabled" theme="islands" size="s" disabled onClick={this.handleClick}>Try click me!</Button>

                <Button theme="islands" size="s" type="link" url="#/https://yandex.ru" onClick={this.handleClick}>Yandex</Button>

                <Button theme="islands" size="s" type="link" url="#/https://yandex.ru" disabled  onClick={this.handleClick}>Yandex</Button>

        );
    }

    renderLink() {
        return (

Link: <Link theme="islands" size="s" url="#/https://yandex.ru">Yandex</Link>

Pseudo link: <Link theme="islands" size="s" onClick={this.handleClick}>Yandex</Link>

        )
    }

    renderTextInput() {
        return (

                <TextInputExample />

        );
    }

    renderPopup() {
        return (

                    <Button theme="islands" size="s" onClick={() => this.handlePopup1AnchorClick()}>Toggle popup 1</Button>
                    <Popup theme="islands" id="p1" visible={this.state.popup1Visible}>
                        <Link theme="islands" size="s">Yandex</Link>
                    </Popup>

                    <Button theme="islands" size="s" onClick={() => this.handlePopup2AnchorClick()}>Toggle popup 2</Button>
                    <Popup theme="islands" visible={this.state.popup2Visible}>
                        <Link theme="islands" size="s" onClick={() => this.handlePopup3AnchorClick()}>Toggle popup 3</Link>
                        <Popup
                            theme="islands"
                            visible={this.state.popup3Visible}
                            onVisibilityChange={v => this.handlePopup3VisibilityChange(v)}
                        >
                            <Link size="s">Yandex</Link>
                        </Popup>
                    </Popup>

        )
    }

    renderRadioGroup() {
        return (

                    <RadioGroupExample/>

                    <Radio theme="islands" type="button" size="l" name="foo" value="70">70</Radio>

        );
    }

    renderCheckboxGroup() {
        return (

                    <CheckboxGroupExample/>

                    <Checkbox theme="islands" type="button" size="l" name="foo" value="70">70</Checkbox>

        );
    }
}

render(React.createElement(Example), document.getElementById('root'));
<commit_msg>update checkboxgroup example<commit_after>import React from 'react';
import { render } from 'react-dom';
import Button from '../blocks/Button';
import Link from '../blocks/Link';
import Popup from '../blocks/Popup';
import TextInput from '../blocks/TextInput';
import Radio from '../blocks/Radio';
import RadioGroup from '../blocks/RadioGroup';
import Checkbox from '../blocks/Checkbox';
import CheckboxGroup from '../blocks/CheckboxGroup';

class RadioGroupExample extends React.Component {
    constructor( props ) {
        super( props );

        this.state = {
            value: '30'
        };

        this.onChange = this.onChange.bind( this );
    }

    render() {
        return (

                <RadioGroup name="phones" value={ this.state.value } theme="islands" size="l" type="button" onChange={ this.onChange }>
                    <Radio value="10">10</Radio>
                    <Radio value="20" disabled>20</Radio>
                    <Radio value="30" onClick={ (e) => console.log('CLICK') }>30</Radio>
                    <Radio value="40">40</Radio>
                    <Radio value="50">50</Radio>
                </RadioGroup>

Вы выбрали: { this.state.value }

        );
    }

    onChange( value ) {
        this.setState( { value } );
    }

}

class CheckboxGroupExample extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            value: ['30', '40']
        };

        this.onChange = this.onChange.bind(this);
        this.onCheckboxCheck = this.onCheckboxCheck.bind(this);
    }

    render() {
        return (

                <CheckboxGroup name="phones" value={this.state.value} theme="islands" type="button" size="l" onChange={this.onChange}>
                    <Checkbox value="10">10</Checkbox>
                    <Checkbox value="20" disabled>20</Checkbox>
                    <Checkbox value="30" focused>30</Checkbox>
                    <Checkbox value="40">40</Checkbox>
                    <Checkbox value="50">50</Checkbox>
                </CheckboxGroup>

Вы выбрали: {this.state.value.join(', ')}

                    <Checkbox theme="islands" type="button" size="l" name="foo" value="40" onCheck={this.onCheckboxCheck}>Forty</Checkbox>

        );
    }

    onChange(value) {
        this.setState({ value });
    }

    onCheckboxCheck(checked, value) {
        console.log(`checked: ${checked}`);
        this.setState({ value: [value] });
    }
}

class TextInputExample extends React.Component {
    constructor(...args) {
        super(...args);

        this.state = {
            value: 'islands'
        };

        this.onInputChange = this.onInputChange.bind(this);
    }

    onInputChange(value) {
        console.log('textinput changed!');
        this.setState({ value });
    }

    render() {
        return (

                    <TextInput theme="islands" size="s" value={this.state.value} placeholder="Fill me!" hasClear onChange={this.onInputChange}/>

                    <TextInput theme="islands" size="s" value={this.state.value.toUpperCase()} hasClear/>

                    <TextInput theme="islands" size="s" value={this.state.value} disabled placeholder="Fill me!" hasClear/>

                    <TextInput theme="islands" size="s" type="password" placeholder="Fill me!"/>

        )
    }
}

class Example extends React.Component {
    constructor(...args) {
        super(...args);
        this.state = {
            popup1Visible: false,
            popup2Visible: false,
            popup3Visible: false,
        };
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        console.log('click!');
    }

    handlePopup1AnchorClick() {
        this.setState({ popup1Visible: !this.state.popup1Visible });
    }

    handlePopup2AnchorClick() {
        this.setState({ popup2Visible: !this.state.popup2Visible });
    }

    handlePopup3AnchorClick() {
        this.setState({ popup3Visible: !this.state.popup3Visible });
    }

    handlePopup3VisibilityChange(val) {
        this.setState({ popup3Visible: val });
    }

    render() {
        return (

                {this.renderButton()}
                {this.renderLink()}
                {this.renderTextInput()}
                {this.renderPopup()}
                {this.renderRadioGroup()}
                {this.renderCheckboxGroup()}

        );
    }

    renderButton() {
        return (

                <Button name="btn1" theme="islands" size="s" onClick={this.handleClick}>Click me!</Button>

                <Button name="btn2-disabled" theme="islands" size="s" disabled onClick={this.handleClick}>Try click me!</Button>

                <Button theme="islands" size="s" type="link" url="#/https://yandex.ru" onClick={this.handleClick}>Yandex</Button>

                <Button theme="islands" size="s" type="link" url="#/https://yandex.ru" disabled  onClick={this.handleClick}>Yandex</Button>

        );
    }

    renderLink() {
        return (

Link: <Link theme="islands" size="s" url="#/https://yandex.ru">Yandex</Link>

Pseudo link: <Link theme="islands" size="s" onClick={this.handleClick}>Yandex</Link>

        )
    }

    renderTextInput() {
        return (

                <TextInputExample />

        );
    }

    renderPopup() {
        return (

                    <Button theme="islands" size="s" onClick={() => this.handlePopup1AnchorClick()}>Toggle popup 1</Button>
                    <Popup theme="islands" id="p1" visible={this.state.popup1Visible}>
                        <Link theme="islands" size="s">Yandex</Link>
                    </Popup>

                    <Button theme="islands" size="s" onClick={() => this.handlePopup2AnchorClick()}>Toggle popup 2</Button>
                    <Popup theme="islands" visible={this.state.popup2Visible}>
                        <Link theme="islands" size="s" onClick={() => this.handlePopup3AnchorClick()}>Toggle popup 3</Link>
                        <Popup
                            theme="islands"
                            visible={this.state.popup3Visible}
                            onVisibilityChange={v => this.handlePopup3VisibilityChange(v)}
                        >
                            <Link size="s">Yandex</Link>
                        </Popup>
                    </Popup>

        )
    }

    renderRadioGroup() {
        return (

                    <RadioGroupExample/>

                    <Radio theme="islands" type="button" size="l" name="foo" value="70">70</Radio>

        );
    }

    renderCheckboxGroup() {
        return (

                <CheckboxGroupExample/>

        );
    }
}

render(React.createElement(Example), document.getElementById('root'));
<|endoftext|>


<commit_before>/** @jsx React.DOM */
'use strict';

var React = require('react/addons');
var ReactDOM = require('react-dom');

var ElevatorFloorIndicator = React.createClass({
  render : function(){
    return {this.props.localCounter}
  }
});

var Elevator = React.createClass({

  changeDirection : function() {
	  if(this.state.direction === 'UP') {
	  	this.state.direction = 'DN';
	  }
	  else if (this.state.direction === 'DN') {
	  	this.state.direction = 'UP';
	  }
	  this.forceUpdate();
	  },

  setFloorList : function() {   },

  setFloor : function(obj) {
    var toFloor = document.getElementById("flrSlct").value;
    if(toFloor === this.state.floorList[0] || toFloor > this.state.floor)
      this.state.direction = 'UP';
    else if(toFloor == this.state.floorList[this.state.floorList.length-1] || toFloor < this.state.floor)
      this.state.direction = 'DN';
	  this.state.floor = toFloor;
	  this.forceUpdate();
  },

  getInitialState : function(){
    return { floor: 0, direction: 'UP', floorList:[0,1,2,3,4,5,6,7,8,9,10] };
  },

  render : function(){
    var floorData     = this.state.floorList,
            floorOption = function(X) {
                return <option>{X}</option>;
            };
    return(

      --Elevator--

      Direction:{this.state.direction}

      <button onClick ={this.changeDirection}>Toggle Direction</button>

      Current Floor:<ElevatorFloorIndicator localCounter={this.state.floor}/>

      (3) Goto Floor <select id="flrSlct" onChange={this.setFloor}>{floorData.map(floorOption)}</select>

      (4) Set Valid Floors (Multi-Select)

    )
  }
});

ReactDOM.render(<Elevator/>, document.getElementById("root"))<commit_msg>Delete elevator.js<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Mixin.create(Ember.Evented, {
    /**
     * The api host, default is current host
     *
     * @property {Ember.String} host
     * @default  ""
     */
    host: '',

    /**
     * The api group url like /v1  /v2
     *
     * @property {Ember.String} rootURL
     * @default  ""
     */
    rootURL: '',

    /**
     * The response data business logic root key like: {'code': 0, 'resp':{'user':[]}, 'msg':''}, the user is rootKey
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    rootKey: '',

    /**
     * The api if rootURL ends with slash , the url should not starts with slash
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    url: '',

    /**
     * The model object primary key
     *
     * @property {Ember.String} primaryKey
     * @default  "_id"
     */
    primaryKey: '_id',

    /**
     * The response data root key like: {'code': 0, 'resp':{'user':[]}, 'msg':''}, the resp is dataRootKey
     *
     * @property {Ember.String} dataRootKey
     * @default  ""
     */
    dataRootKey: '',

    /**
     * @The model object primary key
     * @function api according to url return the request api
     * @property {Ember.String} primaryKey
     * @return   "api"
     */
    api: function() {
        return this.host + this.rootURL + this.url;
    }.property('url', 'host', 'rootURL'),

    /**
     * @function save save the record to backend
     * @returns  response data
     */
    save: function(model) {
        let $this = this;
        let primaryKey = this.get('primaryKey');
        let record = {};

        //filter model data
        Ember.$.each(Object.keys(model), function(index, key) {
            if ($this.model.hasOwnProperty(key)) {
                record[key] = model[key];
            }
        });

        //check if is new data
        let url = this.get('api');
        let method = 'post';
        if (model[primaryKey]) {
            record[primaryKey] = model[primaryKey];
            url = this.get('api') + '/' + model[primaryKey];
            method = 'put';
        }

        let _ajax = {
            type: method,
            url: url,
            data: record,
            dataType: 'json',
            traditional: true
        }

        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.ajax(_ajax).done(function(data) {
                resolve(data);
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },

    /**
     * @function createRecord create current model ember object
     * @returns  Object
     */
    createRecord: function() {
        return Ember.Object.create(this.model);
    },

    /**
     * @function deleteRecord delete the record from backend
     * @returns  response data
     */
    deleteRecord: function(model) {
        let _model = Ember.Object.create(model),
            self = this,
            url = this.get('api') + '/' + model[this.get('primaryKey')];

        let _ajax = {
            type: 'delete',
            url: url,
            dataType: 'json'
        }

        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.ajax(_ajax).done(function(data) {
                resolve(data);
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },
    /**
     * @function find find the records from backend according to params
     * @returns  response data
     */
    find: function(params) {
        let $this = this;
        params = $this._filterParams(params);
        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.getJSON($this.get('api'), params || {}).done(function(data) {
                let resp = null;
                try {
                    resp = $this._resp(data);
                } catch (e) {
                    reject(e);
                    return;
                }
                let dataList = resp[$this.get('rootKey')];
                if (!Ember.isArray(dataList)) {
                    reject('response data resolve error rootKey ' + $this.get('rootKey') + ' is undefined');
                }
                Ember.$.each(dataList || [], function(index, i) {
                    dataList.push(Ember.Object.create(i));
                });
                resolve(dataList);
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },

    /**
     * @function findOne find only one according to primary id
     * @returns  response data
     */
    findOne: function(id) {
        let $this = this;
        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.getJSON($this.get('api') + '/' + id).done(function(data) {
                let resp = null;
                try {
                    resp = $this._resp(data);
                } catch (e) {
                    reject(e);
                    return;
                }
                let dataObject = resp[$this.get('rootKey')];
                if(Ember.isNone(dataObject)){
                    let message = 'response data resolve error rootKey ' + $this.get('rootKey') + ' is undefined';
                    Ember.Logger.error(message);
                    reject(message);
                }
                return Ember.Object.create(dataObject || {});
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },

    /**
     * @function _filterParams filter request params
     * @returns  filtered params
     */
    _filterParams: function(params) {
        if (!params) {
            return;
        }
        for (let k in params) {
            if (params.hasOwnProperty(k) && !params[k]) {
                delete params[k];
            }
        }
        return params;
    },

    _resp: function(data) {
        let dataRootKey = this.get('dataRootKey'),
            resp = data;
        if (!Ember.isBlank(dataRootKey)) {
            if (Ember.isNone(data[dataRootKey])) {
                let message = 'response data dataRootKey "' + dataRootKey + '" is undefined';
                Ember.Logger.error(message);
                throw message;
            } else {
                return data[dataRootKey];
            }
        }
        return resp;
    }
});<commit_msg>fix #2<commit_after>import Ember from 'ember';

export default Ember.Mixin.create(Ember.Evented, {
    /**
     * The api host, default is current host
     *
     * @property {Ember.String} host
     * @default  ""
     */
    host: '',

    /**
     * The api group url like /v1  /v2
     *
     * @property {Ember.String} rootURL
     * @default  ""
     */
    rootURL: '',

    /**
     * The response data business logic root key like: {'code': 0, 'resp':{'user':[]}, 'msg':''}, the user is rootKey
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    rootKey: '',

    /**
     * The api if rootURL ends with slash , the url should not starts with slash
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    url: '',

    /**
     * The model object primary key
     *
     * @property {Ember.String} primaryKey
     * @default  "_id"
     */
    primaryKey: '_id',

    /**
     * The response data root key like: {'code': 0, 'resp':{'user':[]}, 'msg':''}, the resp is dataRootKey
     *
     * @property {Ember.String} dataRootKey
     * @default  ""
     */
    dataRootKey: '',

    /**
     * @The model object primary key
     * @function api according to url return the request api
     * @property {Ember.String} primaryKey
     * @return   "api"
     */
    api: function() {
        return this.host + this.rootURL + this.url;
    }.property('url', 'host', 'rootURL'),

    /**
     * @function save save the record to backend
     * @returns  response data
     */
    save: function(model) {
        let $this = this;
        let primaryKey = this.get('primaryKey');
        let record = {};

        //filter model data
        Ember.$.each(Object.keys(model), function(index, key) {
            if ($this.model.hasOwnProperty(key)) {
                record[key] = model[key];
            }
        });

        //check if is new data
        let url = this.get('api');
        let method = 'post';
        if (model[primaryKey]) {
            record[primaryKey] = model[primaryKey];
            url = this.get('api') + '/' + model[primaryKey];
            method = 'put';
        }

        let _ajax = {
            type: method,
            url: url,
            data: record,
            dataType: 'json',
            traditional: true
        }

        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.ajax(_ajax).done(function(data) {
                resolve(data);
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },

    /**
     * @function createRecord create current model ember object
     * @returns  Object
     */
    createRecord: function() {
        return Ember.Object.create(this.model);
    },

    /**
     * @function deleteRecord delete the record from backend
     * @returns  response data
     */
    deleteRecord: function(model) {
        let _model = Ember.Object.create(model),
            self = this,
            url = this.get('api') + '/' + model[this.get('primaryKey')];

        let _ajax = {
            type: 'delete',
            url: url,
            dataType: 'json'
        }

        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.ajax(_ajax).done(function(data) {
                resolve(data);
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },
    /**
     * @function find find the records from backend according to params
     * @returns  response data
     */
    find: function(params) {
        let $this = this;
        params = $this._filterParams(params);
        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.getJSON($this.get('api'), params || {}).done(function(data) {
                let resp = null;
                try {
                    resp = $this._resp(data);
                } catch (e) {
                    reject(e);
                    return;
                }
                let dataList = resp[$this.get('rootKey')];
                if (!Ember.isArray(dataList)) {
                    reject('response data resolve error rootKey ' + $this.get('rootKey') + ' is undefined');
                }
                Ember.$.each(dataList || [], function(index, i) {
                    dataList.push(Ember.Object.create(i));
                });
                resolve(dataList);
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },

    /**
     * @function findOne find only one according to primary id
     * @returns  response data
     */
    findOne: function(id) {
        let $this = this;
        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.getJSON($this.get('api') + '/' + id).done(function(data) {
                let resp = null;
                try {
                    resp = $this._resp(data);
                } catch (e) {
                    reject(e);
                    return;
                }
                let dataObject = resp[$this.get('rootKey')];
                if(Ember.isNone(dataObject)){
                    let message = 'response data resolve error rootKey ' + $this.get('rootKey') + ' is undefined';
                    Ember.Logger.error(message);
                    reject(message);
                }
                resolve(Ember.Object.create(dataObject || {}));
            }).fail(function(jqXHR, responseText, errorThrown) {
                reject(`${responseText} ${errorThrown}`);
            });
        });
    },

    /**
     * @function _filterParams filter request params
     * @returns  filtered params
     */
    _filterParams: function(params) {
        if (!params) {
            return;
        }
        for (let k in params) {
            if (params.hasOwnProperty(k) && !params[k]) {
                delete params[k];
            }
        }
        return params;
    },

    _resp: function(data) {
        let dataRootKey = this.get('dataRootKey'),
            resp = data;
        if (!Ember.isBlank(dataRootKey)) {
            if (Ember.isNone(data[dataRootKey])) {
                let message = 'response data dataRootKey "' + dataRootKey + '" is undefined';
                Ember.Logger.error(message);
                throw message;
            } else {
                return data[dataRootKey];
            }
        }
        return resp;
    }
});
<|endoftext|>


<commit_before>import Ember from 'ember';

/**
  The Ember Infinity Route Mixin enables an application route to load paginated
  records for the route `model` as triggered by the controller (or Infinity Loader
  component).

  @class RouteMixin
  @namespace EmberInfinity
  @module ember-infinity/mixins/route
  @extends Ember.Mixin
*/
export default Ember.Mixin.create({

  /**
    @private
    @property _perPage
    @type Integer
    @default 25
  */
  _perPage: 25,

  /**
    @private
    @property _currentPage
    @type Integer
    @default 0
  */
  _currentPage: 0,

  /**
    @private
    @property _extraParams
    @type Object
    @default {}
  */
  _extraParams: {},

  /**
    @private
    @property _loadingMore
    @type Boolean
    @default false
  */
  _loadingMore: false,

  /**
    @private
    @property _totalPages
    @type Integer
    @default 0
  */
  _totalPages: 0,

  /**
    @private
    @property _infinityModelName
    @type String
    @default null
  */
  _infinityModelName: null,

  /**
    @private
    @property _modelPath
    @type String
    @default 'controller.model'
  */
  _modelPath: 'controller.model',

  /**
   * Name of the "per page" param in the
   * resource request payload
   * @type {String}
   * @default  "per_page"
   */
  perPageParam: 'per_page',

  /**
   * Name of the "page" param in the
   * resource request payload
   * @type {String}
   * @default "page"
   */
  pageParam: 'page',

  /**
   * Path of the "total pages" param in
   * the HTTP response
   * @type {String}
   * @default  "meta.total_pages"
   */
  totalPagesParam: 'meta.total_pages',

  /**
    @private
    @property _canLoadMore
    @type Boolean
    @default false
  */
  _canLoadMore: Ember.computed('_totalPages', '_currentPage', function() {
    var totalPages  = this.get('_totalPages');
    var currentPage = this.get('_currentPage');
    return (totalPages && currentPage) ? (currentPage < totalPages) : false;
  }),

  /**
    Use the infinityModel method in the place of `this.store.find('model')` to
    initialize the Infinity Model for your route.

    @method infinityModel
    @param {String} modelName The name of the model.
    @param {Object} options Optional, the perPage and startingPage to load from.
    @return {Ember.RSVP.Promise}
  */
  infinityModel(modelName, options) {

    if (Ember.isEmpty(this.store) || Ember.isEmpty(this.store.find)){
      throw new Ember.Error("Ember Data store is not available to infinityModel");
    } else if (modelName === undefined) {
      throw new Ember.Error("You must pass a Model Name to infinityModel");
    }

    this.set('_infinityModelName', modelName);

    options = options ? Ember.merge({}, options) : {};
    var startingPage = options.startingPage || 1;
    var perPage      = options.perPage || this.get('_perPage');
    var modelPath    = options.modelPath || this.get('_modelPath');

    delete options.startingPage;
    delete options.perPage;
    delete options.modelPath;

    this.set('_perPage', perPage);
    this.set('_modelPath', modelPath);
    this.set('_extraParams', options);

    var requestPayloadBase = {};
    requestPayloadBase[this.get('perPageParam')] = perPage;
    requestPayloadBase[this.get('pageParam')] = startingPage;

    var params = Ember.merge(requestPayloadBase, options);
    var promise = this.store.find(modelName, params);

    promise.then(
      infinityModel => {
        var totalPages = infinityModel.get(this.get('totalPagesParam'));
        this.set('_currentPage', startingPage);
        this.set('_totalPages', totalPages);
        infinityModel.set('reachedInfinity', !this.get('_canLoadMore'));
        Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
          lastPageLoaded: startingPage,
          totalPages: totalPages,
          newObjects: infinityModel
        });
      },
      () => {
        throw new Ember.Error("Could not fetch Infinity Model. Please check your serverside configuration.");
      }
    );

    return promise;
  },

  /**
   Trigger a load of the next page of results.

   @method infinityLoad
   @return {Boolean}
   */
  _infinityLoad() {
    var nextPage    = this.get('_currentPage') + 1;
    var perPage     = this.get('_perPage');
    var totalPages  = this.get('_totalPages');
    var model       = this.get(this.get('_modelPath'));
    var modelName   = this.get('_infinityModelName');

    if (!this.get('_loadingMore') && this.get('_canLoadMore')) {
      this.set('_loadingMore', true);

      var params = Ember.merge({ page: nextPage, per_page: perPage }, this.get('_extraParams'));
      var promise = this.store.find(modelName, params);

      promise.then(
        infinityModel => {
          model.pushObjects(infinityModel.get('content'));
          this.set('_loadingMore', false);
          this.set('_currentPage', nextPage);
          Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
            lastPageLoaded: nextPage,
            totalPages: totalPages,
            newObjects: infinityModel
          });
          if (!this.get('_canLoadMore')) {
            this.set(this.get('_modelPath') + '.reachedInfinity', true);
            Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', {
              totalPages: totalPages
            });
          }
        },
        () => {
          this.set('_loadingMore', false);
          throw new Ember.Error("You must pass a Model Name to infinityModel");
        }
      );
    } else {
      if (!this.get('_canLoadMore')) {
        this.set(this.get('_modelPath') + '.reachedInfinity', true);
        Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', { totalPages: totalPages });
      }
    }
    return false;
  },

  actions: {
    infinityLoad() {
      this._infinityLoad();
    }
  }
});
<commit_msg>fixed wrong error message<commit_after>import Ember from 'ember';

/**
  The Ember Infinity Route Mixin enables an application route to load paginated
  records for the route `model` as triggered by the controller (or Infinity Loader
  component).

  @class RouteMixin
  @namespace EmberInfinity
  @module ember-infinity/mixins/route
  @extends Ember.Mixin
*/
export default Ember.Mixin.create({

  /**
    @private
    @property _perPage
    @type Integer
    @default 25
  */
  _perPage: 25,

  /**
    @private
    @property _currentPage
    @type Integer
    @default 0
  */
  _currentPage: 0,

  /**
    @private
    @property _extraParams
    @type Object
    @default {}
  */
  _extraParams: {},

  /**
    @private
    @property _loadingMore
    @type Boolean
    @default false
  */
  _loadingMore: false,

  /**
    @private
    @property _totalPages
    @type Integer
    @default 0
  */
  _totalPages: 0,

  /**
    @private
    @property _infinityModelName
    @type String
    @default null
  */
  _infinityModelName: null,

  /**
    @private
    @property _modelPath
    @type String
    @default 'controller.model'
  */
  _modelPath: 'controller.model',

  /**
   * Name of the "per page" param in the
   * resource request payload
   * @type {String}
   * @default  "per_page"
   */
  perPageParam: 'per_page',

  /**
   * Name of the "page" param in the
   * resource request payload
   * @type {String}
   * @default "page"
   */
  pageParam: 'page',

  /**
   * Path of the "total pages" param in
   * the HTTP response
   * @type {String}
   * @default  "meta.total_pages"
   */
  totalPagesParam: 'meta.total_pages',

  /**
    @private
    @property _canLoadMore
    @type Boolean
    @default false
  */
  _canLoadMore: Ember.computed('_totalPages', '_currentPage', function() {
    var totalPages  = this.get('_totalPages');
    var currentPage = this.get('_currentPage');
    return (totalPages && currentPage) ? (currentPage < totalPages) : false;
  }),

  /**
    Use the infinityModel method in the place of `this.store.find('model')` to
    initialize the Infinity Model for your route.

    @method infinityModel
    @param {String} modelName The name of the model.
    @param {Object} options Optional, the perPage and startingPage to load from.
    @return {Ember.RSVP.Promise}
  */
  infinityModel(modelName, options) {

    if (Ember.isEmpty(this.store) || Ember.isEmpty(this.store.find)){
      throw new Ember.Error("Ember Data store is not available to infinityModel");
    } else if (modelName === undefined) {
      throw new Ember.Error("You must pass a Model Name to infinityModel");
    }

    this.set('_infinityModelName', modelName);

    options = options ? Ember.merge({}, options) : {};
    var startingPage = options.startingPage || 1;
    var perPage      = options.perPage || this.get('_perPage');
    var modelPath    = options.modelPath || this.get('_modelPath');

    delete options.startingPage;
    delete options.perPage;
    delete options.modelPath;

    this.set('_perPage', perPage);
    this.set('_modelPath', modelPath);
    this.set('_extraParams', options);

    var requestPayloadBase = {};
    requestPayloadBase[this.get('perPageParam')] = perPage;
    requestPayloadBase[this.get('pageParam')] = startingPage;

    var params = Ember.merge(requestPayloadBase, options);
    var promise = this.store.find(modelName, params);

    promise.then(
      infinityModel => {
        var totalPages = infinityModel.get(this.get('totalPagesParam'));
        this.set('_currentPage', startingPage);
        this.set('_totalPages', totalPages);
        infinityModel.set('reachedInfinity', !this.get('_canLoadMore'));
        Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
          lastPageLoaded: startingPage,
          totalPages: totalPages,
          newObjects: infinityModel
        });
      },
      () => {
        throw new Ember.Error("Could not fetch Infinity Model. Please check your serverside configuration.");
      }
    );

    return promise;
  },

  /**
   Trigger a load of the next page of results.

   @method infinityLoad
   @return {Boolean}
   */
  _infinityLoad() {
    var nextPage    = this.get('_currentPage') + 1;
    var perPage     = this.get('_perPage');
    var totalPages  = this.get('_totalPages');
    var model       = this.get(this.get('_modelPath'));
    var modelName   = this.get('_infinityModelName');

    if (!this.get('_loadingMore') && this.get('_canLoadMore')) {
      this.set('_loadingMore', true);

      var params = Ember.merge({ page: nextPage, per_page: perPage }, this.get('_extraParams'));
      var promise = this.store.find(modelName, params);

      promise.then(
        infinityModel => {
          model.pushObjects(infinityModel.get('content'));
          this.set('_loadingMore', false);
          this.set('_currentPage', nextPage);
          Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
            lastPageLoaded: nextPage,
            totalPages: totalPages,
            newObjects: infinityModel
          });
          if (!this.get('_canLoadMore')) {
            this.set(this.get('_modelPath') + '.reachedInfinity', true);
            Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', {
              totalPages: totalPages
            });
          }
        },
        () => {
          this.set('_loadingMore', false);
          throw new Ember.Error("Could not fetch Infinity Model. Please check your serverside configuration.");
        }
      );
    } else {
      if (!this.get('_canLoadMore')) {
        this.set(this.get('_modelPath') + '.reachedInfinity', true);
        Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', { totalPages: totalPages });
      }
    }
    return false;
  },

  actions: {
    infinityLoad() {
      this._infinityLoad();
    }
  }
});
<|endoftext|>


<commit_before>Modules.VERSION = '2.0.1';
Modules.UTILS = true;

// dependsOn - object that adds a dependson attribute functionality to xul preference elements.
// Just add the attribute to the desired xul element and let the script do its thing. dependson accepts comma-separated or semicolon-separated strings in the following format:
//	[!]element[:value] where:
//		element - id of an element associated with a preference or the id of the preference element
//		(optional) ! - before element, checks for the opposite condition
//		(optional) :value - value is some specific value that element must have in order for the condition to return true
//	To condition for several dependencies: ',' is equivalent to AND and ';' to OR
//	examples:
//		element1 - checks if element1 is true
//		element2:someValue - checks if element2 has value of 'someValue'
//		element3:5 - checks if element3 has value 5
//		!element4 - checks if element4 is false
//		!element5:someOtherValue - checks if element5 has any value other than 'someOtherValue'
//		element6,element7:someValue - check if element6 is true and element7 has value of 'someValue'
//		element8:someValue;element9:someOtherValue - checks if element8 has value of 'someValue' or element9 has value of 'someOtherValue'
this.dependsOn = {
	getAll: function() {
		return $$("[dependson]");
	},

	handleEvent: function(e) {
		if(e.target.localName != 'preference' || !e.target.id) { return; }

		var fields = $$("[preference='"+e.target.id+"']");
		var elements = this.getAll();
		var alreadyChanged = new Set();

		for(let field of fields) {
			if(!field.id) { continue; }

			for(let node of elements) {
				if(alreadyChanged.has(node)) { continue; }

				if(node.getAttribute('dependson').contains(field.id)) {
					this.updateElement(node);
					alreadyChanged.add(node);
				}
			}
		}

		for(let node of elements) {
			if(alreadyChanged.has(node)) { continue; }

			if(node.getAttribute('dependson').contains(e.target.id)) {
				this.updateElement(node);
				alreadyChanged.add(node);
			}
		}
	},

	updateAll: function() {
		let elements = this.getAll();
		for(let node of elements) {
			this.updateElement(node);
		}
	},

	updateElement: function(el) {
		let attr = el.getAttribute('dependson');
		if(!attr) { return; }

		let dependencies = attr.split(',');
		for(let d of dependencies) {
			let alternates = d.split(';');
			let a = 0;
			while(a < alternates.length) {
				let inverse = false;
				let dependency = alternates[a].split(':');
				if(dependency[0].contains('!')) {
					inverse = true;
					dependency[0] = dependency[0].replace('!', '');
				}

				dependency[0] = trim(dependency[0]);
				if(dependency[0] == '') { continue; }

				if(dependency.length == 2) {
					dependency[1] = trim(dependency[1]);
				}

				let pref = $(dependency[0]);
				if(!pref) {
					Cu.reportError("Element of ID '"+dependency[0]+"' could not be found!");
					return;
				}

				if(pref.localName != 'preference') {
					pref = $(pref.getAttribute('preference'));
				}
				switch(pref.type) {
					case 'int':
						var value = (dependency.length == 2) ? parseInt(dependency[1]) : 0;
						break;
					case 'bool':
						var value = (dependency.length == 1) ? true : (dependency[1] == 'true') ? true : false;
						break;
					case 'string':
					default:
						var value = (dependency.length == 2) ? dependency[1] : '';
						break;
				}

				a++;
				if( (!inverse && pref.value !== value) || (inverse && pref.value === value) ) {
					if(a < alternates.length) { continue; }

					el.setAttribute('disabled', 'true');
					return;
				}
				else if(a < alternates.length) {
					el.removeAttribute('disabled');
					return;
				}
			}
		}
		el.removeAttribute('disabled');
	}
};

// initScales - every <scale> node should be properly initialized if it has a preference attribute; should work in most cases.
// If you want to bypass this you can set onsyncfrompreference attribute on the scale.
this.initScales = function() {
	var scales = $$('scale');
	for(let scale of scales) {
		if(!scale.getAttribute('onsyncfrompreference') && scale.getAttribute('preference')) {
			scale.value = $(scale.getAttribute('preference')).value;
		}
	}
};

// keys - should automatically take care of all the labels, entries and actions of any keysets to be registered through the Keysets object.
// It looks for and expects each keyset to be layouted (is this even a word?) in the XUL options page as such:
// 	<checkbox keysetAccel="keyName" preference="pref-to-accel"/>
//	<checkbox keysetAlt="keyName" preference="pref-to-alt"/>
//	<checkbox keysetShift="keyName" preference="pref-to-shift"/>
//	<menulist keyset="keyName" preference="pref-to-keycode"/>
this.keys = {
	all: [],

	handleEvent: function(e) {
		this.fillKeyCodes();
	},

	init: function() {
		let done = new Set();
		let all = $$('[keyset]');
		for(let node of all) {
			let id = node.getAttribute('keyset');
			if(done.has(id)) { continue; }
			done.add(id);

			if(!node.firstChild) {
				node.appendChild(document.createElement('menupopup'));
			}

			let key = {
				id: id,
				node: node,
				accelBox: $$('[keysetAccel="'+id+'"]')[0],
				shiftBox: $$('[keysetShift="'+id+'"]')[0],
				altBox: $$('[keysetAlt="'+id+'"]')[0],
				get disabled () { return trueAttribute(this.node, 'disabled'); },
				get keycode () { return this.node.value; },
				get accel () { return this.accelBox.checked; },
				get shift () { return this.shiftBox.checked; },
				get alt () { return this.altBox.checked; },
				get menu () { return this.node.firstChild; }
			};

			this.all.push(key);
			Keysets.fillKeyStrings(key);

			Listeners.add(key.node, 'command', this);
			Listeners.add(key.accelBox, 'command', this);
			Listeners.add(key.shiftBox, 'command', this);
			Listeners.add(key.altBox, 'command', this);
		}
	},

	uninit: function() {
		for(let key of this.all) {
			Listeners.remove(key.node, 'command', this);
			Listeners.remove(key.accelBox, 'command', this);
			Listeners.remove(key.shiftBox, 'command', this);
			Listeners.remove(key.altBox, 'command', this);
		}
	},

	fillKeycodes: function() {
		for(let key of this.all) {
			let available = Keysets.getAvailable(key, this.all);
			if(!available[key.keycode]) {
				key.keycode = 'none';
			}

			var item = key.menu.firstChild.nextSibling;
			while(item) {
				item.setAttribute('hidden', 'true');
				item.setAttribute('disabled', 'true');
				item = item.nextSibling;
			}
			if(key.keycode == 'none') {
				key.menu.parentNode.selectedItem = key.menu.firstChild;
				$(key.menu.parentNode.getAttribute('preference')).value = 'none';
			}

			for(let item of key.menu.childNodes) {
				let keycode = item.getAttribute('value');
				if(!available[keycode]) { continue; }

				item.removeAttribute('hidden');
				item.removeAttribute('disabled');
				if(keycode == key.keycode) {
					key.menu.parentNode.selectedItem = item;
					// It has the annoying habit of re-selecting the first (none) entry when selecting a menuitem with '*' as value
					if(keycode == '*') {
						var itemIndex = key.menu.parentNode.selectedIndex;
						aSync(function() { key.menu.parentNode.selectedIndex = itemIndex; });
					}
				}
			}
		}
	}
};

// In case I need subdialogs:
//   http://mxr.mozilla.org/mozilla-central/source/browser/components/preferences/in-content/subdialogs.js
//   http://mxr.mozilla.org/mozilla-central/source/browser/components/preferences/in-content/preferences.xul#197

// categories - aid to properly display preferences in a tab, just like the current native options tab
// adapted from http://mxr.mozilla.org/mozilla-central/source/browser/components/preferences/in-content/preferences.js
// unlike in the original, we don't use hidden, instead we use collapsed to switch between panels, so that all the bindings remain applied throughout the page.
this.categories = {
	lastHash: "",

	get categories () { return $('categories'); },
	get prefPane () { return $('mainPrefPane'); },

	handleEvent: function(e) {
		switch(e.type) {
			case 'select':
				this.gotoPref(e.target.value)
				break;

			case 'keydown':
				if(e.keyCode == e.DOM_VK_TAB) {
					setAttribute(this.categories, "keyboard-navigation", "true");
				}
				break;

			case 'mousedown':
				removeAttribute(this.categories, "keyboard-navigation");
				break;

			case 'hashchange':
				this.gotoPref();
				break;
		}
	},

	init: function() {
		document.documentElement.instantApply = true;

		// sometimes it may happen that the overlays aren't loaded in the correct order, which means the categories might not be ordered correctly,
		// it's definitely better UX if the categories are always in the same order, so we try to ensure this
		let categories = $('categories');
		let node = categories.firstChild;
		while(node) {
			let position = node.getAttribute('position');
			if(position) {
				position = parseInt(position);
				let sibling = null;
				let previous = node.previousSibling;
				while(previous) {
					let related = previous.getAttribute('position');
					if(related) {
						related = parseInt(related);
						if(position < related) {
							sibling = previous;
						} else {
							break;
						}
					}
					previous = previous.previousSibling;
				}

				if(sibling) {
					categories.insertBefore(node, sibling);
				}
			}

			node = node.nextSibling;
		}

		Listeners.add(this.categories, "select", this);
		Listeners.add(document.documentElement, "keydown", this);
		Listeners.add(this.categories, "mousedown", this);
		Listeners.add(window, "hashchange", this);

		this.gotoPref();
		this.dynamicPadding();
	},

	uninit: function() {
		Listeners.remove(this.categories, "select", this);
		Listeners.remove(document.documentElement, "keydown", this);
		Listeners.remove(this.categories, "mousedown", this);
		Listeners.remove(window, "hashchange", this);
	},

	// Make the space above the categories list shrink on low window heights
	dynamicPadding: function() {
		let catPadding = parseInt(getComputedStyle(this.categories).paddingTop);
		let fullHeight = this.categories.lastElementChild.getBoundingClientRect().bottom;
		let mediaRule = `
			@media (max-height: ${fullHeight}px) {
				#categories {
					padding-top: calc(100vh - ${fullHeight - catPadding}px);
				}
			}
		`;
		let mediaStyle = document.createElementNS('http://www.w3.org/1999/xhtml', 'html:style');
		mediaStyle.setAttribute('type', 'text/css');
		mediaStyle.appendChild(document.createCDATASection(mediaRule));
		document.documentElement.appendChild(mediaStyle);
	},

	gotoPref: function(aCategory) {
		const kDefaultCategoryInternalName = this.categories.lastElementChild.value;
		let hash = document.location.hash;
		let category = aCategory || hash.substr(1) || Prefs.lastPrefPane || kDefaultCategoryInternalName;
		category = this.friendlyPrefCategoryNameToInternalName(category);

		// Updating the hash (below) or changing the selected category will re-enter gotoPref.
		if(this.lastHash == category) { return; }

		let item = this.categories.querySelector(".category[value="+category+"]");
		if(!item) {
			category = kDefaultCategoryInternalName;
			item = this.categories.querySelector(".category[value="+category+"]");
		}

		let newHash = this.internalPrefCategoryNameToFriendlyName(category);
		if(this.lastHash || category != kDefaultCategoryInternalName) {
			document.location.hash = newHash;
		}

		// Need to set the lastHash before setting categories.selectedItem since the categories 'select' event will re-enter the gotoPref codepath.
		this.lastHash = category;
		this.categories.selectedItem = item;
		setAttribute(document.documentElement, 'currentcategory', category);
		Prefs.lastPrefPane = category; // I can't save the last category in a persisted attribute because it persists to the hashed url

		window.history.replaceState(category, document.title);
		this.search(category, "data-category");
		document.querySelector(".main-content").scrollTop = 0;
	},

	search: function(aQuery, aAttribute) {
		let elements = this.prefPane.children;
		for(let element of elements) {
			if(element.nodeName == 'preferences') { continue; }

			let attributeValue = element.getAttribute(aAttribute);
			element.collapsed = (attributeValue != aQuery);
		}
	},

	friendlyPrefCategoryNameToInternalName: function(aName) {
		if(aName.startsWith("pane")) {
			return aName;
		}

		return "pane" + aName.substring(0,1).toUpperCase() + aName.substr(1);
	},

	// This function is duplicated inside of utilityOverlay.js's openPreferences.
	internalPrefCategoryNameToFriendlyName: function(aName) {
		return (aName || "").replace(/^pane./, function(toReplace) { return toReplace[4].toLowerCase(); });
	}
};

Modules.LOADMODULE = function() {
	callOnLoad(window, function() {
		dependsOn.updateAll();
		Listeners.add(window, "change", dependsOn);

		initScales();
		keys.init();
		categories.init();
	});
};

Modules.UNLOADMODULE = function() {
	Listeners.remove(window, "change", dependsOn);
	keys.uninit();
	categories.uninit();

	if(UNLOADED) {
		window.close();
	}
};
<commit_msg>don't remember paneAbout, default to first pane<commit_after>Modules.VERSION = '2.0.2';
Modules.UTILS = true;

// dependsOn - object that adds a dependson attribute functionality to xul preference elements.
// Just add the attribute to the desired xul element and let the script do its thing. dependson accepts comma-separated or semicolon-separated strings in the following format:
//	[!]element[:value] where:
//		element - id of an element associated with a preference or the id of the preference element
//		(optional) ! - before element, checks for the opposite condition
//		(optional) :value - value is some specific value that element must have in order for the condition to return true
//	To condition for several dependencies: ',' is equivalent to AND and ';' to OR
//	examples:
//		element1 - checks if element1 is true
//		element2:someValue - checks if element2 has value of 'someValue'
//		element3:5 - checks if element3 has value 5
//		!element4 - checks if element4 is false
//		!element5:someOtherValue - checks if element5 has any value other than 'someOtherValue'
//		element6,element7:someValue - check if element6 is true and element7 has value of 'someValue'
//		element8:someValue;element9:someOtherValue - checks if element8 has value of 'someValue' or element9 has value of 'someOtherValue'
this.dependsOn = {
	getAll: function() {
		return $$("[dependson]");
	},

	handleEvent: function(e) {
		if(e.target.localName != 'preference' || !e.target.id) { return; }

		var fields = $$("[preference='"+e.target.id+"']");
		var elements = this.getAll();
		var alreadyChanged = new Set();

		for(let field of fields) {
			if(!field.id) { continue; }

			for(let node of elements) {
				if(alreadyChanged.has(node)) { continue; }

				if(node.getAttribute('dependson').contains(field.id)) {
					this.updateElement(node);
					alreadyChanged.add(node);
				}
			}
		}

		for(let node of elements) {
			if(alreadyChanged.has(node)) { continue; }

			if(node.getAttribute('dependson').contains(e.target.id)) {
				this.updateElement(node);
				alreadyChanged.add(node);
			}
		}
	},

	updateAll: function() {
		let elements = this.getAll();
		for(let node of elements) {
			this.updateElement(node);
		}
	},

	updateElement: function(el) {
		let attr = el.getAttribute('dependson');
		if(!attr) { return; }

		let dependencies = attr.split(',');
		for(let d of dependencies) {
			let alternates = d.split(';');
			let a = 0;
			while(a < alternates.length) {
				let inverse = false;
				let dependency = alternates[a].split(':');
				if(dependency[0].contains('!')) {
					inverse = true;
					dependency[0] = dependency[0].replace('!', '');
				}

				dependency[0] = trim(dependency[0]);
				if(dependency[0] == '') { continue; }

				if(dependency.length == 2) {
					dependency[1] = trim(dependency[1]);
				}

				let pref = $(dependency[0]);
				if(!pref) {
					Cu.reportError("Element of ID '"+dependency[0]+"' could not be found!");
					return;
				}

				if(pref.localName != 'preference') {
					pref = $(pref.getAttribute('preference'));
				}
				switch(pref.type) {
					case 'int':
						var value = (dependency.length == 2) ? parseInt(dependency[1]) : 0;
						break;
					case 'bool':
						var value = (dependency.length == 1) ? true : (dependency[1] == 'true') ? true : false;
						break;
					case 'string':
					default:
						var value = (dependency.length == 2) ? dependency[1] : '';
						break;
				}

				a++;
				if( (!inverse && pref.value !== value) || (inverse && pref.value === value) ) {
					if(a < alternates.length) { continue; }

					el.setAttribute('disabled', 'true');
					return;
				}
				else if(a < alternates.length) {
					el.removeAttribute('disabled');
					return;
				}
			}
		}
		el.removeAttribute('disabled');
	}
};

// initScales - every <scale> node should be properly initialized if it has a preference attribute; should work in most cases.
// If you want to bypass this you can set onsyncfrompreference attribute on the scale.
this.initScales = function() {
	var scales = $$('scale');
	for(let scale of scales) {
		if(!scale.getAttribute('onsyncfrompreference') && scale.getAttribute('preference')) {
			scale.value = $(scale.getAttribute('preference')).value;
		}
	}
};

// keys - should automatically take care of all the labels, entries and actions of any keysets to be registered through the Keysets object.
// It looks for and expects each keyset to be layouted (is this even a word?) in the XUL options page as such:
// 	<checkbox keysetAccel="keyName" preference="pref-to-accel"/>
//	<checkbox keysetAlt="keyName" preference="pref-to-alt"/>
//	<checkbox keysetShift="keyName" preference="pref-to-shift"/>
//	<menulist keyset="keyName" preference="pref-to-keycode"/>
this.keys = {
	all: [],

	handleEvent: function(e) {
		this.fillKeyCodes();
	},

	init: function() {
		let done = new Set();
		let all = $$('[keyset]');
		for(let node of all) {
			let id = node.getAttribute('keyset');
			if(done.has(id)) { continue; }
			done.add(id);

			if(!node.firstChild) {
				node.appendChild(document.createElement('menupopup'));
			}

			let key = {
				id: id,
				node: node,
				accelBox: $$('[keysetAccel="'+id+'"]')[0],
				shiftBox: $$('[keysetShift="'+id+'"]')[0],
				altBox: $$('[keysetAlt="'+id+'"]')[0],
				get disabled () { return trueAttribute(this.node, 'disabled'); },
				get keycode () { return this.node.value; },
				get accel () { return this.accelBox.checked; },
				get shift () { return this.shiftBox.checked; },
				get alt () { return this.altBox.checked; },
				get menu () { return this.node.firstChild; }
			};

			this.all.push(key);
			Keysets.fillKeyStrings(key);

			Listeners.add(key.node, 'command', this);
			Listeners.add(key.accelBox, 'command', this);
			Listeners.add(key.shiftBox, 'command', this);
			Listeners.add(key.altBox, 'command', this);
		}
	},

	uninit: function() {
		for(let key of this.all) {
			Listeners.remove(key.node, 'command', this);
			Listeners.remove(key.accelBox, 'command', this);
			Listeners.remove(key.shiftBox, 'command', this);
			Listeners.remove(key.altBox, 'command', this);
		}
	},

	fillKeycodes: function() {
		for(let key of this.all) {
			let available = Keysets.getAvailable(key, this.all);
			if(!available[key.keycode]) {
				key.keycode = 'none';
			}

			var item = key.menu.firstChild.nextSibling;
			while(item) {
				item.setAttribute('hidden', 'true');
				item.setAttribute('disabled', 'true');
				item = item.nextSibling;
			}
			if(key.keycode == 'none') {
				key.menu.parentNode.selectedItem = key.menu.firstChild;
				$(key.menu.parentNode.getAttribute('preference')).value = 'none';
			}

			for(let item of key.menu.childNodes) {
				let keycode = item.getAttribute('value');
				if(!available[keycode]) { continue; }

				item.removeAttribute('hidden');
				item.removeAttribute('disabled');
				if(keycode == key.keycode) {
					key.menu.parentNode.selectedItem = item;
					// It has the annoying habit of re-selecting the first (none) entry when selecting a menuitem with '*' as value
					if(keycode == '*') {
						var itemIndex = key.menu.parentNode.selectedIndex;
						aSync(function() { key.menu.parentNode.selectedIndex = itemIndex; });
					}
				}
			}
		}
	}
};

// In case I need subdialogs:
//   http://mxr.mozilla.org/mozilla-central/source/browser/components/preferences/in-content/subdialogs.js
//   http://mxr.mozilla.org/mozilla-central/source/browser/components/preferences/in-content/preferences.xul#197

// categories - aid to properly display preferences in a tab, just like the current native options tab
// adapted from http://mxr.mozilla.org/mozilla-central/source/browser/components/preferences/in-content/preferences.js
// unlike in the original, we don't use hidden, instead we use collapsed to switch between panels, so that all the bindings remain applied throughout the page.
this.categories = {
	lastHash: "",

	get categories () { return $('categories'); },
	get prefPane () { return $('mainPrefPane'); },

	handleEvent: function(e) {
		switch(e.type) {
			case 'select':
				this.gotoPref(e.target.value)
				break;

			case 'keydown':
				if(e.keyCode == e.DOM_VK_TAB) {
					setAttribute(this.categories, "keyboard-navigation", "true");
				}
				break;

			case 'mousedown':
				removeAttribute(this.categories, "keyboard-navigation");
				break;

			case 'hashchange':
				this.gotoPref();
				break;
		}
	},

	init: function() {
		document.documentElement.instantApply = true;

		// sometimes it may happen that the overlays aren't loaded in the correct order, which means the categories might not be ordered correctly,
		// it's definitely better UX if the categories are always in the same order, so we try to ensure this
		let categories = $('categories');
		let node = categories.firstChild;
		while(node) {
			let position = node.getAttribute('position');
			if(position) {
				position = parseInt(position);
				let sibling = null;
				let previous = node.previousSibling;
				while(previous) {
					let related = previous.getAttribute('position');
					if(related) {
						related = parseInt(related);
						if(position < related) {
							sibling = previous;
						} else {
							break;
						}
					}
					previous = previous.previousSibling;
				}

				if(sibling) {
					categories.insertBefore(node, sibling);
				}
			}

			node = node.nextSibling;
		}

		Listeners.add(this.categories, "select", this);
		Listeners.add(document.documentElement, "keydown", this);
		Listeners.add(this.categories, "mousedown", this);
		Listeners.add(window, "hashchange", this);

		this.gotoPref();
		this.dynamicPadding();
	},

	uninit: function() {
		Listeners.remove(this.categories, "select", this);
		Listeners.remove(document.documentElement, "keydown", this);
		Listeners.remove(this.categories, "mousedown", this);
		Listeners.remove(window, "hashchange", this);
	},

	// Make the space above the categories list shrink on low window heights
	dynamicPadding: function() {
		let catPadding = parseInt(getComputedStyle(this.categories).paddingTop);
		let fullHeight = this.categories.lastElementChild.getBoundingClientRect().bottom;
		let mediaRule = `
			@media (max-height: ${fullHeight}px) {
				#categories {
					padding-top: calc(100vh - ${fullHeight - catPadding}px);
				}
			}
		`;
		let mediaStyle = document.createElementNS('http://www.w3.org/1999/xhtml', 'html:style');
		mediaStyle.setAttribute('type', 'text/css');
		mediaStyle.appendChild(document.createCDATASection(mediaRule));
		document.documentElement.appendChild(mediaStyle);
	},

	gotoPref: function(aCategory) {
		const kDefaultCategoryInternalName = this.categories.firstElementChild.value;
		let hash = document.location.hash;
		let category = aCategory || hash.substr(1) || Prefs.lastPrefPane || kDefaultCategoryInternalName;
		category = this.friendlyPrefCategoryNameToInternalName(category);

		// Updating the hash (below) or changing the selected category will re-enter gotoPref.
		if(this.lastHash == category) { return; }

		let item = this.categories.querySelector(".category[value="+category+"]");
		if(!item) {
			category = kDefaultCategoryInternalName;
			item = this.categories.querySelector(".category[value="+category+"]");
		}

		let newHash = this.internalPrefCategoryNameToFriendlyName(category);
		if(this.lastHash || category != kDefaultCategoryInternalName) {
			document.location.hash = newHash;
		}

		// Need to set the lastHash before setting categories.selectedItem since the categories 'select' event will re-enter the gotoPref codepath.
		this.lastHash = category;
		this.categories.selectedItem = item;
		setAttribute(document.documentElement, 'currentcategory', category);

		// I can't save the last category in a persisted attribute because it persists to the hashed url;
		// don't remember the About pane, there's no need to and it would hardly be useful
		if(category != 'paneAbout') {
			Prefs.lastPrefPane = category;
		}

		window.history.replaceState(category, document.title);
		this.search(category, "data-category");
		document.querySelector(".main-content").scrollTop = 0;
	},

	search: function(aQuery, aAttribute) {
		let elements = this.prefPane.children;
		for(let element of elements) {
			if(element.nodeName == 'preferences') { continue; }

			let attributeValue = element.getAttribute(aAttribute);
			element.collapsed = (attributeValue != aQuery);
		}
	},

	friendlyPrefCategoryNameToInternalName: function(aName) {
		if(aName.startsWith("pane")) {
			return aName;
		}

		return "pane" + aName.substring(0,1).toUpperCase() + aName.substr(1);
	},

	// This function is duplicated inside of utilityOverlay.js's openPreferences.
	internalPrefCategoryNameToFriendlyName: function(aName) {
		return (aName || "").replace(/^pane./, function(toReplace) { return toReplace[4].toLowerCase(); });
	}
};

Modules.LOADMODULE = function() {
	callOnLoad(window, function() {
		dependsOn.updateAll();
		Listeners.add(window, "change", dependsOn);

		initScales();
		keys.init();
		categories.init();
	});
};

Modules.UNLOADMODULE = function() {
	Listeners.remove(window, "change", dependsOn);
	keys.uninit();
	categories.uninit();

	if(UNLOADED) {
		window.close();
	}
};
<|endoftext|>


<commit_before>require('./dist/styles');
<commit_msg>chore: remove deprecated styles in addons/jest<commit_after><|endoftext|>


<commit_before>const userLanguage = document.documentElement.lang;

module.exports = {

    props: {
        'action': {
            type: String,
            required: true
        },
        'data': {
            type: Object,
            default: function() {
                return {};
            }
        },
    },

    created: function() {
        this.initForm(this.data);
    },

    data: function() {
        return {
            datePickerConfig: {
                format: 'YYYY-MM-DD',
                altInput: true,
                altFormat: 'd.m.Y',
                locale: userLanguage === 'en' ? null : require("flatpickr/dist/l10n/"+userLanguage+".js")[userLanguage]
            },
            timePickerConfig: {
                enableTime: true,
                noCalendar: true,
                time_24hr: true,
                enableSeconds: true,
                format: 'kk:mm:ss',
                altInput: true,
                altFormat: 'H:i:S',
                locale: userLanguage === 'en' ? null : require("flatpickr/dist/l10n/"+userLanguage+".js")[userLanguage]
            },
            datetimePickerConfig: {
                enableTime: true,
                time_24hr: true,
                enableSeconds: true,
                format: 'YYYY-MM-DD kk:mm:ss',
                altInput: true,
                altFormat: 'd.m.Y H:i:S',
                locale: userLanguage === 'en' ? null : require("flatpickr/dist/l10n/"+userLanguage+".js")[userLanguage]
            }
        }
    },

    methods: {
        onSubmit() {
            return this.$validator.validateAll()
                .then(result => {
                    if (!result) {
                        return false;
                    }
                    axios.post(this.action, this.form)
                        .then(response => this.onSuccess(response.data))
                        .catch(errors => this.onFail(errors.response.data))
                });
        },
        onSuccess(data) {
            window.location.replace(data.redirect)
        },
        onFail(errors) {
            Object.keys(errors).map(key => this.$validator.errorBag.add(key, errors[key][0]));
        },
        initForm: function(data) {
            if(typeof this.form == typeof undefined) {
                console.error('You do not specified form');
            }
            this.form = data;
            // this.form.activated = data.activated;
            // this.form.first_name = data.first_name;
            // ...
        }
    }
};
<commit_msg>changed datepicker format, because of carbons time<commit_after>const userLanguage = document.documentElement.lang;

module.exports = {

    props: {
        'action': {
            type: String,
            required: true
        },
        'data': {
            type: Object,
            default: function() {
                return {};
            }
        },
    },

    created: function() {
        this.initForm(this.data);
    },

    data: function() {
        return {
            datePickerConfig: {
                dateFormat: 'Y-m-d H:i:S',
                altInput: true,
                altFormat: 'd.m.Y',
                locale: userLanguage === 'en' ? null : require("flatpickr/dist/l10n/"+userLanguage+".js")[userLanguage]
            },
            timePickerConfig: {
                enableTime: true,
                noCalendar: true,
                time_24hr: true,
                enableSeconds: true,
                dateFormat: 'H:i:S',
                altInput: true,
                altFormat: 'H:i:S',
                locale: userLanguage === 'en' ? null : require("flatpickr/dist/l10n/"+userLanguage+".js")[userLanguage]
            },
            datetimePickerConfig: {
                enableTime: true,
                time_24hr: true,
                enableSeconds: true,
                dateFormat: 'Y-m-d H:i:S',
                altInput: true,
                altFormat: 'd.m.Y H:i:S',
                locale: userLanguage === 'en' ? null : require("flatpickr/dist/l10n/"+userLanguage+".js")[userLanguage]
            }
        }
    },

    methods: {
        onSubmit() {
            return this.$validator.validateAll()
                .then(result => {
                    if (!result) {
                        return false;
                    }
                    axios.post(this.action, this.form)
                        .then(response => this.onSuccess(response.data))
                        .catch(errors => this.onFail(errors.response.data))
                });
        },
        onSuccess(data) {
            window.location.replace(data.redirect)
        },
        onFail(errors) {
            Object.keys(errors).map(key => this.$validator.errorBag.add(key, errors[key][0]));
        },
        initForm: function(data) {
            if(typeof this.form == typeof undefined) {
                console.error('You do not specified form');
            }
            this.form = data;
            // this.form.activated = data.activated;
            // this.form.first_name = data.first_name;
            // ...
        }
    }
};
<|endoftext|>


<commit_before>const MongoClient = require('mongodb').MongoClient;
const roomMap = {
  'C0D2Z96AG': 'hubot-testing',
  'C03AFTJHG': 'general',
  'C4YTEUTJ6': 'generaltsos',
  'CCE6AE5GE': 'investing',
  'CEFH3HW1M': 'whatsplaying'
};

module.exports = function (robot) {

  MongoClient.connect(process.env.MONGODB_URL, {
      useNewUrlParser: true
    },
    function (err, client) {
      if (err) {
        robot.logger.error('Auditor was unable to connect to slack_messages connection!');
        robot.logger.error(err);
      }

      robot.logger.debug('Successfully authenticated with mongo');

      let db = client.db(process.env.MONGODB_DBNAME);
      let collection = db.collection('slack_messages');

      robot.hear(/^.*$/, function (msg) {
        let room = msg.message.room;
        if (room.startsWith('G') || room.startsWith('D')) {
          robot.logger.info('Skipping messages from ignored room.');
          return;
        }
        let savedMessage = {
          'room': roomMap[room],
          'room_id': room,
          'user': msg.message.user.name,
          'user_id': msg.message.user.id,
          'message_id': msg.message.id,
          'text': msg.message.text,
          'timestamp': Date.now()
        };
        collection.insertOne(savedMessage, function (err, res) {
          if (err) {
            robot.logger.error('Auditor was unable to save message!');
            robot.logger.error(err);
          }
        });
      });
    });
};
<commit_msg>Adding room mapping.<commit_after>const MongoClient = require('mongodb').MongoClient;
const roomMap = {
  'C0D2Z96AG': 'hubot-testing',
  'C03AFTJHG': 'general',
  'C4YTEUTJ6': 'generaltsos',
  'CCE6AE5GE': 'investing',
  'CBZU99HCL': 'prayers',
  'CEFH3HW1M': 'whatsplaying'
};

module.exports = function (robot) {

  MongoClient.connect(process.env.MONGODB_URL, {
      useNewUrlParser: true
    },
    function (err, client) {
      if (err) {
        robot.logger.error('Auditor was unable to connect to slack_messages connection!');
        robot.logger.error(err);
      }

      robot.logger.debug('Successfully authenticated with mongo');

      let db = client.db(process.env.MONGODB_DBNAME);
      let collection = db.collection('slack_messages');

      robot.hear(/^.*$/, function (msg) {
        let room = msg.message.room;
        if (room.startsWith('G') || room.startsWith('D')) {
          robot.logger.info('Skipping messages from ignored room.');
          return;
        }
        let savedMessage = {
          'room': roomMap[room],
          'room_id': room,
          'user': msg.message.user.name,
          'user_id': msg.message.user.id,
          'message_id': msg.message.id,
          'text': msg.message.text,
          'timestamp': Date.now()
        };
        collection.insertOne(savedMessage, function (err, res) {
          if (err) {
            robot.logger.error('Auditor was unable to save message!');
            robot.logger.error(err);
          }
        });
      });
    });
};
<|endoftext|>