<commit_before>var tc = tc || {};
tc.gam = tc.gam || {};
tc.gam.project_widgets = tc.gam.project_widgets || {};

tc.gam.project_widgets.needs = function(options) {
    tc.util.log('project.needs');
    var dom = options.dom,
        modal = options.app.components.modal,
        merlin,
        MAX_AVATARS = 5,
        self = {};

    /**
     * Function: isProjectMember
     * Is the user a member of this project?
     * TODO: find a common place for this to live.
     */
    var isProjectMember = function() {
        return (
            (options.project_user.is_member) ||
            (options.project_user.is_project_admin) ||
            (options.user && options.user.is_admin) ||
            (options.user && options.user.is_leader)
        );
    };

    self._getUserId = function() {
        if (options.user && options.user.u_id) {
            return options.user.u_id;
        }
        return null;
    };

    self._getDetailTemplateData = function(need_details) {
        var new_details = tc.jQ.extend(true, {
                day: function() { return this.date ? (new Date(this.date).getUTCDate()) : ''; },
                month: function() { return this.date ? (new Date(this.date).getUTCMonth()+1) : ''; },
                isVolunteer: function() { return this.type === 'volunteer'; },
                isInKind: function() { return this.type === 'inkind'; }
            }, need_details);

        //Special cases for the first volunteer
        new_details.has_first = need_details.volunteers.length > 0;
        if (new_details.has_first) {
            new_details.first_vol = need_details.volunteers[0];
            new_details.volunteers = need_details.volunteers.slice(1, MAX_AVATARS+1);
            new_details.vol_count_minus_one = need_details.volunteers.length-1;
            new_details.avatar = function() { return this.avatar_path ? (options.media_root + this.avatar_path) : '/static/images/thumb_genAvatar.jpg'; };
        }

        return new_details;
    };

    var mergeDetailTemplate = function(need_details) {
        var new_details = self._getDetailTemplateData(need_details),
            $html = ich.need_detail_tmpl(new_details);

        dom.find('.need-stack').html($html);
        updateNeed(need_details);
    };

    self._isVolunteer = function(id, volunteers) {
        var i;
        for (i=0; i<volunteers.length; i++) {
            if (parseInt(volunteers[i].id, 10) === parseInt(id, 10)) {
                return true;
            }
        }
        return false;
    };

    self._getVolunteerButtonConfig = function(vols_needed, volunteers, user_id) {
        if (self._isVolunteer(user_id, volunteers)) {
            return { cssClass: 'in-process', text: 'I am helping'};
        } else {
            if (vols_needed !== volunteers.length) {
                return { cssClass: 'active', text: 'I can help'};
            } else {
                return { cssClass: 'complete', text: 'Complete!'};
            }
        }
    };

    self._getProgressElementWidth = function(max_width, cur_vol_count, vols_needed) {
        return max_width * cur_vol_count / vols_needed;
    };

    self._updateVolunteerProgress = function($container, need) {
        var $volCount = $container.find('.volunteer-count strong'),
            $progress = $container.find('.progress'),
            quantityNum = parseInt(need.quantity, 10);

        $volCount.text(need.volunteers.length);
        $progress.width(self._getProgressElementWidth($progress.parent().width(), need.volunteers.length, quantityNum));
    };

    self._updateVolunteerButton = function($container, need) {
        var $helpLink = $container.find('.help-link'),
            quantityNum = parseInt(need.quantity, 10),
            buttonConfig = self._getVolunteerButtonConfig(quantityNum, need.volunteers, self._getUserId());

        $helpLink
            .removeClass('active in-process complete')
            .addClass(buttonConfig.cssClass)
            .text(buttonConfig.text);
    };

    self._updateSmallAvatars = function($container, need) {
        var $avatars = $container.find('.vol-avatars'),
            $avatar_html;

        $avatar_html = ich.need_vol_avatars({
            volunteers: need.volunteers.slice(0, MAX_AVATARS),
            avatar: function() {return this.avatar_path ? (options.media_root + this.avatar_path) : '/static/images/thumb_genAvatar.jpg'; }
        });

        $avatars.html($avatar_html);
    };

    var updateNeed = self.updateNeed = function(need) {
        var $needContainer = tc.jQ('.need[data-id|="'+need.id+'"]');

        self._updateVolunteerProgress($needContainer, need);
        self._updateVolunteerButton($needContainer, need);
        self._updateSmallAvatars($needContainer, need);
    };

    /**
     * Function: volunteer
     * The user can volunteer for a specific need.
     */
    var volunteer = function(need, message, callback) {
        var $error_msg = modal.options.element.find('.error-msg').hide();

        tc.jQ.ajax({
            url: '/rest/v1/needs/'+need.id+'/volunteers/',
            data: { member_id: self._getUserId() },
            dataType: 'json',
            type: 'POST',
            success: function(volunteer_data, status, xhr) {
                tc.jQ.ajax({
                    url: '/directmsg',
                    type: 'POST',
                    data: {
                        message: message,
                        to_user_id: options.project_data.info.owner.u_id
                    },
                    success: function(message_data, status, xhr) {
                        callback(volunteer_data);
                    },
                    error: function(xhr, status, error) {
                        $error_msg.show();
                    }
                });

            },
            error: function(xhr, status, error) {
                $error_msg.show();
            }
        });
    };

    /**
     * Function: initMerlin
     * Initialize the merlin object for validation of the modal dialog.
     */
    var initMerlin = function(need) {
        var merlin;

        //Please note that this is very tricky due to the fact that the
        //content of the modal is actually being cloned by the modal widget.
        //This means that there are duplicate elements in the DOM, even
        //ids. This means you should be very specific and careful with your
        //selectors. Also, Pretty Checkboxes rely on label[for] input[id]
        //matching. You will have issues if you have duplicates of these.
        //Yes, this is from experience. =)

        //Make the Pretty Checkbox elements relative to the #modal have
        //ids that you unique from the template.
        modal.options.element.find('.volunteer-agree-section input').attr('id', 'unique-volunteer-agree');
        modal.options.element.find('.volunteer-agree-section label').attr('for', 'unique-volunteer-agree');
        modal.options.element.find('input[type=checkbox]').prettyCheckboxes();

        //We are using merlin only for the built-in validation in this case.

        merlin =  new tc.merlin(options.app, {
            name:'volunteer',
            dom:modal.options.element.find('.user-volunteer-modal.merlin'),
            next_button:modal.options.element.find('.user-volunteer-modal.merlin a.send'),
            first_step:'volunteer_agree',
            use_hashchange:false,
            steps: {
                'volunteer_agree': {
                    selector:'.step.user-volunteer',
                    //These selectors are within the context of the "merlin" root element
                    inputs:{
                        //This is the message being sent to the organizer.
                        'volunteer_agree_msg': {
                            selector:'.volunteer-agree-msg',
                            validators:['max-300'],
                            hint:'',
                            counter: {
                                // selector: jQuery selectory for element to fill with counter.
                                selector: '.charlimit.charlimit-volunteer-agree-msg',
                                // limit: Character limit for counter.  This should be in align
                                // with a validator of max-X.
                                limit: 300
                            }
                        },
                        //This is the checkbox to agree to sign up.
                        'volunteer_agree':{
                            selector:'input.volunteer-agree',
                            validators:['required']
                        },
                        'add_quantity': {
                            selector:'input.add-quantity',
                            validators: function(merlinInput, $element, step, onSubmit) {
                                if ($element) {
                                    return tc.validate($element, ['required', 'numeric']);
                                } else {
                                    return {valid:true};
                                }
                            }
                        }
                    },
                    init:function(merlin, dom) {
                        merlin.options.next_button.click(function(event) {
                            var $this = tc.jQ(this),
                                message = merlin.options.steps.volunteer_agree.inputs.volunteer_agree_msg.dom.val();

                            if (!$this.hasClass('disabled')) {
                                volunteer(need, message, function(data){
                                    if (self.need_id) {
                                        tc.gam.project_data.getNeedDetails(self.need_id, mergeDetailTemplate);
                                    } else {
                                        tc.gam.project_data.getNeedDetails(data.need_id, updateNeed);
                                    }
                                    modal.hide();
                                });
                            }
                        });
                    }
                }
            }
        });

        merlin.show_step('volunteer_agree');
    };

    /**
     * Function: showModal
     * Show the volunteer modal to the user
     */
    var showModal = function(need) {
        //use ICanHaz to fill in the modal content template
        var $needDetailsContent = tc.jQ('.modal-content .volunteer-agree-section .volunteer-agree-label'),
            $html;

        if (need.type === 'inkind') {
            $html = ich.add_inkind_need_tmpl(need);
        } else {
            $html = ich.add_vol_need_tmpl(need);
        }

        $needDetailsContent.html($html);

        //NOTE: the source_element gets cloned here, so be careful
        //binding events!
        modal.show({
            app:options.app,
            source_element:tc.jQ('.template-content.user-volunteer-modal')
        });

        initMerlin(need);
    };

    var bindNeedHelpLinks = self.bindNeedHelpLinks = function() {
        tc.jQ('.help-link').die('click').live('click', function(event) {
            event.preventDefault();

            var $this = tc.jQ(this),
                need_id = $this.parents('li.need').attr('data-id');

            if ($this.hasClass('active')) {
                if (isProjectMember()) {
                    tc.gam.project_data.getNeedDetails(need_id, showModal);
                } else {
                    modal.show({
                        app:options.app,
                        source_element:tc.jQ('.modal-content.volunteer-no-member')
                    });
                }
            }
        });
    };

    var bindNeedDeleteLinks = self.bindNeedDeleteLinks = function() {
        tc.jQ('a.need-delete').die('click').live('click', function(event) {
            event.preventDefault();
            options.app.components.modal.show({
                app:options.app,
                source_element:tc.jQ('.modal-content.remove-need'),
                submit: function(){
                    tc.gam.project_data.deleteNeed(event.target.href.split(',')[1],
                                                   function(data, status, xhr) {
                                                       if(data == 'False'){return false;}
                                                       window.location.hash = 'show,needs';
                                                       window.location.reload();
                                                   });
                }
            });
        });
    };

    /**
     * Function: bindEvents
     * Bind events for this widget
     */
    var bindEvents = function() {
        tc.jQ(tc).bind('show-project-widget', function(event, widgetName, id) {
            if (options.name === widgetName) {
                tc.util.log('&&& showing ' + options.name);

                //We're going to show one need in detail, so go fetch
                //the details and setup the template
                if (id) {
                    self.need_id = id;
                    tc.gam.project_data.getNeedDetails(self.need_id, function(need_details) {
                        mergeDetailTemplate(need_details);
                        dom.show();
                    });
                } else {
                    dom.show();
                }
            } else {
                tc.util.log('&&& hiding ' + options.name);
                dom.hide();
            }
        });

        bindNeedHelpLinks();
        bindNeedDeleteLinks();

    };

    bindEvents();

    return self;
};
<commit_msg>send quantity to the volunteer handler<commit_after>var tc = tc || {};
tc.gam = tc.gam || {};
tc.gam.project_widgets = tc.gam.project_widgets || {};

tc.gam.project_widgets.needs = function(options) {
    tc.util.log('project.needs');
    var dom = options.dom,
        modal = options.app.components.modal,
        merlin,
        MAX_AVATARS = 5,
        self = {};

    /**
     * Function: isProjectMember
     * Is the user a member of this project?
     * TODO: find a common place for this to live.
     */
    var isProjectMember = function() {
        return (
            (options.project_user.is_member) ||
            (options.project_user.is_project_admin) ||
            (options.user && options.user.is_admin) ||
            (options.user && options.user.is_leader)
        );
    };

    self._getUserId = function() {
        if (options.user && options.user.u_id) {
            return options.user.u_id;
        }
        return null;
    };

    self._getDetailTemplateData = function(need_details) {
        var new_details = tc.jQ.extend(true, {
                day: function() { return this.date ? (new Date(this.date).getUTCDate()) : ''; },
                month: function() { return this.date ? (new Date(this.date).getUTCMonth()+1) : ''; },
                isVolunteer: function() { return this.type === 'volunteer'; },
                isInKind: function() { return this.type === 'inkind'; }
            }, need_details);

        //Special cases for the first volunteer
        new_details.has_first = need_details.volunteers.length > 0;
        if (new_details.has_first) {
            new_details.first_vol = need_details.volunteers[0];
            new_details.volunteers = need_details.volunteers.slice(1, MAX_AVATARS+1);
            new_details.vol_count_minus_one = need_details.volunteers.length-1;
            new_details.avatar = function() { return this.avatar_path ? (options.media_root + this.avatar_path) : '/static/images/thumb_genAvatar.jpg'; };
        }

        return new_details;
    };

    var mergeDetailTemplate = function(need_details) {
        var new_details = self._getDetailTemplateData(need_details),
            $html = ich.need_detail_tmpl(new_details);

        dom.find('.need-stack').html($html);
        updateNeed(need_details);
    };

    self._isVolunteer = function(id, volunteers) {
        var i;
        for (i=0; i<volunteers.length; i++) {
            if (parseInt(volunteers[i].id, 10) === parseInt(id, 10)) {
                return true;
            }
        }
        return false;
    };

    self._getVolunteerButtonConfig = function(vols_needed, volunteers, user_id) {
        if (self._isVolunteer(user_id, volunteers)) {
            return { cssClass: 'in-process', text: 'I am helping'};
        } else {
            if (vols_needed !== volunteers.length) {
                return { cssClass: 'active', text: 'I can help'};
            } else {
                return { cssClass: 'complete', text: 'Complete!'};
            }
        }
    };

    self._getProgressElementWidth = function(max_width, cur_vol_count, vols_needed) {
        return max_width * cur_vol_count / vols_needed;
    };

    self._updateVolunteerProgress = function($container, need) {
        var $volCount = $container.find('.volunteer-count strong'),
            $progress = $container.find('.progress'),
            quantityNum = parseInt(need.quantity, 10);

        $volCount.text(need.volunteers.length);
        $progress.width(self._getProgressElementWidth($progress.parent().width(), need.volunteers.length, quantityNum));
    };

    self._updateVolunteerButton = function($container, need) {
        var $helpLink = $container.find('.help-link'),
            quantityNum = parseInt(need.quantity, 10),
            buttonConfig = self._getVolunteerButtonConfig(quantityNum, need.volunteers, self._getUserId());

        $helpLink
            .removeClass('active in-process complete')
            .addClass(buttonConfig.cssClass)
            .text(buttonConfig.text);
    };

    self._updateSmallAvatars = function($container, need) {
        var $avatars = $container.find('.vol-avatars'),
            $avatar_html;

        $avatar_html = ich.need_vol_avatars({
            volunteers: need.volunteers.slice(0, MAX_AVATARS),
            avatar: function() {return this.avatar_path ? (options.media_root + this.avatar_path) : '/static/images/thumb_genAvatar.jpg'; }
        });

        $avatars.html($avatar_html);
    };

    var updateNeed = self.updateNeed = function(need) {
        var $needContainer = tc.jQ('.need[data-id|="'+need.id+'"]');

        self._updateVolunteerProgress($needContainer, need);
        self._updateVolunteerButton($needContainer, need);
        self._updateSmallAvatars($needContainer, need);
    };

    /**
     * Function: volunteer
     * The user can volunteer for a specific need.
     */
    var volunteer = function(need, quantity, message, callback) {
        var $error_msg = modal.options.element.find('.error-msg').hide();

        tc.jQ.ajax({
            url: '/rest/v1/needs/'+need.id+'/volunteers/',
            data: { member_id: self._getUserId(), quantity: quantity },
            dataType: 'json',
            type: 'POST',
            success: function(volunteer_data, status, xhr) {
                tc.jQ.ajax({
                    url: '/directmsg',
                    type: 'POST',
                    data: {
                        message: message,
                        to_user_id: options.project_data.info.owner.u_id
                    },
                    success: function(message_data, status, xhr) {
                        callback(volunteer_data);
                    },
                    error: function(xhr, status, error) {
                        $error_msg.show();
                    }
                });

            },
            error: function(xhr, status, error) {
                $error_msg.show();
            }
        });
    };

    /**
     * Function: initMerlin
     * Initialize the merlin object for validation of the modal dialog.
     */
    var initMerlin = function(need) {
        var merlin;

        //Please note that this is very tricky due to the fact that the
        //content of the modal is actually being cloned by the modal widget.
        //This means that there are duplicate elements in the DOM, even
        //ids. This means you should be very specific and careful with your
        //selectors. Also, Pretty Checkboxes rely on label[for] input[id]
        //matching. You will have issues if you have duplicates of these.
        //Yes, this is from experience. =)

        //Make the Pretty Checkbox elements relative to the #modal have
        //ids that you unique from the template.
        modal.options.element.find('.volunteer-agree-section input').attr('id', 'unique-volunteer-agree');
        modal.options.element.find('.volunteer-agree-section label').attr('for', 'unique-volunteer-agree');
        modal.options.element.find('input[type=checkbox]').prettyCheckboxes();

        //We are using merlin only for the built-in validation in this case.

        merlin =  new tc.merlin(options.app, {
            name:'volunteer',
            dom:modal.options.element.find('.user-volunteer-modal.merlin'),
            next_button:modal.options.element.find('.user-volunteer-modal.merlin a.send'),
            first_step:'volunteer_agree',
            use_hashchange:false,
            steps: {
                'volunteer_agree': {
                    selector:'.step.user-volunteer',
                    //These selectors are within the context of the "merlin" root element
                    inputs:{
                        //This is the message being sent to the organizer.
                        'volunteer_agree_msg': {
                            selector:'.volunteer-agree-msg',
                            validators:['max-300'],
                            hint:'',
                            counter: {
                                // selector: jQuery selectory for element to fill with counter.
                                selector: '.charlimit.charlimit-volunteer-agree-msg',
                                // limit: Character limit for counter.  This should be in align
                                // with a validator of max-X.
                                limit: 300
                            }
                        },
                        //This is the checkbox to agree to sign up.
                        'volunteer_agree':{
                            selector:'input.volunteer-agree',
                            validators:['required']
                        },
                        'add_quantity': {
                            selector:'input.add-quantity',
                            validators: function(merlinInput, $element, step, onSubmit) {
                                if ($element.length > 0) {
                                    return tc.validate($element, ['required', 'numeric']);
                                } else {
                                    return {valid:true};
                                }
                            }
                        }
                    },
                    init:function(merlin, dom) {
                        merlin.options.next_button.click(function(event) {
                            var $this = tc.jQ(this),
                                message = merlin.options.steps.volunteer_agree.inputs.volunteer_agree_msg.dom.val();

                            if (!$this.hasClass('disabled')) {
                                var quantity = parseInt(merlin.options.steps.volunteer_agree.inputs.add_quantity.dom.val(), 10) || 1;

                                volunteer(need, quantity, message, function(data){
                                    if (self.need_id) {
                                        tc.gam.project_data.getNeedDetails(self.need_id, mergeDetailTemplate);
                                    } else {
                                        tc.gam.project_data.getNeedDetails(data.need_id, updateNeed);
                                    }
                                    modal.hide();
                                });
                            }
                        });
                    }
                }
            }
        });

        merlin.show_step('volunteer_agree');
    };

    /**
     * Function: showModal
     * Show the volunteer modal to the user
     */
    var showModal = function(need) {
        //use ICanHaz to fill in the modal content template
        var $needDetailsContent = tc.jQ('.modal-content .volunteer-agree-section .volunteer-agree-label'),
            $html;

        if (need.type === 'inkind') {
            $html = ich.add_inkind_need_tmpl(need);
        } else {
            $html = ich.add_vol_need_tmpl(need);
        }

        $needDetailsContent.html($html);

        //NOTE: the source_element gets cloned here, so be careful
        //binding events!
        modal.show({
            app:options.app,
            source_element:tc.jQ('.template-content.user-volunteer-modal')
        });

        initMerlin(need);
    };

    var bindNeedHelpLinks = self.bindNeedHelpLinks = function() {
        tc.jQ('.help-link').die('click').live('click', function(event) {
            event.preventDefault();

            var $this = tc.jQ(this),
                need_id = $this.parents('li.need').attr('data-id');

            if ($this.hasClass('active')) {
                if (isProjectMember()) {
                    tc.gam.project_data.getNeedDetails(need_id, showModal);
                } else {
                    modal.show({
                        app:options.app,
                        source_element:tc.jQ('.modal-content.volunteer-no-member')
                    });
                }
            }
        });
    };

    var bindNeedDeleteLinks = self.bindNeedDeleteLinks = function() {
        tc.jQ('a.need-delete').die('click').live('click', function(event) {
            event.preventDefault();
            options.app.components.modal.show({
                app:options.app,
                source_element:tc.jQ('.modal-content.remove-need'),
                submit: function(){
                    tc.gam.project_data.deleteNeed(event.target.href.split(',')[1],
                                                   function(data, status, xhr) {
                                                       if(data == 'False'){return false;}
                                                       window.location.hash = 'show,needs';
                                                       window.location.reload();
                                                   });
                }
            });
        });
    };

    /**
     * Function: bindEvents
     * Bind events for this widget
     */
    var bindEvents = function() {
        tc.jQ(tc).bind('show-project-widget', function(event, widgetName, id) {
            if (options.name === widgetName) {
                tc.util.log('&&& showing ' + options.name);

                //We're going to show one need in detail, so go fetch
                //the details and setup the template
                if (id) {
                    self.need_id = id;
                    tc.gam.project_data.getNeedDetails(self.need_id, function(need_details) {
                        mergeDetailTemplate(need_details);
                        dom.show();
                    });
                } else {
                    dom.show();
                }
            } else {
                tc.util.log('&&& hiding ' + options.name);
                dom.hide();
            }
        });

        bindNeedHelpLinks();
        bindNeedDeleteLinks();

    };

    bindEvents();

    return self;
};
<|endoftext|>


<commit_before>var express		= require('express');
var fs			= require('fs');
var io			= require('socket.io');
var crypto		= require('crypto');

var app			= express.createServer();
var staticDir	= express.static;

io				= io.listen(app);

var opts = {
	port: process.env.PORT || 1948,
	baseDir : __dirname + '/../../../'
};

io.sockets.on('connection', function(socket) {
	socket.on('slidechanged', function(slideData) {
		if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return;
		if (createHash(slideData.secret) === slideData.socketId) {
			slideData.secret = null;
			socket.broadcast.emit(slideData.socketId, slideData);
		};
	});
});

app.configure(function() {
	[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
		app.use('/' + dir, staticDir(opts.baseDir + dir));
	});
});

app.get("/", function(req, res) {
	res.writeHead(200, {'Content-Type': 'text/html'});
	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
});

app.get("/token", function(req,res) {
	var ts = new Date().getTime();
	var rand = Math.floor(Math.random()*9999999);
	var secret = ts.toString() + rand.toString();
	res.send({secret: secret, socketId: createHash(secret)});
});

var createHash = function(secret) {
	var cipher = crypto.createCipher('blowfish', secret);
	return(cipher.final('hex'));
};

// Actually listen
app.listen(opts.port || null);

var brown = '\033[33m',
	green = '\033[32m',
	reset = '\033[0m';

console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );<commit_msg>Fix app server<commit_after>var express		= require('express');
var fs			= require('fs');
var io			= require('socket.io');
var crypto		= require('crypto');

var app			= express.createServer();
var staticDir	= express.static;

io				= io.listen(app);

var opts = {
	port: process.env.PORT || 1948,
	baseDir : __dirname + '/../../../'
};

io.sockets.on('connection', function(socket) {
	socket.on('slidechanged', function(slideData) {
		if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return;
		if (createHash(slideData.secret) === slideData.socketId) {
			slideData.secret = null;
			socket.broadcast.emit(slideData.socketId, slideData);
		};
	});
});

app.configure(function() {
	[ 'static/css', 'static/js', 'static/plugin', 'static/lib', 'static/img' ].forEach(function(dir) {
		app.use('/' + dir, staticDir(opts.baseDir + dir));
	});
});

app.get("/", function(req, res) {
	res.writeHead(200, {'Content-Type': 'text/html'});
	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
});

app.get("/token", function(req,res) {
	var ts = new Date().getTime();
	var rand = Math.floor(Math.random()*9999999);
	var secret = ts.toString() + rand.toString();
	res.send({secret: secret, socketId: createHash(secret)});
});

var createHash = function(secret) {
	var cipher = crypto.createCipher('blowfish', secret);
	return(cipher.final('hex'));
};

// Actually listen
app.listen(opts.port || null);

var brown = '\033[33m',
	green = '\033[32m',
	reset = '\033[0m';

console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );<|endoftext|>


<commit_before><commit_msg>Agrego vector de tangentes al camino. El vector de texturas ya lo tenía.<commit_after><|endoftext|>


<commit_before><commit_msg>Fixed potential bug<commit_after><|endoftext|>


<commit_before><commit_msg>Adding a "If you've talked to someone before"<commit_after><|endoftext|>


<commit_before><commit_msg>removed sink from server impred<commit_after><|endoftext|>


<commit_before>const Long = require('long')

const INT8_SIZE = 1
const INT16_SIZE = 2
const INT32_SIZE = 4
const INT64_SIZE = 8

const MOST_SIGNIFICANT_BIT = 0x80 // 128
const OTHER_BITS = 0x7f // 127
const UNSIGNED_INT32_MAX_NUMBER = 0xffffff80
const UNSIGNED_INT64_MAX_NUMBER = Long.fromBytes([-1, -1, -1, -1, -1, -1, -1, -128])

module.exports = class Encoder {
  static encodeZigZag(value) {
    return (value << 1) ^ (value >> 31)
  }

  static encodeZigZag64(value) {
    const longValue = Long.fromValue(value)
    return longValue.shiftLeft(1).xor(longValue.shiftRight(63))
  }
  constructor() {
    this.buffer = Buffer.alloc(0)
  }

  writeInt8(value) {
    const tempBuffer = Buffer.alloc(INT8_SIZE)
    tempBuffer.writeInt8(value)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeInt16(value) {
    const tempBuffer = Buffer.alloc(INT16_SIZE)
    tempBuffer.writeInt16BE(value)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeInt32(value) {
    const tempBuffer = Buffer.alloc(INT32_SIZE)
    tempBuffer.writeInt32BE(value)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeInt64(value) {
    const tempBuffer = Buffer.alloc(INT64_SIZE)
    const longValue = Long.fromValue(value)
    tempBuffer.writeInt32BE(longValue.getHighBits(), 0)
    tempBuffer.writeInt32BE(longValue.getLowBits(), 4)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeBoolean(value) {
    value ? this.writeInt8(1) : this.writeInt8(0)
    return this
  }

  writeString(value) {
    if (value == null) {
      this.writeInt16(-1)
      return this
    }

    const byteLength = Buffer.byteLength(value, 'utf8')
    this.writeInt16(byteLength)
    const tempBuffer = Buffer.alloc(byteLength)
    tempBuffer.write(value, 0, byteLength, 'utf8')
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeBytes(value) {
    if (value == null) {
      this.writeInt32(-1)
      return this
    }

    if (Buffer.isBuffer(value)) {
      // raw bytes
      this.writeInt32(value.length)
      this.buffer = Buffer.concat([this.buffer, value])
    } else {
      const valueToWrite = String(value)
      const byteLength = Buffer.byteLength(valueToWrite, 'utf8')
      this.writeInt32(byteLength)
      const tempBuffer = Buffer.alloc(byteLength)
      tempBuffer.write(valueToWrite, 0, byteLength, 'utf8')
      this.buffer = Buffer.concat([this.buffer, tempBuffer])
    }

    return this
  }

  writeEncoder(value) {
    if (value instanceof Encoder !== true) {
      throw new Error('value should be an instance of Encoder')
    }

    this.buffer = Buffer.concat([this.buffer, value.buffer])
    return this
  }

  writeArray(array, type) {
    this.writeInt32(array.length)
    array.forEach(value => {
      switch (type || typeof value) {
        case 'int32':
        case 'number':
          this.writeInt32(value)
          break
        case 'string':
          this.writeString(value)
          break
        case 'object':
          this.writeEncoder(value)
          break
      }
    })
    return this
  }

  // Based on:
  // https://github.com/addthis/stream-lib/blob/master/src/main/java/com/clearspring/analytics/util/Varint.java#L106
  writeSignedVarInt32(value) {
    const byteArray = []
    let encodedValue = Encoder.encodeZigZag(value)

    while ((encodedValue & UNSIGNED_INT32_MAX_NUMBER) !== 0) {
      byteArray.push((encodedValue & OTHER_BITS) | MOST_SIGNIFICANT_BIT)
      encodedValue >>>= 7
    }

    byteArray.push(encodedValue & OTHER_BITS)
    this.buffer = Buffer.concat([this.buffer, Buffer.from(byteArray)])
    return this
  }

  writeSignedVarInt64(value) {
    const byteArray = []
    let longValue = Encoder.encodeZigZag64(value)

    while (longValue.and(UNSIGNED_INT64_MAX_NUMBER).notEquals(Long.fromInt(0))) {
      byteArray.push(
        longValue
          .and(OTHER_BITS)
          .or(MOST_SIGNIFICANT_BIT)
          .toInt()
      )
      longValue = longValue.shiftRightUnsigned(7)
    }

    byteArray.push(longValue.toInt())

    this.buffer = Buffer.concat([this.buffer, Buffer.from(byteArray)])
    return this
  }

  size() {
    return Buffer.byteLength(this.buffer)
  }

  toJSON() {
    return this.buffer.toJSON()
  }
}
<commit_msg>Rename writeSignedVarInt32 to varInt and writeSignedVarInt64 to varLong<commit_after>const Long = require('long')

const INT8_SIZE = 1
const INT16_SIZE = 2
const INT32_SIZE = 4
const INT64_SIZE = 8

const MOST_SIGNIFICANT_BIT = 0x80 // 128
const OTHER_BITS = 0x7f // 127
const UNSIGNED_INT32_MAX_NUMBER = 0xffffff80
const UNSIGNED_INT64_MAX_NUMBER = Long.fromBytes([-1, -1, -1, -1, -1, -1, -1, -128])

module.exports = class Encoder {
  static encodeZigZag(value) {
    return (value << 1) ^ (value >> 31)
  }

  static encodeZigZag64(value) {
    const longValue = Long.fromValue(value)
    return longValue.shiftLeft(1).xor(longValue.shiftRight(63))
  }
  constructor() {
    this.buffer = Buffer.alloc(0)
  }

  writeInt8(value) {
    const tempBuffer = Buffer.alloc(INT8_SIZE)
    tempBuffer.writeInt8(value)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeInt16(value) {
    const tempBuffer = Buffer.alloc(INT16_SIZE)
    tempBuffer.writeInt16BE(value)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeInt32(value) {
    const tempBuffer = Buffer.alloc(INT32_SIZE)
    tempBuffer.writeInt32BE(value)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeInt64(value) {
    const tempBuffer = Buffer.alloc(INT64_SIZE)
    const longValue = Long.fromValue(value)
    tempBuffer.writeInt32BE(longValue.getHighBits(), 0)
    tempBuffer.writeInt32BE(longValue.getLowBits(), 4)
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeBoolean(value) {
    value ? this.writeInt8(1) : this.writeInt8(0)
    return this
  }

  writeString(value) {
    if (value == null) {
      this.writeInt16(-1)
      return this
    }

    const byteLength = Buffer.byteLength(value, 'utf8')
    this.writeInt16(byteLength)
    const tempBuffer = Buffer.alloc(byteLength)
    tempBuffer.write(value, 0, byteLength, 'utf8')
    this.buffer = Buffer.concat([this.buffer, tempBuffer])
    return this
  }

  writeBytes(value) {
    if (value == null) {
      this.writeInt32(-1)
      return this
    }

    if (Buffer.isBuffer(value)) {
      // raw bytes
      this.writeInt32(value.length)
      this.buffer = Buffer.concat([this.buffer, value])
    } else {
      const valueToWrite = String(value)
      const byteLength = Buffer.byteLength(valueToWrite, 'utf8')
      this.writeInt32(byteLength)
      const tempBuffer = Buffer.alloc(byteLength)
      tempBuffer.write(valueToWrite, 0, byteLength, 'utf8')
      this.buffer = Buffer.concat([this.buffer, tempBuffer])
    }

    return this
  }

  writeEncoder(value) {
    if (value instanceof Encoder !== true) {
      throw new Error('value should be an instance of Encoder')
    }

    this.buffer = Buffer.concat([this.buffer, value.buffer])
    return this
  }

  writeArray(array, type) {
    this.writeInt32(array.length)
    array.forEach(value => {
      switch (type || typeof value) {
        case 'int32':
        case 'number':
          this.writeInt32(value)
          break
        case 'string':
          this.writeString(value)
          break
        case 'object':
          this.writeEncoder(value)
          break
      }
    })
    return this
  }

  // Based on:
  // https://github.com/addthis/stream-lib/blob/master/src/main/java/com/clearspring/analytics/util/Varint.java#L106
  writeVarInt(value) {
    const byteArray = []
    let encodedValue = Encoder.encodeZigZag(value)

    while ((encodedValue & UNSIGNED_INT32_MAX_NUMBER) !== 0) {
      byteArray.push((encodedValue & OTHER_BITS) | MOST_SIGNIFICANT_BIT)
      encodedValue >>>= 7
    }

    byteArray.push(encodedValue & OTHER_BITS)
    this.buffer = Buffer.concat([this.buffer, Buffer.from(byteArray)])
    return this
  }

  writeVarLong(value) {
    const byteArray = []
    let longValue = Encoder.encodeZigZag64(value)

    while (longValue.and(UNSIGNED_INT64_MAX_NUMBER).notEquals(Long.fromInt(0))) {
      byteArray.push(
        longValue
          .and(OTHER_BITS)
          .or(MOST_SIGNIFICANT_BIT)
          .toInt()
      )
      longValue = longValue.shiftRightUnsigned(7)
    }

    byteArray.push(longValue.toInt())

    this.buffer = Buffer.concat([this.buffer, Buffer.from(byteArray)])
    return this
  }

  size() {
    return Buffer.byteLength(this.buffer)
  }

  toJSON() {
    return this.buffer.toJSON()
  }
}
<|endoftext|>


<commit_before><commit_msg>Use secure connection<commit_after><|endoftext|>


<commit_before><commit_msg>marlins and nationals postings<commit_after><|endoftext|>


<commit_before><commit_msg>Fixed silly mistake in auto scrolling code<commit_after><|endoftext|>


<commit_before><commit_msg>ポップアップにバナーとリンクを表示<commit_after><|endoftext|>


<commit_before><commit_msg>filename in local for closing over<commit_after><|endoftext|>


<commit_before><commit_msg>3k turnout<commit_after><|endoftext|>


<commit_before><commit_msg>fix: Hide chart legend<commit_after><|endoftext|>


<commit_before><commit_msg>Fix: startZoom not applended, zoomend event<commit_after><|endoftext|>


<commit_before><commit_msg>fix for #31<commit_after><|endoftext|>


<commit_before><commit_msg>nix commented chat height js<commit_after><|endoftext|>


<commit_before><commit_msg>Fixed alive flag glitches<commit_after><|endoftext|>


<commit_before><commit_msg>Updated main.js with typeSuggestor code<commit_after><|endoftext|>


<commit_before><commit_msg>快捷键：注释和取消注释支持任意输入框<commit_after><|endoftext|>


<commit_before><commit_msg>自动化随机测试 和 自定义请求头：报错时显示布局<commit_after><|endoftext|>


<commit_before><commit_msg>added couric<commit_after><|endoftext|>


<commit_before><commit_msg>fix proposal display<commit_after><|endoftext|>


<commit_before><commit_msg>Update main.js<commit_after><|endoftext|>


<commit_before><commit_msg>Fixed the demo<commit_after><|endoftext|>


<commit_before><commit_msg>Fix some more JS bugs....<commit_after><|endoftext|>


<commit_before><commit_msg>API parsing function added<commit_after><|endoftext|>


<commit_before><commit_msg>Update JS for medication modal content added in e2a776f<commit_after><|endoftext|>


<commit_before><commit_msg>Forgot a localised.<commit_after><|endoftext|>


<commit_before><commit_msg>Move about js to jquery for browser compatibility<commit_after><|endoftext|>


<commit_before><commit_msg>Reduce duplicated code by adding a function to create repo list items.<commit_after><|endoftext|>


<commit_before><commit_msg>update to Schedule in page.js<commit_after><|endoftext|>


<commit_before><commit_msg>Add sub-pixel shift and remove stroke from light meter<commit_after><|endoftext|>


<commit_before><commit_msg>bug fix<commit_after><|endoftext|>


<commit_before><commit_msg>Focus on new tab when created using button.<commit_after><|endoftext|>


<commit_before><commit_msg>Added override classes<commit_after><|endoftext|>


<commit_before><commit_msg>create a wish<commit_after><|endoftext|>


<commit_before>// keymage.js - Javascript keyboard event handling

(function(global, exports, undefined) {
    function _bind(fn, ctx) {
        return function() {
            return fn.apply(ctx, arguments);
        };
    }

    // Defining all keys
    var MODPROPS = ['shiftKey', 'ctrlKey', 'altKey', 'metaKey'];
    var MODS = {
        'shift': 'shiftKey',
        'ctrl': 'ctrlKey', 'control': 'ctrlKey',
        'alt': 'altKey', 'option': 'altKey',
        'win': 'metaKey', 'cmd': 'metaKey', 'super': 'metaKey',
                          'meta': 'metaKey',
        // default modifier for os x is cmd and for others is ctrl
        'defmod': ~navigator.userAgent.indexOf('Mac OS X') ?
            'metaKey' : 'ctrlKey'
        };

    var KEYS = {
        'backspace': 8,
        'tab': 9,
        'enter': 13, 'return': 13,
        'pause': 19,
        'caps': 20, 'caps-lock': 20,
        'escape': 27, 'esc': 27,
        'space': 32,
        'pgup': 33, 'page-up': 33,
        'pgdown': 34, 'page-down': 34,
        'end': 35,
        'home': 36,
        'ins': 45, 'insert': 45,
        'del': 46, 'delete': 46,

        'left': 37,
        'up': 38,
        'right': 39,
        'down': 40,

        '*': 106,
        '+': 107, 'plus': 107,
        '-': 109, 'minus': 109,
        ';': 186,
        '=': 187,
        ',': 188,
        '.': 190,
        '/': 191,
        '`': 192,
        '[': 219,
        '\\': 220,
        ']': 221,
        "'": 222
    };

    var i;
    // numpad
    for (i = 0; i < 10; i++) {
        KEYS['num-' + i] = i + 95;
    }
    // top row 0-9
    for (i = 0; i < 10; i++) {
        KEYS[i.toString()] = i + 48;
    }
    // f1-f24
    for (i = 1; i < 25; i++) {
        KEYS['f' + i] = i + 111;
    }
    // alphabet
    for (i = 65; i < 91; i++) {
        KEYS[String.fromCharCode(i).toLowerCase()] = i;
    }

    // -----------------------
    // Actual work is done here

    var chains = [];

    var STATE = {
        MATCH: 1,
        PARTIAL: 2,
        INTERRUPT: 3
    };

    function parseKeystring(keystring) {
        var keychain = [];
        var combs = keystring.split(' ');

        var keySplit, keyInfo, key, keyCode, mod;
        for (var i = 0; i < combs.length; i++) {
            keySplit = combs[i].split('-');

            key = keySplit[keySplit.length - 1];
            keyInfo = {keyCode: KEYS[key]};

            if (!keyInfo.keyCode) {
                throw 'Unknown key "' + key + '" in keystring "' +
                    keystring + '"';
            }

            for (var j = 0; j < keySplit.length - 1; j++) {
                key = keySplit[j];
                mod = MODS[key];
                if (!mod) {
                    throw 'Unknown modifier "' + key + '" in keystring "' +
                        keystring + '"';
                }
                keyInfo[mod] = true;
            }

            keychain.push(keyInfo);
        }

        keychain.original = keystring;
        return keychain;
    }

    function eventInfo(e) {
        var info = {keyCode: e.keyCode};
        for (var i = 0; i < MODPROPS.length; i++) {
            if (e[MODPROPS[i]]) {
                info[MODPROPS[i]] = true;
            }
        }
        return info;
    }

    function infoEqual(i1, i2) {
        var k;
        var eq = true;

        for (k in i1) {
            eq = eq && i1[k] == i2[k];
        }

        for (k in i2) {
            eq = eq && i2[k] == i1[k];
        }

        return eq;
    }

    var sequence = [];
    function dispatch(e) {
        var info = eventInfo(e);
        var seq = sequence.slice();
        seq.push(info);
        var chain, handler;
        var state = STATE.MATCH;

        for (var i = 0; i < chains.length; i++) {
            chain = chains[i][0];
            handler = chains[i][1];

            state = STATE.MATCH;
            for (var j = 0; j < seq.length; j++) {
                if (!infoEqual(seq[j], chain[j])) {
                    state = STATE.INTERRUPT;
                    break;
                }
            }

            if (state === STATE.MATCH) {
                // If we've got a match, but sequence is smaller than chain,
                // this is not a real match.
                if (seq.length < chain.length) {
                    state = STATE.PARTIAL;
                }

                // By breaking in any case here we explicitly have no
                // possibility to support chain which is part of other
                // chain.
                break;
            }
        }

        switch (state) {
        case STATE.INTERRUPT:
            sequence = [];
            break;
        case STATE.PARTIAL:
            sequence = seq;
            break;
        case STATE.MATCH:
            sequence = [];
            for (i = 0; i < [handler].length; i++) {
                // var handler = [handler][i];
                var res = handler(e, chain.original);
                if (res === false) {
                    e.preventDefault();
                }
            }
        }
    }

    var keymage = exports.keymage = function(scope, keystring, fn) {
        if (keystring === undefined && fn === undefined) {
            return function(keystring, fn) {
                return keymage(scope, keystring, fn);
            };
        }

        if (fn === undefined && typeof keystring === 'function') {
            fn = keystring;
            keystring = scope;
            scope = 'all';
        }

        chains.push([parseKeystring(keystring), fn]);
    };

    window.addEventListener('keydown', dispatch, false);

    return keymage;

})(this,
   (typeof module !== 'undefined' && module.exports ? module.exports : this));
<commit_msg>add scopes<commit_after>// keymage.js - Javascript keyboard event handling

(function(global, exports, undefined) {
    function _bind(fn, ctx) {
        return function() {
            return fn.apply(ctx, arguments);
        };
    }

    // Defining all keys
    var MODPROPS = ['shiftKey', 'ctrlKey', 'altKey', 'metaKey'];
    var MODS = {
        'shift': 'shiftKey',
        'ctrl': 'ctrlKey', 'control': 'ctrlKey',
        'alt': 'altKey', 'option': 'altKey',
        'win': 'metaKey', 'cmd': 'metaKey', 'super': 'metaKey',
                          'meta': 'metaKey',
        // default modifier for os x is cmd and for others is ctrl
        'defmod': ~navigator.userAgent.indexOf('Mac OS X') ?
            'metaKey' : 'ctrlKey'
        };

    var KEYS = {
        'backspace': 8,
        'tab': 9,
        'enter': 13, 'return': 13,
        'pause': 19,
        'caps': 20, 'caps-lock': 20,
        'escape': 27, 'esc': 27,
        'space': 32,
        'pgup': 33, 'page-up': 33,
        'pgdown': 34, 'page-down': 34,
        'end': 35,
        'home': 36,
        'ins': 45, 'insert': 45,
        'del': 46, 'delete': 46,

        'left': 37,
        'up': 38,
        'right': 39,
        'down': 40,

        '*': 106,
        '+': 107, 'plus': 107,
        '-': 109, 'minus': 109,
        ';': 186,
        '=': 187,
        ',': 188,
        '.': 190,
        '/': 191,
        '`': 192,
        '[': 219,
        '\\': 220,
        ']': 221,
        "'": 222
    };

    var i;
    // numpad
    for (i = 0; i < 10; i++) {
        KEYS['num-' + i] = i + 95;
    }
    // top row 0-9
    for (i = 0; i < 10; i++) {
        KEYS[i.toString()] = i + 48;
    }
    // f1-f24
    for (i = 1; i < 25; i++) {
        KEYS['f' + i] = i + 111;
    }
    // alphabet
    for (i = 65; i < 91; i++) {
        KEYS[String.fromCharCode(i).toLowerCase()] = i;
    }

    // Sequence matching states

    var STATE = {
        MATCH: 1,
        PARTIAL: 2,
        INTERRUPT: 3
    };

    // -----------------------
    // Actual work is done here

    var allChains = {
        all: []
    };

    var currentScope = 'all';

    function parseKeystring(keystring) {
        var keychain = [];
        var combs = keystring.split(' ');

        var keySplit, keyInfo, key, keyCode, mod;
        for (var i = 0; i < combs.length; i++) {
            keySplit = combs[i].split('-');

            key = keySplit[keySplit.length - 1];
            keyInfo = {keyCode: KEYS[key]};

            if (!keyInfo.keyCode) {
                throw 'Unknown key "' + key + '" in keystring "' +
                    keystring + '"';
            }

            for (var j = 0; j < keySplit.length - 1; j++) {
                key = keySplit[j];
                mod = MODS[key];
                if (!mod) {
                    throw 'Unknown modifier "' + key + '" in keystring "' +
                        keystring + '"';
                }
                keyInfo[mod] = true;
            }

            keychain.push(keyInfo);
        }

        keychain.original = keystring;
        return keychain;
    }

    function eventInfo(e) {
        var info = {keyCode: e.keyCode};
        for (var i = 0; i < MODPROPS.length; i++) {
            if (e[MODPROPS[i]]) {
                info[MODPROPS[i]] = true;
            }
        }
        return info;
    }

    function infoEqual(i1, i2) {
        var k;
        var eq = true;

        for (k in i1) {
            eq = eq && i1[k] == i2[k];
        }

        for (k in i2) {
            eq = eq && i2[k] == i1[k];
        }

        return eq;
    }

    var sequence = [];

    function dispatch(e) {
        var info = eventInfo(e);
        var seq = sequence.slice();
        seq.push(info);
        var chain, handler;
        var state = STATE.MATCH;

        var chains = allChains[currentScope];
        if (currentScope != 'all') {
            chains = chains.slice().concat(allChains.all);
        }

        for (var i = 0; i < chains.length; i++) {
            chain = chains[i][0];
            handler = chains[i][1];

            state = STATE.MATCH;
            for (var j = 0; j < seq.length; j++) {
                if (!infoEqual(seq[j], chain[j])) {
                    state = STATE.INTERRUPT;
                    break;
                }
            }

            if (state === STATE.MATCH) {
                // If we've got a match, but sequence is smaller than chain,
                // this is not a real match.
                if (seq.length < chain.length) {
                    state = STATE.PARTIAL;
                }

                // By breaking in any case here we explicitly have no
                // possibility to support chain which is part of other
                // chain.
                break;
            }
        }

        switch (state) {
        case STATE.INTERRUPT:
            sequence = [];
            break;
        case STATE.PARTIAL:
            sequence = seq;
            break;
        case STATE.MATCH:
            sequence = [];
            for (i = 0; i < [handler].length; i++) {
                // var handler = [handler][i];
                var res = handler(e, chain.original);
                if (res === false) {
                    e.preventDefault();
                }
            }
        }
    }

    var keymage = exports.keymage = function(scope, keystring, fn) {
        if (keystring === undefined && fn === undefined) {
            return function(keystring, fn) {
                return keymage(scope, keystring, fn);
            };
        }

        if (fn === undefined && typeof keystring === 'function') {
            fn = keystring;
            keystring = scope;
            scope = 'all';
        }

        var chains = allChains[scope] || (allChains[scope] = []);
        chains.push([parseKeystring(keystring), fn]);
    };

    keymage.setScope = function(scope) {
        currentScope = scope;
    };
    keymage.getScope = function() { return currentScope; };

    window.addEventListener('keydown', dispatch, false);
    return keymage;
})(this,
   (typeof module !== 'undefined' && module.exports ? module.exports : this));
<|endoftext|>


<commit_before><commit_msg>Create ro.js<commit_after><|endoftext|>


<commit_before>var interpolate = require('interpolate'),
    capitalize = require('capitalize'),
    isWindow = require('isWindow'),
    isDocument = require('isDocument'),
    style = require('computedStyle'),
    from_px = require('from_px'),
    to_px = require('to_px'),
    css = require('css')

var dimensions = module.exports = function (el) {
  if(!(this instanceof dimensions)) return new dimensions(el);

  this.el = el

  this.innerWidth = this.inner('width', 'left', 'right').bind(this)
  this.innerHeight = this.inner('height', 'top', 'bottom').bind(this)
  this.height = this.wh('height', 'top', 'bottom').bind(this)
  this.width = this.wh('width', 'left', 'right').bind(this)
  this.outerWidth = this.outer('width', 'left', 'right').bind(this)
  this.outerHeight = this.outer('height', 'top', 'bottom').bind(this)
}

dimensions.prototype.from_window = function (name) {
  return this.el.document.documentElement[interpolate('client%s', name)]
}

// Get document width or height
// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
// whichever is greatest
dimensions.prototype.from_document = function (name) {
  name = capitalize(name)

  var scroll_name = interpolate('scroll%s', name)
  var offset_name = interpolate('offset%s', name)
  var client_name = interpolate('client%s', name)

  var body_scroll = this.el.body[scroll_name]
  var document_scroll = this.el.documentElement[scroll_name]

  var body_offset = this.el.body[offset_name]
  var document_offset = this.el.documentElement[offset_name]

  var body_client = this.el.body[client_name]
  var document_client = this.el.documentElement[client_name]

  return Math.max(body_scroll, document_scroll, body_offset, document_offset, body_client, document_client)
}

dimensions.prototype.inner = function (name, from, to) {
  return function () {
    if(isWindow(this.el))
      return this.from_window(name)

    if(isDocument(this.el))
      return this.from_document(name)

    var content = from_px(style(this.el, name))
    var boxSizing = style(this.el, 'box-sizing')
    var padding = 0
    var border = 0

    // if the width property already includes padding and not the border
    // then just get the width computedValue
    if(boxSizing === 'padding-box')
      return content

    // if the width includes the border value AND padding
    // calculate the border to then remove it from the width value
    if(boxSizing === 'border-box') {
      border += from_px(style(this.el, interpolate('border-%s-%s', from, name)))
      border += from_px(style(this.el, interpolate('border-%s-%s', to, name)))
    }

    // if the width value doesn't include the padding, calculate it
    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))

    return content + padding - border
  }
}

dimensions.prototype.wh = function (name, from, to) {
  return function (value) {
    if(isWindow(this.el))
      return this.from_window(name)

    if(isDocument(this.el))
      return this.from_document(name)

    return type(value) !== 'undefined' ? this.wh.set(name, value) : this.wh.get(name, from, to)
  }
}

dimensions.prototype.wh.set = function (name, value) {
  var to_set = Object.create(null)
  value = to_px(value)

  to_set[name] = value
  css(this.el, to_set)
}

dimensions.prototype.wh.get = function (name, from, to) {
  var content = from_px(style(this.el, name))
  var boxSizing = style(this.el, 'box-sizing')
  var padding = 0
  var border = 0

  if((boxSizing === 'content-box') || !boxSizing)
    return content

  // if the width property includes padding
  if(boxSizing === 'padding-box') {
    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))
  }

  // if the width property includes padding AND border
  if(boxSizing === 'border-box') {
    border += from_px(style(this.el, interpolate('border-%s-%s', from, name)))
    border += from_px(style(this.el, interpolate('border-%s-%s', to, name)))
    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))
  }

  return content - padding - border
}

dimensions.prototype.outer = function (name, from, to) {
  return function (marginplus) {
    if(isWindow(this.el))
      return this.from_window(name)

    if(isDocument(this.el))
      return this.from_document(name)

    var content = from_px(style(this.el, name))
    var boxSizing = style(this.el, 'box-sizing')
    var padding = 0
    var border = 0
    var margin = 0

    // width already includes border and padding
    if(boxSizing === 'border-box' && !marginplus)
      return content

    margin += from_px(style(this.el, interpolate('margin-%', from)))
    margin += from_px(style(this.el, interpolate('margin-%', to)))

    // width already includes border and padding
    if(boxSizing === 'border-box' && marginplus)
      return content + margin

    border += from_px(style(this.el, interpolate('border-%s-%s', from, name)))
    border += from_px(style(this.el, interpolate('border-%s-%s', to, name)))

    // width already includes padding (but NOT border)
    if(boxSizing === 'padding-box' & !marginplus)
      return content + border

    // width already includes padding (but NOT border)
    if(boxSizing === 'padding-box' & marginplus)
      return content + border + margin

    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))

    if(!marginplus)
      return content + border + margin

    return content + border + margin + padding
  }
}<commit_msg>margin -> padding<commit_after>var interpolate = require('interpolate'),
    capitalize = require('capitalize'),
    isWindow = require('isWindow'),
    isDocument = require('isDocument'),
    style = require('computedStyle'),
    from_px = require('from_px'),
    to_px = require('to_px'),
    css = require('css')

var dimensions = module.exports = function (el) {
  if(!(this instanceof dimensions)) return new dimensions(el);

  this.el = el

  this.innerWidth = this.inner('width', 'left', 'right').bind(this)
  this.innerHeight = this.inner('height', 'top', 'bottom').bind(this)
  this.height = this.wh('height', 'top', 'bottom').bind(this)
  this.width = this.wh('width', 'left', 'right').bind(this)
  this.outerWidth = this.outer('width', 'left', 'right').bind(this)
  this.outerHeight = this.outer('height', 'top', 'bottom').bind(this)
}

dimensions.prototype.from_window = function (name) {
  return this.el.document.documentElement[interpolate('client%s', name)]
}

// Get document width or height
// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
// whichever is greatest
dimensions.prototype.from_document = function (name) {
  name = capitalize(name)

  var scroll_name = interpolate('scroll%s', name)
  var offset_name = interpolate('offset%s', name)
  var client_name = interpolate('client%s', name)

  var body_scroll = this.el.body[scroll_name]
  var document_scroll = this.el.documentElement[scroll_name]

  var body_offset = this.el.body[offset_name]
  var document_offset = this.el.documentElement[offset_name]

  var body_client = this.el.body[client_name]
  var document_client = this.el.documentElement[client_name]

  return Math.max(body_scroll, document_scroll, body_offset, document_offset, body_client, document_client)
}

dimensions.prototype.inner = function (name, from, to) {
  return function () {
    if(isWindow(this.el))
      return this.from_window(name)

    if(isDocument(this.el))
      return this.from_document(name)

    var content = from_px(style(this.el, name))
    var boxSizing = style(this.el, 'box-sizing')
    var padding = 0
    var border = 0

    // if the width property already includes padding and not the border
    // then just get the width computedValue
    if(boxSizing === 'padding-box')
      return content

    // if the width includes the border value AND padding
    // calculate the border to then remove it from the width value
    if(boxSizing === 'border-box') {
      border += from_px(style(this.el, interpolate('border-%s-%s', from, name)))
      border += from_px(style(this.el, interpolate('border-%s-%s', to, name)))
    }

    // if the width value doesn't include the padding, calculate it
    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))

    return content + padding - border
  }
}

dimensions.prototype.wh = function (name, from, to) {
  return function (value) {
    if(isWindow(this.el))
      return this.from_window(name)

    if(isDocument(this.el))
      return this.from_document(name)

    return type(value) !== 'undefined' ? this.wh.set(name, value) : this.wh.get(name, from, to)
  }
}

dimensions.prototype.wh.set = function (name, value) {
  var to_set = Object.create(null)
  value = to_px(value)

  to_set[name] = value
  css(this.el, to_set)
}

dimensions.prototype.wh.get = function (name, from, to) {
  var content = from_px(style(this.el, name))
  var boxSizing = style(this.el, 'box-sizing')
  var padding = 0
  var border = 0

  if((boxSizing === 'content-box') || !boxSizing)
    return content

  // if the width property includes padding
  if(boxSizing === 'padding-box') {
    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))
  }

  // if the width property includes padding AND border
  if(boxSizing === 'border-box') {
    border += from_px(style(this.el, interpolate('border-%s-%s', from, name)))
    border += from_px(style(this.el, interpolate('border-%s-%s', to, name)))
    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))
  }

  return content - padding - border
}

dimensions.prototype.outer = function (name, from, to) {
  return function (marginplus) {
    if(isWindow(this.el))
      return this.from_window(name)

    if(isDocument(this.el))
      return this.from_document(name)

    var content = from_px(style(this.el, name))
    var boxSizing = style(this.el, 'box-sizing')
    var padding = 0
    var border = 0
    var margin = 0

    // width already includes border and padding
    if(boxSizing === 'border-box' && !marginplus)
      return content

    margin += from_px(style(this.el, interpolate('margin-%', from)))
    margin += from_px(style(this.el, interpolate('margin-%', to)))

    // width already includes border and padding
    if(boxSizing === 'border-box' && marginplus)
      return content + margin

    border += from_px(style(this.el, interpolate('border-%s-%s', from, name)))
    border += from_px(style(this.el, interpolate('border-%s-%s', to, name)))

    // width already includes padding (but NOT border)
    if(boxSizing === 'padding-box' & !marginplus)
      return content + border

    // width already includes padding (but NOT border)
    if(boxSizing === 'padding-box' & marginplus)
      return content + border + margin

    padding += from_px(style(this.el, interpolate('padding-%', from)))
    padding += from_px(style(this.el, interpolate('padding-%', to)))

    if(!marginplus)
      return content + border + padding

    return content + border + margin + padding
  }
}<|endoftext|>


<commit_before>/**
 * The Dispatcher module is responsible for dealing with events and their handlers.
 *
 * @module core
 * @submodule dispatcher
 */

var dispatcher = (function() {
  /**
   * Contains the list of event listeners grouped by event type.
   *
   * @private
   * @type {Object}
   */
  var listeners = {},
      editableSelector;

  /**
   * Sets up events that are triggered on modifying an element.
   *
   * @method setupElementEvents
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupElementEvents = function($document, notifier) {
    $document.on('focus.editable', editableSelector, function(event) {
      if(this.getAttribute(config.pastingAttribute)) return;
      notifier('focus', this);
    }).on('blur.editable', editableSelector, function(event) {
      if(this.getAttribute(config.pastingAttribute)) return;
      notifier('blur', this);
    }).on('copy.editable', editableSelector, function(event) {
      log('Copy');
      notifier('clipboard', this, 'copy', selectionWatcher.getFreshSelection());
    }).on('cut.editable', editableSelector, function(event) {
      log('Cut');
      notifier('clipboard', this, 'cut', selectionWatcher.getFreshSelection());
    }).on('paste.editable', editableSelector, function(event) {
      log('Paste');
      notifier('clipboard', this, 'paste', selectionWatcher.getFreshSelection());
    });
  };

  /**
   * Sets up events that are triggered on keyboard events.
   * Keyboard definitions are in {{#crossLink "Keyboard"}}{{/crossLink}}.
   *
   * @method setupKeyboardEvents
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupKeyboardEvents = function($document, notifier) {
    var dispatchSwitchEvent = function(event, element, direction) {
      var cursor;

      if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
        return;

      cursor = selectionWatcher.getSelection();
      if (!cursor || cursor.isSelection) return;

      // Detect if the browser moved the cursor in the next tick.
      // If the cursor stays at its position, fire the switch event.
      setTimeout(function() {
        var newCursor = selectionWatcher.forceCursor();
        if (newCursor.equals(cursor)) {
          event.preventDefault();
          event.stopPropagation();
          notifier('switch', element, direction, newCursor);
        }
      }, 1);
    };

    $document.on('keydown.editable', editableSelector, function(event) {
      keyboard.dispatchKeyEvent(event, this);
    });

    keyboard.on('left', function(event) {
      log('Left key pressed');
      dispatchSwitchEvent(event, this, 'before');
    }).on('up', function(event) {
      log('Up key pressed');
      dispatchSwitchEvent(event, this, 'before');
    }).on('right', function(event) {
      log('Right key pressed');
      dispatchSwitchEvent(event, this, 'after');
    }).on('down', function(event) {
      log('Down key pressed');
      dispatchSwitchEvent(event, this, 'after');
    }).on('tab', function(event) {
      log('Tab key pressed');
    }).on('shiftTab', function(event) {
      log('Shift+Tab key pressed');
    }).on('esc', function(event) {
      log('Esc key pressed');
    }).on('backspace', function(event) {
      log('Backspace key pressed');

      var range = selectionWatcher.getFreshRange();
      if (range.isCursor) {
        var cursor = range.getCursor();
        if ( cursor.isAtBeginning() ) {
          event.preventDefault();
          event.stopPropagation();
          notifier('merge', this, 'before', cursor);
        }
      }
    }).on('delete', function(event) {
      log('Delete key pressed');

      var range = selectionWatcher.getFreshRange();
      if (range.isCursor) {
        var cursor = range.getCursor();
        if(cursor.isAtTextEnd()) {
          event.preventDefault();
          event.stopPropagation();
          notifier('merge', this, 'after', cursor);
        }
      }
    }).on('enter', function(event) {
      log('Enter key pressed');

      event.preventDefault();
      event.stopPropagation();
      var range = selectionWatcher.getFreshRange();
      var cursor = range.forceCursor();

      if (cursor.isAtBeginning()) {
        notifier('insert', this, 'before', cursor);
      } else if(cursor.isAtTextEnd()) {
        notifier('insert', this, 'after', cursor);
      } else {
        notifier('split', this, cursor.before(), cursor.after(), cursor);
      }

    }).on('shiftEnter', function(event) {
      log('Shift+Enter key pressed');
      event.preventDefault();
      event.stopPropagation();
      var cursor = selectionWatcher.forceCursor();
      notifier('newline', this, cursor);
    });
  };

  /**
   * Sets up events that are triggered on a selection change.
   *
   * @method setupSelectionChangeEvents
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupSelectionChangeEvents = function($document, notifier) {
    var selectionDirty = false;
    var suppressSelectionChanges = false;

    // fires on mousemove (thats probably a bit too much)
    // catches changes like 'select all' from context menu
    $document.on('selectionchange.editable', function(event) {
      if (suppressSelectionChanges) {
        selectionDirty = true;
      } else {
        notifier();
      }
    });

    // listen for selection changes by mouse so we can
    // suppress the selectionchange event and only fire the
    // change event on mouseup
    $document.on('mousedown.editable', editableSelector, function(event) {
      if (config.mouseMoveSelectionChanges === false) {
        suppressSelectionChanges = true;

        // Without this timeout the previous selection is active
        // until the mouseup event (no. not good).
        setTimeout(notifier, 0);
      }

      $document.on('mouseup.editableSelection', function(event) {
        $document.off('.editableSelection');
        suppressSelectionChanges = false;

        if (selectionDirty) {
          selectionDirty = false;
          notifier();
        }
      });
    });
  };

  /**
   * Fallback solution to support selection change events on browsers that don't
   * support selectionChange.
   *
   * @method setupSelectionChangeFallback
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupSelectionChangeFallback = function($document, notifier) {
    // listen for selection changes by mouse
    $document.on('mouseup.editableSelection', function(event) {

      // In Opera when clicking outside of a block
      // it does not update the selection as it should
      // without the timeout
      setTimeout(notifier, 0);
    });

    // listen for selection changes by keys
    $document.on('keyup.editable', editableSelector, function(event) {

      // when pressing Command + Shift + Left for example the keyup is only triggered
      // after at least two keys are released. Strange. The culprit seems to be the
      // Command key. Do we need a workaround?
      notifier();
    });
  };

  return {
    addListener: function(event, listener) {
      if (listeners[event] === undefined) {
        listeners[event] = [];
      }

      listeners[event].unshift(listener);
    },

    removeListener: function(event, listener) {
      var eventListeners = listeners[event];
      if (eventListeners === undefined) return;

      for (var i=0, len=eventListeners.length; i < len; i++) {
        if (eventListeners[i] === listener){
          eventListeners.splice(i, 1);
          break;
        }
      }
    },

    notifyListeners: function(event) {
      var eventListeners = listeners[event];
      if (eventListeners === undefined) return;

      for (var i=0, len=eventListeners.length; i < len; i++) {
        if(eventListeners[i].apply(
            Editable,
            Array.prototype.slice.call(arguments).splice(1)
        ) === false)
          break;
      }
    },

    /**
     * Sets up all events that Editable.JS is catching.
     *
     * @method setup
     */
    setup: function() {

      var $document = $(document);
      var eventType = null;
      editableSelector = '.' + config.editableClass;

      listeners = {};
      // TODO check the config.event object to prevent
      // registering invalid handlers
      for(eventType in config.event) {
        this.addListener(eventType, config.event[eventType]);
      }

      // setup all events notifications
      setupElementEvents($document, this.notifyListeners);
      setupKeyboardEvents($document, this.notifyListeners);

      // cache selectionChanged function for simplicity
      var selectionChanged = selectionWatcher.selectionChanged;

      if (browserFeatures.selectionchange) {
        setupSelectionChangeEvents($document, selectionChanged);
      } else {
        setupSelectionChangeFallback($document, selectionChanged);
      }
    }
  };
})();
<commit_msg>Change insert direction for empty editable<commit_after>/**
 * The Dispatcher module is responsible for dealing with events and their handlers.
 *
 * @module core
 * @submodule dispatcher
 */

var dispatcher = (function() {
  /**
   * Contains the list of event listeners grouped by event type.
   *
   * @private
   * @type {Object}
   */
  var listeners = {},
      editableSelector;

  /**
   * Sets up events that are triggered on modifying an element.
   *
   * @method setupElementEvents
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupElementEvents = function($document, notifier) {
    $document.on('focus.editable', editableSelector, function(event) {
      if(this.getAttribute(config.pastingAttribute)) return;
      notifier('focus', this);
    }).on('blur.editable', editableSelector, function(event) {
      if(this.getAttribute(config.pastingAttribute)) return;
      notifier('blur', this);
    }).on('copy.editable', editableSelector, function(event) {
      log('Copy');
      notifier('clipboard', this, 'copy', selectionWatcher.getFreshSelection());
    }).on('cut.editable', editableSelector, function(event) {
      log('Cut');
      notifier('clipboard', this, 'cut', selectionWatcher.getFreshSelection());
    }).on('paste.editable', editableSelector, function(event) {
      log('Paste');
      notifier('clipboard', this, 'paste', selectionWatcher.getFreshSelection());
    });
  };

  /**
   * Sets up events that are triggered on keyboard events.
   * Keyboard definitions are in {{#crossLink "Keyboard"}}{{/crossLink}}.
   *
   * @method setupKeyboardEvents
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupKeyboardEvents = function($document, notifier) {
    var dispatchSwitchEvent = function(event, element, direction) {
      var cursor;

      if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
        return;

      cursor = selectionWatcher.getSelection();
      if (!cursor || cursor.isSelection) return;

      // Detect if the browser moved the cursor in the next tick.
      // If the cursor stays at its position, fire the switch event.
      setTimeout(function() {
        var newCursor = selectionWatcher.forceCursor();
        if (newCursor.equals(cursor)) {
          event.preventDefault();
          event.stopPropagation();
          notifier('switch', element, direction, newCursor);
        }
      }, 1);
    };

    $document.on('keydown.editable', editableSelector, function(event) {
      keyboard.dispatchKeyEvent(event, this);
    });

    keyboard.on('left', function(event) {
      log('Left key pressed');
      dispatchSwitchEvent(event, this, 'before');
    }).on('up', function(event) {
      log('Up key pressed');
      dispatchSwitchEvent(event, this, 'before');
    }).on('right', function(event) {
      log('Right key pressed');
      dispatchSwitchEvent(event, this, 'after');
    }).on('down', function(event) {
      log('Down key pressed');
      dispatchSwitchEvent(event, this, 'after');
    }).on('tab', function(event) {
      log('Tab key pressed');
    }).on('shiftTab', function(event) {
      log('Shift+Tab key pressed');
    }).on('esc', function(event) {
      log('Esc key pressed');
    }).on('backspace', function(event) {
      log('Backspace key pressed');

      var range = selectionWatcher.getFreshRange();
      if (range.isCursor) {
        var cursor = range.getCursor();
        if ( cursor.isAtBeginning() ) {
          event.preventDefault();
          event.stopPropagation();
          notifier('merge', this, 'before', cursor);
        }
      }
    }).on('delete', function(event) {
      log('Delete key pressed');

      var range = selectionWatcher.getFreshRange();
      if (range.isCursor) {
        var cursor = range.getCursor();
        if(cursor.isAtTextEnd()) {
          event.preventDefault();
          event.stopPropagation();
          notifier('merge', this, 'after', cursor);
        }
      }
    }).on('enter', function(event) {
      log('Enter key pressed');

      event.preventDefault();
      event.stopPropagation();
      var range = selectionWatcher.getFreshRange();
      var cursor = range.forceCursor();

      if (cursor.isAtTextEnd()) {
        notifier('insert', this, 'after', cursor);
      } else if(cursor.isAtBeginning()) {
        notifier('insert', this, 'before', cursor);
      } else {
        notifier('split', this, cursor.before(), cursor.after(), cursor);
      }

    }).on('shiftEnter', function(event) {
      log('Shift+Enter key pressed');
      event.preventDefault();
      event.stopPropagation();
      var cursor = selectionWatcher.forceCursor();
      notifier('newline', this, cursor);
    });
  };

  /**
   * Sets up events that are triggered on a selection change.
   *
   * @method setupSelectionChangeEvents
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupSelectionChangeEvents = function($document, notifier) {
    var selectionDirty = false;
    var suppressSelectionChanges = false;

    // fires on mousemove (thats probably a bit too much)
    // catches changes like 'select all' from context menu
    $document.on('selectionchange.editable', function(event) {
      if (suppressSelectionChanges) {
        selectionDirty = true;
      } else {
        notifier();
      }
    });

    // listen for selection changes by mouse so we can
    // suppress the selectionchange event and only fire the
    // change event on mouseup
    $document.on('mousedown.editable', editableSelector, function(event) {
      if (config.mouseMoveSelectionChanges === false) {
        suppressSelectionChanges = true;

        // Without this timeout the previous selection is active
        // until the mouseup event (no. not good).
        setTimeout(notifier, 0);
      }

      $document.on('mouseup.editableSelection', function(event) {
        $document.off('.editableSelection');
        suppressSelectionChanges = false;

        if (selectionDirty) {
          selectionDirty = false;
          notifier();
        }
      });
    });
  };

  /**
   * Fallback solution to support selection change events on browsers that don't
   * support selectionChange.
   *
   * @method setupSelectionChangeFallback
   * @param {HTMLElement} $document: The document element.
   * @param {Function} notifier: The callback to be triggered when the event is caught.
   */
  var setupSelectionChangeFallback = function($document, notifier) {
    // listen for selection changes by mouse
    $document.on('mouseup.editableSelection', function(event) {

      // In Opera when clicking outside of a block
      // it does not update the selection as it should
      // without the timeout
      setTimeout(notifier, 0);
    });

    // listen for selection changes by keys
    $document.on('keyup.editable', editableSelector, function(event) {

      // when pressing Command + Shift + Left for example the keyup is only triggered
      // after at least two keys are released. Strange. The culprit seems to be the
      // Command key. Do we need a workaround?
      notifier();
    });
  };

  return {
    addListener: function(event, listener) {
      if (listeners[event] === undefined) {
        listeners[event] = [];
      }

      listeners[event].unshift(listener);
    },

    removeListener: function(event, listener) {
      var eventListeners = listeners[event];
      if (eventListeners === undefined) return;

      for (var i=0, len=eventListeners.length; i < len; i++) {
        if (eventListeners[i] === listener){
          eventListeners.splice(i, 1);
          break;
        }
      }
    },

    notifyListeners: function(event) {
      var eventListeners = listeners[event];
      if (eventListeners === undefined) return;

      for (var i=0, len=eventListeners.length; i < len; i++) {
        if(eventListeners[i].apply(
            Editable,
            Array.prototype.slice.call(arguments).splice(1)
        ) === false)
          break;
      }
    },

    /**
     * Sets up all events that Editable.JS is catching.
     *
     * @method setup
     */
    setup: function() {

      var $document = $(document);
      var eventType = null;
      editableSelector = '.' + config.editableClass;

      listeners = {};
      // TODO check the config.event object to prevent
      // registering invalid handlers
      for(eventType in config.event) {
        this.addListener(eventType, config.event[eventType]);
      }

      // setup all events notifications
      setupElementEvents($document, this.notifyListeners);
      setupKeyboardEvents($document, this.notifyListeners);

      // cache selectionChanged function for simplicity
      var selectionChanged = selectionWatcher.selectionChanged;

      if (browserFeatures.selectionchange) {
        setupSelectionChangeEvents($document, selectionChanged);
      } else {
        setupSelectionChangeFallback($document, selectionChanged);
      }
    }
  };
})();
<|endoftext|>


<commit_before>const monocleStore = require('../react/store/monocleStore');
const updateState = require('../react/actions/index').updateState;
const sendInitialState = require('../react/actions/index').sendInitialState;

const reactInterceptor = function(name, component) {
  return function(state, callback) {
    monocleStore.dispatch(updateState(name, state));
    return component.setState(state, callback);
  }
}

const grabInitialState = function(name, obj) {
  monocleStore.dispatch(sendInitialState(name, obj));
  return obj;
};

module.exports = {
  reactInterceptor,
  grabInitialState,
};
<commit_msg>fix linter issues with reactInterceptor<commit_after>const monocleStore = require('../react/store/monocleStore');
const updateState = require('../react/actions/index').updateState;
const sendInitialState = require('../react/actions/index').sendInitialState;

export function reactInterceptor(name, component) {
  return (state, callback) => {
    monocleStore.dispatch(updateState(name, state));
    return component.setState(state, callback);
  };
}

export function grabInitialState(name, obj) {
  monocleStore.dispatch(sendInitialState(name, obj));
  return obj;
}

module.exports = {
  reactInterceptor,
  grabInitialState,
};
<|endoftext|>