<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,
        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: "User ID, Name or email",
            button: "Start Chat",
            focus: true
        };
    },

    getInitialState: function() {
        return {
            user: null,
            queryList: [],
            addressSelected: false,
            selected: 0,
            hover: false,
        };
    },

    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();
    },

    componentDidUpdate: function() {
        // As the user scrolls with the arrow keys keep the selected item
        // at the top of the window.
        if (this.scrollElement && !this.state.hover) {
            var elementHeight = this.queryListElement.getBoundingClientRect().height;
            this.scrollElement.scrollTop = (this.state.selected * elementHeight) - elementHeight;
        }
    },

    onStartChat: function() {
        var addr;

        // Either an address tile was created, or text input is being used
        if (this.state.user) {
            addr = this.state.user.userId;
        } else {
            addr = this.refs.textinput.value;
        }

        // Check if the addr is a valid type
        if (Invite.getAddressType(addr) === "mx") {
            var room = this._getDirectMessageRoom(addr);
            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, addr);
            } else {
                this._startChat(addr);
            }
        } else if (Invite.getAddressType(addr) === "email") {
            this._startChat(addr);
        } else {
            // Nothing to do, so focus back on the textinput
            this.refs.textinput.focus();
        }
    },

    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();
            if (this.state.selected > 0) {
                this.setState({
                    selected: this.state.selected - 1,
                    hover : false,
                });
            }
        } else if (e.keyCode === 40) { // down arrow
            e.stopPropagation();
            e.preventDefault();
            if (this.state.selected < this._maxSelected(this.state.queryList)) {
                this.setState({
                    selected: this.state.selected + 1,
                    hover : false,
                });
            }
        } else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            if (this.state.queryList.length > 0) {
                this.setState({
                    user: this.state.queryList[this.state.selected],
                    addressSelected: true,
                    queryList: [],
                    hover : false,
                });
            }
        }
    },

    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);
            });
        }

        // Make sure the selected item isn't outside the list bounds
        var selected = this.state.selected;
        var maxSelected = this._maxSelected(queryList);
        if (selected > maxSelected) {
            selected = maxSelected;
        }

        this.setState({
            queryList: queryList,
            selected: selected,
        });
    },

    onDismissed: function() {
        this.setState({
            user: null,
            addressSelected: false,
            selected: 0,
            queryList: [],
        });
    },

    onClick: function(index) {
        var self = this;
        return function() {
            self.setState({
                user: self.state.queryList[index],
                addressSelected: true,
                queryList: [],
                hover: false,
            });
        };
    },

    onMouseEnter: function(index) {
        var self = this;
        return function() {
            self.setState({
                selected: index,
                hover: true,
            });
        };
    },

    onMouseLeave: function() {
        this.setState({ hover : false });
    },

    createQueryListTiles: function() {
        var self = this;
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        var AddressTile = sdk.getComponent("elements.AddressTile");
        var maxSelected = this._maxSelected(this.state.queryList);
        var queryList = [];

        // Only create the query elements if there are queries
        if (this.state.queryList.length > 0) {
            for (var i = 0; i <= maxSelected; i++) {
                var classes = classNames({
                    "mx_ChatInviteDialog_queryListElement": true,
                    "mx_ChatInviteDialog_selected": this.state.selected === i,
                });

                // NOTE: Defaulting to "vector" as the network, until the network backend stuff is done.
                // Saving the queryListElement so we can use it to work out, in the componentDidUpdate
                // method, how far to scroll when using the arrow keys
                queryList.push(

 { this.queryListElement = ref; }} >
                        <AddressTile user={this.state.queryList[i]} justified={true} networkName="vector" networkUrl="img/search-icon-vector.svg" />

                );
            }
        }
        return 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) {
                    return room;
                }
            }
        }
        return null;
    },

    _startChat: function(addr) {
        // Start the chat
        createRoom().then(function(roomId) {
            return Invite.inviteToRoom(roomId, addr);
        })
        .catch(function(err) {
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Failure to invite user",
                description: err.toString()
            });
            return null;
        })
        .done();

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

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

    _maxSelected: function(list) {
        var listSize = list.length === 0 ? 0 : list.length - 1;
        var maxSelected = listSize > (TRUNCATE_QUERY_LIST - 1) ? (TRUNCATE_QUERY_LIST - 1) : listSize
        return maxSelected;
    },

    // 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();

        // 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;
    },

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

        var query;
        if (this.state.addressSelected) {
            var AddressTile = sdk.getComponent("elements.AddressTile");
            query = (
                <AddressTile user={this.state.user} canDismiss={true} onDismissed={this.onDismissed} />
            );
        } else {
            query = (
                <textarea 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 queryList;
        var queryListElements = this.createQueryListTiles();
        if (queryListElements.length > 0) {
            queryList = (

 {this.scrollElement = ref}}>
                    { queryListElements }

            );
        }

        return (

                    {this.props.title}

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

                    { this.props.description }

{ query }

                    { queryList }

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

        );
    }
});
<commit_msg>Don't re-use parted rooms for DMs<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,
        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: "User ID, Name or email",
            button: "Start Chat",
            focus: true
        };
    },

    getInitialState: function() {
        return {
            user: null,
            queryList: [],
            addressSelected: false,
            selected: 0,
            hover: false,
        };
    },

    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();
    },

    componentDidUpdate: function() {
        // As the user scrolls with the arrow keys keep the selected item
        // at the top of the window.
        if (this.scrollElement && !this.state.hover) {
            var elementHeight = this.queryListElement.getBoundingClientRect().height;
            this.scrollElement.scrollTop = (this.state.selected * elementHeight) - elementHeight;
        }
    },

    onStartChat: function() {
        var addr;

        // Either an address tile was created, or text input is being used
        if (this.state.user) {
            addr = this.state.user.userId;
        } else {
            addr = this.refs.textinput.value;
        }

        // Check if the addr is a valid type
        if (Invite.getAddressType(addr) === "mx") {
            var room = this._getDirectMessageRoom(addr);
            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, addr);
            } else {
                this._startChat(addr);
            }
        } else if (Invite.getAddressType(addr) === "email") {
            this._startChat(addr);
        } else {
            // Nothing to do, so focus back on the textinput
            this.refs.textinput.focus();
        }
    },

    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();
            if (this.state.selected > 0) {
                this.setState({
                    selected: this.state.selected - 1,
                    hover : false,
                });
            }
        } else if (e.keyCode === 40) { // down arrow
            e.stopPropagation();
            e.preventDefault();
            if (this.state.selected < this._maxSelected(this.state.queryList)) {
                this.setState({
                    selected: this.state.selected + 1,
                    hover : false,
                });
            }
        } else if (e.keyCode === 13) { // enter
            e.stopPropagation();
            e.preventDefault();
            if (this.state.queryList.length > 0) {
                this.setState({
                    user: this.state.queryList[this.state.selected],
                    addressSelected: true,
                    queryList: [],
                    hover : false,
                });
            }
        }
    },

    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);
            });
        }

        // Make sure the selected item isn't outside the list bounds
        var selected = this.state.selected;
        var maxSelected = this._maxSelected(queryList);
        if (selected > maxSelected) {
            selected = maxSelected;
        }

        this.setState({
            queryList: queryList,
            selected: selected,
        });
    },

    onDismissed: function() {
        this.setState({
            user: null,
            addressSelected: false,
            selected: 0,
            queryList: [],
        });
    },

    onClick: function(index) {
        var self = this;
        return function() {
            self.setState({
                user: self.state.queryList[index],
                addressSelected: true,
                queryList: [],
                hover: false,
            });
        };
    },

    onMouseEnter: function(index) {
        var self = this;
        return function() {
            self.setState({
                selected: index,
                hover: true,
            });
        };
    },

    onMouseLeave: function() {
        this.setState({ hover : false });
    },

    createQueryListTiles: function() {
        var self = this;
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        var AddressTile = sdk.getComponent("elements.AddressTile");
        var maxSelected = this._maxSelected(this.state.queryList);
        var queryList = [];

        // Only create the query elements if there are queries
        if (this.state.queryList.length > 0) {
            for (var i = 0; i <= maxSelected; i++) {
                var classes = classNames({
                    "mx_ChatInviteDialog_queryListElement": true,
                    "mx_ChatInviteDialog_selected": this.state.selected === i,
                });

                // NOTE: Defaulting to "vector" as the network, until the network backend stuff is done.
                // Saving the queryListElement so we can use it to work out, in the componentDidUpdate
                // method, how far to scroll when using the arrow keys
                queryList.push(

 { this.queryListElement = ref; }} >
                        <AddressTile user={this.state.queryList[i]} justified={true} networkName="vector" networkUrl="img/search-icon-vector.svg" />

                );
            }
        }
        return 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(addr) {
        // Start the chat
        createRoom().then(function(roomId) {
            return Invite.inviteToRoom(roomId, addr);
        })
        .catch(function(err) {
            var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createDialog(ErrorDialog, {
                title: "Failure to invite user",
                description: err.toString()
            });
            return null;
        })
        .done();

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

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

    _maxSelected: function(list) {
        var listSize = list.length === 0 ? 0 : list.length - 1;
        var maxSelected = listSize > (TRUNCATE_QUERY_LIST - 1) ? (TRUNCATE_QUERY_LIST - 1) : listSize
        return maxSelected;
    },

    // 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();

        // 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;
    },

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

        var query;
        if (this.state.addressSelected) {
            var AddressTile = sdk.getComponent("elements.AddressTile");
            query = (
                <AddressTile user={this.state.user} canDismiss={true} onDismissed={this.onDismissed} />
            );
        } else {
            query = (
                <textarea 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 queryList;
        var queryListElements = this.createQueryListTiles();
        if (queryListElements.length > 0) {
            queryList = (

 {this.scrollElement = ref}}>
                    { queryListElements }

            );
        }

        return (

                    {this.props.title}

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

                    { this.props.description }

{ query }

                    { queryList }

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

        );
    }
});
<|endoftext|>


<commit_before>(function(a,b){if(typeof define==="function"&&define.amd){define([],b);}else{if(typeof exports==="object"){module.exports=b();}else{a.X2JS=b();}}}(this,function(){return function(z){var t="1.2.0";z=z||{};i();u();function i(){if(z.escapeMode===undefined){z.escapeMode=true;}z.attributePrefix=z.attributePrefix||"_";z.arrayAccessForm=z.arrayAccessForm||"none";z.emptyNodeForm=z.emptyNodeForm||"text";if(z.enableToStringFunc===undefined){z.enableToStringFunc=true;}z.arrayAccessFormPaths=z.arrayAccessFormPaths||[];if(z.skipEmptyTextNodesForObj===undefined){z.skipEmptyTextNodesForObj=true;}if(z.stripWhitespaces===undefined){z.stripWhitespaces=true;}z.datetimeAccessFormPaths=z.datetimeAccessFormPaths||[];if(z.useDoubleQuotes===undefined){z.useDoubleQuotes=false;}z.xmlElementsFilter=z.xmlElementsFilter||[];z.jsonPropertiesFilter=z.jsonPropertiesFilter||[];if(z.keepCData===undefined){z.keepCData=false;}}var h={ELEMENT_NODE:1,TEXT_NODE:3,CDATA_SECTION_NODE:4,COMMENT_NODE:8,DOCUMENT_NODE:9};function u(){}function x(B){var C=B.localName;if(C==null){C=B.baseName;}if(C==null||C==""){C=B.nodeName;}return C;}function r(B){return B.prefix;}function s(B){if(typeof(B)=="string"){return B.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");}else{return B;}}function k(B){return B.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&");}function w(C,F,D,E){var B=0;for(;B<C.length;B++){var G=C[B];if(typeof G==="string"){if(G==E){break;}}else{if(G instanceof RegExp){if(G.test(E)){break;}}else{if(typeof G==="function"){if(G(F,D,E)){break;}}}}}return B!=C.length;}function n(D,B,C){switch(z.arrayAccessForm){case"property":if(!(D[B] instanceof Array)){D[B+"_asArray"]=[D[B]];}else{D[B+"_asArray"]=D[B];}break;}if(!(D[B] instanceof Array)&&z.arrayAccessFormPaths.length>0){if(w(z.arrayAccessFormPaths,D,B,C)){D[B]=[D[B]];}}}function a(G){var E=G.split(/[-T:+Z]/g);var F=new Date(E[0],E[1]-1,E[2]);var D=E[5].split(".");F.setHours(E[3],E[4],D[0]);if(D.length>1){F.setMilliseconds(D[1]);}if(E[6]&&E[7]){var C=E[6]*60+Number(E[7]);var B=/\d\d-\d\d:\d\d$/.test(G)?"-":"+";C=0+(B=="-"?-1*C:C);F.setMinutes(F.getMinutes()-C-F.getTimezoneOffset());}else{if(G.indexOf("Z",G.length-1)!==-1){F=new Date(Date.UTC(F.getFullYear(),F.getMonth(),F.getDate(),F.getHours(),F.getMinutes(),F.getSeconds(),F.getMilliseconds()));}}return F;}function q(D,B,C){if(z.datetimeAccessFormPaths.length>0){var E=C.split(".#")[0];if(w(z.datetimeAccessFormPaths,D,B,E)){return a(D);}else{return D;}}else{return D;}}function b(E,C,B,D){if(C==h.ELEMENT_NODE&&z.xmlElementsFilter.length>0){return w(z.xmlElementsFilter,E,B,D);}else{return true;}}function A(D,J){if(D.nodeType==h.DOCUMENT_NODE){var K=new Object;var B=D.childNodes;for(var L=0;L<B.length;L++){var C=B.item(L);if(C.nodeType==h.ELEMENT_NODE){var I=x(C);K[I]=A(C,I);}}return K;}else{if(D.nodeType==h.ELEMENT_NODE){var K=new Object;K.__cnt=0;var B=D.childNodes;for(var L=0;L<B.length;L++){var C=B.item(L);var I=x(C);if(C.nodeType!=h.COMMENT_NODE){var H=J+"."+I;if(b(K,C.nodeType,I,H)){K.__cnt++;if(K[I]==null){K[I]=A(C,H);n(K,I,H);}else{if(K[I]!=null){if(!(K[I] instanceof Array)){K[I]=[K[I]];n(K,I,H);}}(K[I])[K[I].length]=A(C,H);}}}}for(var E=0;E<D.attributes.length;E++){var F=D.attributes.item(E);K.__cnt++;K[z.attributePrefix+F.name]=F.value;}var G=r(D);if(G!=null&&G!=""){K.__cnt++;K.__prefix=G;}if(K["#text"]!=null){K.__text=K["#text"];if(K.__text instanceof Array){K.__text=K.__text.join("\n");}if(z.stripWhitespaces){K.__text=K.__text.trim();}delete K["#text"];if(z.arrayAccessForm=="property"){delete K["#text_asArray"];}K.__text=q(K.__text,I,J+"."+I);}if(K["#cdata-section"]!=null){K.__cdata=K["#cdata-section"];delete K["#cdata-section"];if(z.arrayAccessForm=="property"){delete K["#cdata-section_asArray"];}}if(K.__cnt==0&&z.emptyNodeForm=="text"){K="";}else{if(K.__cnt==1&&K.__text!=null){K=K.__text;}else{if(K.__cnt==1&&K.__cdata!=null&&!z.keepCData){K=K.__cdata;}else{if(K.__cnt>1&&K.__text!=null&&z.skipEmptyTextNodesForObj){if((z.stripWhitespaces&&K.__text=="")||(K.__text.trim()=="")){delete K.__text;}}}}}delete K.__cnt;if(z.enableToStringFunc&&(K.__text!=null||K.__cdata!=null)){K.toString=function(){return(this.__text!=null?this.__text:"")+(this.__cdata!=null?this.__cdata:"");};}return K;}else{if(D.nodeType==h.TEXT_NODE||D.nodeType==h.CDATA_SECTION_NODE){return D.nodeValue;}}}}function o(I,F,H,C){var E="<"+((I!=null&&I.__prefix!=null)?(I.__prefix+":"):"")+F;if(H!=null){for(var G=0;G<H.length;G++){var D=H[G];var B=I[D];if(z.escapeMode){B=s(B);}E+=" "+D.substr(z.attributePrefix.length)+"=";if(z.useDoubleQuotes){E+='"'+B+'"';}else{E+="'"+B+"'";}}}if(!C){E+=">";}else{E+="/>";}return E;}function j(C,B){return"</"+(C.__prefix!=null?(C.__prefix+":"):"")+B+">";}function v(C,B){return C.indexOf(B,C.length-B.length)!==-1;}function y(C,B){if((z.arrayAccessForm=="property"&&v(B.toString(),("_asArray")))||B.toString().indexOf(z.attributePrefix)==0||B.toString().indexOf("__")==0||(C[B] instanceof Function)){return true;}else{return false;}}function m(D){var C=0;if(D instanceof Object){for(var B in D){if(y(D,B)){continue;}C++;}}return C;}function l(D,B,C){return z.jsonPropertiesFilter.length==0||C==""||w(z.jsonPropertiesFilter,D,B,C);}function c(D){var C=[];if(D instanceof Object){for(var B in D){if(B.toString().indexOf("__")==-1&&B.toString().indexOf(z.attributePrefix)==0){C.push(B);}}}return C;}function g(C){var B="";if(C.__cdata!=null){B+="<![CDATA["+C.__cdata+"]]>";}if(C.__text!=null){if(z.escapeMode){B+=s(C.__text);}else{B+=C.__text;}}return B;}function d(C){var B="";if(C instanceof Object){B+=g(C);}else{if(C!=null){if(z.escapeMode){B+=s(C);}else{B+=C;}}}return B;}function p(C,B){if(C===""){return B;}else{return C+"."+B;}}function f(D,G,F,E){var B="";if(D.length==0){B+=o(D,G,F,true);}else{for(var C=0;C<D.length;C++){B+=o(D[C],G,c(D[C]),false);B+=e(D[C],p(E,G));B+=j(D[C],G);}}return B;}function e(I,H){var B="";var F=m(I);if(F>0){for(var E in I){if(y(I,E)||(H!=""&&!l(I,E,p(H,E)))){continue;}var D=I[E];var G=c(D);if(D==null||D==undefined){B+=o(D,E,G,true);}else{if(D instanceof Object){if(D instanceof Array){B+=f(D,E,G,H);}else{if(D instanceof Date){B+=o(D,E,G,false);B+=D.toISOString();B+=j(D,E);}else{var C=m(D);if(C>0||D.__text!=null||D.__cdata!=null){B+=o(D,E,G,false);B+=e(D,p(H,E));B+=j(D,E);}else{B+=o(D,E,G,true);}}}}else{B+=o(D,E,G,false);B+=d(D);B+=j(D,E);}}}}B+=d(I);return B;}this.parseXmlString=function(D){var F=window.ActiveXObject||"ActiveXObject" in window;if(D===undefined){return null;}var E;if(window.DOMParser){var G=new window.DOMParser();var B=null;if(!F){try{B=G.parseFromString("INVALID","text/xml").getElementsByTagName("parsererror")[0].namespaceURI;}catch(C){B=null;}}try{E=G.parseFromString(D,"text/xml");if(B!=null&&E.getElementsByTagNameNS(B,"parsererror").length>0){E=null;}}catch(C){E=null;}}else{if(D.indexOf("<?")==0){D=D.substr(D.indexOf("?>")+2);}E=new ActiveXObject("Microsoft.XMLDOM");E.async="false";E.loadXML(D);}return E;};this.asArray=function(B){if(B===undefined||B==null){return[];}else{if(B instanceof Array){return B;}else{return[B];}}};this.toXmlDateTime=function(B){if(B instanceof Date){return B.toISOString();}else{if(typeof(B)==="number"){return new Date(B).toISOString();}else{return null;}}};this.asDateTime=function(B){if(typeof(B)=="string"){return a(B);}else{return B;}};this.xml2json=function(B){return A(B);};this.xml_str2json=function(B){var C=this.parseXmlString(B);if(C!=null){return this.xml2json(C);}else{return null;}};this.json2xml_str=function(B){return e(B,"");};this.json2xml=function(C){var B=this.json2xml_str(C);return this.parseXmlString(B);};this.getVersion=function(){return t;};};}));<commit_msg>remove external xml2json.min.js file<commit_after><|endoftext|>


<commit_before>'use strict';
const mocha = require('mocha');
const assert = require('assert');
const webdriver = require('selenium-webdriver');

// // copies updated files into electron binary folder
// const ncp = require('ncp');
//
// ncp.limit = 16;
// ncp('main.js', '../Desktop', function (err){
//   if(err){
//     return console.error(err);
//   }
//   console.log("Successfully copied and updated into Electron Binary folder!");
// })

// npm install chromedriver and selenium-webdriver
// copy all files in git-started except for electron modules within the node_modules
// run this command in terminal before running webdriver
// node_modules/chromedriver/bin/chromedriver
// then, run
// node webdriver.js

describe("4 Main React Components Render onto DOM", function(){
  var driver = new webdriver.Builder()
    .usingServer('http://localhost:9515')
    .withCapabilities({chromeOptions: {
      // Here is the path to your Electron binary.
      binary: 'node_modules/electron-prebuilt/dist/Electron.app/Contents/MacOS/Electron'}})
    .forBrowser('electron')
    .build();

  it('Animation element must render', function(){
    var AnimationElem = driver.findElements(webdriver.By.id('Animation'))
      .then(function(value){
        assert(AnimationElem).to.equal(!null);
      });
  })

  it('Terminal element must render', function(){
    var TerminalElem = driver.findElements(webdriver.By.id('Terminal'))
      .then(function(value){
        assert(TerminalElem).to.equal(!null);
      });
  })

  it('Dashboard element must render', function(){
    var DashElem = driver.findElements(webdriver.By.id('Dashboard'))
      .then(function(value){
        assert(DashElem).to.equal(!null);
      });
  })

  it('Lesson element must render', function(){
    var LessonElem = driver.findElements(webdriver.By.id('Lesson'))
      .then(function(value){
        assert(LessonElem).to.equal(!null);
      });
  })

// driver.wait(function() {
//     return driver.getTitle().then(function(title) {
//       return title === 'webdriver - Google Search';
//       console.log(1)
//     });
// }, 1000);
driver.quit();

})
<commit_msg>Fixed instruction error in comments for running tests<commit_after>'use strict';
const mocha = require('mocha');
const assert = require('assert');
const webdriver = require('selenium-webdriver');

// // copies updated files into electron binary folder
// const ncp = require('ncp');
//
// ncp.limit = 16;
// ncp('main.js', '../Desktop', function (err){
//   if(err){
//     return console.error(err);
//   }
//   console.log("Successfully copied and updated into Electron Binary folder!");
// })

// npm install chromedriver and selenium-webdriver
// copy all files in git-started except for electron modules within the node_modules
// run this command in terminal before running webdriver
// node_modules/chromedriver/bin/chromedriver
// then, run
// mocha webdriver.js

describe("4 Main React Components Render onto DOM", function(){
  var driver = new webdriver.Builder()
    .usingServer('http://localhost:9515')
    .withCapabilities({chromeOptions: {
      // Here is the path to your Electron binary.
      binary: 'node_modules/electron-prebuilt/dist/Electron.app/Contents/MacOS/Electron'}})
    .forBrowser('electron')
    .build();

  it('Animation element must render', function(){
    var AnimationElem = driver.findElements(webdriver.By.id('Animation'))
      .then(function(value){
        assert(AnimationElem).to.equal(!null);
      });
  })

  it('Terminal element must render', function(){
    var TerminalElem = driver.findElements(webdriver.By.id('Terminal'))
      .then(function(value){
        assert(TerminalElem).to.equal(!null);
      });
  })

  it('Dashboard element must render', function(){
    var DashElem = driver.findElements(webdriver.By.id('Dashboard'))
      .then(function(value){
        assert(DashElem).to.equal(!null);
      });
  })

  it('Lesson element must render', function(){
    var LessonElem = driver.findElements(webdriver.By.id('Lesson'))
      .then(function(value){
        assert(LessonElem).to.equal(!null);
      });
  })

// driver.wait(function() {
//     return driver.getTitle().then(function(title) {
//       return title === 'webdriver - Google Search';
//       console.log(1)
//     });
// }, 1000);
driver.quit();

})
<|endoftext|>


<commit_before>/*
 * grunt-templatizer
 * https://github.com/dlmanning/grunt-templatizer
 *
 * Copyright (c) 2013 David Manning
 * Licensed under the MIT license.
 */

var jade     = require('jade')
  , uglifyjs = require('uglify-js')
  , path     = require('path')
  , fs       = require('fs');

'use strict';

function beautify(code) {
    return uglifyjs.parse(code).print_to_string({beautify: true, indent_level: 2});
}

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('templatizer', 'Templatize!', function () {
    var templateFileList = []
      , filePathArray = []
      , folders = []
      , templates = []
      , isWindows = process.platform === 'win32'
      , pathSep = path.sep || (isWindows ? '\\' : '/')
      , pathDepth = 0
      , numberOfFiles = 0
      , placesToLook = [
          __dirname + '/../../jade/runtime.min.js',
          __dirname + '/../node_modules/jade/runtime.min.js',
          __dirname + '/jaderuntime.min.js'
        ]
      , jadeRuntime = grunt.file.read(grunt.util._.find(placesToLook, fs.existsSync))
      , outputFile = this.files[0].dest
      , output = [
          '(function () {',
          'var root = this, exports = {};',
          '',
          '// The jade runtime:',
          'var ' + jadeRuntime,
          ''
        ].join('\n');

    if(this.filesSrc.length === 0) {
      grunt.warn("No template files in src path!");
    }

    this.files.forEach(function (file) {
      var fileList = file.src;
      templateFileList = fileList.filter(function (f) {
        return (grunt.file.exists(f) && path.extname(f) === '.jade');
      });
    });

    numberOfFiles = templateFileList.length;

    // How far down does the folder depth go?
    templateFileList.forEach( function (filepath) {
      filepath.split('/').forEach( function (element, index) {
        if (pathDepth <= index) {
          filePathArray.push([]);
          pathDepth += 1;
        }
      });
    });

    // Populate a [pathDepth x numberOfFiles] array
    templateFileList.forEach( function (filepath) {
      var pathDepthFilled = 0;
      filepath.split('/').forEach( function (element, index) {
        filePathArray[index].push(element);
        pathDepthFilled += 1;
      });
      while (pathDepthFilled < pathDepth) {
        filePathArray[pathDepthFilled].push('');
        pathDepthFilled += 1;
      }
    });

    var directoriesAreAllTheSame = true;
    var uniqueDirectoryLevel = 0;

    // Figure out at what level folder names start branching
    for(var i = 1; directoriesAreAllTheSame && i < pathDepth; i++) {
      directoriesAreAllTheSame = false;
      filePathArray[i].forEach( function (element, index, arr) {
        if (index !== numberOfFiles - 1) {
          directoriesAreAllTheSame = (arr[index] === arr[index + 1]);
        }
      });
      uniqueDirectoryLevel = i;
    }

    // populate folders[] and templates[] with appropriate names
    for(var i = 0; i < numberOfFiles; i++) {
      folderObjectName = '';
      for (var j = uniqueDirectoryLevel; j + 1 < pathDepth && filePathArray[j + 1][i] !== ''; j++)
        folderObjectName += '.' + filePathArray[j][i] ;
      templates.push('exports' + folderObjectName + '.' + path.basename(filePathArray[j][i], '.jade'));
      // are we dealing with the filename now?
      if (uniqueDirectoryLevel + 1 < pathDepth && filePathArray[uniqueDirectoryLevel + 1][i] !== '')
        folders.push(folderObjectName.slice(1));
    }

    folders = grunt.util._.uniq(folders);

    folders = grunt.util._.sortBy(folders, function (folder) {
      var arr = folder.split('.');
      return arr.length;
    });

    output += '\n// create our folder objects';
    folders.forEach(function (folder) {
      output += '\nexports.' + folder + ' = {};';
    });
    output += '\n';

    templateFileList.forEach(function (file, index) {
      var compiledTemplate = beautify(jade.compile(grunt.file.read(file), {client: true, pretty: false, filename: file, compileDebug: false}).toString());
      output += [
        '',
        '// ' + file + ' compiled template',
        templates[index] + ' = ' + compiledTemplate + ';',
        ''
      ].join('\n');
    });

    output += [
        '\n',
        '// attach to window or export with commonJS',
        'if (typeof module !== "undefined") {',
        '    module.exports = exports;',
        '} else if (typeof define === "function" && define.amd) {',
        '    define(exports);',
        '} else {',
        '    root.templatizer = exports;',
        '}',
        '',
        '})();'
    ].join('\n');

    grunt.file.write(outputFile, output);

    grunt.log.writeln('File "' + outputFile + '" created.');

  });
};
<commit_msg>Bracket syntax for identifiers<commit_after>/*
 * grunt-templatizer
 * https://github.com/dlmanning/grunt-templatizer
 *
 * Copyright (c) 2013 David Manning
 * Licensed under the MIT license.
 */

var jade     = require('jade')
  , uglifyjs = require('uglify-js')
  , path     = require('path')
  , fs       = require('fs');

'use strict';

function beautify(code) {
    return uglifyjs.parse(code).print_to_string({beautify: true, indent_level: 2});
}

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('templatizer', 'Templatize!', function () {
    var templateFileList = []
      , filePathArray = []
      , folders = []
      , templates = []
      , isWindows = process.platform === 'win32'
      , pathSep = path.sep || (isWindows ? '\\' : '/')
      , pathDepth = 0
      , numberOfFiles = 0
      , placesToLook = [
          __dirname + '/../../jade/runtime.min.js',
          __dirname + '/../node_modules/jade/runtime.min.js',
          __dirname + '/jaderuntime.min.js'
        ]
      , jadeRuntime = grunt.file.read(grunt.util._.find(placesToLook, fs.existsSync))
      , outputFile = this.files[0].dest
      , output = [
          '(function () {',
          'var root = this, exports = {};',
          '',
          '// The jade runtime:',
          'var ' + jadeRuntime,
          ''
        ].join('\n');

    if(this.filesSrc.length === 0) {
      grunt.warn("No template files in src path!");
    }

    this.files.forEach(function (file) {
      var fileList = file.src;
      templateFileList = fileList.filter(function (f) {
        return (grunt.file.exists(f) && path.extname(f) === '.jade');
      });
    });

    numberOfFiles = templateFileList.length;

    // How far down does the folder depth go?
    templateFileList.forEach( function (filepath) {
      filepath.split('/').forEach( function (element, index) {
        if (pathDepth <= index) {
          filePathArray.push([]);
          pathDepth += 1;
        }
      });
    });

    // Populate a [pathDepth x numberOfFiles] array
    templateFileList.forEach( function (filepath) {
      var pathDepthFilled = 0;
      filepath.split('/').forEach( function (element, index) {
        filePathArray[index].push(element);
        pathDepthFilled += 1;
      });
      while (pathDepthFilled < pathDepth) {
        filePathArray[pathDepthFilled].push('');
        pathDepthFilled += 1;
      }
    });

    var directoriesAreAllTheSame = true;
    var uniqueDirectoryLevel = 0;

    // Figure out at what level folder names start branching
    for(var i = 1; directoriesAreAllTheSame && i < pathDepth; i++) {
      directoriesAreAllTheSame = false;
      filePathArray[i].forEach( function (element, index, arr) {
        if (index !== numberOfFiles - 1) {
          directoriesAreAllTheSame = (arr[index] === arr[index + 1]);
        }
      });
      uniqueDirectoryLevel = i;
    }

    // populate folders[] and templates[] with appropriate names
    for(var i = 0; i < numberOfFiles; i++) {
      folderObjectName = '';
      for (var j = uniqueDirectoryLevel; j + 1 < pathDepth && filePathArray[j + 1][i] !== ''; j++)
        folderObjectName += '[\'' + filePathArray[j][i] + '\']';
      templates.push('exports' + folderObjectName + '[\'' + path.basename(filePathArray[j][i], '.jade') + '\']');
      // are we dealing with the filename now?
      if (uniqueDirectoryLevel + 1 < pathDepth && filePathArray[uniqueDirectoryLevel + 1][i] !== '')
        folders.push(folderObjectName.slice(1));
    }

    folders = grunt.util._.uniq(folders);

    folders = grunt.util._.sortBy(folders, function (folder) {
      var arr = folder.split('.');
      return arr.length;
    });

    output += '\n// create our folder objects';
    folders.forEach(function (folder) {
      output += '\nexports[' + folder + ' = {};';
    });
    output += '\n';

    templateFileList.forEach(function (file, index) {
      var compiledTemplate = beautify(jade.compile(grunt.file.read(file), {client: true, pretty: false, filename: file, compileDebug: false}).toString());
      output += [
        '',
        '// ' + file + ' compiled template',
        templates[index] + ' = ' + compiledTemplate + ';',
        ''
      ].join('\n');
    });

    output += [
        '\n',
        '// attach to window or export with commonJS',
        'if (typeof module !== "undefined") {',
        '    module.exports = exports;',
        '} else if (typeof define === "function" && define.amd) {',
        '    define(exports);',
        '} else {',
        '    root.templatizer = exports;',
        '}',
        '',
        '})();'
    ].join('\n');

    grunt.file.write(outputFile, output);

    grunt.log.writeln('File "' + outputFile + '" created.');

  });
};
<|endoftext|>


<commit_before>'use strict'

const BLUE = '#2887fc'
const WHITE = '#ffffff'
const LIGHT = '#f8f8f8'
const MID = '#ececec'
const DARK = '#333333'

module.exports = {
	BLUE,
	WHITE,
	LIGHT,
	MID,
	DARK
}<commit_msg>Updated colors<commit_after>'use strict'

const BLUE = '#2d90e8'
const WHITE = '#ffffff'
const LIGHT = '#f5f5f5'
const MID = '#ececec'
const DARK = '#333333'

module.exports = {
	BLUE,
	WHITE,
	LIGHT,
	MID,
	DARK
}<|endoftext|>


<commit_before>import moment from "moment";

Meteor.methods({
  currentUserCanAccessHome(homeId) {
    const currentUserId = Meteor.userId();

    const home = Homes.findOne(homeId);

    if (!home) {
      return false;
    }

    const userGroupIds = Meteor.call("getSingleUserGroupIds", currentUserId);

    const userIsInHomeGroup = userGroupIds.includes(home.groupId);

    const userIsAdmin = Roles.userIsInRole(currentUserId, ["admin"]);

    if (userIsInHomeGroup || userIsAdmin) {
      return true;
    }

    return false;
  },
  getHomeResidentIds: function(homeId) {
    // Get all residents of specific home
    var residents = Residents.find({ homeId: homeId }).fetch();

    // Create an array containing only resident IDs
    var residentIds = _.map(residents, function(resident) {
      return resident._id;
    });

    // return the resident IDs array
    return residentIds;
  },
  getHomeCurrentResidencies(homeId) {
    /*
    Get all residencies for a home where the resident has not moved out.
     */
    // Get all residents of specific home who are not departed
    return Residencies.find({
      homeId,
      moveOut: {
        $exists: false
      }
    }).fetch();
  },
  getHomeCurrentResidentIds(homeId) {
    /*
    Get all resident IDs for a given home based on residency status.
     */
    const homeCurrentResidencies = Meteor.call(
      "getHomeCurrentResidencies",
      homeId
    );

    const homeCurrentResidentIds = _.map(homeCurrentResidencies, function(
      residency
    ) {
      return residency.residentId;
    });

    return homeCurrentResidentIds;
  },
  getHomeCurrentAndActiveResidentIds: function(homeId) {
    /*
    Get all residents of specific home who have an active residency and are not on hiatus
     */
    const onHiatus = false;

    const currentResidentIds = Meteor.call("getHomeCurrentResidentIds", homeId);

    const residents = Residents.find(
      {
        _id: {
          $in: currentResidentIds
        },
        onHiatus
      },
      { sort: { firstName: 1 } }
    ).fetch();

    // Create an array containing only resident IDs
    const residentIds = _.map(residents, function(resident) {
      return resident._id;
    });

    // return the resident IDs array
    return residentIds;
  },
  getHomeCurrentAndActiveResidentCount: function(homeId) {
    // Get all current residents for specific home (not departed or on hiatus)
    const homeCurrentResidentIds = Meteor.call(
      "getHomeCurrentAndActiveResidentIds",
      homeId
    );

    // Count the length of current resident IDs list
    const homeCurrentResidentsCount = homeCurrentResidentIds.length;

    return homeCurrentResidentsCount;
  },
  getHomeActivities: function(homeId) {
    // Get all resident of this home
    var homeResidentIds = Meteor.call("getHomeCurrentResidentIds", homeId);

    // Get an array of all activity Ids for residents of this home
    var homeResidentActivitiesQuery = Activities.find(
      { residentIds: { $elemMatch: { $in: homeResidentIds } } },
      { sort: { activityDate: -1 } }
    );

    // return all activities from activity IDs array
    var homeActivities = homeResidentActivitiesQuery.fetch();

    // Create a custom activities array using collection helpers
    // for resident names, activity type, and time ago
    var homeActivitiesArray = homeActivities.map(function(activity) {
      return {
        residents: activity.residentNames(),
        type: activity.activityType(),
        duration: activity.duration,
        activityDate: activity.activityDate
      };
    });

    return homeActivitiesArray;
  },
  getHomeActivityLevelCounts: function(homeId) {
    // // Get home residents by calling getHomeResidentIds
    var residentIds = Meteor.call("getHomeCurrentAndActiveResidentIds", homeId);

    var residentActivityLevelCounts = {
      inactive: 0,
      semiActive: 0,
      active: 0
    };

    if (residentIds) {
      // Get count of recent active days for each resident
      _.each(residentIds, function(residentId) {
        const residentRecentActiveDaysCount = Meteor.call(
          "getResidentRecentActiveDaysCount",
          residentId
        );

        // Check resident activity level
        if (residentRecentActiveDaysCount === 0) {
          // If zero activities, resident is inactive
          residentActivityLevelCounts.inactive += 1;
        } else if (residentRecentActiveDaysCount < 5) {
          // If less than five activities, resident is semi-active
          // TODO: refactor to use activity threshold variable
          residentActivityLevelCounts.semiActive += 1;
        } else if (residentRecentActiveDaysCount >= 5) {
          // If greater than or equal to five activities, resident is active
          // TODO: refactor to use activity threshold variable
          residentActivityLevelCounts.active += 1;
        }
      });
    }

    return residentActivityLevelCounts;
  },
  getHomeActivityCountTrend(homeId) {
    // Get home residents
    const residentIds = Meteor.call(
      "getHomeCurrentAndActiveResidentIds",
      homeId
    );

    // Arrays for daily activity level counts
    const inactivityCounts = [];
    const semiActivityCounts = [];
    const activityCounts = [];
    const date = [];

    // Date one week ago (six days, since today counts as one day)
    const startDate = moment().subtract(6, "days");

    // Get a date object for the end of day today
    const endDate = moment().endOf("day");

    for (
      let currentDay = moment(startDate);
      currentDay.isBefore(endDate);
      currentDay.add(1, "day")
    ) {
      // Set up placeholder activity counts object
      const dailyActivityCounts = {
        date: currentDay.toDate(),
        inactive: 0,
        semiActive: 0,
        active: 0
      };

      // Get activity level for each resident
      // Compare it against the baseline
      _.each(residentIds, function(residentId) {
        const result = Meteor.call(
          "getResidentRecentActiveDaysCount",
          residentId,
          currentDay.toDate()
        );

        if (result === 0) {
          dailyActivityCounts.inactive += 1;
        } else if (result < 5 && result > 0) {
          dailyActivityCounts.semiActive += 1;
        } else if (result >= 5) {
          dailyActivityCounts.active += 1;
        }
      });

      // Add daily activity levels to activity counts arrays
      inactivityCounts.push(dailyActivityCounts.inactive);
      semiActivityCounts.push(dailyActivityCounts.semiActive);
      activityCounts.push(dailyActivityCounts.active);
      date.push(dailyActivityCounts.date);
    }

    return { activityCounts, inactivityCounts, semiActivityCounts, date };
  },
  getHomeSelectOptionsWithGroups() {
    // Get all Groups
    var groups = Groups.find().fetch();

    // Create an array of Group IDs
    var groupIDs = _.map(groups, function(group) {
      return group._id;
    });

    // Create select options for Homes input
    // Grouping homes by group
    var homeSelectOptionsWithGroups = _.map(groupIDs, function(groupId) {
      // Find the name of this group
      var groupName = Groups.findOne(groupId).name;

      // Get all homes of this group
      var groupHomes = Homes.find({ groupId: groupId }).fetch();

      // Create a homes array with name/ID pairs for label/value
      var homesOptions = _.map(groupHomes, function(groupHome) {
        // Combine resident first name and last initial
        var homeName = groupHome.name;

        // Create option for this home, with home ID as the value
        return { label: homeName, value: groupHome._id };
      });

      // Return residents and home as option group
      return { optgroup: groupName, options: homesOptions };
    });

    return homeSelectOptionsWithGroups;
  },
  getUserVisibleHomeIds(userId) {
    const userPermissions = Permissions.find({ userId }).fetch();

    const userGroups = userPermissions.map(permission => permission.groupId);

    const userVisibleHomes = Homes.find({
      groupId: { $in: userGroups }
    }).fetch();

    return userVisibleHomes.map(home => home._id);
  }
});
<commit_msg>Check for admin role when getting user visible home IDs<commit_after>import moment from "moment";

Meteor.methods({
  currentUserCanAccessHome(homeId) {
    const currentUserId = Meteor.userId();

    const home = Homes.findOne(homeId);

    if (!home) {
      return false;
    }

    const userGroupIds = Meteor.call("getSingleUserGroupIds", currentUserId);

    const userIsInHomeGroup = userGroupIds.includes(home.groupId);

    const userIsAdmin = Roles.userIsInRole(currentUserId, ["admin"]);

    if (userIsInHomeGroup || userIsAdmin) {
      return true;
    }

    return false;
  },
  getHomeResidentIds: function(homeId) {
    // Get all residents of specific home
    var residents = Residents.find({ homeId: homeId }).fetch();

    // Create an array containing only resident IDs
    var residentIds = _.map(residents, function(resident) {
      return resident._id;
    });

    // return the resident IDs array
    return residentIds;
  },
  getHomeCurrentResidencies(homeId) {
    /*
    Get all residencies for a home where the resident has not moved out.
     */
    // Get all residents of specific home who are not departed
    return Residencies.find({
      homeId,
      moveOut: {
        $exists: false
      }
    }).fetch();
  },
  getHomeCurrentResidentIds(homeId) {
    /*
    Get all resident IDs for a given home based on residency status.
     */
    const homeCurrentResidencies = Meteor.call(
      "getHomeCurrentResidencies",
      homeId
    );

    const homeCurrentResidentIds = _.map(homeCurrentResidencies, function(
      residency
    ) {
      return residency.residentId;
    });

    return homeCurrentResidentIds;
  },
  getHomeCurrentAndActiveResidentIds: function(homeId) {
    /*
    Get all residents of specific home who have an active residency and are not on hiatus
     */
    const onHiatus = false;

    const currentResidentIds = Meteor.call("getHomeCurrentResidentIds", homeId);

    const residents = Residents.find(
      {
        _id: {
          $in: currentResidentIds
        },
        onHiatus
      },
      { sort: { firstName: 1 } }
    ).fetch();

    // Create an array containing only resident IDs
    const residentIds = _.map(residents, function(resident) {
      return resident._id;
    });

    // return the resident IDs array
    return residentIds;
  },
  getHomeCurrentAndActiveResidentCount: function(homeId) {
    // Get all current residents for specific home (not departed or on hiatus)
    const homeCurrentResidentIds = Meteor.call(
      "getHomeCurrentAndActiveResidentIds",
      homeId
    );

    // Count the length of current resident IDs list
    const homeCurrentResidentsCount = homeCurrentResidentIds.length;

    return homeCurrentResidentsCount;
  },
  getHomeActivities: function(homeId) {
    // Get all resident of this home
    var homeResidentIds = Meteor.call("getHomeCurrentResidentIds", homeId);

    // Get an array of all activity Ids for residents of this home
    var homeResidentActivitiesQuery = Activities.find(
      { residentIds: { $elemMatch: { $in: homeResidentIds } } },
      { sort: { activityDate: -1 } }
    );

    // return all activities from activity IDs array
    var homeActivities = homeResidentActivitiesQuery.fetch();

    // Create a custom activities array using collection helpers
    // for resident names, activity type, and time ago
    var homeActivitiesArray = homeActivities.map(function(activity) {
      return {
        residents: activity.residentNames(),
        type: activity.activityType(),
        duration: activity.duration,
        activityDate: activity.activityDate
      };
    });

    return homeActivitiesArray;
  },
  getHomeActivityLevelCounts: function(homeId) {
    // // Get home residents by calling getHomeResidentIds
    var residentIds = Meteor.call("getHomeCurrentAndActiveResidentIds", homeId);

    var residentActivityLevelCounts = {
      inactive: 0,
      semiActive: 0,
      active: 0
    };

    if (residentIds) {
      // Get count of recent active days for each resident
      _.each(residentIds, function(residentId) {
        const residentRecentActiveDaysCount = Meteor.call(
          "getResidentRecentActiveDaysCount",
          residentId
        );

        // Check resident activity level
        if (residentRecentActiveDaysCount === 0) {
          // If zero activities, resident is inactive
          residentActivityLevelCounts.inactive += 1;
        } else if (residentRecentActiveDaysCount < 5) {
          // If less than five activities, resident is semi-active
          // TODO: refactor to use activity threshold variable
          residentActivityLevelCounts.semiActive += 1;
        } else if (residentRecentActiveDaysCount >= 5) {
          // If greater than or equal to five activities, resident is active
          // TODO: refactor to use activity threshold variable
          residentActivityLevelCounts.active += 1;
        }
      });
    }

    return residentActivityLevelCounts;
  },
  getHomeActivityCountTrend(homeId) {
    // Get home residents
    const residentIds = Meteor.call(
      "getHomeCurrentAndActiveResidentIds",
      homeId
    );

    // Arrays for daily activity level counts
    const inactivityCounts = [];
    const semiActivityCounts = [];
    const activityCounts = [];
    const date = [];

    // Date one week ago (six days, since today counts as one day)
    const startDate = moment().subtract(6, "days");

    // Get a date object for the end of day today
    const endDate = moment().endOf("day");

    for (
      let currentDay = moment(startDate);
      currentDay.isBefore(endDate);
      currentDay.add(1, "day")
    ) {
      // Set up placeholder activity counts object
      const dailyActivityCounts = {
        date: currentDay.toDate(),
        inactive: 0,
        semiActive: 0,
        active: 0
      };

      // Get activity level for each resident
      // Compare it against the baseline
      _.each(residentIds, function(residentId) {
        const result = Meteor.call(
          "getResidentRecentActiveDaysCount",
          residentId,
          currentDay.toDate()
        );

        if (result === 0) {
          dailyActivityCounts.inactive += 1;
        } else if (result < 5 && result > 0) {
          dailyActivityCounts.semiActive += 1;
        } else if (result >= 5) {
          dailyActivityCounts.active += 1;
        }
      });

      // Add daily activity levels to activity counts arrays
      inactivityCounts.push(dailyActivityCounts.inactive);
      semiActivityCounts.push(dailyActivityCounts.semiActive);
      activityCounts.push(dailyActivityCounts.active);
      date.push(dailyActivityCounts.date);
    }

    return { activityCounts, inactivityCounts, semiActivityCounts, date };
  },
  getHomeSelectOptionsWithGroups() {
    // Get all Groups
    var groups = Groups.find().fetch();

    // Create an array of Group IDs
    var groupIDs = _.map(groups, function(group) {
      return group._id;
    });

    // Create select options for Homes input
    // Grouping homes by group
    var homeSelectOptionsWithGroups = _.map(groupIDs, function(groupId) {
      // Find the name of this group
      var groupName = Groups.findOne(groupId).name;

      // Get all homes of this group
      var groupHomes = Homes.find({ groupId: groupId }).fetch();

      // Create a homes array with name/ID pairs for label/value
      var homesOptions = _.map(groupHomes, function(groupHome) {
        // Combine resident first name and last initial
        var homeName = groupHome.name;

        // Create option for this home, with home ID as the value
        return { label: homeName, value: groupHome._id };
      });

      // Return residents and home as option group
      return { optgroup: groupName, options: homesOptions };
    });

    return homeSelectOptionsWithGroups;
  },
  getUserVisibleHomeIds(userId) {
    const selector = {};

    const userIsAdmin = Roles.userIsInRole(userId, "admin");

    // non-admin should see only homes belonging to assigned permission group(s)
    if (!userIsAdmin) {
      const userPermissions = Permissions.find({ userId }).fetch();

      const userGroups = userPermissions.map(permission => permission.groupId);

      selector.groupId = { $in: userGroups };
    }

    return Homes.find(selector).map(home => home._id);
  }
});
<|endoftext|>


<commit_before>// TODO: Move Android permissions that are currently done natively in Java to here
// Exception is bluetooth as the library doesn't appear to handle Bluetooth on Android

// TODO: Just use the library when they add Android requestMultiple permissions
// TODO: Permissions alert wrapper so don't lose the first iOS permission request

import firebase from 'app/services/Firebase';
import {request, PERMISSIONS} from 'react-native-permissions';
import { Platform, PermissionsAndroid } from 'react-native';

export default async function() {
    if (Platform.OS !== 'ios') {
        PermissionsAndroid.requestMultiple([
            PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
            PermissionsAndroid.PERMISSIONS.CAMERA,
            PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
            PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE
        ]);
    } else {
        // TODO: analytics as a user prop
        // result can have granted true/false or status unknown based on iOS version
        // see https://rnfirebase.io/docs/v3.2.x/messaging/reference/messaging
        await firebase.messaging().requestPermission();
        // await request(PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL); // seems to cause illegal callback invocations, so just do bluetooth separately
        await request(PERMISSIONS.IOS.CAMERA);
        await request(PERMISSIONS.IOS.MICROPHONE);
        await request(PERMISSIONS.IOS.PHOTO_LIBRARY);
    }
};
<commit_msg>asking for more android permissions at startup<commit_after>// TODO: Move Android permissions that are currently done natively in Java to here
// Exception is bluetooth as the library doesn't appear to handle Bluetooth on Android

// TODO: Just use the library when they add Android requestMultiple permissions
// TODO: Permissions alert wrapper so don't lose the first iOS permission request

import firebase from 'app/services/Firebase';
import {request, PERMISSIONS} from 'react-native-permissions';
import { Platform, PermissionsAndroid } from 'react-native';

export default async function() {
    if (Platform.OS !== 'ios') {
        PermissionsAndroid.requestMultiple([
            PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
            PermissionsAndroid.PERMISSIONS.CAMERA,
            PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
            PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
            PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
        ]);
    } else {
        // TODO: analytics as a user prop
        // result can have granted true/false or status unknown based on iOS version
        // see https://rnfirebase.io/docs/v3.2.x/messaging/reference/messaging
        await firebase.messaging().requestPermission();
        // await request(PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL); // seems to cause illegal callback invocations, so just do bluetooth separately
        await request(PERMISSIONS.IOS.CAMERA);
        await request(PERMISSIONS.IOS.MICROPHONE);
        await request(PERMISSIONS.IOS.PHOTO_LIBRARY);
    }
};
<|endoftext|>


<commit_before>if (typeof PRODUCTION === 'undefined') PRODUCTION = false;
(function() {

var fb = angular.module('fractalBakery', []);
fb.controller('MainCtrl', ['$scope', function($scope) {
    $scope.fractalParams = {
        eps: 1e-10,
        maxIter: 25,

        bounds: {
            reMin: -2,
            reMax: 2,
            imMin: 2,
            imMax: -1
        },

        roots: [
            { root: { Re: -1, Im: 0 }, hue: 0 },
            { root: { Re:  0, Im: 1 }, hue: 0.167 },
            { root: { Re:  0, Im: 0 }, hue: 0.333 },
            { root: { Re:  1, Im: 0 }, hue: 0.667 }
        ]
    };
    var fp = $scope.fractalParams;

    $scope.width = 800;
    $scope.height = 600;
    $scope.tolExponent = 10;
    $scope.activeRoot = fp.roots[0];

    $scope.zoomStack = [];

    $scope.updateTolerance = function() {
        fp.eps = Math.pow(10, -1 * $scope.tolExponent);
    };

    $scope.reToX = function(Re) {
        return $scope.width / (fp.bounds.reMax - fp.bounds.reMin) *
            (Re - fp.bounds.reMin);
    };

    $scope.imToY = function(Im) {
        return $scope.height / (fp.bounds.imMax - fp.bounds.imMin) *
            (Im - fp.bounds.imMin);
    };

    $scope.xToRe = function(x) {
        return (fp.bounds.reMax - fp.bounds.reMin) / $scope.width * x +
            fp.bounds.reMin;
    };

    $scope.yToIm = function(y) {
        return (fp.bounds.imMax - fp.bounds.imMin) / $scope.height * y +
            fp.bounds.imMin;
    };

    function mouseToComplex(mouse) {
        return {
            Re: $scope.xToRe(mouse.x),
            Im: $scope.yToIm(mouse.y)
        };
    }

    var fractalViewer = jQuery('#fractal fractal-viewer'), win = jQuery(window);
    function mouseCoords($event) {
        var offset = fractalViewer.offset();
        return {
            x: $event.clientX - offset.left + win.scrollLeft(),
            y: $event.clientY - offset.top + win.scrollTop()
        };
    }

    $scope.mouseMove = function($event) {
        if ($scope.dragRoot) {
            $scope.dragRoot.root = mouseToComplex(mouseCoords($event));
        } else if ($scope.zooming) {
            updateZoomPath($event);
        }
    };

    function updateZoomPath($event) {
        var endPoint = mouseCoords($event);
        $scope.zoomPath = 'M' + ($scope.zoomBegin.x + 0.5) + ',' +
            ($scope.zoomBegin.y + 0.5) + ' H' + (endPoint.x + 0.5) + ' V' +
            (endPoint.y + 0.5) + ' H' + ($scope.zoomBegin.x + 0.5) + ' z';
    }

    $scope.rootColour = function(root) {
        return 'hsl(' + Math.round(root.hue * 360) + ',80%,60%)';
    };

    function randomQuantized(N) {
        return Math.floor(Math.random() * N) / N;
    }

    $scope.addNewRoot = function() {
        var newRoot = {
            root: {
                Re: fp.bounds.reMin + randomQuantized(100) *
                    (fp.bounds.reMax - fp.bounds.reMin),
                Im: fp.bounds.imMin + randomQuantized(100) *
                    (fp.bounds.imMax - fp.bounds.imMin)
            },

            hue: randomQuantized(100)
        };
        fp.roots.push(newRoot);
        $scope.activeRoot = newRoot;
    };

    $scope.removeActiveRoot = function() {
        if (fp.roots.length === 2) {
            alert('Must have at least one root.');
            return;
        }

        var index = fp.roots.indexOf($scope.activeRoot);
        fp.roots.splice(index, 1);
        $scope.activeRoot = fp.roots[0];
    };

    $scope.beginZooming = function($event) {
        if ($scope.zooming) return;

        $scope.zooming = true;

        var point = mouseCoords($event),
            complex = mouseToComplex(point);
        $scope.zoomBegin = {
            Re: complex.Re,
            Im: complex.Im,
            x: point.x,
            y: point.y
        };
    };

    $scope.zoomIn = function($event) {
        if (!$scope.zooming) return;

        $scope.zooming = false;
        $scope.zoomPath = '';

        var point = mouseToComplex(mouseCoords($event));

        $scope.zoomStack.push(fp.bounds);

        fp.bounds = {
            reMin: Math.min($scope.zoomBegin.Re, point.Re),
            reMax: Math.max($scope.zoomBegin.Re, point.Re),
            imMin: Math.max($scope.zoomBegin.Im, point.Im),
            imMax: Math.min($scope.zoomBegin.Im, point.Im)
        };
    };

    $scope.restorePreviousZoom = function() {
        fp.bounds = $scope.zoomStack.pop();
    };
}]);

})();

<commit_msg>Set the zoom path to a sensible value after zooming.<commit_after>if (typeof PRODUCTION === 'undefined') PRODUCTION = false;
(function() {

var fb = angular.module('fractalBakery', []);
fb.controller('MainCtrl', ['$scope', function($scope) {
    $scope.fractalParams = {
        eps: 1e-10,
        maxIter: 25,

        bounds: {
            reMin: -2,
            reMax: 2,
            imMin: 2,
            imMax: -1
        },

        roots: [
            { root: { Re: -1, Im: 0 }, hue: 0 },
            { root: { Re:  0, Im: 1 }, hue: 0.167 },
            { root: { Re:  0, Im: 0 }, hue: 0.333 },
            { root: { Re:  1, Im: 0 }, hue: 0.667 }
        ]
    };
    var fp = $scope.fractalParams;

    $scope.width = 800;
    $scope.height = 600;
    $scope.tolExponent = 10;
    $scope.activeRoot = fp.roots[0];

    $scope.zoomStack = [];

    $scope.updateTolerance = function() {
        fp.eps = Math.pow(10, -1 * $scope.tolExponent);
    };

    $scope.reToX = function(Re) {
        return $scope.width / (fp.bounds.reMax - fp.bounds.reMin) *
            (Re - fp.bounds.reMin);
    };

    $scope.imToY = function(Im) {
        return $scope.height / (fp.bounds.imMax - fp.bounds.imMin) *
            (Im - fp.bounds.imMin);
    };

    $scope.xToRe = function(x) {
        return (fp.bounds.reMax - fp.bounds.reMin) / $scope.width * x +
            fp.bounds.reMin;
    };

    $scope.yToIm = function(y) {
        return (fp.bounds.imMax - fp.bounds.imMin) / $scope.height * y +
            fp.bounds.imMin;
    };

    function mouseToComplex(mouse) {
        return {
            Re: $scope.xToRe(mouse.x),
            Im: $scope.yToIm(mouse.y)
        };
    }

    var fractalViewer = jQuery('#fractal fractal-viewer'), win = jQuery(window);
    function mouseCoords($event) {
        var offset = fractalViewer.offset();
        return {
            x: $event.clientX - offset.left + win.scrollLeft(),
            y: $event.clientY - offset.top + win.scrollTop()
        };
    }

    $scope.mouseMove = function($event) {
        if ($scope.dragRoot) {
            $scope.dragRoot.root = mouseToComplex(mouseCoords($event));
        } else if ($scope.zooming) {
            updateZoomPath($event);
        }
    };

    function updateZoomPath($event) {
        var endPoint = mouseCoords($event);
        $scope.zoomPath = 'M' + ($scope.zoomBegin.x + 0.5) + ',' +
            ($scope.zoomBegin.y + 0.5) + ' H' + (endPoint.x + 0.5) + ' V' +
            (endPoint.y + 0.5) + ' H' + ($scope.zoomBegin.x + 0.5) + ' z';
    }

    $scope.rootColour = function(root) {
        return 'hsl(' + Math.round(root.hue * 360) + ',80%,60%)';
    };

    function randomQuantized(N) {
        return Math.floor(Math.random() * N) / N;
    }

    $scope.addNewRoot = function() {
        var newRoot = {
            root: {
                Re: fp.bounds.reMin + randomQuantized(100) *
                    (fp.bounds.reMax - fp.bounds.reMin),
                Im: fp.bounds.imMin + randomQuantized(100) *
                    (fp.bounds.imMax - fp.bounds.imMin)
            },

            hue: randomQuantized(100)
        };
        fp.roots.push(newRoot);
        $scope.activeRoot = newRoot;
    };

    $scope.removeActiveRoot = function() {
        if (fp.roots.length === 2) {
            alert('Must have at least one root.');
            return;
        }

        var index = fp.roots.indexOf($scope.activeRoot);
        fp.roots.splice(index, 1);
        $scope.activeRoot = fp.roots[0];
    };

    $scope.beginZooming = function($event) {
        if ($scope.zooming) return;

        $scope.zooming = true;

        var point = mouseCoords($event),
            complex = mouseToComplex(point);
        $scope.zoomBegin = {
            Re: complex.Re,
            Im: complex.Im,
            x: point.x,
            y: point.y
        };
    };

    $scope.zoomIn = function($event) {
        if (!$scope.zooming) return;

        $scope.zooming = false;
        $scope.zoomPath = 'M0,0';

        var point = mouseToComplex(mouseCoords($event));

        $scope.zoomStack.push(fp.bounds);

        fp.bounds = {
            reMin: Math.min($scope.zoomBegin.Re, point.Re),
            reMax: Math.max($scope.zoomBegin.Re, point.Re),
            imMin: Math.max($scope.zoomBegin.Im, point.Im),
            imMax: Math.min($scope.zoomBegin.Im, point.Im)
        };
    };

    $scope.restorePreviousZoom = function() {
        fp.bounds = $scope.zoomStack.pop();
    };
}]);

})();

<|endoftext|>


<commit_before>/**
 * DNA
 * (c) 2015-2016 Chialab (http://www.chialab.com) <dev@chialab.io>
 * http://dna.chialab.io
 *
 * Just another components pattern.
 * Use with Custom Elements specs.
 */
import { mix, prop, shim, DOM, MIXINS } from './src/core.js';
import { registry } from './src/lib/registry.js';

/**
 * @namespace DNA
 */
export { mix, prop, shim, DOM, MIXINS };
export { registry };

/**
 * Register a new component.
 * @method define
 * @memberof! DNA.
 * @static
 *
 * @param {String} name The id of the component.
 * @param {Function} Ctr The component constructor.
 * @param {Object} config Optional component configuration.
 */
export function define(tagName, Component, config) {
    return registry.define(tagName, Component, config);
}
/**
 * Create and append a new component instance.
 * @method render
 * @memberof! DNA.
 * @static
 *
 * @param {HTMLElement} node The parent node.
 * @param {Function} Component The component constructor.
 * @param {Object} props Optional set of properties to set to the component.
 * @return {HTMLElement} The new component instance.
 */
export function render(node, Component, props) {
    let element = new Component();
    for (let k in props) {
        element[k] = props[k];
    }
    DOM.appendChild(node, element);
    return element;
}

/**
 * Simple Custom Component with some behaviors.
 * @class BaseComponent
 * @extends HTMLElement
 * @memberof DNA.
 * @static
 *
 * @example
 * // my-component.js
 * import { BaseComponent } from '@dnajs/core';
 * export class MyComponent extends BaseComponent {
 *   static get observedAttributes() {
 *     return ['...', '...'];
 *   }
 *   get css() {
 *     return '...';
 *   }
 *   get events() {
 *     return {
 *       '...': '...'
 *     };
 *   }
 *   get template() {
 *     return '...';
 *   }
 *   get properties() {
 *     return { ... };
 *   }
 * }
 */
export class BaseComponent extends mix(
    shim(self.HTMLElement)
).with(
    MIXINS.ComponentMixin,
    MIXINS.PropertiesMixin,
    MIXINS.StyleMixin,
    MIXINS.EventsMixin,
    MIXINS.TemplateMixin
) {}
<commit_msg>docs: fix BaseComponent sample<commit_after>/**
 * DNA
 * (c) 2015-2016 Chialab (http://www.chialab.com) <dev@chialab.io>
 * http://dna.chialab.io
 *
 * Just another components pattern.
 * Use with Custom Elements specs.
 */
import { mix, prop, shim, DOM, MIXINS } from './src/core.js';
import { registry } from './src/lib/registry.js';

/**
 * @namespace DNA
 */
export { mix, prop, shim, DOM, MIXINS };
export { registry };

/**
 * Register a new component.
 * @method define
 * @memberof! DNA.
 * @static
 *
 * @param {String} name The id of the component.
 * @param {Function} Ctr The component constructor.
 * @param {Object} config Optional component configuration.
 */
export function define(tagName, Component, config) {
    return registry.define(tagName, Component, config);
}
/**
 * Create and append a new component instance.
 * @method render
 * @memberof! DNA.
 * @static
 *
 * @param {HTMLElement} node The parent node.
 * @param {Function} Component The component constructor.
 * @param {Object} props Optional set of properties to set to the component.
 * @return {HTMLElement} The new component instance.
 */
export function render(node, Component, props) {
    let element = new Component();
    for (let k in props) {
        element[k] = props[k];
    }
    DOM.appendChild(node, element);
    return element;
}

/**
 * Simple Custom Component with some behaviors.
 * @class BaseComponent
 * @extends HTMLElement
 * @memberof DNA.
 * @static
 *
 * @example
 * ```js
 * // my-component.js
 * import { BaseComponent } from '@dnajs/core';
 * export class MyComponent extends BaseComponent {
 *   static get observedAttributes() {
 *     return ['...', '...'];
 *   }
 *   get css() {
 *     return '...';
 *   }
 *   get events() {
 *     return {
 *       '...': '...'
 *     };
 *   }
 *   get template() {
 *     return '...';
 *   }
 *   get properties() {
 *     return { ... };
 *   }
 * }
 * ```
 */
export class BaseComponent extends mix(
    shim(self.HTMLElement)
).with(
    MIXINS.ComponentMixin,
    MIXINS.PropertiesMixin,
    MIXINS.StyleMixin,
    MIXINS.EventsMixin,
    MIXINS.TemplateMixin
) {}
<|endoftext|>