<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var React = require("react");
var classNames = require('classnames');
var sdk = require("../../../index");
var Invite = require("../../../Invite");
var createRoom = require("../../../createRoom");
var MatrixClientPeg = require("../../../MatrixClientPeg");
var DMRoomMap = require('../../../utils/DMRoomMap');
var rate_limited_func = require("../../../ratelimitedfunc");
var dis = require("../../../dispatcher");
var Modal = require('../../../Modal');

const TRUNCATE_QUERY_LIST = 40;

module.exports = React.createClass({
    displayName: "ChatInviteDialog",
    propTypes: {
        title: React.PropTypes.string,
        description: React.PropTypes.oneOfType([
            React.PropTypes.element,
            React.PropTypes.string,
        ]),
        value: React.PropTypes.string,
        placeholder: React.PropTypes.string,
        roomId: React.PropTypes.string,
        button: React.PropTypes.string,
        focus: React.PropTypes.bool,
        onFinished: React.PropTypes.func.isRequired
    },

    getDefaultProps: function() {
        return {
            title: "Start a chat",
            description: "Who would you like to communicate with?",
            value: "",
            placeholder: "Email, name or matrix ID",
            button: "Start Chat",
            focus: true
        };
    },

    getInitialState: function() {
        return {
            error: false,
            inviteList: [],
            queryList: [],
        };
    },

    componentDidMount: function() {
        if (this.props.focus) {
            // Set the cursor at the end of the text input
            this.refs.textinput.value = this.props.value;
        }
        this._updateUserList();
    },

    onButtonClick: function() {
        var inviteList = this.state.inviteList.slice();
        // Check the text input field to see if user has an unconverted address
        // If there is and it's valid add it to the local inviteList
        const addrType = Invite.getAddressType(this.refs.textinput.value);
        if (addrType !== null) {
            inviteList.push(this.refs.textinput.value);
        } else if (this.refs.textinput.value.length > 0) {
            this.setState({ error: true });
            return;
        }

        if (inviteList.length > 0) {
            if (this._isDmChat(inviteList)) {
                // Direct Message chat
                var room = this._getDirectMessageRoom(inviteList[0]);
                if (room) {
                    // A Direct Message room already exists for this user and you
                    // so go straight to that room
                    dis.dispatch({
                        action: 'view_room',
                        room_id: room.roomId,
                    });
                    this.props.onFinished(true, inviteList[0]);
                } else {
                    this._startChat(inviteList);
                }
            } else {
                // Multi invite chat
                this._startChat(inviteList);
            }
        } else {
            // No addresses supplied
            this.setState({ error: true });
        }
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    onKeyDown: function(e) {
        if (e.keyCode === 27) { // escape
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(false);
        } else if (e.keyCode === 38) { // up arrow
            e.stopPropagation();
            e.preventDefault();
            this.addressSelector.moveSelectionUp();
        } else if (e.keyCode === 40) { // down arrow
            e.stopPropagation();
            e.preventDefault();
            this.addressSelector.moveSelectionDown();
        } else if (this.state.queryList.length > 0 && (e.keyCode === 188, e.keyCode === 13 || e.keyCode === 9)) { // comma or enter or tab
            e.stopPropagation();
            e.preventDefault();
            this.addressSelector.chooseSelection();
        } else if (this.refs.textinput.value.length === 0 && this.state.inviteList.length && e.keyCode === 8) { // backspace
            e.stopPropagation();
            e.preventDefault();
            this.onDismissed(this.state.inviteList.length - 1)();
        } else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            if (this.state.queryList.length > 0) {
                this.addressSelector.chooseSelection();
            } else {
                const addrType = Invite.getAddressType(this.refs.textinput.value);
                if (addrType !== null) {
                    const inviteList = this.state.inviteList.slice();
                    inviteList.push(this.refs.textinput.value.trim());
                    this.setState({
                        inviteList: inviteList,
                        queryList: [],
                    });
                } else {
                    this.setState({ error: true });
                }
            }
        } else if (e.keyCode === 188 || e.keyCode === 9) { // comma or tab
            e.stopPropagation();
            e.preventDefault();
            const addrType = Invite.getAddressType(this.refs.textinput.value);
            if (addrType !== null) {
                var inviteList = this.state.inviteList.slice();
                inviteList.push(this.refs.textinput.value.trim());
                this.setState({
                    inviteList: inviteList,
                    queryList: [],
                });
            } else {
                this.setState({ error: true });
            }
        }
    },

    onQueryChanged: function(ev) {
        var query = ev.target.value;
        var queryList = [];

        // Only do search if there is something to search
        if (query.length > 0) {
            queryList = this._userList.filter((user) => {
                return this._matches(query, user);
            });
        }

        this.setState({
            queryList: queryList,
            error: false,
        });
    },

    onDismissed: function(index) {
        var self = this;
        return function() {
            var inviteList = self.state.inviteList.slice();
            inviteList.splice(index, 1);
            self.setState({
                inviteList: inviteList,
                queryList: [],
            });
        }
    },

    onClick: function(index) {
        var self = this;
        return function() {
            self.onSelected(index);
        };
    },

    onSelected: function(index) {
        var inviteList = this.state.inviteList.slice();
        inviteList.push(this.state.queryList[index].userId);
        this.setState({
            inviteList: inviteList,
            queryList: [],
        });
    },

    _getDirectMessageRoom: function(addr) {
        const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
        var dmRooms = dmRoomMap.getDMRoomsForUserId(addr);
        if (dmRooms.length > 0) {
            // Cycle through all the DM rooms and find the first non forgotten or parted room
            for (let i = 0; i < dmRooms.length; i++) {
                let room = MatrixClientPeg.get().getRoom(dmRooms[i]);
                if (room) {
                    const me = room.getMember(MatrixClientPeg.get().credentials.userId);
                    if (me.membership == 'join') {
                        return room;
                    }
                }
            }
        }
        return null;
    },

    _startChat: function(addrs) {
        if (MatrixClientPeg.get().isGuest()) {
            var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
            Modal.createDialog(NeedToRegisterDialog, {
                title: "Please Register",
                description: "Guest users can't invite users. Please register."
            });
            return;
        }

        if (this.props.roomId) {
            // Invite new user to a room
            var self = this;
            Invite.inviteMultipleToRoom(this.props.roomId, addrs)
            .then(function(addrs) {
                var room = MatrixClientPeg.get().getRoom(self.props.roomId);
                return self._showAnyInviteErrors(addrs, room);
            })
            .catch(function(err) {
                console.error(err.stack);
                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createDialog(ErrorDialog, {
                    title: "Failure to invite",
                    description: err.toString()
                });
                return null;
            })
            .done();
        } else if (this._isDmChat(addrs)) {
            // Start the DM chat
            createRoom({dmUserId: addrs[0]})
            .catch(function(err) {
                console.error(err.stack);
                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createDialog(ErrorDialog, {
                    title: "Failure to invite user",
                    description: err.toString()
                });
                return null;
            })
            .done();
        } else {
            // Start multi user chat
            var self = this;
            var room;
            createRoom().then(function(roomId) {
                room = MatrixClientPeg.get().getRoom(roomId);
                return Invite.inviteMultipleToRoom(roomId, addrs);
            })
            .then(function(addrs) {
                return self._showAnyInviteErrors(addrs, room);
            })
            .catch(function(err) {
                console.error(err.stack);
                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createDialog(ErrorDialog, {
                    title: "Failure to invite",
                    description: err.toString()
                });
                return null;
            })
            .done();
        }

        // Close - this will happen before the above, as that is async
        this.props.onFinished(true, addrs);
    },

    _updateUserList: new rate_limited_func(function() {
        // Get all the users
        this._userList = MatrixClientPeg.get().getUsers();
    }, 500),

    // This is the search algorithm for matching users
    _matches: function(query, user) {
        var name = user.displayName.toLowerCase();
        var uid = user.userId.toLowerCase();
        query = query.toLowerCase();

        // don't match any that are already on the invite list
        if (this._isOnInviteList(uid)) {
            return false;
        }

        // ignore current user
        if (uid === MatrixClientPeg.get().credentials.userId) {
            return false;
        }

        // direct prefix matches
        if (name.indexOf(query) === 0 || uid.indexOf(query) === 0) {
            return true;
        }

        // strip @ on uid and try matching again
        if (uid.length > 1 && uid[0] === "@" && uid.substring(1).indexOf(query) === 0) {
            return true;
        }

        // split spaces in name and try matching constituent parts
        var parts = name.split(" ");
        for (var i = 0; i < parts.length; i++) {
            if (parts[i].indexOf(query) === 0) {
                return true;
            }
        }
        return false;
    },

    _isOnInviteList: function(uid) {
        for (let i = 0; i < this.state.inviteList.length; i++) {
            if (this.state.inviteList[i].toLowerCase() === uid) {
                return true;
            }
        }
        return false;
    },

    _isDmChat: function(addrs) {
        if (addrs.length === 1 && Invite.getAddressType(addrs[0]) === "mx" && !this.props.roomId) {
            return true;
        } else {
            return false;
        }
    },

    _showAnyInviteErrors: function(addrs, room) {
        // Show user any errors
        var errorList = [];
        for (var addr in addrs) {
            if (addrs.hasOwnProperty(addr) && addrs[addr] === "error") {
                errorList.push(addr);
            }
        }

        if (errorList.length > 0) {
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Failed to invite the following users to the " + room.name + " room:",
                description: errorList.join(", "),
            });
        }
        return addrs;
    },

    render: function() {
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        var AddressSelector = sdk.getComponent("elements.AddressSelector");
        this.scrollElement = null;

        var query = [];
        // create the invite list
        if (this.state.inviteList.length > 0) {
            var AddressTile = sdk.getComponent("elements.AddressTile");
            for (let i = 0; i < this.state.inviteList.length; i++) {
                query.push(
                    <AddressTile key={i} address={this.state.inviteList[i]} canDismiss={true} onDismissed={ this.onDismissed(i) } />
                );
            }
        }

        // Add the query at the end
        query.push(
            <textarea key={this.state.inviteList.length}
                rows="1"
                id="textinput"
                ref="textinput"
                className="mx_ChatInviteDialog_input"
                onChange={this.onQueryChanged}
                placeholder={this.props.placeholder}
                defaultValue={this.props.value}
                autoFocus={this.props.focus}>
            </textarea>
        );

        var error;
        var addressSelector;
        if (this.state.error) {
            error =
You have entered an invalid contact. Try using their Matrix ID or email address.

        } else {
            addressSelector = (
                <AddressSelector ref={(ref) => {this.addressSelector = ref}}
                    addressList={ this.state.queryList }
                    onSelected={ this.onSelected }
                    truncateAt={ TRUNCATE_QUERY_LIST } />
            );
        }

        return (

                    {this.props.title}

                    <TintableSvg src="img/icons-close-button.svg" width="35" height="35" />

                    { this.props.description }

{ query }

                    { error }
                    { addressSelector }

                    <button className="mx_Dialog_primary" onClick={this.onButtonClick}>
                        {this.props.button}
                    </button>

        );
    }
});
<commit_msg>Keep old behaviour of submitting on enter<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var React = require("react");
var classNames = require('classnames');
var sdk = require("../../../index");
var Invite = require("../../../Invite");
var createRoom = require("../../../createRoom");
var MatrixClientPeg = require("../../../MatrixClientPeg");
var DMRoomMap = require('../../../utils/DMRoomMap');
var rate_limited_func = require("../../../ratelimitedfunc");
var dis = require("../../../dispatcher");
var Modal = require('../../../Modal');

const TRUNCATE_QUERY_LIST = 40;

module.exports = React.createClass({
    displayName: "ChatInviteDialog",
    propTypes: {
        title: React.PropTypes.string,
        description: React.PropTypes.oneOfType([
            React.PropTypes.element,
            React.PropTypes.string,
        ]),
        value: React.PropTypes.string,
        placeholder: React.PropTypes.string,
        roomId: React.PropTypes.string,
        button: React.PropTypes.string,
        focus: React.PropTypes.bool,
        onFinished: React.PropTypes.func.isRequired
    },

    getDefaultProps: function() {
        return {
            title: "Start a chat",
            description: "Who would you like to communicate with?",
            value: "",
            placeholder: "Email, name or matrix ID",
            button: "Start Chat",
            focus: true
        };
    },

    getInitialState: function() {
        return {
            error: false,
            inviteList: [],
            queryList: [],
        };
    },

    componentDidMount: function() {
        if (this.props.focus) {
            // Set the cursor at the end of the text input
            this.refs.textinput.value = this.props.value;
        }
        this._updateUserList();
    },

    onButtonClick: function() {
        var inviteList = this.state.inviteList.slice();
        // Check the text input field to see if user has an unconverted address
        // If there is and it's valid add it to the local inviteList
        const addrType = Invite.getAddressType(this.refs.textinput.value);
        if (addrType !== null) {
            inviteList.push(this.refs.textinput.value);
        } else if (this.refs.textinput.value.length > 0) {
            this.setState({ error: true });
            return;
        }

        if (inviteList.length > 0) {
            if (this._isDmChat(inviteList)) {
                // Direct Message chat
                var room = this._getDirectMessageRoom(inviteList[0]);
                if (room) {
                    // A Direct Message room already exists for this user and you
                    // so go straight to that room
                    dis.dispatch({
                        action: 'view_room',
                        room_id: room.roomId,
                    });
                    this.props.onFinished(true, inviteList[0]);
                } else {
                    this._startChat(inviteList);
                }
            } else {
                // Multi invite chat
                this._startChat(inviteList);
            }
        } else {
            // No addresses supplied
            this.setState({ error: true });
        }
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    onKeyDown: function(e) {
        if (e.keyCode === 27) { // escape
            e.stopPropagation();
            e.preventDefault();
            this.props.onFinished(false);
        } else if (e.keyCode === 38) { // up arrow
            e.stopPropagation();
            e.preventDefault();
            this.addressSelector.moveSelectionUp();
        } else if (e.keyCode === 40) { // down arrow
            e.stopPropagation();
            e.preventDefault();
            this.addressSelector.moveSelectionDown();
        } else if (this.state.queryList.length > 0 && (e.keyCode === 188, e.keyCode === 13 || e.keyCode === 9)) { // comma or enter or tab
            e.stopPropagation();
            e.preventDefault();
            this.addressSelector.chooseSelection();
        } else if (this.refs.textinput.value.length === 0 && this.state.inviteList.length && e.keyCode === 8) { // backspace
            e.stopPropagation();
            e.preventDefault();
            this.onDismissed(this.state.inviteList.length - 1)();
        } else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            if (this.refs.textinput.value == '') {
                // if there's nothing in the input box, submit the form
                this.onButtonClick();
            } else if (this.state.queryList.length > 0) {
                this.addressSelector.chooseSelection();
            } else {
                const addrType = Invite.getAddressType(this.refs.textinput.value);
                if (addrType !== null) {
                    const inviteList = this.state.inviteList.slice();
                    inviteList.push(this.refs.textinput.value.trim());
                    this.setState({
                        inviteList: inviteList,
                        queryList: [],
                    });
                } else {
                    this.setState({ error: true });
                }
            }
        } else if (e.keyCode === 188 || e.keyCode === 9) { // comma or tab
            e.stopPropagation();
            e.preventDefault();
            const addrType = Invite.getAddressType(this.refs.textinput.value);
            if (addrType !== null) {
                var inviteList = this.state.inviteList.slice();
                inviteList.push(this.refs.textinput.value.trim());
                this.setState({
                    inviteList: inviteList,
                    queryList: [],
                });
            } else {
                this.setState({ error: true });
            }
        }
    },

    onQueryChanged: function(ev) {
        var query = ev.target.value;
        var queryList = [];

        // Only do search if there is something to search
        if (query.length > 0) {
            queryList = this._userList.filter((user) => {
                return this._matches(query, user);
            });
        }

        this.setState({
            queryList: queryList,
            error: false,
        });
    },

    onDismissed: function(index) {
        var self = this;
        return function() {
            var inviteList = self.state.inviteList.slice();
            inviteList.splice(index, 1);
            self.setState({
                inviteList: inviteList,
                queryList: [],
            });
        }
    },

    onClick: function(index) {
        var self = this;
        return function() {
            self.onSelected(index);
        };
    },

    onSelected: function(index) {
        var inviteList = this.state.inviteList.slice();
        inviteList.push(this.state.queryList[index].userId);
        this.setState({
            inviteList: inviteList,
            queryList: [],
        });
    },

    _getDirectMessageRoom: function(addr) {
        const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
        var dmRooms = dmRoomMap.getDMRoomsForUserId(addr);
        if (dmRooms.length > 0) {
            // Cycle through all the DM rooms and find the first non forgotten or parted room
            for (let i = 0; i < dmRooms.length; i++) {
                let room = MatrixClientPeg.get().getRoom(dmRooms[i]);
                if (room) {
                    const me = room.getMember(MatrixClientPeg.get().credentials.userId);
                    if (me.membership == 'join') {
                        return room;
                    }
                }
            }
        }
        return null;
    },

    _startChat: function(addrs) {
        if (MatrixClientPeg.get().isGuest()) {
            var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
            Modal.createDialog(NeedToRegisterDialog, {
                title: "Please Register",
                description: "Guest users can't invite users. Please register."
            });
            return;
        }

        if (this.props.roomId) {
            // Invite new user to a room
            var self = this;
            Invite.inviteMultipleToRoom(this.props.roomId, addrs)
            .then(function(addrs) {
                var room = MatrixClientPeg.get().getRoom(self.props.roomId);
                return self._showAnyInviteErrors(addrs, room);
            })
            .catch(function(err) {
                console.error(err.stack);
                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createDialog(ErrorDialog, {
                    title: "Failure to invite",
                    description: err.toString()
                });
                return null;
            })
            .done();
        } else if (this._isDmChat(addrs)) {
            // Start the DM chat
            createRoom({dmUserId: addrs[0]})
            .catch(function(err) {
                console.error(err.stack);
                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createDialog(ErrorDialog, {
                    title: "Failure to invite user",
                    description: err.toString()
                });
                return null;
            })
            .done();
        } else {
            // Start multi user chat
            var self = this;
            var room;
            createRoom().then(function(roomId) {
                room = MatrixClientPeg.get().getRoom(roomId);
                return Invite.inviteMultipleToRoom(roomId, addrs);
            })
            .then(function(addrs) {
                return self._showAnyInviteErrors(addrs, room);
            })
            .catch(function(err) {
                console.error(err.stack);
                var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createDialog(ErrorDialog, {
                    title: "Failure to invite",
                    description: err.toString()
                });
                return null;
            })
            .done();
        }

        // Close - this will happen before the above, as that is async
        this.props.onFinished(true, addrs);
    },

    _updateUserList: new rate_limited_func(function() {
        // Get all the users
        this._userList = MatrixClientPeg.get().getUsers();
    }, 500),

    // This is the search algorithm for matching users
    _matches: function(query, user) {
        var name = user.displayName.toLowerCase();
        var uid = user.userId.toLowerCase();
        query = query.toLowerCase();

        // don't match any that are already on the invite list
        if (this._isOnInviteList(uid)) {
            return false;
        }

        // ignore current user
        if (uid === MatrixClientPeg.get().credentials.userId) {
            return false;
        }

        // direct prefix matches
        if (name.indexOf(query) === 0 || uid.indexOf(query) === 0) {
            return true;
        }

        // strip @ on uid and try matching again
        if (uid.length > 1 && uid[0] === "@" && uid.substring(1).indexOf(query) === 0) {
            return true;
        }

        // split spaces in name and try matching constituent parts
        var parts = name.split(" ");
        for (var i = 0; i < parts.length; i++) {
            if (parts[i].indexOf(query) === 0) {
                return true;
            }
        }
        return false;
    },

    _isOnInviteList: function(uid) {
        for (let i = 0; i < this.state.inviteList.length; i++) {
            if (this.state.inviteList[i].toLowerCase() === uid) {
                return true;
            }
        }
        return false;
    },

    _isDmChat: function(addrs) {
        if (addrs.length === 1 && Invite.getAddressType(addrs[0]) === "mx" && !this.props.roomId) {
            return true;
        } else {
            return false;
        }
    },

    _showAnyInviteErrors: function(addrs, room) {
        // Show user any errors
        var errorList = [];
        for (var addr in addrs) {
            if (addrs.hasOwnProperty(addr) && addrs[addr] === "error") {
                errorList.push(addr);
            }
        }

        if (errorList.length > 0) {
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Failed to invite the following users to the " + room.name + " room:",
                description: errorList.join(", "),
            });
        }
        return addrs;
    },

    render: function() {
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        var AddressSelector = sdk.getComponent("elements.AddressSelector");
        this.scrollElement = null;

        var query = [];
        // create the invite list
        if (this.state.inviteList.length > 0) {
            var AddressTile = sdk.getComponent("elements.AddressTile");
            for (let i = 0; i < this.state.inviteList.length; i++) {
                query.push(
                    <AddressTile key={i} address={this.state.inviteList[i]} canDismiss={true} onDismissed={ this.onDismissed(i) } />
                );
            }
        }

        // Add the query at the end
        query.push(
            <textarea key={this.state.inviteList.length}
                rows="1"
                id="textinput"
                ref="textinput"
                className="mx_ChatInviteDialog_input"
                onChange={this.onQueryChanged}
                placeholder={this.props.placeholder}
                defaultValue={this.props.value}
                autoFocus={this.props.focus}>
            </textarea>
        );

        var error;
        var addressSelector;
        if (this.state.error) {
            error =
You have entered an invalid contact. Try using their Matrix ID or email address.

        } else {
            addressSelector = (
                <AddressSelector ref={(ref) => {this.addressSelector = ref}}
                    addressList={ this.state.queryList }
                    onSelected={ this.onSelected }
                    truncateAt={ TRUNCATE_QUERY_LIST } />
            );
        }

        return (

                    {this.props.title}

                    <TintableSvg src="img/icons-close-button.svg" width="35" height="35" />

                    { this.props.description }

{ query }

                    { error }
                    { addressSelector }

                    <button className="mx_Dialog_primary" onClick={this.onButtonClick}>
                        {this.props.button}
                    </button>

        );
    }
});
<|endoftext|>


<commit_before>/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import sdk from '../../../index';
import SdkConfig from '../../../SdkConfig';
import { _t } from '../../../languageHandler';

export default React.createClass({
    displayName: 'CreateRoomDialog',
    propTypes: {
        onFinished: React.PropTypes.func.isRequired,
    },

    componentDidMount: function() {
        const config = SdkConfig.get();
        // Dialog shows inverse of m.federate (noFederate) strict false check to skip undefined check (default = true)
        this.defaultNoFederate = config.default_federate === false;
    },

    onOk: function() {
        this.props.onFinished(true, this.refs.textinput.value, this.refs.checkbox.checked);
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    render: function() {
        const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
        return (
            <BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
                onEnterPressed={this.onOk}
                title={_t('Create Room')}
            >

                         { _t('Room name (optional)') }

                        <input id="textinput" ref="textinput" className="mx_CreateRoomDialog_input" autoFocus={true} size="64" onKeyDown={this.onKeyDown} />

                        { _t('Advanced options') }

                            <input type="checkbox" id="checkbox" ref="checkbox" defaultChecked={this.defaultNoFederate} />

                                { _t('Block users on other matrix homeservers from joining this room') }

                                ({ _t('This setting cannot be changed later!') })

                    <button onClick={this.onCancel}>
                        { _t('Cancel') }
                    </button>
                    <button className="mx_Dialog_primary" onClick={this.onOk}>
                        { _t('Create Room') }
                    </button>

            </BaseDialog>
        );
    },
});
<commit_msg>remove redundant&stale onKeyDown<commit_after>/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import sdk from '../../../index';
import SdkConfig from '../../../SdkConfig';
import { _t } from '../../../languageHandler';

export default React.createClass({
    displayName: 'CreateRoomDialog',
    propTypes: {
        onFinished: React.PropTypes.func.isRequired,
    },

    componentDidMount: function() {
        const config = SdkConfig.get();
        // Dialog shows inverse of m.federate (noFederate) strict false check to skip undefined check (default = true)
        this.defaultNoFederate = config.default_federate === false;
    },

    onOk: function() {
        this.props.onFinished(true, this.refs.textinput.value, this.refs.checkbox.checked);
    },

    onCancel: function() {
        this.props.onFinished(false);
    },

    render: function() {
        const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
        return (
            <BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished}
                onEnterPressed={this.onOk}
                title={_t('Create Room')}
            >

                         { _t('Room name (optional)') }

                        <input id="textinput" ref="textinput" className="mx_CreateRoomDialog_input" autoFocus={true} size="64" />

                        { _t('Advanced options') }

                            <input type="checkbox" id="checkbox" ref="checkbox" defaultChecked={this.defaultNoFederate} />

                                { _t('Block users on other matrix homeservers from joining this room') }

                                ({ _t('This setting cannot be changed later!') })

                    <button onClick={this.onCancel}>
                        { _t('Cancel') }
                    </button>
                    <button className="mx_Dialog_primary" onClick={this.onOk}>
                        { _t('Create Room') }
                    </button>

            </BaseDialog>
        );
    },
});
<|endoftext|>


<commit_before>/**
 * Idea Hub notice component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { useFeature } from '../../hooks/useFeature';
import { CORE_MODULES } from '../../googlesitekit/modules/datastore/constants';
import { CORE_SITE } from '../../googlesitekit/datastore/site/constants';
import { MODULES_IDEA_HUB } from '../../modules/idea-hub/datastore/constants';
import GoogleLogoIcon from '../../../svg/logo-g.svg';
import Link from '../Link';

const { useSelect } = Data;

function WPDashboardIdeaHub() {
	const isIdeaHubEnabled = useFeature( 'ideaHubModule' );

	const {
		hasSavedIdeas,
		isModuleActive,
		dashboardURL,
	} = useSelect( ( select ) => {
		if ( ! isIdeaHubEnabled ) {
			return {};
		}
		const savedIdeas = select( MODULES_IDEA_HUB ).getSavedIdeas();
		const adminURL = select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard' );
		return {
			isModuleActive: select( CORE_MODULES ).isModuleActive( 'idea-hub' ),
			hasSavedIdeas: savedIdeas && savedIdeas.length,
			dashboardURL: `${ adminURL }#saved-ideas`,
		};
	} );

	if ( ! isModuleActive && ! hasSavedIdeas ) {
		return null;
	}

	return (

				<GoogleLogoIcon width="16" height="16" />

					{ __( 'Site Kit', 'google-site-kit' ) }

				<Link href={ dashboardURL }>
					{ __( 'Need some inspiration? Revisit your saved ideas in Site Kit', 'google-site-kit' ) }
				</Link>

				<Link href={ dashboardURL }>
					{ __( 'View', 'google-site-kit' ) }
				</Link>

	);
}

export default WPDashboardIdeaHub;
<commit_msg>Remove translation for plugin name.<commit_after>/**
 * Idea Hub notice component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { useFeature } from '../../hooks/useFeature';
import { CORE_MODULES } from '../../googlesitekit/modules/datastore/constants';
import { CORE_SITE } from '../../googlesitekit/datastore/site/constants';
import { MODULES_IDEA_HUB } from '../../modules/idea-hub/datastore/constants';
import GoogleLogoIcon from '../../../svg/logo-g.svg';
import Link from '../Link';

const { useSelect } = Data;

function WPDashboardIdeaHub() {
	const isIdeaHubEnabled = useFeature( 'ideaHubModule' );

	const {
		hasSavedIdeas,
		isModuleActive,
		dashboardURL,
	} = useSelect( ( select ) => {
		if ( ! isIdeaHubEnabled ) {
			return {};
		}
		const savedIdeas = select( MODULES_IDEA_HUB ).getSavedIdeas();
		const adminURL = select( CORE_SITE ).getAdminURL( 'googlesitekit-dashboard' );
		return {
			isModuleActive: select( CORE_MODULES ).isModuleActive( 'idea-hub' ),
			hasSavedIdeas: savedIdeas && savedIdeas.length,
			dashboardURL: `${ adminURL }#saved-ideas`,
		};
	} );

	if ( ! isModuleActive && ! hasSavedIdeas ) {
		return null;
	}

	return (

				<GoogleLogoIcon width="16" height="16" />

					  Site Kit

				<Link href={ dashboardURL }>
					{ __( 'Need some inspiration? Revisit your saved ideas in Site Kit', 'google-site-kit' ) }
				</Link>

				<Link href={ dashboardURL }>
					{ __( 'View', 'google-site-kit' ) }
				</Link>

	);
}

export default WPDashboardIdeaHub;
<|endoftext|>


<commit_before>var get = Ember.get;

export default Ember.Component.extend({
  classNameBindings: [':tag-drop', 'tag::no-category', 'tags:has-drop','categoryStyle','tagClass'],
  categoryStyle: Discourse.computed.setting('category_style'), // match the category-drop style
  currentCategory: Em.computed.or('secondCategory', 'firstCategory'),
  showFilterByTag: Discourse.computed.setting('show_filter_by_tag'),
  showTagDropdown: Em.computed.and('showFilterByTag', 'tags'),
  tagId: null,

  tagName: 'li',

  tags: function() {
    if (this.siteSettings.tags_sort_alphabetically) {
      return Discourse.Site.currentProp('top_tags').sort();
    } else {
      return Discourse.Site.currentProp('top_tags');
    }
  }.property('site.top_tags'),

  iconClass: function() {
    if (this.get('expanded')) { return "fa fa-caret-down"; }
    return "fa fa-caret-right";
  }.property('expanded'),

  tagClass: function() {
    if (this.get('tagId')) {
      return "tag-" + this.get('tagId');
    } else {
      return "tag_all";
    }
  }.property('tagId'),

  allTagsUrl: function() {
    if (this.get('currentCategory')) {
      return this.get('currentCategory.url');
    } else {
      return "/";
    }
  }.property('firstCategory', 'secondCategory'),

  allTagsLabel: function() {
    return I18n.t("tagging.selector_all_tags");
  }.property('tag'),

  dropdownButtonClass: function() {
    var result = 'badge-category category-dropdown-button';
    if (Em.isNone(this.get('tag'))) {
      result += ' home';
    }
    return result;
  }.property('tag'),

  clickEventName: function() {
    return "click.tag-drop-" + (this.get('tag') || "all");
  }.property('tag'),

  actions: {
    expand: function() {
      var self = this;

      if(!this.get('renderTags')){
        this.set('renderTags',true);
        Em.run.next(function(){
          self.send('expand');
        });
        return;
      }

      if (this.get('expanded')) {
        this.close();
        return;
      }

      if (this.get('tags')) {
        this.set('expanded', true);
      }
      var $dropdown = this.$()[0];

      this.$('a[data-drop-close]').on('click.tag-drop', function() {
        self.close();
      });

      Em.run.next(function(){
        self.$('.cat a').add('html').on(self.get('clickEventName'), function(e) {
          var $target = $(e.target),
              closest = $target.closest($dropdown);

          if ($(e.currentTarget).hasClass('badge-wrapper')){
            self.close();
          }

          return ($(e.currentTarget).hasClass('badge-category') || (closest.length && closest[0] === $dropdown)) ? true : self.close();
        });
      });
    }
  },

  removeEvents: function(){
    $('html').off(this.get('clickEventName'));
    this.$('a[data-drop-close]').off('click.tag-drop');
  },

  close: function() {
    this.removeEvents();
    this.set('expanded', false);
  },

  willDestroyElement: function() {
    this.removeEvents();
  }

});
<commit_msg>FIX: enabling sort tags alphabetically can break everything<commit_after>var get = Ember.get;

export default Ember.Component.extend({
  classNameBindings: [':tag-drop', 'tag::no-category', 'tags:has-drop','categoryStyle','tagClass'],
  categoryStyle: Discourse.computed.setting('category_style'), // match the category-drop style
  currentCategory: Em.computed.or('secondCategory', 'firstCategory'),
  showFilterByTag: Discourse.computed.setting('show_filter_by_tag'),
  showTagDropdown: Em.computed.and('showFilterByTag', 'tags'),
  tagId: null,

  tagName: 'li',

  tags: function() {
    if (this.siteSettings.tags_sort_alphabetically && Discourse.Site.currentProp('top_tags')) {
      return Discourse.Site.currentProp('top_tags').sort();
    } else {
      return Discourse.Site.currentProp('top_tags');
    }
  }.property('site.top_tags'),

  iconClass: function() {
    if (this.get('expanded')) { return "fa fa-caret-down"; }
    return "fa fa-caret-right";
  }.property('expanded'),

  tagClass: function() {
    if (this.get('tagId')) {
      return "tag-" + this.get('tagId');
    } else {
      return "tag_all";
    }
  }.property('tagId'),

  allTagsUrl: function() {
    if (this.get('currentCategory')) {
      return this.get('currentCategory.url');
    } else {
      return "/";
    }
  }.property('firstCategory', 'secondCategory'),

  allTagsLabel: function() {
    return I18n.t("tagging.selector_all_tags");
  }.property('tag'),

  dropdownButtonClass: function() {
    var result = 'badge-category category-dropdown-button';
    if (Em.isNone(this.get('tag'))) {
      result += ' home';
    }
    return result;
  }.property('tag'),

  clickEventName: function() {
    return "click.tag-drop-" + (this.get('tag') || "all");
  }.property('tag'),

  actions: {
    expand: function() {
      var self = this;

      if(!this.get('renderTags')){
        this.set('renderTags',true);
        Em.run.next(function(){
          self.send('expand');
        });
        return;
      }

      if (this.get('expanded')) {
        this.close();
        return;
      }

      if (this.get('tags')) {
        this.set('expanded', true);
      }
      var $dropdown = this.$()[0];

      this.$('a[data-drop-close]').on('click.tag-drop', function() {
        self.close();
      });

      Em.run.next(function(){
        self.$('.cat a').add('html').on(self.get('clickEventName'), function(e) {
          var $target = $(e.target),
              closest = $target.closest($dropdown);

          if ($(e.currentTarget).hasClass('badge-wrapper')){
            self.close();
          }

          return ($(e.currentTarget).hasClass('badge-category') || (closest.length && closest[0] === $dropdown)) ? true : self.close();
        });
      });
    }
  },

  removeEvents: function(){
    $('html').off(this.get('clickEventName'));
    this.$('a[data-drop-close]').off('click.tag-drop');
  },

  close: function() {
    this.removeEvents();
    this.set('expanded', false);
  },

  willDestroyElement: function() {
    this.removeEvents();
  }

});
<|endoftext|>


<commit_before>import Delta from 'quill-delta';
import Parchment from 'parchment';
import Block from '../blots/block';
import Inline from '../blots/inline';
import TextBlot from '../blots/text';

class Code extends Inline {
  insertBefore(blot, ref) {
    if (blot instanceof TextBlot) {
      super.insertBefore(blot, ref);
    } else if (blot instanceof Inline) {
      blot.moveChildren(this, ref);
    } // ignore embeds
  }
}
Code.blotName = 'code';
Code.tagName = 'CODE';

class CodeBlock extends Block {
  static create(value) {
    let domNode = super.create(value);
    domNode.setAttribute('spellcheck', false);
    return domNode;
  }

  static formats() {
    return true;
  }

  delta() {
    let text = this.domNode.textContent;
    if (text.endsWith('\n')) {      // Should always be true
      text = text.slice(0, -1);
    }
    return text.split('\n').reduce((delta, frag) => {
      return delta.insert(frag).insert('\n', this.formats());
    }, new Delta());
  }

  format(name, value) {
    if (name === this.statics.blotName && value) return;
    let [text, ] = this.descendant(TextBlot, this.length() - 1);
    if (text != null) {
      text.deleteAt(text.length() - 1, 1);
    }
    super.format(name, value);
  }

  formatAt(index, length, name, value) {
    if (length === 0) return;
    if (Parchment.query(name, Parchment.Scope.BLOCK) == null ||
        (name === this.statics.blotName && value === this.statics.formats(this.domNode))) {
      return;
    }
    let nextNewline = this.newlineIndex(index);
    if (nextNewline < 0 || nextNewline >= index + length) return;
    let prevNewline = this.newlineIndex(index, true) + 1;
    let isolateLength = nextNewline - prevNewline + 1;
    let blot = this.isolate(prevNewline, isolateLength);
    let next = blot.next;
    blot.format(name, value);
    if (next instanceof CodeBlock) {
      next.formatAt(0, index - prevNewline + length - isolateLength, name, value);
    }
  }

  insertAt(index, value, def) {
    if (def != null) return;
    let [text, offset] = this.descendant(TextBlot, index);
    text.insertAt(offset, value);
  }

  length() {
    let length = this.domNode.textContent.length;
    if (!this.domNode.textContent.endsWith('\n')) {
      return length + 1;
    }
    return length;
  }

  newlineIndex(searchIndex, reverse = false) {
    if (!reverse) {
      let offset = this.domNode.textContent.slice(searchIndex).indexOf('\n');
      return offset > -1 ? searchIndex + offset : -1;
    } else {
      return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n');
    }
  }

  optimize(context) {
    if (!this.domNode.textContent.endsWith('\n')) {
      this.appendChild(Parchment.create('text', '\n'));
    }
    super.optimize(context);
    let next = this.next;
    if (next != null && next.prev === this &&
        next.statics.blotName === this.statics.blotName &&
        this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) {
      next.optimize(context);
      next.moveChildren(this);
      next.remove();
    }
  }

  replace(target) {
    super.replace(target);
    [].slice.call(this.domNode.querySelectorAll('*')).forEach(function(node) {
      let blot = Parchment.find(node);
      if (blot == null) {
        node.parentNode.removeChild(node);
      } else if (blot instanceof Parchment.Embed) {
        blot.remove();
      } else {
        blot.unwrap();
      }
    });
  }
}
CodeBlock.blotName = 'code-block';
CodeBlock.tagName = 'PRE';
CodeBlock.TAB = '  ';

export { Code, CodeBlock as default };
<commit_msg>fix removing inline<commit_after>import Delta from 'quill-delta';
import Parchment from 'parchment';
import Block from '../blots/block';
import Inline from '../blots/inline';
import TextBlot from '../blots/text';

class Code extends Inline {
  insertBefore(blot, ref) {
    if (blot instanceof TextBlot || blot.statics.blotName === Inline.blotName) {
      super.insertBefore(blot, ref);
    } else if (blot instanceof Inline) {
      blot.moveChildren(this, ref);
    } // ignore embeds
  }
}
Code.blotName = 'code';
Code.tagName = 'CODE';

class CodeBlock extends Block {
  static create(value) {
    let domNode = super.create(value);
    domNode.setAttribute('spellcheck', false);
    return domNode;
  }

  static formats() {
    return true;
  }

  delta() {
    let text = this.domNode.textContent;
    if (text.endsWith('\n')) {      // Should always be true
      text = text.slice(0, -1);
    }
    return text.split('\n').reduce((delta, frag) => {
      return delta.insert(frag).insert('\n', this.formats());
    }, new Delta());
  }

  format(name, value) {
    if (name === this.statics.blotName && value) return;
    let [text, ] = this.descendant(TextBlot, this.length() - 1);
    if (text != null) {
      text.deleteAt(text.length() - 1, 1);
    }
    super.format(name, value);
  }

  formatAt(index, length, name, value) {
    if (length === 0) return;
    if (Parchment.query(name, Parchment.Scope.BLOCK) == null ||
        (name === this.statics.blotName && value === this.statics.formats(this.domNode))) {
      return;
    }
    let nextNewline = this.newlineIndex(index);
    if (nextNewline < 0 || nextNewline >= index + length) return;
    let prevNewline = this.newlineIndex(index, true) + 1;
    let isolateLength = nextNewline - prevNewline + 1;
    let blot = this.isolate(prevNewline, isolateLength);
    let next = blot.next;
    blot.format(name, value);
    if (next instanceof CodeBlock) {
      next.formatAt(0, index - prevNewline + length - isolateLength, name, value);
    }
  }

  insertAt(index, value, def) {
    if (def != null) return;
    let [text, offset] = this.descendant(TextBlot, index);
    text.insertAt(offset, value);
  }

  length() {
    let length = this.domNode.textContent.length;
    if (!this.domNode.textContent.endsWith('\n')) {
      return length + 1;
    }
    return length;
  }

  newlineIndex(searchIndex, reverse = false) {
    if (!reverse) {
      let offset = this.domNode.textContent.slice(searchIndex).indexOf('\n');
      return offset > -1 ? searchIndex + offset : -1;
    } else {
      return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n');
    }
  }

  optimize(context) {
    if (!this.domNode.textContent.endsWith('\n')) {
      this.appendChild(Parchment.create('text', '\n'));
    }
    super.optimize(context);
    let next = this.next;
    if (next != null && next.prev === this &&
        next.statics.blotName === this.statics.blotName &&
        this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) {
      next.optimize(context);
      next.moveChildren(this);
      next.remove();
    }
  }

  replace(target) {
    super.replace(target);
    [].slice.call(this.domNode.querySelectorAll('*')).forEach(function(node) {
      let blot = Parchment.find(node);
      if (blot == null) {
        node.parentNode.removeChild(node);
      } else if (blot instanceof Parchment.Embed) {
        blot.remove();
      } else {
        blot.unwrap();
      }
    });
  }
}
CodeBlock.blotName = 'code-block';
CodeBlock.tagName = 'PRE';
CodeBlock.TAB = '  ';

export { Code, CodeBlock as default };
<|endoftext|>


<commit_before>require("babel-register", {
  presets: [
    'es2015',
    'stage-0'
  ]
});

import path from 'path';
import fs from 'fs';
import defaultWebpackDev from './webpack.config';
import defaultWebpackProd from './webpack.prod.config';
import BowerWebpackPlugin from 'bower-webpack-plugin';
// import deepAssign from 'deep-assign';
import UI from '../models/ui';

function loadCustomWebpack(ui, config) {
  const custom = {};
  try {
    // attempt to load custom config
    custom.dev = require(path.resolve(config.basePath, config.webpack.dev)).default;
    custom.prod = require(path.resolve(config.basePath, config.webpack.prod)).default;
    custom.dev.resolve = {
      root: [
        path.resolve(config.basePath, 'node_modules'),
        path.resolve(config.basePath, 'src/lib')
      ]
    };
    custom.prod.resolve = {
      root: [
        path.resolve(config.basePath, 'node_modules'),
        path.resolve(config.basePath, 'src/lib')
      ]
    };

    ui.writeInfo('Using custom webpack config...');

    return custom;
  } catch (e) {
    ui.writeError(`Could not load custom webpack config. ${e}`);
    if (!config.webpack.dev) {
      ui.writeError(`Did not specify path to 'webpack.dev'.`);
    }
    if (!config.webpack.prod) {
      ui.writeError(`Did not specify path to 'webpack.prod'.`);
    }
    return {};
  }
}

export default function (config) {

  const ui = new UI();
  var bundle = {
    entry: path.resolve(config.basePath, './src/assets/js/index.js'),
    output: {
      path: path.resolve(config.basePath, './dist/assets/js')
    }
  };

  // attempt custom webpack config
  var customWebpack = {};
  if (config.webpack) {
    customWebpack = loadCustomWebpack(ui, config);
  } else {
    ui.writeInfo('Using metalpress default webpack config...');
  }

  // jquery configuration
  if (config.jquery) {
    defaultWebpackDev.externals = defaultWebpackProd.externals = {
      'jquery': 'jQuery'
    }

    ui.writeInfo('Using jQuery from external source entry...');
  }

  // plugins (bower-webpack-plugin)
  if (config.bower) {
    try {

      if (!defaultWebpackDev.plugins || !defaultWebpackDev.plugins.length) {
        defaultWebpackDev.plugins = [];
      }
      if (!defaultWebpackProd.plugins || !defaultWebpackProd.plugins.length) {
        defaultWebpackProd.plugins = [];
      }
      var bowerConfig = new BowerWebpackPlugin({
        modulesDirectories: ['src/lib'],
        manifestFiles: 'bower.json'
      });
      defaultWebpackDev.plugins.push(bowerConfig);
      defaultWebpackProd.plugins.push(bowerConfig);

      ui.writeInfo('Using bower packages from "src/lib" specified in bower.json...');
    } catch (e) { ui.writeError(e) }
  }

  // return webpack configuration
  return {
    dev: Object.assign(
      {},
      defaultWebpackDev,
      bundle,
      customWebpack.dev
    ),
    prod: Object.assign(
      {},
      defaultWebpackProd,
      bundle,
      customWebpack.prod
    )
  };

}

export function omitWebpack (config) {
  var newConfig = {};
  for (let c in config) {
    if (c !== 'webpack') {
      newConfig[c] = config[c];
    }
  }
  return newConfig;
}<commit_msg>fix(nope): nada<commit_after>require("babel-register", {
  presets: [
    'es2015',
    'stage-0'
  ]
});

import path from 'path';
import fs from 'fs';
import defaultWebpackDev from './webpack.config';
import defaultWebpackProd from './webpack.prod.config';
import BowerWebpackPlugin from 'bower-webpack-plugin';
// import deepAssign from 'deep-assign';
import UI from '../models/ui';

function loadCustomWebpack(ui, config) {
  const custom = {};
  try {
    // attempt to load custom config
    custom.dev = require(path.resolve(config.basePath, config.webpack.dev)).default;
    custom.prod = require(path.resolve(config.basePath, config.webpack.prod)).default;
    custom.dev.resolve = {
      root: [
        path.resolve(config.basePath, 'node_modules'),
        path.resolve(config.basePath, 'src/lib')
      ]
    };
    custom.prod.resolve = {
      root: [
        path.resolve(config.basePath, 'node_modules'),
        path.resolve(config.basePath, 'src/lib')
      ]
    };

    ui.writeInfo('Using custom webpack config...');

    return custom;
  } catch (e) {
    ui.writeError(`Could not load custom webpack config. ${e}`);
    if (!config.webpack.dev) {
      ui.writeError(`Did not specify path to 'webpack.dev'.`);
    }
    if (!config.webpack.prod) {
      ui.writeError(`Did not specify path to 'webpack.prod'.`);
    }
    return {};
  }
}

export default function (config) {

  const ui = new UI();
  var bundle = {
    entry: path.resolve(config.basePath, './src/assets/js/index.js'),
    output: {
      path: path.resolve(config.basePath, './dist/assets/js')
    }
  };

  // attempt custom webpack config
  var customWebpack = {};
  if (config.webpack) {
    customWebpack = loadCustomWebpack(ui, config);
  } else {
    ui.writeInfo('Using metalpress default webpack config...');
  }

  // jquery configuration
  if (config.jquery) {
    defaultWebpackDev.externals = defaultWebpackProd.externals = {
      'jquery': 'jQuery'
    }

    ui.writeInfo('Using jQuery from external source entry...');
  }

  // plugins (bower-webpack-plugin)
  if (config.bower) {
    try {

      if (!defaultWebpackDev.plugins || !defaultWebpackDev.plugins.length) {
        defaultWebpackDev.plugins = [];
      }
      if (!defaultWebpackProd.plugins || !defaultWebpackProd.plugins.length) {
        defaultWebpackProd.plugins = [];
      }
      var bowerConfig = new BowerWebpackPlugin({
        modulesDirectories: ['src/lib'],
        manifestFiles: 'bower.json'
      });
      defaultWebpackDev.plugins.push(bowerConfig);
      defaultWebpackProd.plugins.push(bowerConfig);

      ui.writeInfo('Using bower packages from "src/lib" specified in bower.json...');
    } catch (e) { ui.writeError(e) }
  }

  // return webpack configuration
  return {
    dev: deepAssign(
      {},
      defaultWebpackDev,
      bundle,
      customWebpack.dev
    ),
    prod: deepAssign(
      {},
      defaultWebpackProd,
      bundle,
      customWebpack.prod
    )
  };

}

export function omitWebpack (config) {
  var newConfig = {};
  for (let c in config) {
    if (c !== 'webpack') {
      newConfig[c] = config[c];
    }
  }
  return newConfig;
}<|endoftext|>


<commit_before>/** facebook.js
* @file: /config/passport/facebook.js
* @description: Handles passport authentication for Facebook strategy
* @parameters: Object(config)
* @exports: Facebook authentication for Passport
*/

var FacebookStrategy = require('passport-facebook').Strategy

module.exports = function(resources) {
  return new FacebookStrategy({
    clientID: resources.config.facebook.app_id,
    clientSecret: resources.config.facebook.app_secret,
    callbackURL: resources.config.facebook.callback,
    profileFields: ['id', 'name', 'displayName', 'picture.type(large)'],
  }, function (access_token, refresh_token, profile, done) {
    var usersdb = resources.collections.users

    usersdb.findAndModify({
      auth: {
        facebook: profile.id
      }
    },
    [
      ['_id','asc'] // sort order
    ], {
      $setOnInsert: {
        auth: {
          facebook: profile.id
        },
        name: {
          display_name: profile.displayName,
          first: profile.name.givenName
        },
        info: {
          website: null,
          description: null,
          blocked: false,
          permission: 'user'
        },
        fb_photo: (profile.photos) ? profile.photos[0].value : '/unknown_user.png',
        active_photo: 'facebook',
      }
    }, {
      new: true,
      upsert: true
    }, function (error, result) {
      done(error, {
        id: result.value._id,
        display_name: result.value.name.display_name,
        fb_photo: result.value.photo,
        active_photo: 'facebook'
      })
    })
  })
}<commit_msg>Fix 500 error on login<commit_after>/** facebook.js
* @file: /config/passport/facebook.js
* @description: Handles passport authentication for Facebook strategy
* @parameters: Object(config)
* @exports: Facebook authentication for Passport
*/

var FacebookStrategy = require('passport-facebook').Strategy

module.exports = function(resources) {
  return new FacebookStrategy({
    clientID: resources.config.facebook.app_id,
    clientSecret: resources.config.facebook.app_secret,
    callbackURL: resources.config.facebook.callback,
    profileFields: ['id', 'name', 'displayName', 'picture.type(large)'],
    enableProof: true
  }, function (access_token, refresh_token, profile, done) {
    var usersdb = resources.collections.users

    usersdb.findAndModify({
      auth: {
        facebook: profile.id
      }
    },
    [
      ['_id','asc'] // sort order
    ], {
      $setOnInsert: {
        auth: {
          facebook: profile.id
        },
        name: {
          display_name: profile.displayName,
          first: profile.name.givenName
        },
        info: {
          website: null,
          description: null,
          blocked: false,
          permission: 'user'
        },
        fb_photo: (profile.photos) ? profile.photos[0].value : '/unknown_user.png',
        active_photo: 'facebook',
      }
    }, {
      new: true,
      upsert: true
    }, function (error, result) {
      done(error, {
        id: result.value._id,
        display_name: result.value.name.display_name,
        fb_photo: result.value.photo,
        active_photo: 'facebook'
      })
    })
  })
}
<|endoftext|>


<commit_before>angular.module('breadcrumb').factory('Trail', function ($http) {
  const submitTrail = (trail, crumbs) => {
    console.log(trail.user_id);
    const length = trail.length.replace(/[^0-9.]/g, '');
    trail.length = length;
    if (!trail.transport) {
      trail.transport = 'WALKING';
      if (length > 5) {
        trail.transport = 'BICYCLING';
      }
      if (length > 20) {
        trail.transport = 'DRIVING';
      }
    }
    if (!trail.difficulty) {
      trail.difficulty = 1;
      if (length > 5) {
        trail.difficulty = 2;
      } else if (length > 20) {
        trail.difficulty = 3;
      }
    }
    trail.crumbs = crumbs;
    return $http({
      method: 'POST',
      // url: 'http://54.203.104.113/trails',
      // url: 'http://192.168.99.100:3000/trails',
      header: {
        'Access-Control-Allow-Origin': '*',
      },
      //TODO: add access_token
      data: trail,
      json: true,
    })
    .then(response => response)
    .catch(err => err);
  };
  return {
    submit: submitTrail,
  };
});
<commit_msg>(feature) refactor trail submission to take a full trail and submit all the crumbs, to the server side<commit_after>angular.module('breadcrumb').factory('Trail', function ($http) {
  const submitTrail = (trail, crumbs) => {
    console.log(trail.user_id);
    const length = trail.length.replace(/[^0-9.]/g, '');
    trail.length = length;
    if (!trail.transport) {
      trail.transport = 'WALKING';
      if (length > 5) {
        trail.transport = 'BICYCLING';
      }
      if (length > 20) {
        trail.transport = 'DRIVING';
      }
    }
    if (!trail.difficulty) {
      trail.difficulty = 1;
      if (length > 5) {
        trail.difficulty = 2;
      } else if (length > 20) {
        trail.difficulty = 3;
      }
    }
    trail.crumbs = crumbs;
    return $http({
      method: 'POST',
      // url: 'http://54.203.104.113/trails',
      url: 'http://192.168.99.100:3000/trails',
      header: {
        'Access-Control-Allow-Origin': '*',
      },
      // TODO: add access_token
      data: trail,
      json: true,
    })
    .then(response => response)
    .catch(err => err);
  };
  return {
    submit: submitTrail,
  };
});
<|endoftext|>


<commit_before>/*
 *  FSTransport.js
 *
 *  David Janes
 *  IOTDB.org
 *  2015-03-07
 *
 *  Copyright [2013-2015] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

var iotdb = require('iotdb');
var iotdb_transport = require('iotdb-transport');
var errors = iotdb_transport.errors;
var _ = iotdb._;

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var watch = require('watch');
var rwlock = require('rwlock'); // maybe should be doing file locking?

var util = require('util');
var url = require('url');

var logger = iotdb.logger({
    name: 'iotdb-transport-fs',
    module: 'FSTransport',
});

var glock = new rwlock();
var verbose = false;

/* --- forward definitions --- */
var _encode;
var _decode;
var _unpack;
var _pack;

/* --- constructor --- */

/**
 *  File System based Transport
 *

 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
var FSTransport = function (initd) {
    var self = this;

    self.initd = _.defaults(
        initd, {
            channel: iotdb_transport.channel,
            unchannel: iotdb_transport.unchannel,
            encode: _encode,
            decode: _decode,
            pack: _pack,
            unpack: _unpack,
            user: null,
            check_changed: true,
        },
        iotdb.keystore().get("/transports/FSTransport/initd"), {
            prefix: path.join(process.cwd(), ".iotdb", "fs"),
            flat_band: null,
        }
    );

    /* for consistency but not used */
    self.native = {};

    self.lock = glock;

    /* ignore errors - they will occur later anyway */
    self.lock.writeLock(function (release) {
        mkdirp.mkdirp(self.initd.prefix, function (error) {
            release();
        });
    });

};

FSTransport.prototype = new iotdb_transport.Transport();
FSTransport.prototype._class = "FSTransport";

/* --- methods --- */

/**
 *  See {iotdb_transport.Transport#list} for documentation.
 */
FSTransport.prototype.list = function (paramd, callback) {
    var self = this;
    var ld;

    self._validate_list(paramd, callback);

    self.lock.readLock(function (release) {
        fs.readdir(self.initd.prefix, function (error, names) {
            release();

            if (error) {
                ld = _.shallowCopy(paramd);
                callback(error, ld);
                return;
            }

            names.sort();
            names.reverse();

            var _pop = function () {
                if (names.length === 0) {
                    callback(null, null);
                    return;
                }

                var name = names.pop();
                var folder = path.join(self.initd.prefix, name);

                var result = self.initd.unchannel(self.initd, folder);
                if (result) {
                    ld = _.shallowCopy(paramd);
                    ld.id = result[0];
                    if (callback(null, ld)) {
                        names = [];
                    }

                    _pop();
                } else {
                    _pop();
                }
            };

            _pop();
        });
    });
};

/**
 *  See {iotdb_transport.Transport#added} for documentation.
 *

 *  NOT FINISHED
 */
FSTransport.prototype.added = function (paramd, callback) {
    var self = this;

    self._validate_added(paramd, callback);
};

/**
 *  See {iotdb_transport.Transport#bands} for documentation.
 */
FSTransport.prototype.bands = function (paramd, callback) {
    var self = this;

    self._validate_bands(paramd, callback);

    if (self.initd.flat_band) {
        self._bands_flat(paramd, callback);
    } else {
        self._bands(paramd, callback);
    }
};

/**
 *  Flat files - read and if it exists return [ id, flat_band ]
 */
FSTransport.prototype._bands_flat = function (paramd, callback) {
    var self = this;
    var channel = self.initd.channel(self.initd, paramd.id);

    var bd = _.shallowCopy(paramd);
    bd.bandd = {};

    self.lock.readLock(function (release) {
        fs.readFile(channel, {
            encoding: 'utf8'
        }, function (error, doc) {
            release();

            if (error) {
                return callback(new errors.NotFound(), bd);
            }

            bd.bandd[self.initd.flat_band] = null;

            return callback(null, bd);
        });
    });
};

/**
 */
FSTransport.prototype._bands = function (paramd, callback) {
    var self = this;
    var channel = self.initd.channel(self.initd, paramd.id);

    var bd = _.shallowCopy(paramd);
    bd.bandd = {};

    self.lock.readLock(function (release) {
        fs.readdir(channel, function (error, names) {
            release();

            if (error) {
                return callback(new errors.NotFound(), bd);
            }

            names.sort();
            names.reverse();

            var _pop = function () {
                if (names.length === 0) {
                    return callback(null, bd);
                }

                var name = names.pop();
                var folder = path.join(channel, name);
                var result = self.initd.unchannel(self.initd, folder);
                if (!result) {
                    _pop();
                    return;
                }

                fs.readFile(folder, {
                    encoding: 'utf8'
                }, function (error, doc) {
                    if (!error) {
                        bd.bandd[name] = null;
                    }

                    _pop();
                });
            };

            _pop();
        });
    });
};

/**
 *  See {iotdb_transport.Transport#get} for documentation.
 */
FSTransport.prototype.get = function (paramd, callback) {
    var self = this;

    self._validate_get(paramd, callback);

    var channel = self.initd.channel(self.initd, paramd.id, paramd.band);

    /* undefined for "don't know"; null for "doesn't exist" */
    self.lock.readLock(function (release) {
        if (verbose) console.log("READ.START", channel);
        fs.readFile(channel, {
            encoding: 'utf8'
        }, function (error, doc) {
            if (verbose) console.log("READ.END", channel);
            release();

            if (error) {
                if (error.code === 'ENOENT') {
                    return callback(new errors.NotFound(), {
                        id: paramd.id,
                        band: paramd.band,
                        value: null,
                    });
                }

                return callback(error, {
                    id: paramd.id,
                    band: paramd.band,
                    value: null,
                });
            }

            try {
                return callback(null, {
                    id: paramd.id,
                    band: paramd.band,
                    user: self.initd.user,
                    value: self.initd.unpack(JSON.parse(doc), paramd.id, paramd.band),
                });
            } catch (x) {
                logger.error({
                    method: "get",
                    cause: "see stack trace",
                    stack: x.stack,
                    channel: channel,
                    doc: doc,
                }, "exception in callback");

                return callback(new errors.Internal(_.error.message(x)), {
                    id: paramd.id,
                    band: paramd.band,
                    user: self.initd.user,
                    value: null,
                });
            }

        });
    });
};

/**
 *  See {iotdb_transport.Transport#update} for documentation.
 */
FSTransport.prototype.put = function (paramd, callback) {
    var self = this;

    self._validate_update(paramd, callback);

    var channel = self.initd.channel(self.initd, paramd.id, paramd.band);
    var d = self.initd.pack(paramd.value, paramd.id, paramd.band);

    var pd = _.shallowCopy(paramd);

    self.lock.writeLock(function (release) {
        mkdirp.mkdirp(path.dirname(channel), function (error) {
            if (error) {
                release();
                return callback(error, pd);
            }

            var old_data = null;
            var new_data = JSON.stringify(d, null, 2);
            if (self.initd.check_changed) {
                try {
                    var old_data = fs.readFileSync(channel);
                } catch (x) {
                }
            }

            if (new_data === old_data) {
                // console.log("UNCHANGED");
                release();
            } else {
                if (verbose) console.log("WRITE.START", channel);
                fs.writeFile(channel, new_data, function() {
                    if (verbose) console.log("WRITE.END", channel);
                    release();
                    process.nextTick(function() {
                        return callback(null, pd);
                    });
                });
            }

        });
    });
};

/**
 *  See {iotdb_transport.Transport#updated} for documentation.
 */
FSTransport.prototype.updated = function (paramd, callback) {
    var self = this;

    self._validate_updated(paramd, callback);

    var last_time = 0;
    var last_id = null;
    var last_band = null;

    var _doit = function (f) {
        var result = self.initd.unchannel(self.initd, f);
        if (!result) {
            return;
        }

        var this_id = result[0];
        var this_band = result[1];

        if (paramd.id && (this_id !== paramd.id)) {
            return;
        }

        if (paramd.band && (this_band !== paramd.band)) {
            return;
        }

        /* debounce, sigh */
        var now = (new Date()).getTime();
        if ((this_id === last_id) && (this_band === last_band) && ((now - last_time) < 50)) {
            return;
        }

        last_id = this_id;
        last_band = this_band;
        last_time = now;

        callback(null, {
            id: this_id,
            band: this_band,
            value: undefined,
            user: self.initd.user,
        });
    };

    self.lock.writeLock(function (release) {
        watch.createMonitor(self.initd.prefix, function (monitor) {
            monitor.on("created", function (f, stat) {
                _doit(f);
            });
            monitor.on("changed", function (f, curr, prev) {
                _doit(f);
            });
            monitor.on("removed", function (f, stat) {
                _doit(f);
            });

            setTimeout(function() {
                release();
            }, 1000);
        });
    });
};

/**
 *  See {iotdb_transport.Transport#remove} for documentation.
 */
FSTransport.prototype.remove = function (paramd, callback) {
    var self = this;

    self._validate_remove(paramd, callback);

    var rd = _.shallowCopy(paramd);
    delete rd.band;
    delete rd.value;

    var channel = self.initd.channel(self.initd, paramd.id);

    self.lock.writeLock(function (release) {
        fs.readdir(channel, function (error, names) {
            if (error) {
                if (error.code === 'ENOENT') {
                    callback(new errors.NotFound(), rd);
                } else {
                    callback(error, rd);
                }
                return;
            }

            names.sort();
            names.reverse();

            var _pop = function () {
                if (names.length === 0) {
                    fs.rmdir(channel, function (error) {});
                    release();

                    callback(null, rd);
                    return;
                }

                var name = names.pop();
                var folder = path.join(channel, name);

                fs.unlink(folder, function (error) {
                    _pop();
                });
            };

            _pop();
        });
    });
};

/* --- internals --- */

var safe_rex = /[\/$%#\]\[]/g;
if (/^win/.test(process.platform)) {
    safe_rex = /[:\/$%#\]\[]/g;
}

var _encode = function (s) {
    return s.replace(safe_rex, function (c) {
        return '%' + c.charCodeAt(0).toString(16);
    });
};

var _decode = function (s) {
    return decodeURIComponent(s);
};

var _unpack = function (d, id, band) {
    return _.d.transform(d, {
        pre: _.ld_compact,
        key: _decode,
    });
};

var _pack = function (d, id, band) {
    return _.d.transform(d, {
        pre: _.ld_compact,
        key: _encode,
    });
};

/**
 *  API
 */
exports.FSTransport = FSTransport;
<commit_msg>don't use 'shallowCopy' any more<commit_after>/*
 *  FSTransport.js
 *
 *  David Janes
 *  IOTDB.org
 *  2015-03-07
 *
 *  Copyright [2013-2015] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

var iotdb = require('iotdb');
var iotdb_transport = require('iotdb-transport');
var errors = iotdb_transport.errors;
var _ = iotdb._;

var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var watch = require('watch');
var rwlock = require('rwlock'); // maybe should be doing file locking?

var util = require('util');
var url = require('url');

var logger = iotdb.logger({
    name: 'iotdb-transport-fs',
    module: 'FSTransport',
});

var glock = new rwlock();
var verbose = false;

/* --- forward definitions --- */
var _encode;
var _decode;
var _unpack;
var _pack;

/* --- constructor --- */

/**
 *  File System based Transport
 *

 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
var FSTransport = function (initd) {
    var self = this;

    self.initd = _.defaults(
        initd, {
            channel: iotdb_transport.channel,
            unchannel: iotdb_transport.unchannel,
            encode: _encode,
            decode: _decode,
            pack: _pack,
            unpack: _unpack,
            user: null,
            check_changed: true,
        },
        iotdb.keystore().get("/transports/FSTransport/initd"), {
            prefix: path.join(process.cwd(), ".iotdb", "fs"),
            flat_band: null,
        }
    );

    /* for consistency but not used */
    self.native = {};

    self.lock = glock;

    /* ignore errors - they will occur later anyway */
    self.lock.writeLock(function (release) {
        mkdirp.mkdirp(self.initd.prefix, function (error) {
            release();
        });
    });

};

FSTransport.prototype = new iotdb_transport.Transport();
FSTransport.prototype._class = "FSTransport";

/* --- methods --- */

/**
 *  See {iotdb_transport.Transport#list} for documentation.
 */
FSTransport.prototype.list = function (paramd, callback) {
    var self = this;
    var ld;

    self._validate_list(paramd, callback);

    self.lock.readLock(function (release) {
        fs.readdir(self.initd.prefix, function (error, names) {
            release();

            if (error) {
                ld = _.d.clone.shallow(paramd);
                callback(error, ld);
                return;
            }

            names.sort();
            names.reverse();

            var _pop = function () {
                if (names.length === 0) {
                    callback(null, null);
                    return;
                }

                var name = names.pop();
                var folder = path.join(self.initd.prefix, name);

                var result = self.initd.unchannel(self.initd, folder);
                if (result) {
                    ld = _.d.clone.shallow(paramd);
                    ld.id = result[0];
                    if (callback(null, ld)) {
                        names = [];
                    }

                    _pop();
                } else {
                    _pop();
                }
            };

            _pop();
        });
    });
};

/**
 *  See {iotdb_transport.Transport#added} for documentation.
 *

 *  NOT FINISHED
 */
FSTransport.prototype.added = function (paramd, callback) {
    var self = this;

    self._validate_added(paramd, callback);
};

/**
 *  See {iotdb_transport.Transport#bands} for documentation.
 */
FSTransport.prototype.bands = function (paramd, callback) {
    var self = this;

    self._validate_bands(paramd, callback);

    if (self.initd.flat_band) {
        self._bands_flat(paramd, callback);
    } else {
        self._bands(paramd, callback);
    }
};

/**
 *  Flat files - read and if it exists return [ id, flat_band ]
 */
FSTransport.prototype._bands_flat = function (paramd, callback) {
    var self = this;
    var channel = self.initd.channel(self.initd, paramd.id);

    var bd = _.d.clone.shallow(paramd);
    bd.bandd = {};

    self.lock.readLock(function (release) {
        fs.readFile(channel, {
            encoding: 'utf8'
        }, function (error, doc) {
            release();

            if (error) {
                return callback(new errors.NotFound(), bd);
            }

            bd.bandd[self.initd.flat_band] = null;

            return callback(null, bd);
        });
    });
};

/**
 */
FSTransport.prototype._bands = function (paramd, callback) {
    var self = this;
    var channel = self.initd.channel(self.initd, paramd.id);

    var bd = _.d.clone.shallow(paramd);
    bd.bandd = {};

    self.lock.readLock(function (release) {
        fs.readdir(channel, function (error, names) {
            release();

            if (error) {
                return callback(new errors.NotFound(), bd);
            }

            names.sort();
            names.reverse();

            var _pop = function () {
                if (names.length === 0) {
                    return callback(null, bd);
                }

                var name = names.pop();
                var folder = path.join(channel, name);
                var result = self.initd.unchannel(self.initd, folder);
                if (!result) {
                    _pop();
                    return;
                }

                fs.readFile(folder, {
                    encoding: 'utf8'
                }, function (error, doc) {
                    if (!error) {
                        bd.bandd[name] = null;
                    }

                    _pop();
                });
            };

            _pop();
        });
    });
};

/**
 *  See {iotdb_transport.Transport#get} for documentation.
 */
FSTransport.prototype.get = function (paramd, callback) {
    var self = this;

    self._validate_get(paramd, callback);

    var channel = self.initd.channel(self.initd, paramd.id, paramd.band);

    /* undefined for "don't know"; null for "doesn't exist" */
    self.lock.readLock(function (release) {
        if (verbose) console.log("READ.START", channel);
        fs.readFile(channel, {
            encoding: 'utf8'
        }, function (error, doc) {
            if (verbose) console.log("READ.END", channel);
            release();

            if (error) {
                if (error.code === 'ENOENT') {
                    return callback(new errors.NotFound(), {
                        id: paramd.id,
                        band: paramd.band,
                        value: null,
                    });
                }

                return callback(error, {
                    id: paramd.id,
                    band: paramd.band,
                    value: null,
                });
            }

            try {
                return callback(null, {
                    id: paramd.id,
                    band: paramd.band,
                    user: self.initd.user,
                    value: self.initd.unpack(JSON.parse(doc), paramd.id, paramd.band),
                });
            } catch (x) {
                logger.error({
                    method: "get",
                    cause: "see stack trace",
                    stack: x.stack,
                    channel: channel,
                    doc: doc,
                }, "exception in callback");

                return callback(new errors.Internal(_.error.message(x)), {
                    id: paramd.id,
                    band: paramd.band,
                    user: self.initd.user,
                    value: null,
                });
            }

        });
    });
};

/**
 *  See {iotdb_transport.Transport#update} for documentation.
 */
FSTransport.prototype.put = function (paramd, callback) {
    var self = this;

    self._validate_update(paramd, callback);

    var channel = self.initd.channel(self.initd, paramd.id, paramd.band);
    var d = self.initd.pack(paramd.value, paramd.id, paramd.band);

    var pd = _.d.clone.shallow(paramd);

    self.lock.writeLock(function (release) {
        mkdirp.mkdirp(path.dirname(channel), function (error) {
            if (error) {
                release();
                return callback(error, pd);
            }

            var old_data = null;
            var new_data = JSON.stringify(d, null, 2);
            if (self.initd.check_changed) {
                try {
                    var old_data = fs.readFileSync(channel);
                } catch (x) {
                }
            }

            if (new_data === old_data) {
                // console.log("UNCHANGED");
                release();
            } else {
                if (verbose) console.log("WRITE.START", channel);
                fs.writeFile(channel, new_data, function() {
                    if (verbose) console.log("WRITE.END", channel);
                    release();
                    process.nextTick(function() {
                        return callback(null, pd);
                    });
                });
            }

        });
    });
};

/**
 *  See {iotdb_transport.Transport#updated} for documentation.
 */
FSTransport.prototype.updated = function (paramd, callback) {
    var self = this;

    self._validate_updated(paramd, callback);

    var last_time = 0;
    var last_id = null;
    var last_band = null;

    var _doit = function (f) {
        var result = self.initd.unchannel(self.initd, f);
        if (!result) {
            return;
        }

        var this_id = result[0];
        var this_band = result[1];

        if (paramd.id && (this_id !== paramd.id)) {
            return;
        }

        if (paramd.band && (this_band !== paramd.band)) {
            return;
        }

        /* debounce, sigh */
        var now = (new Date()).getTime();
        if ((this_id === last_id) && (this_band === last_band) && ((now - last_time) < 50)) {
            return;
        }

        last_id = this_id;
        last_band = this_band;
        last_time = now;

        callback(null, {
            id: this_id,
            band: this_band,
            value: undefined,
            user: self.initd.user,
        });
    };

    self.lock.writeLock(function (release) {
        watch.createMonitor(self.initd.prefix, function (monitor) {
            monitor.on("created", function (f, stat) {
                _doit(f);
            });
            monitor.on("changed", function (f, curr, prev) {
                _doit(f);
            });
            monitor.on("removed", function (f, stat) {
                _doit(f);
            });

            setTimeout(function() {
                release();
            }, 1000);
        });
    });
};

/**
 *  See {iotdb_transport.Transport#remove} for documentation.
 */
FSTransport.prototype.remove = function (paramd, callback) {
    var self = this;

    self._validate_remove(paramd, callback);

    var rd = _.d.clone.shallow(paramd);
    delete rd.band;
    delete rd.value;

    var channel = self.initd.channel(self.initd, paramd.id);

    self.lock.writeLock(function (release) {
        fs.readdir(channel, function (error, names) {
            if (error) {
                if (error.code === 'ENOENT') {
                    callback(new errors.NotFound(), rd);
                } else {
                    callback(error, rd);
                }
                return;
            }

            names.sort();
            names.reverse();

            var _pop = function () {
                if (names.length === 0) {
                    fs.rmdir(channel, function (error) {});
                    release();

                    callback(null, rd);
                    return;
                }

                var name = names.pop();
                var folder = path.join(channel, name);

                fs.unlink(folder, function (error) {
                    _pop();
                });
            };

            _pop();
        });
    });
};

/* --- internals --- */

var safe_rex = /[\/$%#\]\[]/g;
if (/^win/.test(process.platform)) {
    safe_rex = /[:\/$%#\]\[]/g;
}

var _encode = function (s) {
    return s.replace(safe_rex, function (c) {
        return '%' + c.charCodeAt(0).toString(16);
    });
};

var _decode = function (s) {
    return decodeURIComponent(s);
};

var _unpack = function (d, id, band) {
    return _.d.transform(d, {
        pre: _.ld_compact,
        key: _decode,
    });
};

var _pack = function (d, id, band) {
    return _.d.transform(d, {
        pre: _.ld_compact,
        key: _encode,
    });
};

/**
 *  API
 */
exports.FSTransport = FSTransport;
<|endoftext|>