<commit_before>import React, { Component, PropTypes } from 'react';
import Lightbox from 'react-images';
import Image from './Image.js';

class Gallery extends Component {
    constructor (props) {
        super(props);

        this.state = {
            images: this.props.images,
            thumbnails: [],
            lightboxIsOpen: this.props.isOpen,
            currentImage: this.props.currentImage,
            containerWidth: 0
        };

        this.handleResize = this.handleResize.bind(this);
        this.closeLightbox = this.closeLightbox.bind(this);
        this.gotoNext = this.gotoNext.bind(this);
        this.gotoPrevious = this.gotoPrevious.bind(this);
        this.handleClickImage = this.handleClickImage.bind(this);
        this.openLightbox = this.openLightbox.bind(this);
        this.onImageSelected = this.onImageSelected.bind(this);
    }

    componentDidMount () {
        this.handleResize();
        window.addEventListener('resize', this.handleResize);
    }

    componentWillReceiveProps (np) {
        if(this.state.images != np.images){
            this.setState({
                images: np.images,
                thumbnails: this.renderThumbs(this._gallery.clientWidth,
                                              np.images)
            });
        }
    }

    componentDidUpdate () {
        if (!this._gallery) return;
        if (this._gallery.clientWidth
            !== this.state.containerWidth){
            this.handleResize();
        }
    }

    handleResize () {
        if (!this._gallery) return;
        this.setState({
            containerWidth: Math.floor(this._gallery.clientWidth),
            thumbnails: this.renderThumbs(this._gallery.clientWidth)
        });
    }

    openLightbox (index, event) {
        event.preventDefault();
        this.setState({
            currentImage: index,
            lightboxIsOpen: true
        });
    }

    closeLightbox () {
        this.setState({
            currentImage: 0,
            lightboxIsOpen: false
        });
    }

    gotoPrevious () {
        this.setState({
            currentImage: this.state.currentImage - 1
        });
    }

    gotoNext () {
        this.setState({
            currentImage: this.state.currentImage + 1
        });
    }

    handleClickImage () {
        if (this.state.currentImage === this.props.images.length - 1)
            return;
        this.gotoNext();
    }

    onImageSelected (index, event) {
        event.preventDefault();
        if(this.props.onImageSelected)
            this.props.onImageSelected(index, this.state.images[index]);
    }

    getOnClickThumbnailFunc () {
        if(!this.props.onClickThumbnail && this.props.enableLightbox)
            return this.openLightbox;
        if(this.props.onClickThumbnail)
            return this.props.onClickThumbnail;
        return null;
    }

    calculateCutOff (len, delta, items) {
        var cutoff = [];
        var cutsum = 0;
        for(var i in items) {
            var item = items[i];
            var fractOfLen = item.scaletwidth / len;
            cutoff[i] = Math.floor(fractOfLen * delta);
            cutsum += cutoff[i];
        }

        var stillToCutOff = delta - cutsum;
        while(stillToCutOff > 0) {
            for(i in cutoff) {
                cutoff[i]++;
                stillToCutOff--;
                if (stillToCutOff < 0) break;
            }
        }
        return cutoff;
    }

    buildImageRow (items, containerWidth) {
        var row = [];
        var len = 0;
        var imgMargin = 2 * this.props.margin;
        while(items.length > 0 && len < containerWidth) {
            var item = items.shift();
            row.push(item);
            len += (item.scaletwidth + imgMargin);
        }

        var delta = len - containerWidth;
        if(row.length > 0 && delta > 0) {
            var cutoff = this.calculateCutOff(len, delta, row);
            for(var i in row) {
                var pixelsToRemove = cutoff[i];
                item = row[i];
                item.marginLeft = -Math.abs(Math.floor(pixelsToRemove / 2));
                item.vwidth = item.scaletwidth - pixelsToRemove;
            }
        }
        else {
            for(var j in row) {
                item = row[j];
                item.marginLeft = 0;
                item.vwidth = item.scaletwidth;
            }
        }
        return row;
    }

    setThumbScale (item) {
        item.scaletwidth =
            Math.floor(this.props.rowHeight
                       * (item.thumbnailWidth / item.thumbnailHeight));
    }

    renderThumbs (containerWidth, images = this.state.images) {
        if (!images) return [];
        if (containerWidth == 0) return [];

        var items = images.slice();
        for (var t in items) {
            this.setThumbScale(items[t]);
        }

        var thumbs = [];
        var rows = [];
        while(items.length > 0) {
            rows.push(this.buildImageRow(items, containerWidth));
        }

        for(var r in rows) {
            for(var i in rows[r]) {
                var item = rows[r][i];
                thumbs.push(item);
            }
        }
        return thumbs;
    }

    render () {
        var images = this.state.thumbnails.map((item, idx) => {
            return <Image
            key={"Image-"+idx+"-"+item.src}
            item={item}
            index={idx}
            margin={this.props.margin}
            height={this.props.rowHeight}
            isSelectable={this.props.enableImageSelection}
            onClick={this.getOnClickThumbnailFunc()}
            onImageSelected={this.onImageSelected}
                />;
        });

        return (

 this._gallery = c}>
                {images}
                <Lightbox
            images={this.props.images}
            backdropClosesModal={this.props.backdropClosesModal}
            currentImage={this.state.currentImage}
            customControls={this.props.customControls}
            enableKeyboardInput={this.props.enableKeyboardInput}
            imageCountSeparator={this.props.imageCountSeparator}
            isOpen={this.state.lightboxIsOpen}
            onClickImage={this.handleClickImage}
            onClickNext={this.gotoNext}
            onClickPrev={this.gotoPrevious}
            onClose={this.closeLightbox}
            showCloseButton={this.props.showCloseButton}
            showImageCount={this.props.showImageCount}
                />

        );
    }
};

Gallery.displayName = 'Gallery';

Gallery.propTypes = {
    images: PropTypes.arrayOf(
        PropTypes.shape({
            src: PropTypes.string.isRequired,
            thumbnail: PropTypes.string.isRequired,
            srcset: PropTypes.array,
            caption: PropTypes.string,
            thumbnailWidth: PropTypes.number.isRequired,
            thumbnailHeight: PropTypes.number.isRequired,
            isSelected: PropTypes.bool
        })
    ).isRequired,
    enableImageSelection: PropTypes.bool,
    onImageSelected: PropTypes.func,
    rowHeight: PropTypes.number,
    margin: PropTypes.number,
    onClickThumbnail: PropTypes.func,
    enableLightbox: PropTypes.bool,
    backdropClosesModal: PropTypes.bool,
    currentImage: PropTypes.number,
    preloadNextImage: PropTypes.bool,
    customControls: PropTypes.arrayOf(PropTypes.node),
    enableKeyboardInput: PropTypes.bool,
    imageCountSeparator: PropTypes.string,
    isOpen: PropTypes.bool,
    onClickImage: PropTypes.func,
    onClickNext: PropTypes.func,
    onClickPrev: PropTypes.func,
    onClose: PropTypes.func,
    showCloseButton: PropTypes.bool,
    showImageCount: PropTypes.bool
};

Gallery.defaultProps = {
    enableImageSelection: true,
    rowHeight: 180,
    margin: 2,
    enableLightbox: true,
    backdropClosesModal: false,
    currentImage: 0,
    preloadNextImage: true,
    enableKeyboardInput: true,
    imageCountSeparator: ' of ',
    isOpen: false,
    showCloseButton: true,
    showImageCount: true
};

module.exports = Gallery;
<commit_msg>+prop for image tagging<commit_after>import React, { Component, PropTypes } from 'react';
import Lightbox from 'react-images';
import Image from './Image.js';

class Gallery extends Component {
    constructor (props) {
        super(props);

        this.state = {
            images: this.props.images,
            thumbnails: [],
            lightboxIsOpen: this.props.isOpen,
            currentImage: this.props.currentImage,
            containerWidth: 0
        };

        this.handleResize = this.handleResize.bind(this);
        this.closeLightbox = this.closeLightbox.bind(this);
        this.gotoNext = this.gotoNext.bind(this);
        this.gotoPrevious = this.gotoPrevious.bind(this);
        this.handleClickImage = this.handleClickImage.bind(this);
        this.openLightbox = this.openLightbox.bind(this);
        this.onImageSelected = this.onImageSelected.bind(this);
    }

    componentDidMount () {
        this.handleResize();
        window.addEventListener('resize', this.handleResize);
    }

    componentWillReceiveProps (np) {
        if(this.state.images != np.images){
            this.setState({
                images: np.images,
                thumbnails: this.renderThumbs(this._gallery.clientWidth,
                                              np.images)
            });
        }
    }

    componentDidUpdate () {
        if (!this._gallery) return;
        if (this._gallery.clientWidth
            !== this.state.containerWidth){
            this.handleResize();
        }
    }

    handleResize () {
        if (!this._gallery) return;
        this.setState({
            containerWidth: Math.floor(this._gallery.clientWidth),
            thumbnails: this.renderThumbs(this._gallery.clientWidth)
        });
    }

    openLightbox (index, event) {
        event.preventDefault();
        this.setState({
            currentImage: index,
            lightboxIsOpen: true
        });
    }

    closeLightbox () {
        this.setState({
            currentImage: 0,
            lightboxIsOpen: false
        });
    }

    gotoPrevious () {
        this.setState({
            currentImage: this.state.currentImage - 1
        });
    }

    gotoNext () {
        this.setState({
            currentImage: this.state.currentImage + 1
        });
    }

    handleClickImage () {
        if (this.state.currentImage === this.props.images.length - 1)
            return;
        this.gotoNext();
    }

    onImageSelected (index, event) {
        event.preventDefault();
        if(this.props.onImageSelected)
            this.props.onImageSelected(index, this.state.images[index]);
    }

    getOnClickThumbnailFunc () {
        if(!this.props.onClickThumbnail && this.props.enableLightbox)
            return this.openLightbox;
        if(this.props.onClickThumbnail)
            return this.props.onClickThumbnail;
        return null;
    }

    calculateCutOff (len, delta, items) {
        var cutoff = [];
        var cutsum = 0;
        for(var i in items) {
            var item = items[i];
            var fractOfLen = item.scaletwidth / len;
            cutoff[i] = Math.floor(fractOfLen * delta);
            cutsum += cutoff[i];
        }

        var stillToCutOff = delta - cutsum;
        while(stillToCutOff > 0) {
            for(i in cutoff) {
                cutoff[i]++;
                stillToCutOff--;
                if (stillToCutOff < 0) break;
            }
        }
        return cutoff;
    }

    buildImageRow (items, containerWidth) {
        var row = [];
        var len = 0;
        var imgMargin = 2 * this.props.margin;
        while(items.length > 0 && len < containerWidth) {
            var item = items.shift();
            row.push(item);
            len += (item.scaletwidth + imgMargin);
        }

        var delta = len - containerWidth;
        if(row.length > 0 && delta > 0) {
            var cutoff = this.calculateCutOff(len, delta, row);
            for(var i in row) {
                var pixelsToRemove = cutoff[i];
                item = row[i];
                item.marginLeft = -Math.abs(Math.floor(pixelsToRemove / 2));
                item.vwidth = item.scaletwidth - pixelsToRemove;
            }
        }
        else {
            for(var j in row) {
                item = row[j];
                item.marginLeft = 0;
                item.vwidth = item.scaletwidth;
            }
        }
        return row;
    }

    setThumbScale (item) {
        item.scaletwidth =
            Math.floor(this.props.rowHeight
                       * (item.thumbnailWidth / item.thumbnailHeight));
    }

    renderThumbs (containerWidth, images = this.state.images) {
        if (!images) return [];
        if (containerWidth == 0) return [];

        var items = images.slice();
        for (var t in items) {
            this.setThumbScale(items[t]);
        }

        var thumbs = [];
        var rows = [];
        while(items.length > 0) {
            rows.push(this.buildImageRow(items, containerWidth));
        }

        for(var r in rows) {
            for(var i in rows[r]) {
                var item = rows[r][i];
                thumbs.push(item);
            }
        }
        return thumbs;
    }

    render () {
        var images = this.state.thumbnails.map((item, idx) => {
            return <Image
            key={"Image-"+idx+"-"+item.src}
            item={item}
            index={idx}
            margin={this.props.margin}
            height={this.props.rowHeight}
            isSelectable={this.props.enableImageSelection}
            onClick={this.getOnClickThumbnailFunc()}
            onImageSelected={this.onImageSelected}
                />;
        });

        return (

 this._gallery = c}>
                {images}
                <Lightbox
            images={this.props.images}
            backdropClosesModal={this.props.backdropClosesModal}
            currentImage={this.state.currentImage}
            customControls={this.props.customControls}
            enableKeyboardInput={this.props.enableKeyboardInput}
            imageCountSeparator={this.props.imageCountSeparator}
            isOpen={this.state.lightboxIsOpen}
            onClickImage={this.handleClickImage}
            onClickNext={this.gotoNext}
            onClickPrev={this.gotoPrevious}
            onClose={this.closeLightbox}
            showCloseButton={this.props.showCloseButton}
            showImageCount={this.props.showImageCount}
                />

        );
    }
};

Gallery.displayName = 'Gallery';

Gallery.propTypes = {
    images: PropTypes.arrayOf(
        PropTypes.shape({
            src: PropTypes.string.isRequired,
            thumbnail: PropTypes.string.isRequired,
            srcset: PropTypes.array,
            caption: PropTypes.string,
            tags: PropTypes.arrayOf(
                PropTypes.shape({
                    value: PropTypes.string.isRequired,
                    title: PropTypes.string.isRequired
                })
            ),
            thumbnailWidth: PropTypes.number.isRequired,
            thumbnailHeight: PropTypes.number.isRequired,
            isSelected: PropTypes.bool
        })
    ).isRequired,
    enableImageSelection: PropTypes.bool,
    onImageSelected: PropTypes.func,
    rowHeight: PropTypes.number,
    margin: PropTypes.number,
    onClickThumbnail: PropTypes.func,
    enableLightbox: PropTypes.bool,
    backdropClosesModal: PropTypes.bool,
    currentImage: PropTypes.number,
    preloadNextImage: PropTypes.bool,
    customControls: PropTypes.arrayOf(PropTypes.node),
    enableKeyboardInput: PropTypes.bool,
    imageCountSeparator: PropTypes.string,
    isOpen: PropTypes.bool,
    onClickImage: PropTypes.func,
    onClickNext: PropTypes.func,
    onClickPrev: PropTypes.func,
    onClose: PropTypes.func,
    showCloseButton: PropTypes.bool,
    showImageCount: PropTypes.bool
};

Gallery.defaultProps = {
    enableImageSelection: true,
    rowHeight: 180,
    margin: 2,
    enableLightbox: true,
    backdropClosesModal: false,
    currentImage: 0,
    preloadNextImage: true,
    enableKeyboardInput: true,
    imageCountSeparator: ' of ',
    isOpen: false,
    showCloseButton: true,
    showImageCount: true
};

module.exports = Gallery;
<|endoftext|>


<commit_before>/* jshint -W117 */
/* a simple MUC connection plugin
 * can only handle a single MUC room
 */
Strophe.addConnectionPlugin('emuc', {
    connection: null,
    roomjid: null,
    myroomjid: null,
    members: {},
    joined: false,
    isOwner: false,
    init: function (conn) {
        this.connection = conn;
    },
    doJoin: function (jid, password) {
        this.myroomjid = jid;
        if (!this.roomjid) {
            this.roomjid = Strophe.getBareJidFromJid(jid);
            // add handlers (just once)
            this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, this.roomjid, {matchBare: true});
            this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null, this.roomjid, {matchBare: true});
            this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null, this.roomjid, {matchBare: true});
            this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null, this.roomjid, {matchBare: true});
        }

        var join = $pres({to: this.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'});
        if (password !== undefined) {
            join.c('password').t(password);
        }
        this.connection.send(join);
    },
    onPresence: function (pres) {
        var from = pres.getAttribute('from');
        var type = pres.getAttribute('type');
        if (type != null) {
            return true;
        }
        if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length) {
            // http://xmpp.org/extensions/xep-0045.html#createroom-instant
            this.isOwner = true;
            var create = $iq({type: 'set', to: this.roomjid})
                    .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'})
                    .c('x', {xmlns: 'jabber:x:data', type: 'submit'});
            this.connection.send(create); // fire away
        }

        var member = {};
        member.show = $(pres).find('>show').text();
        member.status = $(pres).find('>status').text();
        var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
        member.affiliation = tmp.attr('affiliation');
        member.role = tmp.attr('role');
        if (from == this.myroomjid) {
            if (member.affiliation == 'owner') this.isOwner = true;
            if (!this.joined) {
                this.joined = true;
                $(document).trigger('joined.muc', [from, member]);
            }
        } else if (this.members[from] === undefined) {
            // new participant
            this.members[from] = member;
            $(document).trigger('entered.muc', [from, member]);
        } else {
            console.log('presence change from', from);
        }
        return true;
    },
    onPresenceUnavailable: function (pres) {
        var from = pres.getAttribute('from');
        delete this.members[from];
        $(document).trigger('left.muc', [from]);
        return true;
    },
    onPresenceError: function (pres) {
        var from = pres.getAttribute('from');
        if ($(pres).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
            $(document).trigger('passwordrequired.muc', [from]);

            // FIXME: remove once moved to passwordrequired which should reuse dojoin
            var ob = this;
            window.setTimeout(function () {
                var given = window.prompt('Password required');
                if (given != null) {
                    // FIXME: reuse doJoin?
                    ob.connection.send($pres({to: ob.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'}).c('password').t(given));
                } else {
                    // user aborted
                }
            }, 50);
        } else {
            console.warn('onPresError ', pres);
        }
        return true;
    },
    sendMessage: function (body, nickname) {
        var msg = $msg({to: this.roomjid, type: 'groupchat'});
        msg.c('body', body).up();
        if (nickname) {
            msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
        }
        this.connection.send(msg);
    },
    onMessage: function (msg) {
        var txt = $(msg).find('>body').text();
        // TODO: <subject/>
        // FIXME: this is a hack. but jingle on muc makes nickchanges hard
        var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(msg.getAttribute('from'));
        if (txt) {
            console.log('chat', nick, txt);

            updateChatConversation(nick, txt);
        }
        return true;
    },
    lockRoom: function (key) {
        //http://xmpp.org/extensions/xep-0045.html#roomconfig
        var ob = this;
        this.connection.sendIQ($iq({to: this.roomjid, type: 'get'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'}),
            function (res) {
                if ($(res).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length) {
                    var formsubmit = $iq({to: ob.roomjid, type: 'set'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
                    formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
                    formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
                    formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
                    // FIXME: is muc#roomconfig_passwordprotectedroom required?
                    this.connection.sendIQ(formsubmit,
                        function (res) {
                            console.log('set room password');
                        },
                        function (err) {
                            console.warn('setting password failed', err);
                        }
                    );
                } else {
                    console.warn('room passwords not supported');
                }
            },
            function (err) {
                console.warn('setting password failed', err);
            }
        );
    }
});

<commit_msg>callback for presence changes, pass stanza as well<commit_after>/* jshint -W117 */
/* a simple MUC connection plugin
 * can only handle a single MUC room
 */
Strophe.addConnectionPlugin('emuc', {
    connection: null,
    roomjid: null,
    myroomjid: null,
    members: {},
    joined: false,
    isOwner: false,
    init: function (conn) {
        this.connection = conn;
    },
    doJoin: function (jid, password) {
        this.myroomjid = jid;
        if (!this.roomjid) {
            this.roomjid = Strophe.getBareJidFromJid(jid);
            // add handlers (just once)
            this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, this.roomjid, {matchBare: true});
            this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null, this.roomjid, {matchBare: true});
            this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null, this.roomjid, {matchBare: true});
            this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null, this.roomjid, {matchBare: true});
        }

        var join = $pres({to: this.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'});
        if (password !== undefined) {
            join.c('password').t(password);
        }
        this.connection.send(join);
    },
    onPresence: function (pres) {
        var from = pres.getAttribute('from');
        var type = pres.getAttribute('type');
        if (type != null) {
            return true;
        }
        if ($(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length) {
            // http://xmpp.org/extensions/xep-0045.html#createroom-instant
            this.isOwner = true;
            var create = $iq({type: 'set', to: this.roomjid})
                    .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'})
                    .c('x', {xmlns: 'jabber:x:data', type: 'submit'});
            this.connection.send(create); // fire away
        }

        var member = {};
        member.show = $(pres).find('>show').text();
        member.status = $(pres).find('>status').text();
        var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
        member.affiliation = tmp.attr('affiliation');
        member.role = tmp.attr('role');
        if (from == this.myroomjid) {
            if (member.affiliation == 'owner') this.isOwner = true;
            if (!this.joined) {
                this.joined = true;
                $(document).trigger('joined.muc', [from, member]);
            }
        } else if (this.members[from] === undefined) {
            // new participant
            this.members[from] = member;
            $(document).trigger('entered.muc', [from, member, pres]);
        } else {
            console.log('presence change from', from);
            $(document).trigger('presence.muc', [from, member, pres]);
        }
        return true;
    },
    onPresenceUnavailable: function (pres) {
        var from = pres.getAttribute('from');
        delete this.members[from];
        $(document).trigger('left.muc', [from]);
        return true;
    },
    onPresenceError: function (pres) {
        var from = pres.getAttribute('from');
        if ($(pres).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length) {
            $(document).trigger('passwordrequired.muc', [from]);

            // FIXME: remove once moved to passwordrequired which should reuse dojoin
            var ob = this;
            window.setTimeout(function () {
                var given = window.prompt('Password required');
                if (given != null) {
                    // FIXME: reuse doJoin?
                    ob.connection.send($pres({to: ob.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'}).c('password').t(given));
                } else {
                    // user aborted
                }
            }, 50);
        } else {
            console.warn('onPresError ', pres);
        }
        return true;
    },
    sendMessage: function (body, nickname) {
        var msg = $msg({to: this.roomjid, type: 'groupchat'});
        msg.c('body', body).up();
        if (nickname) {
            msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
        }
        this.connection.send(msg);
    },
    onMessage: function (msg) {
        var txt = $(msg).find('>body').text();
        // TODO: <subject/>
        // FIXME: this is a hack. but jingle on muc makes nickchanges hard
        var nick = $(msg).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text() || Strophe.getResourceFromJid(msg.getAttribute('from'));
        if (txt) {
            console.log('chat', nick, txt);

            updateChatConversation(nick, txt);
        }
        return true;
    },
    lockRoom: function (key) {
        //http://xmpp.org/extensions/xep-0045.html#roomconfig
        var ob = this;
        this.connection.sendIQ($iq({to: this.roomjid, type: 'get'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'}),
            function (res) {
                if ($(res).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length) {
                    var formsubmit = $iq({to: ob.roomjid, type: 'set'}).c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'});
                    formsubmit.c('x', {xmlns: 'jabber:x:data', type: 'submit'});
                    formsubmit.c('field', {'var': 'FORM_TYPE'}).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
                    formsubmit.c('field', {'var': 'muc#roomconfig_roomsecret'}).c('value').t(key).up().up();
                    // FIXME: is muc#roomconfig_passwordprotectedroom required?
                    this.connection.sendIQ(formsubmit,
                        function (res) {
                            console.log('set room password');
                        },
                        function (err) {
                            console.warn('setting password failed', err);
                        }
                    );
                } else {
                    console.warn('room passwords not supported');
                }
            },
            function (err) {
                console.warn('setting password failed', err);
            }
        );
    }
});

<|endoftext|>


<commit_before>var load = function (name, definition){
  if (typeof(module) !== 'undefined' && module.exports) { // Node.js
    module.exports = definition(require('escape-html'));
  } else {
    var escapeHtml = function (str) {
      var div = document.createElement('div');
      div.appendChild(document.createTextNode(str));
      return div.innerHTML;
    };

    if (typeof define === 'function'){ // AMD
      define(function() { return definition(escapeHtml) });
    } else { // Browser

      var notjs = definition(escapeHtml);
      var global = this;
      var old = global[name];
      notjs.noConflict = function () {
        global[name] = old;
        return notjs;
      };
      global[name] = notjs;
    }
  }
};

load('notjs', function(escape) {
function stringBuilder() {
  this.buffer = [];
};

stringBuilder.prototype._toAttr = function(obj) {
  var ret = [];
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      if(key === 'data' && typeof(obj[key]) === 'object') {
        for (var subkey in obj[key]) {
          if (obj[key].hasOwnProperty(subkey)) {
            ret.push('data-'+subkey+'="'+obj.data[subkey]+'"');
          }
        }
      } else {
        ret.push(''+key+'="'+obj[key]+'"');
      }
    }
  }

  var str = new String(ret.join(' ')); //Wrap with 'String' so it has methods/properties
  str.original = obj;
  return str;
}

stringBuilder.prototype.reset = function() {
  this.buffer = [];
}

stringBuilder.prototype.complete = function() {
  return this.buffer.join('');
};

stringBuilder.prototype.pushStartToken = function(name) {
  this.buffer.push('<');
  this.buffer.push(name);
  this.buffer.push('>');
};

stringBuilder.prototype.rewriteStartTokenAttributes = function(obj) {
  var attr = this._toAttr(obj);
  var last = this.buffer.pop();
  if (last == '>') {
    this.buffer.push(' ');
    this.buffer.push(attr);
    this.buffer.push('>');
  } else {
    throw new Error('Attempted to rewrite tag with attributes when there was no tag');
  }
};

stringBuilder.prototype.pushEndToken = function(name) {
  this.buffer.push('</');
  this.buffer.push(name);
  this.buffer.push('>');
};

stringBuilder.prototype.pushSingleToken = function(name) {
  this.buffer.push('<');
  this.buffer.push(name);
  this.buffer.push(' />');
};

var merge = function(dest, source) {
  for (prop in source) {
    if (source.hasOwnProperty(prop)) {
      if (prop != 'data') {
        obj[prop] = source[prop];
      } else { //merge data, too, ignore all else
        obj[prop] = merge(obj[prop] || {}, source[prop]);
      }
    }
  }
}

stringBuilder.prototype.rewriteSingleTokenAttributes = function(obj) {
  var third = this.buffer[this.buffer.length-3];
  if (third === ' ') { //Attrs already exist on tag, get previous and overwrite any duplicates
    var newobj = merge(third.original, obj);
    var attr = this._toAttr(newobj);
    this.buffer[this.buffer.length-3] = attr;
  } else {
    var attr = this._toAttr(obj);
    attr.original = obj;
    var last = this.buffer.pop();
    if (last == ' />') {
      this.buffer.push(' ');
      this.buffer.push(attr);
      this.buffer.push(' />');
    } else {
      throw new Error('Attempted to rewrite tag with attributes when there was no tag');
    }
  }
};

stringBuilder.prototype.pushRaw = function(str, noEscape) {
  if (noEscape) {
    this.buffer.push(str);
  } else {
    this.buffer.push(escape(str));
  }
};

stringBuilder.prototype.addClass = function(name) {
  //if the 3rd elem form the top is ' ', then attr already exists and we should add classes to that
  var third = this.buffer[this.buffer.length-3];
  var last = this.buffer.pop();
  if (third === ' ') {
    var oldattr = this.buffer.pop();

    var obj = oldattr.original;

    obj['class'] = ((obj['class'] ? obj['class']+' ' : '')+name);
    var attr = this._toAttr(obj);

    this.buffer.push(attr);
    this.buffer.push(last);
  } else { //otherwise, insert an attr with the class
    this.buffer.push(last);
    if (last === '>') { //start
      this.rewriteStartTokenAttributes({class: name});
    } else if (last === ' />') { //single
      this.rewriteSingleTokenAttributes({class: name});
    } else {
      throw new Error('Attempted to add a class to a tag when there was no closed tag');
    }
  }
}

stringBuilder.prototype.dropLastToken = function() {
  this.buffer.pop();
  this.buffer.pop();
  this.buffer.pop();
}

var indicator = '$';
var defaultScopeName = indicator+'scope';

function jshtmlProxy(builder) {
  var ret = function(scopeName, scope) {
    if (!scope) {
      scope = scopeName;
      scopeName = defaultScopeName;
    }
    var proxy = Proxy.createFunction({ //TODO: Alternate implementation for the newer harmony proxy API supported by Firefox
      getPropertyDescriptor: function(key) {return {value: true}}, //We are all the properties!
      get: function(rec, key) {
        return (function() {
            if (key === scopeName) {
              return scope;
            }
            if (key === indicator) {
              return proxy;
            }
            if (key.slice(-1) === indicator) {
              builder.pushSingleToken(key.slice(0,key.length-1));
              var classproxy = Proxy.createFunction({
                get: function(rec, key) {
                  builder.addClass(key);
                  return classproxy;
                }
              },
              function(obj) {
                builder.rewriteSingleTokenAttributes(obj);
                return classproxy;
              });
              return classproxy;
            } else if (key.slice(0,1) === indicator) {
              builder.pushEndToken(key.slice(1));
              return;
            }  else {
              builder.pushStartToken(key);
              var classproxy = Proxy.createFunction({
                get: function(rec, key) {
                  builder.addClass(key);
                  return classproxy;
                }
              },
              function(obj) {
                builder.rewriteStartTokenAttributes(obj);
                return classproxy;
              });
              return classproxy;
            }
          })();
      }
    }, function(str, noEscape) {
      if (typeof(str) === 'function') {
        builder.pushRaw((/\/\*!?(?:\@preserve)?[ \t]*(?:\r\n|\n)([\s\S]*?)(?:\r\n|\n)\s*\*\//).exec(str.toString())[1], noEscape);
      } else {
        builder.pushRaw(str, noEscape);
      }
    });

    return proxy;
  };

  ret.collect = function() {
    return builder.complete();
  };

  ret.restart = function() {
    builder.reset();
  }

  return ret;
}

var funcStringCache = {}; //Cache stringified functions for implied usage

function prepareFunc(func, builder) { //Prepare an implied function for repeated use
  funcStringCache[func] = funcStringCache[func] || ('(function() { with (proxy(scope)) { return ('+func.toString()+')('+defaultScopeName+'); } })()');

  return function(scope) {
    var scope = scope || {};
    var builder = builder || stringBuilder;
    var proxy = jshtmlProxy(new builder());
    var ret = eval(funcStringCache[func]);
    if (ret && typeof(ret.then) === 'function') { //looks like a promise. Let's use it why don't we?
      ret.proxy = proxy;
      return ret;
    }
    return proxy.collect();
  }
}

function renderFunction(func, scope, builder) {
  return prepareFunc(func, builder)(scope);
}

function renderPath(path, options, cb) {
  if (typeof(options) === 'function') {
    cb = options;
    options = {};
  }

  var func = require(path); //Yes.
  var ret;
  var err;
  if (options.explicit) { //Assume implied use rather than explicit 'with' by default
    try {
      ret = func(options.scope);
    } catch (e) {
      err = e;
    }
  } else {
    try {
      ret = renderFunction(func, options.scope, options.builder);
    } catch (e) {
      err = e;
    }
  }
  if (ret && typeof(ret.then) === 'function') { //looks like a promise. Let's use it why don't we?
    return ret.then(function(data) {
      if (data) {
        cb(err, data);
      } else {
        cb(err, ret.proxy.collect());
      }
    });
  }
  process.nextTick(function() {
    cb(err, ret);
  });
}

var exposed = {
  string: stringBuilder, //TODO: More output format builders, eg DOM Objects, Buffer
  create: function(builder) {
    return jshtmlProxy((builder && new builder()) || new stringBuilder());
  },
  prepareFunc: prepareFunc,
  renderFunc: renderFunction,
  renderPath: renderPath,
  __express: renderPath
};

return exposed;

});<commit_msg>add configurable to trap for node 0.11<commit_after>var load = function (name, definition){
  if (typeof(module) !== 'undefined' && module.exports) { // Node.js
    module.exports = definition(require('escape-html'));
  } else {
    var escapeHtml = function (str) {
      var div = document.createElement('div');
      div.appendChild(document.createTextNode(str));
      return div.innerHTML;
    };

    if (typeof define === 'function'){ // AMD
      define(function() { return definition(escapeHtml) });
    } else { // Browser

      var notjs = definition(escapeHtml);
      var global = this;
      var old = global[name];
      notjs.noConflict = function () {
        global[name] = old;
        return notjs;
      };
      global[name] = notjs;
    }
  }
};

load('notjs', function(escape) {
function stringBuilder() {
  this.buffer = [];
};

stringBuilder.prototype._toAttr = function(obj) {
  var ret = [];
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      if(key === 'data' && typeof(obj[key]) === 'object') {
        for (var subkey in obj[key]) {
          if (obj[key].hasOwnProperty(subkey)) {
            ret.push('data-'+subkey+'="'+obj.data[subkey]+'"');
          }
        }
      } else {
        ret.push(''+key+'="'+obj[key]+'"');
      }
    }
  }

  var str = new String(ret.join(' ')); //Wrap with 'String' so it has methods/properties
  str.original = obj;
  return str;
}

stringBuilder.prototype.reset = function() {
  this.buffer = [];
}

stringBuilder.prototype.complete = function() {
  return this.buffer.join('');
};

stringBuilder.prototype.pushStartToken = function(name) {
  this.buffer.push('<');
  this.buffer.push(name);
  this.buffer.push('>');
};

stringBuilder.prototype.rewriteStartTokenAttributes = function(obj) {
  var attr = this._toAttr(obj);
  var last = this.buffer.pop();
  if (last == '>') {
    this.buffer.push(' ');
    this.buffer.push(attr);
    this.buffer.push('>');
  } else {
    throw new Error('Attempted to rewrite tag with attributes when there was no tag');
  }
};

stringBuilder.prototype.pushEndToken = function(name) {
  this.buffer.push('</');
  this.buffer.push(name);
  this.buffer.push('>');
};

stringBuilder.prototype.pushSingleToken = function(name) {
  this.buffer.push('<');
  this.buffer.push(name);
  this.buffer.push(' />');
};

var merge = function(dest, source) {
  for (prop in source) {
    if (source.hasOwnProperty(prop)) {
      if (prop != 'data') {
        obj[prop] = source[prop];
      } else { //merge data, too, ignore all else
        obj[prop] = merge(obj[prop] || {}, source[prop]);
      }
    }
  }
}

stringBuilder.prototype.rewriteSingleTokenAttributes = function(obj) {
  var third = this.buffer[this.buffer.length-3];
  if (third === ' ') { //Attrs already exist on tag, get previous and overwrite any duplicates
    var newobj = merge(third.original, obj);
    var attr = this._toAttr(newobj);
    this.buffer[this.buffer.length-3] = attr;
  } else {
    var attr = this._toAttr(obj);
    attr.original = obj;
    var last = this.buffer.pop();
    if (last == ' />') {
      this.buffer.push(' ');
      this.buffer.push(attr);
      this.buffer.push(' />');
    } else {
      throw new Error('Attempted to rewrite tag with attributes when there was no tag');
    }
  }
};

stringBuilder.prototype.pushRaw = function(str, noEscape) {
  if (noEscape) {
    this.buffer.push(str);
  } else {
    this.buffer.push(escape(str));
  }
};

stringBuilder.prototype.addClass = function(name) {
  //if the 3rd elem form the top is ' ', then attr already exists and we should add classes to that
  var third = this.buffer[this.buffer.length-3];
  var last = this.buffer.pop();
  if (third === ' ') {
    var oldattr = this.buffer.pop();

    var obj = oldattr.original;

    obj['class'] = ((obj['class'] ? obj['class']+' ' : '')+name);
    var attr = this._toAttr(obj);

    this.buffer.push(attr);
    this.buffer.push(last);
  } else { //otherwise, insert an attr with the class
    this.buffer.push(last);
    if (last === '>') { //start
      this.rewriteStartTokenAttributes({class: name});
    } else if (last === ' />') { //single
      this.rewriteSingleTokenAttributes({class: name});
    } else {
      throw new Error('Attempted to add a class to a tag when there was no closed tag');
    }
  }
}

stringBuilder.prototype.dropLastToken = function() {
  this.buffer.pop();
  this.buffer.pop();
  this.buffer.pop();
}

var indicator = '$';
var defaultScopeName = indicator+'scope';

function jshtmlProxy(builder) {
  var ret = function(scopeName, scope) {
    if (!scope) {
      scope = scopeName;
      scopeName = defaultScopeName;
    }
    var proxy = Proxy.createFunction({ //TODO: Alternate implementation for the newer harmony proxy API supported by Firefox
      getPropertyDescriptor: function(key) {return {value: true, configurable: true}}, //We are all the properties!
      get: function(rec, key) {
        return (function() {
            if (key === scopeName) {
              return scope;
            }
            if (key === indicator) {
              return proxy;
            }
            if (key.slice(-1) === indicator) {
              builder.pushSingleToken(key.slice(0,key.length-1));
              var classproxy = Proxy.createFunction({
                get: function(rec, key) {
                  builder.addClass(key);
                  return classproxy;
                }
              },
              function(obj) {
                builder.rewriteSingleTokenAttributes(obj);
                return classproxy;
              });
              return classproxy;
            } else if (key.slice(0,1) === indicator) {
              builder.pushEndToken(key.slice(1));
              return;
            }  else {
              builder.pushStartToken(key);
              var classproxy = Proxy.createFunction({
                get: function(rec, key) {
                  builder.addClass(key);
                  return classproxy;
                }
              },
              function(obj) {
                builder.rewriteStartTokenAttributes(obj);
                return classproxy;
              });
              return classproxy;
            }
          })();
      }
    }, function(str, noEscape) {
      if (typeof(str) === 'function') {
        builder.pushRaw((/\/\*!?(?:\@preserve)?[ \t]*(?:\r\n|\n)([\s\S]*?)(?:\r\n|\n)\s*\*\//).exec(str.toString())[1], noEscape);
      } else {
        builder.pushRaw(str, noEscape);
      }
    });

    return proxy;
  };

  ret.collect = function() {
    return builder.complete();
  };

  ret.restart = function() {
    builder.reset();
  }

  return ret;
}

var funcStringCache = {}; //Cache stringified functions for implied usage

function prepareFunc(func, builder) { //Prepare an implied function for repeated use
  funcStringCache[func] = funcStringCache[func] || ('(function() { with (proxy(scope)) { return ('+func.toString()+')('+defaultScopeName+'); } })()');

  return function(scope) {
    var scope = scope || {};
    var builder = builder || stringBuilder;
    var proxy = jshtmlProxy(new builder());
    var ret = eval(funcStringCache[func]);
    if (ret && typeof(ret.then) === 'function') { //looks like a promise. Let's use it why don't we?
      ret.proxy = proxy;
      return ret;
    }
    return proxy.collect();
  }
}

function renderFunction(func, scope, builder) {
  return prepareFunc(func, builder)(scope);
}

function renderPath(path, options, cb) {
  if (typeof(options) === 'function') {
    cb = options;
    options = {};
  }

  var func = require(path); //Yes.
  var ret;
  var err;
  if (options.explicit) { //Assume implied use rather than explicit 'with' by default
    try {
      ret = func(options.scope);
    } catch (e) {
      err = e;
    }
  } else {
    try {
      ret = renderFunction(func, options.scope, options.builder);
    } catch (e) {
      err = e;
    }
  }
  if (ret && typeof(ret.then) === 'function') { //looks like a promise. Let's use it why don't we?
    return ret.then(function(data) {
      if (data) {
        cb(err, data);
      } else {
        cb(err, ret.proxy.collect());
      }
    });
  }
  process.nextTick(function() {
    cb(err, ret);
  });
}

var exposed = {
  string: stringBuilder, //TODO: More output format builders, eg DOM Objects, Buffer
  create: function(builder) {
    return jshtmlProxy((builder && new builder()) || new stringBuilder());
  },
  prepareFunc: prepareFunc,
  renderFunc: renderFunction,
  renderPath: renderPath,
  __express: renderPath
};

return exposed;

});<|endoftext|>


<commit_before>Canvas2D.Manager = Class.create( {
    initialize : function() {
	this.plugins = [];
	this.books   = $H();
    },

    addBook : function(book) {
	unless( book instanceof Canvas2D.Book, function() {
	    throw( "Manager::addBook: book must be instance of Canvas2D.Book" );
	} );
	this.books.set(book.name, book);
	return book;
    },

    setupBook : function(id) {
	return this.addBook(new Canvas2D.Book(id));
    },

    getBook : function(id) {
	return this.books[id];
    },

    startAll :function() {
	if( Canvas2D.ImageManager.isReady() ) {
	    this.plugins.each(function(plugin) { plugin.start();     } );
	    this.books  .each(function(book)   { book.value.start(); } );
	} else {
	    this.startAll.bind(this).delay(0.01);
	}
    }
} );
<commit_msg>fixed bad calling of hash getter method<commit_after>Canvas2D.Manager = Class.create( {
    initialize : function() {
	this.plugins = [];
	this.books   = $H();
    },

    addBook : function(book) {
	unless( book instanceof Canvas2D.Book, function() {
	    throw( "Manager::addBook: book must be instance of Canvas2D.Book" );
	} );
	this.books.set(book.name, book);
	return book;
    },

    setupBook : function(id) {
	return this.addBook(new Canvas2D.Book(id));
    },

    getBook : function(id) {
	return this.books.get(id);
    },

    startAll :function() {
	if( Canvas2D.ImageManager.isReady() ) {
	    this.plugins.each(function(plugin) { plugin.start();     } );
	    this.books  .each(function(book)   { book.value.start(); } );
	} else {
	    this.startAll.bind(this).delay(0.01);
	}
    }
} );
<|endoftext|>


<commit_before>/**
	manage non-player characters

	@namespace GAS
	@class npc
**/

GAS.npc = {

	COMFORT_RADIUS: 25,
	WATCH_RADIUS: 15,
	REACT_RADIUS: 5,

	DRIFTING: 0,
	WATCHING: 1,
	DANCING: 2,
	EVADING: 3,

	list: [],

	scratch: {
		p: SOAR.vector.create(),
		q: SOAR.quaternion.create()
	},

	/**
		init npc objects

		@method init
	**/

	init: function() {

		// generate test paddler
		var p = SOAR.vector.create(0, 0, -25);
		var o = GAS.paddler.create();
		o.position.copy(p);
		o.haste = 1;

		this.list.push( {
			center: p,
			object: o,
			status: this.DRIFTING,
			target: SOAR.vector.create(),
			period: 0,
			health: 0,
			damage: 0
		} );
		GAS.map.add(o.position, GAS.paddler.RADIUS, o);

	},

	/**
		update npc objects

		@method update
	**/

	update: function() {
		var proximity = false;
		var i, il, n;

		for (i = 0, il = this.list.length; i < il; i++) {
			n = this.list[i];
			n.distance = GAS.player.position.distance(n.object.position);

			// handle npc behaviors
			switch (n.status) {

			case this.DRIFTING:
				if (n.distance < this.WATCH_RADIUS) {
					n.status = this.WATCHING;
					n.object.haste = 0;
					console.log("drifting -> watching");
				}
				this.drift(n);
				break;

			case this.WATCHING:
				if (n.distance > this.WATCH_RADIUS) {
					n.status = this.DRIFTING;
					n.object.haste = 1;
					console.log("watching -> drifting");
				}
				if (n.distance < this.REACT_RADIUS) {
//					n.status = this.EVADING;
//					n.object.haste = 2;
//					console.log("watching -> evading");
					proximity = true;
				}
				this.watch(n);
				break;

			case this.DANCING:
				this.dance(n);
				break;

			case this.EVADING:
				if (n.distance > this.REACT_RADIUS) {
					n.status = this.WATCHING;
					n.object.haste = 0;
					console.log("evading -> watching");
				}
				this.evade(n);
				break;

			}

			// actually move the object
			n.object.update();

		}

		// advise player of npc proximity
		GAS.player.motion.movefast = !proximity;

	},

	/**
		implement stochastic "drifting" behavior

		@method drift
		@param o object, the npc object
	**/

	drift: function(o) {
		var p = this.scratch.p;
		p.copy(o.center).sub(o.object.position);
		if (p.length() > this.COMFORT_RADIUS) {
			p.norm().mul(0.01);
			o.target.add(p).norm();
		}
		if (o.period <= 0) {
			o.period = GAS.random(5, 10);
			o.target.set( GAS.random(-1, 1), GAS.random(-0.5, 0.5), GAS.random(-1, 1) ).norm();
		}

		o.object.pointTo(o.target, 0.05);
		o.period -= SOAR.interval * 0.001;
	},

	/**
		implement watching behavior

		@method watch
		@param o object, the npc object
	**/

	watch: function(o) {
		var p = this.scratch.p;
		p.copy(GAS.player.position).sub(o.object.position).norm();
		o.object.pointTo(p, 0.1);
	},

	/**
		implement dancing behavior

		@method dance
		@param o object, the npc object
	**/

	dance: function(o) {

	},

	/**
		implement evasive behavior

		@method evade
		@param o object, the npc object
	**/

	evade: function(o) {

	},

};<commit_msg>Implemented evasion.<commit_after>/**
	manage non-player characters

	@namespace GAS
	@class npc
**/

GAS.npc = {

	COMFORT_RADIUS: 25,
	WATCH_RADIUS: 15,
	REACT_RADIUS: 5,

	DRIFTING: 0,
	WATCHING: 1,
	DANCING: 2,
	EVADING: 3,

	list: [],

	scratch: {
		p: SOAR.vector.create(),
		q: SOAR.quaternion.create()
	},

	/**
		init npc objects

		@method init
	**/

	init: function() {

		// generate test paddler
		var p = SOAR.vector.create(0, 0, -25);
		var o = GAS.paddler.create();
		o.position.copy(p);
		o.haste = 1;

		this.list.push( {
			center: p,
			object: o,
			status: this.DRIFTING,
			target: SOAR.vector.create(),
			period: 0,
			health: 0,
			damage: 0
		} );
		GAS.map.add(o.position, GAS.paddler.RADIUS, o);

	},

	/**
		update npc objects

		@method update
	**/

	update: function() {
		var proximity = false;
		var i, il, n;

		for (i = 0, il = this.list.length; i < il; i++) {
			n = this.list[i];
			n.distance = GAS.player.position.distance(n.object.position);

			// handle npc behaviors
			switch (n.status) {

			case this.DRIFTING:
				if (n.distance < this.WATCH_RADIUS) {
					n.status = this.WATCHING;
					n.object.haste = 0;
					console.log("drifting -> watching");
				}
				this.drift(n);
				break;

			case this.WATCHING:
				if (n.distance > this.WATCH_RADIUS) {
					n.status = this.DRIFTING;
					n.object.haste = 1;
					console.log("watching -> drifting");
				}
				if (n.distance < this.REACT_RADIUS) {
					n.status = this.EVADING;
					n.object.haste = 2;
					console.log("watching -> evading");
					proximity = true;
				}
				this.watch(n);
				break;

			case this.DANCING:
				this.dance(n);
				break;

			case this.EVADING:
				if (n.distance > this.REACT_RADIUS) {
					n.status = this.WATCHING;
					n.object.haste = 0;
					console.log("evading -> watching");
					proximity = true;
				}
				this.evade(n);
				break;

			}

			// actually move the object
			n.object.update();

		}

		// advise player of npc proximity
		GAS.player.motion.movefast = !proximity;

	},

	/**
		implement stochastic "drifting" behavior

		@method drift
		@param o object, the npc object
	**/

	drift: function(o) {
		var p = this.scratch.p;
		p.copy(o.center).sub(o.object.position);
		if (p.length() > this.COMFORT_RADIUS) {
			p.norm().mul(0.01);
			o.target.add(p).norm();
		}
		if (o.period <= 0) {
			o.period = GAS.random(5, 10);
			o.target.set( GAS.random(-1, 1), GAS.random(-0.5, 0.5), GAS.random(-1, 1) ).norm();
		}

		o.object.pointTo(o.target, 0.05);
		o.period -= SOAR.interval * 0.001;
	},

	/**
		implement watching behavior

		@method watch
		@param o object, the npc object
	**/

	watch: function(o) {
		var p = this.scratch.p;
		p.copy(GAS.player.position).sub(o.object.position).norm();
		o.object.pointTo(p, 0.1);
	},

	/**
		implement dancing behavior

		@method dance
		@param o object, the npc object
	**/

	dance: function(o) {

	},

	/**
		implement evasive behavior

		@method evade
		@param o object, the npc object
	**/

	evade: function(o) {
		var p = this.scratch.p;
		p.copy(o.object.position).sub(GAS.player.position).norm();
		o.object.pointTo(p, 0.25);
	},

};<|endoftext|>


<commit_before>var OBB = {

	// Check for a collision.
	// A and B have to be fo type Phaser.Physic.Arcade.Body
	collisionCheck: function(A, B) {
		var ARect = OBB.createRectangle([A.center.x, A.center.y], OBB.rotateVector([A.halfWidth, A.halfHeight], Phaser.Math.degToRad(A.angle)));
		var BRect = OBB.createRectangle([B.center.x, B.center.y], OBB.rotateVector([B.halfWidth, B.halfHeight], Phaser.Math.degToRad(B.angle)));

		return OBB.isecRects(ARect, BRect);
	},

	// We assume a vector consisting of [halfWidth, halfHeight], which can be used to point from the center to the edges
	// We need to rotate this vector in order to create the rotated rectangle
	rotateVector: function(vector, radians) {
		var x = vector[0] * Math.cos(radians) - vector[1] * Math.sin(radians);
		var y = vector[0] * Math.sin(radians) + vector[1] * Math.cos(radians);

		return [x, y];
	},

	// This function takes a center point (in this format [x,y]) and a rotated vector (in this format [x,y])
	// and returns a rectangle in this format: [x1, y1, x2, y2, x3, y3, x4, y4]
	createRectangle: function(center, vector) {
		var x = vector[0],
			y = vector[1],

			x1 = center[0] + x,
			y1 = center[1] + y,

			x2 = center[0] + x,
			y2 = center[1] - y,

			x3 = center[0] - x,
			y3 = center[1] - y,

			x4 = center[0] - x,
			y4 = center[1] + y;

		return [x1, y1, x2, y2, x3, y3, x4, y4];
	},

	// Source: https://gist.github.com/shamansir/3007244
	edgeTest: function(p1, p2, p3, r2) {
		var rot = [-(p2[1] - p1[1]),
			p2[0] - p1[0]
		];

		var ref = (rot[0] * (p3[0] - p1[0]) +
			rot[1] * (p3[1] - p1[1])) >= 0;

		for (var i = 0, il = r2.length; i < il; i += 2) {
			if (((rot[0] * (r2[i] - p1[0]) +
					rot[1] * (r2[i + 1] - p1[1])) >= 0) === ref) return false;
		}

		return true;
	},

	// source: https://gist.github.com/shamansir/3007244
	isecRects: function(r1, r2) {
		if (!r1 || !r2) throw new Error('Rects are not accessible');

		var pn, px;
		for (var pi = 0, pl = r1.length; pi < pl; pi += 2) {
			pn = (pi === (pl - 2)) ? 0 : pi + 2; // next point
			px = (pn === (pl - 2)) ? 0 : pn + 2;
			if (OBB.edgeTest([r1[pi], r1[pi + 1]], [r1[pn], r1[pn + 1]], [r1[px], r1[px + 1]], r2)) return false;
		}
		for (var pi = 0, pl = r2.length; pi < pl; pi += 2) {
			pn = (pi === (pl - 2)) ? 0 : pi + 2; // next point
			px = (pn === (pl - 2)) ? 0 : pn + 2;
			if (OBB.edgeTest([r2[pi], r2[pi + 1]], [r2[pn], r2[pn + 1]], [r2[px], r2[px + 1]], r1)) return false;
		}
		return true;
	},

	// Overwrite the seperate function for OBB collision detection
	separate: function(body1, body2, processCallback, callbackContext, overlapOnly) {
		if (!body1.enable || !body2.enable) {
			return false;
		}

		//  Is there a custom process callback? If it returns true then we can carry on, otherwise we should abort.
		if (processCallback && processCallback.call(callbackContext, body1.sprite, body2.sprite) === false) {
			return false;
		}

		return OBB.collisionCheck(body1, body2);
	},

	// This function is used to overwrite the seperate function of a Phase.Arcade.World instance.
	setOBB: function(gameRef) {
		OBB.game = gameRef;
		gameRef.physics.arcade.separate = OBB.separate;
	}

};
<commit_msg>Fix bounding box rotation<commit_after>var OBB = {

	// Check for a collision.
	// A and B have to be fo type Phaser.Physic.Arcade.Body
	collisionCheck: function  (A, B) {
		var ARect = OBB.createRectangle(A);
		var BRect = OBB.createRectangle(B);

		return OBB.isecRects(ARect, BRect);
	},

	// We assume a vector consisting of [halfWidth, halfHeight], which can be used to point from the center to the edges
	// We need to rotate this vector in order to create the rotated rectangle
	rotateVector: function (vector, radians) {
		var x = vector[0] * Math.cos(radians) - vector[1] * Math.sin(radians);
		var y = vector[0] * Math.sin(radians) + vector[1] * Math.cos(radians);

		return [x, y];
	},

	// This function takes a center point (in this format [x,y]) and a rotated vector (in this format [x,y])
	// and returns a rectangle in this format: [x1, y1, x2, y2, x3, y3, x4, y4]
	createRectangle: function (body) {
		var center = [body.center.x, body.center.y];

		var vector1 = OBB.rotateVector([body.halfWidth, body.halfHeight], Phaser.Math.degToRad(body.rotation));
		var vector2 = OBB.rotateVector([body.halfWidth, body.halfHeight], Phaser.Math.degToRad(-body.rotation));

		var x1 = center[0] + vector1[0],
		y1 = center[1] + vector1[1],

		x2 = center[0] + vector2[0],
		y2 = center[1] - vector2[1],

		x3 = center[0] - vector1[0],
		y3 = center[1] - vector1[1],

		x4 = center[0] - vector2[0],
		y4 = center[1] + vector2[1];

		return [x1, y1, x2, y2, x3, y3, x4, y4];
	},

	// Source: https://gist.github.com/shamansir/3007244
	edgeTest:function (p1, p2, p3, r2) {
		var rot = [ -(p2[1] - p1[1]),
					  p2[0] - p1[0] ];

		var ref = (rot[0] * (p3[0] - p1[0]) +
				   rot[1] * (p3[1] - p1[1])) >= 0;

		for (var i = 0, il = r2.length; i < il; i+=2) {
			if (((rot[0] * (r2[i]   - p1[0]) +
				  rot[1] * (r2[i+1] - p1[1])) >= 0) === ref) return false;
		}

		return true;
	},

	// source: https://gist.github.com/shamansir/3007244
	isecRects: function (r1, r2) {
		if (!r1 || !r2) throw new Error('Rects are not accessible');

		var pn, px;
		for (var pi = 0, pl = r1.length; pi < pl; pi += 2) {
			pn = (pi === (pl - 2)) ? 0 : pi + 2; // next point
			px = (pn === (pl - 2)) ? 0 : pn + 2;
			if (OBB.edgeTest([r1[pi], r1[pi+1]],
						 [r1[pn], r1[pn+1]],
						 [r1[px], r1[px+1]], r2)) return false;
		}
		for (var pi = 0, pl = r2.length; pi < pl; pi += 2) {
			pn = (pi === (pl - 2)) ? 0 : pi + 2; // next point
			px = (pn === (pl - 2)) ? 0 : pn + 2;
			if (OBB.edgeTest([r2[pi], r2[pi+1]],
						 [r2[pn], r2[pn+1]],
						 [r2[px], r2[px+1]], r1)) return false;
		}
		return true;
	},

	// Overwrite the seperate function for OBB collision detection
	separate: function (body1, body2, processCallback, callbackContext, overlapOnly) {
		if (!body1.enable || !body2.enable)
		{
			return false;
		}

		//  Is there a custom process callback? If it returns true then we can carry on, otherwise we should abort.
		if (processCallback && processCallback.call(callbackContext, body1.sprite, body2.sprite) === false)
		{
			return false;
		}

		return OBB.collisionCheck(body1, body2);
	},

	// This function is used to overwrite the seperate function of a Phase.Arcade.World instance.
	setOBB: function (gameRef) {
		OBB.game = gameRef;
		gameRef.physics.arcade.separate = OBB.separate;
	}

};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Pavel Aksonov
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */

import {PUSH_ACTION, POP_ACTION2, FOCUS_ACTION, JUMP_ACTION, INIT_ACTION, REPLACE_ACTION, RESET_ACTION, POP_ACTION, REFRESH_ACTION} from './Actions';
import assert from 'assert';
import Immutable from 'immutable';
import {getInitialState} from './State';

function findElement(state, key) {
    if (state.sceneKey != key){
        if (state.children){
            let result = undefined;
            state.children.forEach(el=>{
                let res = findElement(el, key);
                if (res){
                    result = res;
                    return res;
                }
            });
            return result;
        } else {
            return false;
        }
    } else {
        return state;
    }
}

function getCurrent(state){
    if (!state.children){
        return state.key;
    }
    return getCurrent(state.children[state.index]);
}

function update(state,action){
    // clone state, TODO: clone effectively?
    if (!state.scenes[action.key] && action.key.indexOf('_')!=-1){
        action.key = action.key.substring(action.key.indexOf('_')+1);
        //console.log("Transform to key="+action.key);
    }
    const newProps = {...state.scenes[action.key], ...action};
    let newState = Immutable.fromJS(state).toJS();

    // change route property
    //newState.scenes[action.key] = newProps;

    // get parent
    const parent = newProps.parent;
    assert(parent, "No parent is defined for route="+action.key);

    // find parent in the state
    let el = findElement(newState, parent);
    assert(el, "Cannot find element for parent="+parent+" within current state:"+JSON.stringify(newState));

    switch (action.type){
        case POP_ACTION2:
        case POP_ACTION:
            // recursive pop parent
            while (el.parent && (el.children.length <= 1 || el.tabs)){
                el = findElement(newState, el.parent);
                assert(el, "Cannot find element for parent="+el.parent+" within current state");
            }
            if (el.children.length > 1) {
                el.children.pop();
                el.index = el.children.length - 1;
                newState.scenes.current = getCurrent(newState);
                return newState;
            } else {
                console.log("Cannot do pop");
                return state;
            }

        case REFRESH_ACTION:
            let ind = -1;
            el.children.forEach((c,i)=>{if (c.sceneKey==action.key){ind=i}});
            assert(ind!=-1, "Cannot find route with key="+action.key+" for parent="+el.key);
            el.children[ind] = getInitialState(newProps, newState.scenes, ind, action);
            return newState;

        case PUSH_ACTION:
            el.children.push(getInitialState(newProps, newState.scenes, el.children.length, action));
            el.index = el.children.length - 1;
            newState.scenes.current = getCurrent(newState);
            return newState;

        case JUMP_ACTION:
            assert(el.tabs, "Parent="+el.key+" is not tab bar, jump action is not valid");
            ind = -1;
            el.children.forEach((c,i)=>{if (c.sceneKey==action.key){ind=i}});
            assert(ind!=-1, "Cannot find route with key="+action.key+" for parent="+el.key);
            el.index = ind;
            return newState;

        case REPLACE_ACTION:
            if (el.children && el.children.length){
                el.children[el.index] = getInitialState(newProps, newState.scenes, el.index, action);
            } else {
                el.children = [getInitialState(newProps, newState.scenes, 0, action)];
            }
            newState.scenes.current = getCurrent(newState);
            return newState;

        default:
            return state;
    }
}

function reducer({initialState, scenes}){
    assert(initialState, "initialState should not be null");
    assert(initialState.key, "initialState.key should not be null");
    assert(scenes, "scenes should not be null");
    assert(scenes.current, "scenes.current should not be null");
    return function(state, action){
        //console.log("ACTION:", action);
        state = state || {...initialState, scenes};
        //console.log("ACTION:", action);
        //console.log("STATE:", JSON.stringify(state));
        assert(action, "action should be defined");
        assert(action.type, "action type should be defined");
        assert(state.scenes, "state.scenes is missed");
        assert(state.scenes.current, "state.scenes.current should be defined");

        if (action.key){
            assert(state.scenes[action.key], "missed route data for key="+action.key);
        } else {
            // set current route for pop action or refresh action
            if (action.type === POP_ACTION || action.type === POP_ACTION2 || action.type === REFRESH_ACTION){
                action.key = state.scenes.current;
            }
        }

        switch (action.type) {
            case POP_ACTION2:
            case POP_ACTION:
            case REFRESH_ACTION:
            case PUSH_ACTION:
            case JUMP_ACTION:
            case REPLACE_ACTION:
                const newState = update(state, action);
                //console.log("NEW STATE:", JSON.stringify(newState));
                return newState;
            default:
                return state;

        }
    }

}

export default reducer;
<commit_msg>add back recording the new `current`.<commit_after>/**
 * Copyright (c) 2015-present, Pavel Aksonov
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */

import {PUSH_ACTION, POP_ACTION2, FOCUS_ACTION, JUMP_ACTION, INIT_ACTION, REPLACE_ACTION, RESET_ACTION, POP_ACTION, REFRESH_ACTION} from './Actions';
import assert from 'assert';
import Immutable from 'immutable';
import {getInitialState} from './State';

function findElement(state, key) {
    if (state.sceneKey != key){
        if (state.children){
            let result = undefined;
            state.children.forEach(el=>{
                let res = findElement(el, key);
                if (res){
                    result = res;
                    return res;
                }
            });
            return result;
        } else {
            return false;
        }
    } else {
        return state;
    }
}

function getCurrent(state){
    if (!state.children){
        return state.key;
    }
    return getCurrent(state.children[state.index]);
}

function update(state,action){
    // clone state, TODO: clone effectively?
    if (!state.scenes[action.key] && action.key.indexOf('_')!=-1){
        action.key = action.key.substring(action.key.indexOf('_')+1);
        //console.log("Transform to key="+action.key);
    }
    const newProps = {...state.scenes[action.key], ...action};
    let newState = Immutable.fromJS(state).toJS();

    // change route property
    //newState.scenes[action.key] = newProps;

    // get parent
    const parent = newProps.parent;
    assert(parent, "No parent is defined for route="+action.key);

    // find parent in the state
    let el = findElement(newState, parent);
    assert(el, "Cannot find element for parent="+parent+" within current state:"+JSON.stringify(newState));

    switch (action.type){
        case POP_ACTION2:
        case POP_ACTION:
            // recursive pop parent
            while (el.parent && (el.children.length <= 1 || el.tabs)){
                el = findElement(newState, el.parent);
                assert(el, "Cannot find element for parent="+el.parent+" within current state");
            }
            if (el.children.length > 1) {
                el.children.pop();
                el.index = el.children.length - 1;
                newState.scenes.current = getCurrent(newState);
                return newState;
            } else {
                console.log("Cannot do pop");
                return state;
            }

        case REFRESH_ACTION:
            let ind = -1;
            el.children.forEach((c,i)=>{if (c.sceneKey==action.key){ind=i}});
            assert(ind!=-1, "Cannot find route with key="+action.key+" for parent="+el.key);
            el.children[ind] = getInitialState(newProps, newState.scenes, ind, action);
            return newState;

        case PUSH_ACTION:
            el.children.push(getInitialState(newProps, newState.scenes, el.children.length, action));
            el.index = el.children.length - 1;
            newState.scenes.current = getCurrent(newState);
            return newState;

        case JUMP_ACTION:
            assert(el.tabs, "Parent="+el.key+" is not tab bar, jump action is not valid");
            ind = -1;
            el.children.forEach((c,i)=>{if (c.sceneKey==action.key){ind=i}});
            assert(ind!=-1, "Cannot find route with key="+action.key+" for parent="+el.key);
            //console.log("SETTING INDEX TO:", ind, el.key, action.key);
            el.index = ind;
            newState.scenes.current = getCurrent(newState);
            //console.log("NEW STATE:", newState);
            return newState;

        case REPLACE_ACTION:
            if (el.children && el.children.length){
                el.children[el.index] = getInitialState(newProps, newState.scenes, el.index, action);
            } else {
                el.children = [getInitialState(newProps, newState.scenes, 0, action)];
            }
            newState.scenes.current = getCurrent(newState);
            return newState;

        default:
            return state;
    }
}

function reducer({initialState, scenes}){
    assert(initialState, "initialState should not be null");
    assert(initialState.key, "initialState.key should not be null");
    assert(scenes, "scenes should not be null");
    assert(scenes.current, "scenes.current should not be null");
    return function(state, action){
        //console.log("ACTION:", action);
        state = state || {...initialState, scenes};
        //console.log("ACTION:", action);
        //console.log("STATE:", JSON.stringify(state));
        assert(action, "action should be defined");
        assert(action.type, "action type should be defined");
        assert(state.scenes, "state.scenes is missed");
        assert(state.scenes.current, "state.scenes.current should be defined");

        if (action.key){
            assert(state.scenes[action.key], "missed route data for key="+action.key);
        } else {
            // set current route for pop action or refresh action
            if (action.type === POP_ACTION || action.type === POP_ACTION2 || action.type === REFRESH_ACTION){
                action.key = state.scenes.current;
            }
        }

        switch (action.type) {
            case POP_ACTION2:
            case POP_ACTION:
            case REFRESH_ACTION:
            case PUSH_ACTION:
            case JUMP_ACTION:
            case REPLACE_ACTION:
                const newState = update(state, action);
                //console.log("NEW STATE:", JSON.stringify(newState));
                return newState;
            default:
                return state;

        }
    }

}

export default reducer;
<|endoftext|>