<commit_before>var EventEmitter = require('events').EventEmitter,
    inherit = require('inherit'),
    request = require('request'),
    vow = require('vow'),
    fs = require('fs');

var apiMethods = {
        sendPhoto: {
            gzip: true,
            file: 'photo'
        },
        sendAudio: {
            gzip: true,
            file: 'audio'
        },
        sendDocument: {
            gzip: true,
            file: 'document'
        },
        sendSticker: {
            gzip: true,
            file: 'sticker'
        },
        sendVideo: {
            gzip: true,
            file: 'video'
        }
    };

request = request.defaults({ json: true });

var VowTelegramBot = inherit(EventEmitter, {

    __constructor: function(options) {
        if (!options.token) {
            throw new Error('Telegram Bot Token is required parameter!');
        }

        this._offset = 0;
        this._url = 'https://api.telegram.org/bot' + options.token + '/';
        //this._webhook = options.webhook;

        this._apiMethods = apiMethods;

        if (options.polling) {
            this.polling(options.polling);
        }

        // this._configureWebHookServer();

    },

    polling: function(options) {
        this._pollingTimeout = options && options.timeout || 3;
        this._pollingLimit = options && options.limit || 100;
        this._startPolling();
    },

    /**
     * Use this method to receive incoming updates using long polling. An Array of Update objects is returned.
     * @param {Object} [params]
     * @param {Number} [params.timeout]
     * @param {Number} [params.limit]
     * @param {Number} [params.offset]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUpdates: function(params, onSuccess, onError) {

        var defer = vow.defer(),
            _this = this,
            timeout,
            offset,
            limit;

        if (params) {
            timeout = params.timeout;
            limit = params.limit;
            offset = params.offset;
        }

        this._request('getUpdates', {
                timeout: timeout || this._pollingTimeout,
                limit: limit || this._pollingLimit,
                offset: offset || this._offset + 1
            })
            .then(function(result) {
                var last = result[result.length - 1]
                last && (_this._offset = last.update_id);
                typeof onSuccess === 'function' && onSuccess(result);
                defer.resolve(result);
            })
            .fail(function(result) {
                typeof onError === 'function' && onError(result);
                defer.reject(result);
            });

        return defer.promise();

    },

    /**
     * A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a User object.
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getMe: function() {
        return this._processRequest('getMe', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.text
     * @param {Boolean} [params.disable_web_page_preview]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendMessage: function(params) {
        return this._processRequest('sendMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.from_chat_id
     * @param {Number} params.message_id
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    forwardMessage: function(params) {
        return this._processRequest('forwardMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.photo
     * @param {String} [params.caption]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendPhoto: function(params) {
        return this._processRequest('sendPhoto', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.audio
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendAudio: function(params) {
        return this._processRequest('sendAudio', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.document
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendDocument: function(params) {
        return this._processRequest('sendDocument', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.sticker
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendSticker: function(params) {
        return this._processRequest('sendSticker', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.video
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendVideo: function(params) {
        return this._processRequest('sendVideo', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.latitude
     * @param {Number} params.longitude
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendLocation: function(params) {
        return this._processRequest('sendLocation', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.action (typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for location data)
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendChatAction: function(params) {
        return this._processRequest('sendChatAction', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.user_id
     * @param {Number} [params.offset]
     * @param {Number} [params.limit]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUserProfilePhotos: function(params) {
        return this._processRequest('getUserProfilePhotos', arguments);
    },

    _processMessages: function(messages) {

        var nameRE = new RegExp('(@' + this.username + ')', 'gi'),
            message;

        for (var i = 0, l = messages.length; i < l; i++) {
            message = messages[i].message;
            if (message) {
                // TODO: move it
                message.text && (message.text = message.text.replace(nameRE, '').trim());
                this.emit('message', message);
            }
        }

    },

    _startPolling: function() {

        var _this = this;

        this.getMe()
            .then(function (data) {
                console.log('Hi! My name is %s', data.username);
                _this.username = data.username;
                _this.id = data.id;
                _this._polling();
            })
            .fail(function(data) {
                console.log(
                    data.description
                        ? data.description + ' [' + data.error_code + ']'
                        : 'Unknown error. Check your token.'
                );
                setTimeout(_this._startPolling.bind(_this), 1000);
            });

    },

    _polling: function() {

        var _this = this;
        this.getUpdates()
            .then(function(messages) {
                _this._processMessages(messages);
                _this._polling();
            })
            .fail(function() {
                _this._polling();
            });

    },

    _processRequest: function(method, params) {

        var args = [method],
            keys = Object.keys(params);

        for (var i = 0, l = keys.length; i < l; i++) {
            args.push(params[keys[i]]);
        }

        return this._request.apply(this, args);

    },

    _request: function(method, params, onSuccess, onError) {

        var defer = vow.defer(),
            action = this._apiMethods[method] || {},
            options = {
                url: this._url + method
            },
            files = params && params[action.file],
            index = 0,
            isURL;

        if (action.file) {
            if (fs.existsSync(params[action.file])) {
                // Local file
                try {
                    params[action.file] = fs.createReadStream(params[action.file]);
                } catch (e) {}
            } else if (params.base64) {
                // Base64-encoded file
                params.isFile = true;
                params[action.file] = new Buffer(params[action.file], 'base64');
            } else {
                // URL
                isURL = true;
            }
        }

        action.gzip && (options.gzip = action.gzip);
        action.headers && (options.headers = action.headers);

        if (isURL) {
            this._tryRequest(defer, index, {
                params: params,
                action: action,
                options: options,
                files: files,
                onSuccess: onSuccess,
                onError: onError
            })
        } else {
            this._requestAPI(options, params, action, onSuccess, onError)
                .then(function(res) {
                    defer.resolve(res);
                }.bind(this))
                .fail(function(res) {
                    defer.reject(res);
                }.bind(this));
        }

        return defer.promise();

    },

    _tryRequest: function(defer, index, ext) {
        var params = ext.params,
            action = ext.action,
            options = ext.options,
            files = ext.files,
            onSuccess = ext.onSuccess,
            onError = ext.onError,
            deferExternals = vow.defer();

        this._requestExternalFiles(deferExternals, files, index, function(data) {
                params[action.file] = new Buffer(data);
                params.isFile = true;
                this._requestAPI(options, params, action, onSuccess, onError)
                    .then(function(res) {
                        defer.resolve(res);
                    }.bind(this))
                    .fail(function(res) {
                        if ((files instanceof Array) && index < files.length - 1) {
                            this._tryRequest(defer, index, ext);
                        } else {
                            defer.reject(res);
                        }
                    }.bind(this));
            }.bind(this))
        return defer.promise();
    },

    _requestAPI: function(options, params, action, onSuccess, onError) {

        var defer = vow.defer();

        try {

            var r = request.post(options, function(err, msg, res) {
                if (res && res.ok) {
                    typeof onSuccess === 'function' && onSuccess(res.result);
                    defer.resolve(res.result);
                } else {
                    typeof onError === 'function' && onError(res);
                    defer.reject(res);
                }
            });

            if (params) {
                var form = r.form();
                for (var i in params) {
                    if (params.hasOwnProperty(i) && i !== 'base64' && i !== 'isFile') {
                        form.append(i, params[i], params.isFile && i === action.file ? { filename: 'image.jpg' } : undefined);
                    }
                }
            }

        } catch (e) {
            defer.reject({ status: 'error', exception: e });
        }

        return defer.promise();

    },

    _requestExternalFiles: function(defer, urls, index, callback) {

        urls instanceof Array || (urls = [urls]);

        var _this = this;

        this._requestFile(urls[index], callback)
            .then(function(res) {
                defer.resolve(res);
            })
            .fail(function(res) {
                index++;
                if (urls[index]) {
                    _this._requestExternalFiles(defer, urls, index, callback);
                } else {
                    defer.reject(res);
                }
            });

        return defer.promise();

    },

    _requestFile: function(url, callback) {

        var defer = vow.defer(),
            data = new Buffer(0),
            req;

        try {
            req = request({ url: url, timeout: 400 })
                .on('data', function(chunk) {
                    data = Buffer.concat([data, chunk]);
                })
                .on('end', function(res) {
                    callback(data);
                    defer.resolve({ status: 'ok', url: url });
                })
                .on('error', function(err) {
                    defer.reject({ status: 'error', url: url, error: err });
                });
        } catch (e) {
            defer.reject({ status: 'error', error: 'Unknown exception', exception: e });
        }

        return defer.promise();

    }

});

module.exports = VowTelegramBot;
<commit_msg>More fixes<commit_after>var EventEmitter = require('events').EventEmitter,
    inherit = require('inherit'),
    request = require('request'),
    vow = require('vow'),
    fs = require('fs');

var apiMethods = {
        sendPhoto: {
            gzip: true,
            file: 'photo'
        },
        sendAudio: {
            gzip: true,
            file: 'audio'
        },
        sendDocument: {
            gzip: true,
            file: 'document'
        },
        sendSticker: {
            gzip: true,
            file: 'sticker'
        },
        sendVideo: {
            gzip: true,
            file: 'video'
        }
    };

request = request.defaults({ json: true });

var VowTelegramBot = inherit(EventEmitter, {

    __constructor: function(options) {
        if (!options.token) {
            throw new Error('Telegram Bot Token is required parameter!');
        }

        this._offset = 0;
        this._url = 'https://api.telegram.org/bot' + options.token + '/';
        //this._webhook = options.webhook;

        this._apiMethods = apiMethods;

        if (options.polling) {
            this.polling(options.polling);
        }

        // this._configureWebHookServer();

    },

    polling: function(options) {
        this._pollingTimeout = options && options.timeout || 3;
        this._pollingLimit = options && options.limit || 100;
        this._startPolling();
    },

    /**
     * Use this method to receive incoming updates using long polling. An Array of Update objects is returned.
     * @param {Object} [params]
     * @param {Number} [params.timeout]
     * @param {Number} [params.limit]
     * @param {Number} [params.offset]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUpdates: function(params, onSuccess, onError) {

        var defer = vow.defer(),
            _this = this,
            timeout,
            offset,
            limit;

        if (params) {
            timeout = params.timeout;
            limit = params.limit;
            offset = params.offset;
        }

        this._request('getUpdates', {
                timeout: timeout || this._pollingTimeout,
                limit: limit || this._pollingLimit,
                offset: offset || this._offset + 1
            })
            .then(function(result) {
                var last = result[result.length - 1]
                last && (_this._offset = last.update_id);
                typeof onSuccess === 'function' && onSuccess(result);
                defer.resolve(result);
            })
            .fail(function(result) {
                typeof onError === 'function' && onError(result);
                defer.reject(result);
            });

        return defer.promise();

    },

    /**
     * A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a User object.
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getMe: function() {
        return this._processRequest('getMe', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.text
     * @param {Boolean} [params.disable_web_page_preview]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendMessage: function(params) {
        return this._processRequest('sendMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.from_chat_id
     * @param {Number} params.message_id
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    forwardMessage: function(params) {
        return this._processRequest('forwardMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.photo
     * @param {String} [params.caption]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendPhoto: function(params) {
        return this._processRequest('sendPhoto', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.audio
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendAudio: function(params) {
        return this._processRequest('sendAudio', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.document
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendDocument: function(params) {
        return this._processRequest('sendDocument', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.sticker
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendSticker: function(params) {
        return this._processRequest('sendSticker', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.video
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendVideo: function(params) {
        return this._processRequest('sendVideo', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.latitude
     * @param {Number} params.longitude
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendLocation: function(params) {
        return this._processRequest('sendLocation', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.action (typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for location data)
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendChatAction: function(params) {
        return this._processRequest('sendChatAction', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.user_id
     * @param {Number} [params.offset]
     * @param {Number} [params.limit]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUserProfilePhotos: function(params) {
        return this._processRequest('getUserProfilePhotos', arguments);
    },

    _processMessages: function(messages) {

        var nameRE = new RegExp('(@' + this.username + ')', 'gi'),
            message;

        for (var i = 0, l = messages.length; i < l; i++) {
            message = messages[i].message;
            if (message) {
                // TODO: move it
                message.text && (message.text = message.text.replace(nameRE, '').trim());
                this.emit('message', message);
            }
        }

    },

    _startPolling: function() {

        var _this = this;

        this.getMe()
            .then(function (data) {
                console.log('Hi! My name is %s', data.username);
                _this.username = data.username;
                _this.id = data.id;
                _this._polling();
            })
            .fail(function(data) {
                console.log(
                    data.description
                        ? data.description + ' [' + data.error_code + ']'
                        : 'Unknown error. Check your token.'
                );
                setTimeout(_this._startPolling.bind(_this), 1000);
            });

    },

    _polling: function() {

        var _this = this;
        this.getUpdates()
            .then(function(messages) {
                _this._processMessages(messages);
                _this._polling();
            })
            .fail(function() {
                _this._polling();
            });

    },

    _processRequest: function(method, params) {

        var args = [method],
            keys = Object.keys(params);

        for (var i = 0, l = keys.length; i < l; i++) {
            args.push(params[keys[i]]);
        }

        return this._request.apply(this, args);

    },

    _request: function(method, params, onSuccess, onError) {

        var defer = vow.defer(),
            action = this._apiMethods[method] || {},
            options = {
                url: this._url + method
            },
            files = params && params[action.file],
            index = 0,
            isURL;

        if (action.file) {
            if (fs.existsSync(params[action.file])) {
                // Local file
                try {
                    params[action.file] = fs.createReadStream(params[action.file]);
                } catch (e) {}
            } else if (params.base64) {
                // Base64-encoded file
                params.isFile = true;
                params[action.file] = new Buffer(params[action.file], 'base64');
            } else {
                // URL
                isURL = true;
            }
        }

        action.gzip && (options.gzip = action.gzip);
        action.headers && (options.headers = action.headers);

        if (isURL) {
            this._tryRequest(defer, index, {
                params: params,
                action: action,
                options: options,
                files: files,
                onSuccess: onSuccess,
                onError: onError
            })
        } else {
            this._requestAPI(options, params, action, onSuccess, onError)
                .then(function(res) {
                    defer.resolve(res);
                })
                .fail(function(res) {
                    defer.reject(res);
                });
        }

        return defer.promise();

    },

    _tryRequest: function(defer, index, ext) {
        var params = ext.params,
            action = ext.action,
            options = ext.options,
            files = ext.files,
            onSuccess = ext.onSuccess,
            onError = ext.onError,
            deferExternals = vow.defer(),
            _this = this;

        this._requestExternalFiles(deferExternals, files, index, function(data) {
                params[action.file] = new Buffer(data);
                params.isFile = true;
                _this._requestAPI(options, params, action, onSuccess, onError)
                    .then(function(res) {
                        defer.resolve(res);
                    })
                    .fail(function(res) {
                        if ((files instanceof Array) && index < files.length - 1) {
                            _this._tryRequest(defer, index, ext);
                        } else {
                            defer.reject(res);
                        }
                    });
            })
            .then(function(res) {
                defer.resolve(res);
            })
            .fail(function(res) {
                defer.reject(res);
            });

        return defer.promise();
    },

    _requestAPI: function(options, params, action, onSuccess, onError) {

        var defer = vow.defer();

        try {

            var r = request.post(options, function(err, msg, res) {
                if (res && res.ok) {
                    typeof onSuccess === 'function' && onSuccess(res.result);
                    defer.resolve(res.result);
                } else {
                    typeof onError === 'function' && onError(res);
                    defer.reject(res);
                }
            });

            if (params) {
                var form = r.form();
                for (var i in params) {
                    if (params.hasOwnProperty(i) && i !== 'base64' && i !== 'isFile') {
                        form.append(i, params[i], params.isFile && i === action.file ? { filename: 'image.jpg' } : undefined);
                    }
                }
            }

        } catch (e) {
            defer.reject({ status: 'error', exception: e });
        }

        return defer.promise();

    },

    _requestExternalFiles: function(defer, urls, index, callback) {

        urls instanceof Array || (urls = [urls]);

        var _this = this;

        this._requestFile(urls[index], callback)
            .then(function(res) {
                defer.resolve(res);
            })
            .fail(function(res) {
                index++;
                if (urls[index]) {
                    _this._requestExternalFiles(defer, urls, index, callback);
                } else {
                    defer.reject(res);
                }
            });

        return defer.promise();

    },

    _requestFile: function(url, callback) {

        var defer = vow.defer(),
            data = new Buffer(0),
            req;

        try {
            req = request({ url: url, timeout: 400 })
                .on('data', function(chunk) {
                    data = Buffer.concat([data, chunk]);
                })
                .on('end', function(res) {
                    callback(data);
                    defer.resolve({ status: 'ok', url: url });
                })
                .on('error', function(err) {
                    defer.reject({ status: 'error', url: url, error: err });
                });
        } catch (e) {
            defer.reject({ status: 'error', error: 'Unknown exception', exception: e });
        }

        return defer.promise();

    }

});

module.exports = VowTelegramBot;
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter,
    inherit = require('inherit'),
    request = require('request'),
    vow = require('vow'),
    fs = require('fs');

var apiMethods = {
        sendPhoto: {
            gzip: true,
            file: 'photo'
        },
        sendAudio: {
            gzip: true,
            file: 'audio'
        },
        sendDocument: {
            gzip: true,
            file: 'document'
        },
        sendSticker: {
            gzip: true,
            file: 'sticker'
        },
        sendVideo: {
            gzip: true,
            file: 'video'
        }
    };

request = request.defaults({ json: true });

var VowTelegramBot = inherit(EventEmitter, {

    __constructor: function(options) {
        if (!options.token) {
            throw new Error('Telegram Bot Token is required parameter!');
        }

        var _this = this;

        this._offset = 0;
        this._url = 'https://api.telegram.org/bot' + options.token + '/';
        //this._webhook = options.webhook;

        this._apiMethods = apiMethods;

        if (options.polling) {
            this._pollingTimeout = options.polling.timeout || 3;
            this._pollingLimit = options.polling.limit || 100;
            this.getMe().then(
                function (data) {
                    console.log('Hi! My name is %s', data.username);
                    _this._polling();
                },
                function(data) {
                    console.log(data.description + ' [' + data.error_code + ']');
                }
            );
        }

        // this._configureWebHookServer();

    },

    /**
     * Use this method to receive incoming updates using long polling. An Array of Update objects is returned.
     * @param {Object} [params]
     * @param {Number} [params.timeout]
     * @param {Number} [params.limit]
     * @param {Number} [params.offset]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUpdates: function(params, onSuccess, onError) {

        var defer = vow.defer(),
            _this = this,
            timeout,
            offset,
            limit;

        if (params) {
            timeout = params.timeout;
            limit = params.limit;
            offset = params.offset;
        }

        this._request('getUpdates', {
            timeout: timeout || this._pollingTimeout,
            limit: limit || this._pollingLimit,
            offset: offset || this._offset + 1
        }).then(
            function(result) {
                var last = result[result.length - 1]
                last && (_this._offset = last.update_id);
                typeof onSuccess === 'function' && onSuccess(result);
                defer.resolve(result);
            },
            function(result) {
                typeof onError === 'function' && onError(result);
                defer.reject(result);
            }
        );

        return defer.promise();

    },

    /**
     * A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a User object.
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getMe: function() {
        return this._processRequest('getMe', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.text
     * @param {Boolean} [params.disable_web_page_preview]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendMessage: function(params) {
        return this._processRequest('sendMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.from_chat_id
     * @param {Number} params.message_id
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    forwardMessage: function(params) {
        return this._processRequest('forwardMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.photo
     * @param {String} [params.caption]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendPhoto: function(params) {
        return this._processRequest('sendPhoto', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.audio
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendAudio: function(params) {
        return this._processRequest('sendAudio', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.document
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendDocument: function(params) {
        return this._processRequest('sendDocument', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.sticker
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendSticker: function(params) {
        return this._processRequest('sendSticker', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.video
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendVideo: function(params) {
        return this._processRequest('sendVideo', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.latitude
     * @param {Number} params.longitude
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendLocation: function(params) {
        return this._processRequest('sendLocation', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.action (typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for location data)
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendChatAction: function(params) {
        return this._processRequest('sendChatAction', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.user_id
     * @param {Number} [params.offset]
     * @param {Number} [params.limit]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUserProfilePhotos: function(params) {
        return this._processRequest('getUserProfilePhotos', arguments);
    },

    _processMessages: function(messages) {

        for (var i = 0, l = messages.length; i < l; i++) {
            if (messages[i].message) {
                this.emit('message', messages[i].message);
            }
        }

    },

    _polling: function() {

        var _this = this;
        this.getUpdates().then(function(messages) {
            _this._processMessages(messages);
            _this._polling();
        });

    },

    _processRequest: function(method, params) {

        var args = [method],
            keys = Object.keys(params);

        for (var i = 0, l = keys.length; i < l; i++) {
            args.push(params[keys[i]]);
        }

        return this._request.apply(this, args);

    },

    _request: function(method, params, onSuccess, onError) {

        this._checkFiles(method, params);

        var defer = vow.defer(),
            action = this._apiMethods[method],
            options = {
                url: this._url + method,
                formData: params
            };

        if (action) {
            options.gzip = action.gzip;
            options.headers = action.headers;
        }

        request(options, function(err, msg, res) {

            if (res && res.ok) {
                typeof onSuccess === 'function' && onSuccess(res.result);
                defer.resolve(res.result);
            } else {
                typeof onError === 'function' && onError(res);
                defer.reject(res);
            }

        });

        return defer.promise();

    },

    _checkFiles: function(method, params) {

        var method = this._apiMethods[method];

        if (method && method.file && typeof params[method.file] === 'string') {
            params[method.file] = fs.createReadStream(params[method.file]);
        }

    }

});

module.exports = VowTelegramBot;
<commit_msg>Unknown error<commit_after>var EventEmitter = require('events').EventEmitter,
    inherit = require('inherit'),
    request = require('request'),
    vow = require('vow'),
    fs = require('fs');

var apiMethods = {
        sendPhoto: {
            gzip: true,
            file: 'photo'
        },
        sendAudio: {
            gzip: true,
            file: 'audio'
        },
        sendDocument: {
            gzip: true,
            file: 'document'
        },
        sendSticker: {
            gzip: true,
            file: 'sticker'
        },
        sendVideo: {
            gzip: true,
            file: 'video'
        }
    };

request = request.defaults({ json: true });

var VowTelegramBot = inherit(EventEmitter, {

    __constructor: function(options) {
        if (!options.token) {
            throw new Error('Telegram Bot Token is required parameter!');
        }

        var _this = this;

        this._offset = 0;
        this._url = 'https://api.telegram.org/bot' + options.token + '/';
        //this._webhook = options.webhook;

        this._apiMethods = apiMethods;

        if (options.polling) {
            this._pollingTimeout = options.polling.timeout || 3;
            this._pollingLimit = options.polling.limit || 100;
            this.getMe().then(
                function (data) {
                    console.log('Hi! My name is %s', data.username);
                    _this._polling();
                },
                function(data) {
                    console.log(
                        data.description
                            ? data.description + ' [' + data.error_code + ']'
                            : 'Unknown error. Check your token.'
                    );
                }
            );
        }

        // this._configureWebHookServer();

    },

    /**
     * Use this method to receive incoming updates using long polling. An Array of Update objects is returned.
     * @param {Object} [params]
     * @param {Number} [params.timeout]
     * @param {Number} [params.limit]
     * @param {Number} [params.offset]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUpdates: function(params, onSuccess, onError) {

        var defer = vow.defer(),
            _this = this,
            timeout,
            offset,
            limit;

        if (params) {
            timeout = params.timeout;
            limit = params.limit;
            offset = params.offset;
        }

        this._request('getUpdates', {
            timeout: timeout || this._pollingTimeout,
            limit: limit || this._pollingLimit,
            offset: offset || this._offset + 1
        }).then(
            function(result) {
                var last = result[result.length - 1]
                last && (_this._offset = last.update_id);
                typeof onSuccess === 'function' && onSuccess(result);
                defer.resolve(result);
            },
            function(result) {
                typeof onError === 'function' && onError(result);
                defer.reject(result);
            }
        );

        return defer.promise();

    },

    /**
     * A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a User object.
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getMe: function() {
        return this._processRequest('getMe', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.text
     * @param {Boolean} [params.disable_web_page_preview]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendMessage: function(params) {
        return this._processRequest('sendMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.from_chat_id
     * @param {Number} params.message_id
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    forwardMessage: function(params) {
        return this._processRequest('forwardMessage', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.photo
     * @param {String} [params.caption]
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendPhoto: function(params) {
        return this._processRequest('sendPhoto', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.audio
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendAudio: function(params) {
        return this._processRequest('sendAudio', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.document
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendDocument: function(params) {
        return this._processRequest('sendDocument', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.sticker
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendSticker: function(params) {
        return this._processRequest('sendSticker', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {InputFile|String} params.video
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendVideo: function(params) {
        return this._processRequest('sendVideo', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {Number} params.latitude
     * @param {Number} params.longitude
     * @param {Number} [params.reply_to_message_id]
     * @param {ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply} [params.reply_markup]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendLocation: function(params) {
        return this._processRequest('sendLocation', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.chat_id
     * @param {String} params.action (typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for location data)
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    sendChatAction: function(params) {
        return this._processRequest('sendChatAction', arguments);
    },

    /**
     * @param {Object} params
     * @param {Number} params.user_id
     * @param {Number} [params.offset]
     * @param {Number} [params.limit]
     * @param {Function} [onSuccess]
     * @param {Function} [onError]
     */
    getUserProfilePhotos: function(params) {
        return this._processRequest('getUserProfilePhotos', arguments);
    },

    _processMessages: function(messages) {

        for (var i = 0, l = messages.length; i < l; i++) {
            if (messages[i].message) {
                this.emit('message', messages[i].message);
            }
        }

    },

    _polling: function() {

        var _this = this;
        this.getUpdates().then(function(messages) {
            _this._processMessages(messages);
            _this._polling();
        });

    },

    _processRequest: function(method, params) {

        var args = [method],
            keys = Object.keys(params);

        for (var i = 0, l = keys.length; i < l; i++) {
            args.push(params[keys[i]]);
        }

        return this._request.apply(this, args);

    },

    _request: function(method, params, onSuccess, onError) {

        this._checkFiles(method, params);

        var defer = vow.defer(),
            action = this._apiMethods[method],
            options = {
                url: this._url + method,
                formData: params
            };

        if (action) {
            options.gzip = action.gzip;
            options.headers = action.headers;
        }

        request(options, function(err, msg, res) {

            if (res && res.ok) {
                typeof onSuccess === 'function' && onSuccess(res.result);
                defer.resolve(res.result);
            } else {
                typeof onError === 'function' && onError(res);
                defer.reject(res);
            }

        });

        return defer.promise();

    },

    _checkFiles: function(method, params) {

        var method = this._apiMethods[method];

        if (method && method.file && typeof params[method.file] === 'string') {
            params[method.file] = fs.createReadStream(params[method.file]);
        }

    }

});

module.exports = VowTelegramBot;
<|endoftext|>


<commit_before>'use strict'

import { TextTransformer } from './text.transformer'
import { ImageTransformer } from './image.transformer'
import { HTMLTransformer } from './html.transformer'
/**
 * Transforms mimetypes into HTMLElements
 */
class Transformime {

  /**
   * Public constructor
   * @param  {function[]} transformers - list of transformers, in reverse priority order.
   */
  constructor (transformers) {
    // Initialize instance variables.
    this.transformers = []
    this.push(TextTransformer)
    this.push(ImageTransformer)
    this.push(HTMLTransformer)
    if (transformers) transformers.map((transformer) => this.push(transformer))
  }
  /**
   * Transforms a mime bundle, using the richest available representation,
   * into an HTMLElement.
   * @param  {any}      bundle - {mimetype1: data1, mimetype2: data2, ...}
   * @param  {Document} document - Any of window.document, iframe.contentDocument
   * @return {Promise<{mimetype: string, el: HTMLElement}>}
   */
  transform (bundle, document) {
    if (this.transformers.length <= 0) {
      // Empty transformers
      return Promise.reject(new Error('No transformers configured'))
    }

    if (Object.keys(bundle).length <= 0) {
      return Promise.reject(new Error('MIME Bundle empty'))
    }

    let richMimetype
    let richTransformer

    // Choose the last transformer as the most rich
    for (let transformer of this.transformers) {
      if (transformer.mimetype) {
        // Make sure the transformer's mimetype is in array format.
        let transformer_mimetypes = transformer.mimetype
        if (!Array.isArray(transformer_mimetypes)) {
          transformer_mimetypes = [transformer_mimetypes]
        }

        for (let transformer_mimetype of transformer_mimetypes) {
          if (transformer_mimetype in bundle) {
            richMimetype = transformer_mimetype
            richTransformer = transformer
          }
        }
      }
    }

    if (richMimetype && richTransformer) {
      // Don't assume the transformation will return a promise.  Also
      // don't assume the transformation will succeed.
      try {
        return Promise.resolve(richTransformer.call(richTransformer,
          richMimetype, bundle[richMimetype], document)).then(el => {
            return { mimetype: richMimetype, el: el }
          })
      } catch (e) {
        return Promise.reject(e)
      }
    } else {
      return Promise.reject(new Error('Transformer(s) for ' + Object.keys(bundle).join(', ') + ' not found.'))
    }
  }

  /**
   * Deletes all transformers by mimetype.
   * @param {string|string[]} mimetype - mimetype the data type (e.g. text/plain, text/html, image/png)
   */
  del (mimetype) {
    // Convert mimetype to an array.
    let mimetypes = mimetype
    if (!Array.isArray(mimetypes)) {
      mimetypes = [mimetypes]
    }

    // Remove each mimetype.
    for (mimetype of mimetypes) {
      for (let i = 0; i < this.transformers.length; i++) {
        var transformer = this.transformers[i]

        // If the mimetype matches the one we want to remove, remove it.
        if (mimetype === transformer.mimetype) {
          this.transformers.splice(i, 1)
          i--

        // If the mimetype we want to remove is in the list of the
        // mimetypes supported by the transformer, remove it from the list.
        // If the transformer mimetype list is then empty, remove the
        // transformer.
        } else if (Array.isArray(transformer.mimetype) && mimetype in transformer.mimetype) {
          if (transformer.mimetype.length === 1) {
            this.transformers.splice(i, 1)
            i--
          } else {
            transformer.mimetype.splice(transformer.mimetype.indexOf(mimetype), 1)
          }
        }
      }
    }
  }

  /**
   * Gets a transformer matching the mimetype
   * @param {string} mimetype - the data type (e.g. text/plain, text/html, image/png)
   * @return {function} Matching transformer
   */
  get (mimetype) {
    // Loop through the transformers array in reverse.
    for (let i = this.transformers.length - 1; i >= 0; i--) {
      let transformer = this.transformers[i]

      // Get an array of the mimetypes that the transformer supports.
      let transformer_mimetypes = transformer.mimetype
      if (!Array.isArray(transformer_mimetypes)) {
        transformer_mimetypes = [transformer_mimetypes]
      }

      // Check if any of the mimetypes match the one we are looking for.
      for (let transformer_mimetype of transformer_mimetypes) {
        if (mimetype === transformer_mimetype) {
          return transformer
        }
      }
    }
  }

  /**
   * Sets a transformer matching the mimetype
   * @param {string|string[]} mimetype - the data type (e.g. text/plain, text/html, image/png)
   * @param {function} transformer
   * @return {function} inserted transformer function (may be different than arg)
   */
  set (mimetype, transformer) {
    this.del(mimetype)
    return this.push(transformer, mimetype)
  }

  /**
   * Appends a transformer to the transformer list.
   * @param  {function} transformer
   * @param  {string|string[]} mimetype
   * @return {function} inserted transformer function (may be different than arg)
   */
  push (transformer, mimetype) {
    // If the mimetype specified is different than the mimetype of the
    // transformer, make a copy of the transformer and set the new mimetype
    // on the copy.
    let transform = transformer
    if (mimetype && transformer.mimetype !== mimetype) {
      transform = this._proxy(transformer, mimetype)
    }

    // Verify a mimetype is set on the transformer.
    if (!transform.mimetype) throw Error('Could not infer transformer mimetype')

    this.transformers.push(transform)
    return transform
  }

  /**
   * Create a proxy to a transformer, using another mimetype.
   * @param  {function} transformer
   * @param  {string|string[]} mimetype
   * @return {function} transformer
   */
  _proxy (transformer, mimetype) {
    let transform = function (...args) { return transformer.call(this, ...args) }
    transform.mimetype = mimetype
    return transform
  }
}

export default {Transformime, TextTransformer, ImageTransformer, HTMLTransformer}
<commit_msg>Updated imports and exports in transformime<commit_after>'use strict'

import { TextTransform } from './text.transform'
import { ImageTransform } from './image.transform'
import { HTMLTransform } from './html.transform'
/**
 * Transforms mimetypes into HTMLElements
 */
class Transformime {

  /**
   * Public constructor
   * @param  {function[]} transformers - list of transformers, in reverse priority order.
   */
  constructor (transformers) {
    // Initialize instance variables.
    this.transformers = []
    this.push(TextTransform)
    this.push(ImageTransform)
    this.push(HTMLTransform)
    if (transformers) transformers.map((transformer) => this.push(transformer))
  }
  /**
   * Transforms a mime bundle, using the richest available representation,
   * into an HTMLElement.
   * @param  {any}      bundle - {mimetype1: data1, mimetype2: data2, ...}
   * @param  {Document} document - Any of window.document, iframe.contentDocument
   * @return {Promise<{mimetype: string, el: HTMLElement}>}
   */
  transform (bundle, document) {
    if (this.transformers.length <= 0) {
      // Empty transformers
      return Promise.reject(new Error('No transformers configured'))
    }

    if (Object.keys(bundle).length <= 0) {
      return Promise.reject(new Error('MIME Bundle empty'))
    }

    let richMimetype
    let richTransformer

    // Choose the last transformer as the most rich
    for (let transformer of this.transformers) {
      if (transformer.mimetype) {
        // Make sure the transformer's mimetype is in array format.
        let transformer_mimetypes = transformer.mimetype
        if (!Array.isArray(transformer_mimetypes)) {
          transformer_mimetypes = [transformer_mimetypes]
        }

        for (let transformer_mimetype of transformer_mimetypes) {
          if (transformer_mimetype in bundle) {
            richMimetype = transformer_mimetype
            richTransformer = transformer
          }
        }
      }
    }

    if (richMimetype && richTransformer) {
      // Don't assume the transformation will return a promise.  Also
      // don't assume the transformation will succeed.
      try {
        return Promise.resolve(richTransformer.call(richTransformer,
          richMimetype, bundle[richMimetype], document)).then(el => {
            return { mimetype: richMimetype, el: el }
          })
      } catch (e) {
        return Promise.reject(e)
      }
    } else {
      return Promise.reject(new Error('Transformer(s) for ' + Object.keys(bundle).join(', ') + ' not found.'))
    }
  }

  /**
   * Deletes all transformers by mimetype.
   * @param {string|string[]} mimetype - mimetype the data type (e.g. text/plain, text/html, image/png)
   */
  del (mimetype) {
    // Convert mimetype to an array.
    let mimetypes = mimetype
    if (!Array.isArray(mimetypes)) {
      mimetypes = [mimetypes]
    }

    // Remove each mimetype.
    for (mimetype of mimetypes) {
      for (let i = 0; i < this.transformers.length; i++) {
        var transformer = this.transformers[i]

        // If the mimetype matches the one we want to remove, remove it.
        if (mimetype === transformer.mimetype) {
          this.transformers.splice(i, 1)
          i--

        // If the mimetype we want to remove is in the list of the
        // mimetypes supported by the transformer, remove it from the list.
        // If the transformer mimetype list is then empty, remove the
        // transformer.
        } else if (Array.isArray(transformer.mimetype) && mimetype in transformer.mimetype) {
          if (transformer.mimetype.length === 1) {
            this.transformers.splice(i, 1)
            i--
          } else {
            transformer.mimetype.splice(transformer.mimetype.indexOf(mimetype), 1)
          }
        }
      }
    }
  }

  /**
   * Gets a transformer matching the mimetype
   * @param {string} mimetype - the data type (e.g. text/plain, text/html, image/png)
   * @return {function} Matching transformer
   */
  get (mimetype) {
    // Loop through the transformers array in reverse.
    for (let i = this.transformers.length - 1; i >= 0; i--) {
      let transformer = this.transformers[i]

      // Get an array of the mimetypes that the transformer supports.
      let transformer_mimetypes = transformer.mimetype
      if (!Array.isArray(transformer_mimetypes)) {
        transformer_mimetypes = [transformer_mimetypes]
      }

      // Check if any of the mimetypes match the one we are looking for.
      for (let transformer_mimetype of transformer_mimetypes) {
        if (mimetype === transformer_mimetype) {
          return transformer
        }
      }
    }
  }

  /**
   * Sets a transformer matching the mimetype
   * @param {string|string[]} mimetype - the data type (e.g. text/plain, text/html, image/png)
   * @param {function} transformer
   * @return {function} inserted transformer function (may be different than arg)
   */
  set (mimetype, transformer) {
    this.del(mimetype)
    return this.push(transformer, mimetype)
  }

  /**
   * Appends a transformer to the transformer list.
   * @param  {function} transformer
   * @param  {string|string[]} mimetype
   * @return {function} inserted transformer function (may be different than arg)
   */
  push (transformer, mimetype) {
    // If the mimetype specified is different than the mimetype of the
    // transformer, make a copy of the transformer and set the new mimetype
    // on the copy.
    let transform = transformer
    if (mimetype && transformer.mimetype !== mimetype) {
      transform = this._proxy(transformer, mimetype)
    }

    // Verify a mimetype is set on the transformer.
    if (!transform.mimetype) throw Error('Could not infer transformer mimetype')

    this.transformers.push(transform)
    return transform
  }

  /**
   * Create a proxy to a transformer, using another mimetype.
   * @param  {function} transformer
   * @param  {string|string[]} mimetype
   * @return {function} transformer
   */
  _proxy (transformer, mimetype) {
    let transform = function (...args) { return transformer.call(this, ...args) }
    transform.mimetype = mimetype
    return transform
  }
}

export default {Transformime, TextTransform, ImageTransform, HTMLTransform}
<|endoftext|>


<commit_before>
// Enable ajax based navigation

// flow

// click internal link
// updates hash/History object
// hash envokes navigate
// if 1st level is changed tells #content navigation has been invoked
// if 2nd level is changed tells .scene navigation has been invoked

u.navigation = function(options) {

//	u.bug("set up navigation")

	// this._nav_navigate_callback = null;
	//
	// // additional info passed to function as JSON object
	// if(typeof(options) == "object") {
	// 	var argument;
	// 	for(argument in options) {
	//
	// 		switch(argument) {
	// 			case "navigate_callback"	: this._nav_navigate_callback		= options[argument]; break;
	// 		}
	//
	// 	}
	// }

	// default starting path
	page._nav_path = page._nav_path ? page._nav_path : u.h.getCleanUrl(location.href);
	page._nav_history = page._nav_history ? page._nav_history : [];

	// internal hash change distribution - content or scene level
	page._navigate = function(url) {

		url = u.h.getCleanUrl(url);

		page._nav_history.unshift(url);

		// stats
		// TODO: this.hash_node add to stats
		u.stats.pageView(url);

		// direct navigation callback to correct level
		// first request or new base-level
		if(!this._nav_path || ((this._nav_path != u.h.getCleanHash(location.hash, 1) && !u.h.popstate) || (this._nav_path != u.h.getCleanUrl(location.href, 1) && u.h.popstate))) {

//			u.bug("base cN:" + url)

			// forward navigation event to #content
			if(this.cN && typeof(this.cN.navigate) == "function") {
				this.cN.navigate(url);
			}

		}
		else {

//			u.bug("base decide:" + url + ", " + (this.cN.scene ? u.nodeId(this.cN.scene) : this.cN.scene))

			// forward navigation event to .scene if it has navigate function
			if(this.cN.scene && this.cN.scene.parentNode && typeof(this.cN.scene.navigate) == "function") {
				this.cN.scene.navigate(url);
			}
			// else forward to content
			else if(this.cN && typeof(this.cN.navigate) == "function") {
				this.cN.navigate(url);
			}

		}

		if(!u.h.popstate) {
			// remember base-level
			this._nav_path = u.h.getCleanHash(location.hash, 1);
		}
		else {
			this._nav_path = u.h.getCleanUrl(location.href, 1);
		}
	}

	// update hash and set hash back node if any
	page.navigate = function(url, node) {
//		u.bug("url:" + url + ", " + u.nodeId(node))

		// remember history node (for tracking purposes)
		this.history_node = node ? node : false;

		// popstate handling
		if(u.h.popstate) {
			history.pushState({}, url, url);
			page._navigate(url);
		}
		// hash handling
		else {
			location.hash = u.h.getCleanUrl(url);
		}

	}

	// sharing via social media (linkedin) might add hashbang
	// remove invalid hash value
	if(location.hash.length && location.hash.match(/^#!/)) {
		location.hash = location.hash.replace(/!/, "");
	}

	// If HASH navigation is preferred - reading initial state from URL

	// set default hash if no hash value is present ()
	// no further navigation - initialize content
	if(!u.h.popstate) {

		if(location.hash.length < 2) {
	//		u.bug("set hash + init content")

			// update hash
			page.navigate(location.href, page);
			// update internal value, so navigation doesn't mess up
			page._nav_path = u.h.getCleanUrl(location.href);
			// init content
			u.init(page.cN);
		}
		// if different hash and url, hash value starts with /
		// load content based on hash
		else if(u.h.getCleanHash(location.hash) != u.h.getCleanUrl(location.href) && location.hash.match(/^#\//)) {
	//		u.bug("init navigate:" + u.h.getCleanHash(location.hash) + "!=" + u.h.getCleanUrl(location.href) + "; ")

			// update internal value, so navigation doesn't mess up
			page._nav_path = u.h.getCleanUrl(location.href);
			// manually invoke navigation to load correct page
			page._navigate();
		}
		// hash and url is aligned, or unusable value
		// init existing content
		else {
	//		u.bug("init content")

			// just go for it
			u.init(page.cN);
		}

	}
	// History Object is preferred
	else {

		// if HASH exists and different from url, translate to url (could be a bookmark shared from HASH enabled browser)
		if(u.h.getCleanHash(location.hash) != u.h.getCleanUrl(location.href) && location.hash.match(/^#\//)) {

			// update internal value, so navigation doesn't mess up
			page._nav_path = u.h.getCleanHash(location.hash);

			// update hash
			page.navigate(u.h.getCleanHash(location.hash), page);

		}
		// everything is ready for go
		else {

			// just go for it
			u.init(page.cN);
		}
	}

	page._initHistory = function() {
//		u.bug("enable HASH navigation")

		u.h.catchEvent(page, page._navigate);
	}

	// set hash event handler with small delay to avoid redirecting when actually just trying to update HASH
	u.t.setTimer(page, page._initHistory, 100);

	// TODO: enable crossbrowser history

	page.historyBack = function() {
		if(this._nav_history.length > 1) {
			this._nav_history.shift();
			return this._nav_history.shift();
		}
		else {
			return "/";
		}
	}
}
<commit_msg>Fixed nav_path error<commit_after>
// Enable ajax based navigation

// flow

// click internal link
// updates hash/History object
// hash envokes navigate
// if 1st level is changed tells #content navigation has been invoked
// if 2nd level is changed tells .scene navigation has been invoked

u.navigation = function(options) {

//	u.bug("set up navigation")

	// this._nav_navigate_callback = null;
	//
	// // additional info passed to function as JSON object
	// if(typeof(options) == "object") {
	// 	var argument;
	// 	for(argument in options) {
	//
	// 		switch(argument) {
	// 			case "navigate_callback"	: this._nav_navigate_callback		= options[argument]; break;
	// 		}
	//
	// 	}
	// }

	// default starting path
	page._nav_path = page._nav_path ? page._nav_path : u.h.getCleanUrl(location.href, 1);
	page._nav_history = page._nav_history ? page._nav_history : [];

	// internal hash change distribution - content or scene level
	page._navigate = function(url) {

		url = u.h.getCleanUrl(url);

		page._nav_history.unshift(url);

		// stats
		// TODO: this.hash_node add to stats
		u.stats.pageView(url);

		// direct navigation callback to correct level
		// first request or new base-level
		if(!this._nav_path || ((this._nav_path != u.h.getCleanHash(location.hash, 1) && !u.h.popstate) || (this._nav_path != u.h.getCleanUrl(location.href, 1) && u.h.popstate))) {

//			u.bug("base cN:" + url)

			// forward navigation event to #content
			if(this.cN && typeof(this.cN.navigate) == "function") {
				this.cN.navigate(url);
			}

		}
		else {

//			u.bug("base decide:" + url + ", " + (this.cN.scene ? u.nodeId(this.cN.scene) : this.cN.scene))

			// forward navigation event to .scene if it has navigate function
			if(this.cN.scene && this.cN.scene.parentNode && typeof(this.cN.scene.navigate) == "function") {
				this.cN.scene.navigate(url);
			}
			// else forward to content
			else if(this.cN && typeof(this.cN.navigate) == "function") {
				this.cN.navigate(url);
			}

		}

		if(!u.h.popstate) {
			// remember base-level
			this._nav_path = u.h.getCleanHash(location.hash, 1);
		}
		else {
			this._nav_path = u.h.getCleanUrl(location.href, 1);
		}
	}

	// update hash and set hash back node if any
	page.navigate = function(url, node) {
//		u.bug("url:" + url + ", " + u.nodeId(node))

		// remember history node (for tracking purposes)
		this.history_node = node ? node : false;

		// popstate handling
		if(u.h.popstate) {
			history.pushState({}, url, url);
			page._navigate(url);
		}
		// hash handling
		else {
			location.hash = u.h.getCleanUrl(url);
		}

	}

	// sharing via social media (linkedin) might add hashbang
	// remove invalid hash value
	if(location.hash.length && location.hash.match(/^#!/)) {
		location.hash = location.hash.replace(/!/, "");
	}

	// If HASH navigation is preferred - reading initial state from URL

	// set default hash if no hash value is present ()
	// no further navigation - initialize content
	if(!u.h.popstate) {

		if(location.hash.length < 2) {
	//		u.bug("set hash + init content")

			// update hash
			page.navigate(location.href, page);
			// update internal value, so navigation doesn't mess up
			page._nav_path = u.h.getCleanUrl(location.href);
			// init content
			u.init(page.cN);
		}
		// if different hash and url, hash value starts with /
		// load content based on hash
		else if(u.h.getCleanHash(location.hash) != u.h.getCleanUrl(location.href) && location.hash.match(/^#\//)) {
	//		u.bug("init navigate:" + u.h.getCleanHash(location.hash) + "!=" + u.h.getCleanUrl(location.href) + "; ")

			// update internal value, so navigation doesn't mess up
			page._nav_path = u.h.getCleanUrl(location.href);
			// manually invoke navigation to load correct page
			page._navigate();
		}
		// hash and url is aligned, or unusable value
		// init existing content
		else {
	//		u.bug("init content")

			// just go for it
			u.init(page.cN);
		}

	}
	// History Object is preferred
	else {

		// if HASH exists and different from url, translate to url (could be a bookmark shared from HASH enabled browser)
		if(u.h.getCleanHash(location.hash) != u.h.getCleanUrl(location.href) && location.hash.match(/^#\//)) {

			// update internal value, so navigation doesn't mess up
			page._nav_path = u.h.getCleanHash(location.hash);

			// update hash
			page.navigate(u.h.getCleanHash(location.hash), page);

		}
		// everything is ready for go
		else {

			// just go for it
			u.init(page.cN);
		}
	}

	page._initHistory = function() {
//		u.bug("enable HASH navigation")

		u.h.catchEvent(page, page._navigate);
	}

	// set hash event handler with small delay to avoid redirecting when actually just trying to update HASH
	u.t.setTimer(page, page._initHistory, 100);

	// TODO: enable crossbrowser history

	page.historyBack = function() {
		if(this._nav_history.length > 1) {
			this._nav_history.shift();
			return this._nav_history.shift();
		}
		else {
			return "/";
		}
	}
}
<|endoftext|>


<commit_before>/** @jsx React.DOM */
var Button = require('react-bootstrap').Button;
var Glyphicon = require('react-bootstrap').Glyphicon;
var Input = require('react-bootstrap').Input;
var Modal = require('react-bootstrap').Modal;
var ModalTrigger = require('react-bootstrap').ModalTrigger;
var Nav = require('react-bootstrap').Nav;
var Navbar = require('react-bootstrap').Navbar;
var Navigation = require('react-router').Navigation;
var Parse = require('parse').Parse;
var React = require('react');

var Markdown = require('./Markdown');
var CodeEditor = require('./CodeEditor');
var TrackDropdown = require('./TrackDropdown');

var WorldModel = require('../models/WorldModel');
var WorldCanvas = require('./WorldCanvas');

require('./WorldPage.css');
var WorldPage = React.createClass({

  mixins: [Navigation],

  getInitialState: function() {
    return {
      worldModel: null,
      worldDefinition: '',
      worldDescription: '',
      worldName: '',
      worldPublic: null,
      worldTrack: null,
      needsSave: false,
      worldStepDefinitions: []
    }
  },

  componentDidMount: function() {
    this.loadWorld(this.props.params.worldId);
  },

  loadWorld: function(worldId) {
    if (!worldId) {
      return;
    }
    var query = new Parse.Query(WorldModel);
    this.setState({isLoading:true})
    query.get(worldId, {
      success: function(worldModel) {
        this.setState({
          worldModel:worldModel,
          worldName:worldModel.get('name'),
          worldDescription:worldModel.get('description'),
          worldDefinition:worldModel.get('definition'),
          worldPublic:worldModel.get('public'),
          worldTrack:worldModel.get('track'),
          worldStepDefinitions:worldModel.get('steps') || [],
          isLoading:false
        });
      }.bind(this),
      error: function(object, error) {
        alert("failed to load: "+error.code+" "+error.message);
      }.bind(this)
    });
  },

  componentWillReceiveProps: function(nextProps) {
    if (nextProps.params.worldId != this.props.params.worldId) {
      this.loadWorld(nextProps.params.worldId);
    }
  },

  handleChange: function() {
    var name = this.refs.nameInput.getDOMNode().value;
    var description = this.refs.descriptionInput.getDOMNode().value;
    var definition = this.refs.definitionInput.getDOMNode().value;
    var isPublic = this.refs.publicCheckbox.getChecked();
    var track = this.refs.trackInput.getValue();

    var needsSave = (
      name != this.state.worldModel.get('name') ||
      definition != this.state.worldModel.get('definition') ||
      description != this.state.worldModel.get('description') ||
      isPublic != this.state.worldModel.get('public') ||
      (track && track.id) != this.state.worldModel.get('track') && this.state.worldModel.get('track').id
    );

    if (!needsSave) {
      var modelSteps = this.state.worldModel.get('steps');
      needsSave = modelSteps.length !== this.state.worldStepDefinitions.length;
      console.log("# steps", modelSteps.length, this.state.worldStepDefinitions.length)
      if (!needsSave) {
        this.state.worldStepDefinitions.forEach(function(stepDefinition, index) {
          console.log("comparing", stepDefinition, "to", modelSteps[index]);
          if (stepDefinition !== modelSteps[index]) {
            needsSave = true;
          }
        });
      }
    }

    this.setState({
      worldName:name,
      worldDescription:description,
      worldDefinition:definition,
      worldPublic:isPublic,
      worldTrack:track,
      needsSave: needsSave
    })
  },

  handleChangeStep: function(index, event) {
    this.state.worldStepDefinitions[index] = event.target.value;
    var modelSteps = this.state.worldModel.get('steps');
    this.setState({worldStepDefinitions:this.state.worldStepDefinitions});
    this.handleChange();
  },

  handleSave: function() {
    this.state.worldModel.set('name', this.refs.nameInput.getDOMNode().value);
    this.state.worldModel.set('description', this.refs.descriptionInput.getDOMNode().value);
    this.state.worldModel.set('definition', this.refs.definitionInput.getDOMNode().value);
    this.state.worldModel.set('public', this.refs.publicCheckbox.getChecked());
    this.state.worldModel.set('track', this.refs.trackInput.getValue());
    this.state.worldModel.set('steps', this.state.worldStepDefinitions);
    this.setState({saving: true});
    this.state.worldModel.save(null, {
      success: function() {
        this.setState({saving: false, needsSave:false});
      }.bind(this)
    })
  },

  handleAddStep: function() {
    var worldStepDefinitions = this.state.worldStepDefinitions;
    if (worldStepDefinitions.length == 0) {
      worldStepDefinitions.push(this.state.worldDefinition);
    } else {
      worldStepDefinitions.push(worldStepDefinitions[worldStepDefinitions.length-1]);
    }
    this.setState({worldStepDefinitions:worldStepDefinitions});
    this.handleChange();
  },

  handleRemoveStep: function(index) {
    var worldStepDefinitions = this.state.worldStepDefinitions;
    worldStepDefinitions.splice(index, 1);
    this.setState({worldStepDefinitions:worldStepDefinitions});
    this.handleChange();
  },

  handleDeleteWorld: function() {
    this.state.worldModel.destroy({
      success: function(world) {
        this.transitionTo('worlds');
      }.bind(this),
      error: function(world, error) {
        alert("There was an error while deleting the world: "+error.code+" "+error.message);
      }.bind(this)
    })
    this.goBack();
  },

  isEditable: function() {
    return this.state.worldModel.get('owner').id == Parse.User.current().id;
  },

  render: function() {
    if (this.state.isLoading || !this.state.worldModel) {
      return
loading...
;
    }

    var content;
    if (this.isEditable()) {
      content = this.renderEditableWorldPane();
    } else {
      content = this.renderWorldPane();
    }

    return (

        {content}

    );
  },

  renderWorldPane: function() {
    return (

{this.state.worldName}

          <Markdown>{this.state.worldDescription}</Markdown>

          <WorldCanvas worldDefinition={this.state.worldDefinition} />

    );
  },

  renderEditableWorldPane: function() {
    var deleteConfirmationModal = (
      <Modal title="Delete World?" animation={false}>

          Are you sure you want to delete this world? This cannot be undone.

          <Button bsStyle="danger" onClick={this.handleDeleteWorld}>Delete World</Button>

      </Modal>
      );
    var stepFields = this.state.worldStepDefinitions.map(function(step, index) {
      return (

            Step {index+1}
            <Glyphicon onClick={this.handleRemoveStep.bind(this, index)} className="pull-right" glyph="remove"/>

          <CodeEditor onChange={this.handleChangeStep.bind(this, index)} className="form-control" value={step}/>

      );
    }.bind(this));
    var stepCanvases = this.state.worldStepDefinitions.map(function(step) {
      return <WorldCanvas worldDefinition={step} />
    });
    return (

          <form>
            {this.state.saving ? "Saving..." : null}

              World Name
              <input ref="nameInput" onChange={this.handleChange} type="text" className="form-control" placeholder="world name" defaultValue={this.state.worldName}/>

              Track
              <TrackDropdown ref="trackInput" onChange={this.handleChange} defaultValue={this.state.worldTrack}/>

              Description/Instructions
              <textarea ref="descriptionInput" onChange={this.handleChange} className="form-control worldDescriptionInput" defaultValue={this.state.worldDescription}></textarea>

              World Definition
              <textarea ref="definitionInput"
                onChange={this.handleChange}
                className="form-control worldDefinitionInput"
                defaultValue={this.state.worldModel.get('definition')} />

              Steps

              {stepFields}
              <Button onClick={this.handleAddStep}>Add Step</Button>

              Privacy
              <Input
                type="checkbox"
                ref="publicCheckbox"
                onClick={this.handleChange}
                defaultChecked={this.state.worldModel.get('public')}
                label="Visible to anyone"/>

            <Button onClick={this.handleSave} className="pull-right" disabled={!this.state.needsSave} bsStyle={this.state.needsSave ? "primary" : "default"}>Save</Button>
            <ModalTrigger modal={deleteConfirmationModal}>
              <Button onClick={this.handleDelete} bsStyle="danger">Delete</Button>
            </ModalTrigger>
          </form>

{this.state.worldName}

              <Markdown>{this.state.worldDescription}</Markdown>

              <WorldCanvas worldDefinition={this.state.worldDefinition} />
              {stepCanvases}

    );
  }
});

module.exports = WorldPage;<commit_msg>update UI for world page<commit_after>/** @jsx React.DOM */
var Button = require('react-bootstrap').Button;
var Glyphicon = require('react-bootstrap').Glyphicon;
var Input = require('react-bootstrap').Input;
var Modal = require('react-bootstrap').Modal;
var ModalTrigger = require('react-bootstrap').ModalTrigger;
var Nav = require('react-bootstrap').Nav;
var Navbar = require('react-bootstrap').Navbar;
var Navigation = require('react-router').Navigation;
var Parse = require('parse').Parse;
var React = require('react');

var Markdown = require('./Markdown');
var CodeEditor = require('./CodeEditor');
var TrackDropdown = require('./TrackDropdown');

var WorldModel = require('../models/WorldModel');
var WorldCanvas = require('./WorldCanvas');

require('./WorldPage.css');
var WorldPage = React.createClass({

  mixins: [Navigation],

  getInitialState: function() {
    return {
      worldModel: null,
      worldDefinition: '',
      worldDescription: '',
      worldName: '',
      worldPublic: null,
      worldTrack: null,
      needsSave: false,
      worldStepDefinitions: []
    }
  },

  componentDidMount: function() {
    this.loadWorld(this.props.params.worldId);
  },

  loadWorld: function(worldId) {
    if (!worldId) {
      return;
    }
    var query = new Parse.Query(WorldModel);
    this.setState({isLoading:true})
    query.get(worldId, {
      success: function(worldModel) {
        this.setState({
          worldModel:worldModel,
          worldName:worldModel.get('name'),
          worldDescription:worldModel.get('description'),
          worldDefinition:worldModel.get('definition'),
          worldPublic:worldModel.get('public'),
          worldTrack:worldModel.get('track'),
          worldStepDefinitions:worldModel.get('steps') || [],
          isLoading:false
        });
      }.bind(this),
      error: function(object, error) {
        alert("failed to load: "+error.code+" "+error.message);
      }.bind(this)
    });
  },

  componentWillReceiveProps: function(nextProps) {
    if (nextProps.params.worldId != this.props.params.worldId) {
      this.loadWorld(nextProps.params.worldId);
    }
  },

  handleChange: function() {
    var name = this.refs.nameInput.getDOMNode().value;
    var description = this.refs.descriptionInput.getDOMNode().value;
    var definition = this.refs.definitionInput.getDOMNode().value;
    var isPublic = this.refs.publicCheckbox.getChecked();
    var track = this.refs.trackInput.getValue();

    var needsSave = (
      name != this.state.worldModel.get('name') ||
      definition != this.state.worldModel.get('definition') ||
      description != this.state.worldModel.get('description') ||
      isPublic != this.state.worldModel.get('public') ||
      (track && track.id) != this.state.worldModel.get('track') && this.state.worldModel.get('track').id
    );

    if (!needsSave) {
      var modelSteps = this.state.worldModel.get('steps');
      needsSave = modelSteps.length !== this.state.worldStepDefinitions.length;
      console.log("# steps", modelSteps.length, this.state.worldStepDefinitions.length)
      if (!needsSave) {
        this.state.worldStepDefinitions.forEach(function(stepDefinition, index) {
          console.log("comparing", stepDefinition, "to", modelSteps[index]);
          if (stepDefinition !== modelSteps[index]) {
            needsSave = true;
          }
        });
      }
    }

    this.setState({
      worldName:name,
      worldDescription:description,
      worldDefinition:definition,
      worldPublic:isPublic,
      worldTrack:track,
      needsSave: needsSave
    })
  },

  handleChangeStep: function(index, event) {
    this.state.worldStepDefinitions[index] = event.target.value;
    var modelSteps = this.state.worldModel.get('steps');
    this.setState({worldStepDefinitions:this.state.worldStepDefinitions});
    this.handleChange();
  },

  handleSave: function() {
    this.state.worldModel.set('name', this.refs.nameInput.getDOMNode().value);
    this.state.worldModel.set('description', this.refs.descriptionInput.getDOMNode().value);
    this.state.worldModel.set('definition', this.refs.definitionInput.getDOMNode().value);
    this.state.worldModel.set('public', this.refs.publicCheckbox.getChecked());
    this.state.worldModel.set('track', this.refs.trackInput.getValue());
    this.state.worldModel.set('steps', this.state.worldStepDefinitions);
    this.setState({saving: true});
    this.state.worldModel.save(null, {
      success: function() {
        this.setState({saving: false, needsSave:false});
      }.bind(this)
    })
  },

  handleAddStep: function() {
    var worldStepDefinitions = this.state.worldStepDefinitions;
    if (worldStepDefinitions.length == 0) {
      worldStepDefinitions.push(this.state.worldDefinition);
    } else {
      worldStepDefinitions.push(worldStepDefinitions[worldStepDefinitions.length-1]);
    }
    this.setState({worldStepDefinitions:worldStepDefinitions});
    this.handleChange();
  },

  handleRemoveStep: function(index) {
    var worldStepDefinitions = this.state.worldStepDefinitions;
    worldStepDefinitions.splice(index, 1);
    this.setState({worldStepDefinitions:worldStepDefinitions});
    this.handleChange();
  },

  handleDeleteWorld: function() {
    this.state.worldModel.destroy({
      success: function(world) {
        this.transitionTo('worlds');
      }.bind(this),
      error: function(world, error) {
        alert("There was an error while deleting the world: "+error.code+" "+error.message);
      }.bind(this)
    })
    this.goBack();
  },

  isEditable: function() {
    return this.state.worldModel.get('owner').id == Parse.User.current().id;
  },

  render: function() {
    if (this.state.isLoading || !this.state.worldModel) {
      return
loading...
;
    }

    var content;
    if (this.isEditable()) {
      content = this.renderEditableWorldPane();
    } else {
      content = this.renderWorldPane();
    }

    return (

        {content}

    );
  },

  renderWorldPane: function() {
    return (

{this.state.worldName}

          <Markdown>{this.state.worldDescription}</Markdown>

          <WorldCanvas worldDefinition={this.state.worldDefinition} />

    );
  },

  renderEditableWorldPane: function() {
    var deleteConfirmationModal = (
      <Modal title="Delete World?" animation={false}>

          Are you sure you want to delete this world? This cannot be undone.

          <Button bsStyle="danger" onClick={this.handleDeleteWorld}>Delete World</Button>

      </Modal>
      );
    var stepFields = this.state.worldStepDefinitions.map(function(step, index) {
      return (

            Step {index+1}
            <Glyphicon onClick={this.handleRemoveStep.bind(this, index)} className="pull-right" glyph="remove"/>

          <CodeEditor onChange={this.handleChangeStep.bind(this, index)} className="form-control" value={step}/>

      );
    }.bind(this));
    var stepCanvases = this.state.worldStepDefinitions.map(function(step, index) {
      return

Step {index+1}

        <WorldCanvas worldDefinition={step} />

    });
    return (

          <form>
            {this.state.saving ? "Saving..." : null}

              World Name
              <input ref="nameInput" onChange={this.handleChange} type="text" className="form-control" placeholder="world name" defaultValue={this.state.worldName}/>

              Track
              <TrackDropdown ref="trackInput" onChange={this.handleChange} defaultValue={this.state.worldTrack}/>

              Description/Instructions
              <textarea ref="descriptionInput" onChange={this.handleChange} className="form-control worldDescriptionInput" defaultValue={this.state.worldDescription}></textarea>

              World Definition
              <CodeEditor ref="definitionInput"
                onChange={this.handleChange}
                defaultValue={this.state.worldModel.get('definition')} />

              Steps

              {stepFields}
              <Button onClick={this.handleAddStep}>Add Step</Button>

              Privacy
              <Input
                type="checkbox"
                ref="publicCheckbox"
                onClick={this.handleChange}
                defaultChecked={this.state.worldModel.get('public')}
                label="Visible to anyone"/>

            <Button onClick={this.handleSave} className="pull-right" disabled={!this.state.needsSave} bsStyle={this.state.needsSave ? "primary" : "default"}>Save</Button>
            <ModalTrigger modal={deleteConfirmationModal}>
              <Button onClick={this.handleDelete} bsStyle="danger">Delete</Button>
            </ModalTrigger>
          </form>

{this.state.worldName}

              <Markdown>{this.state.worldDescription}</Markdown>

              <WorldCanvas worldDefinition={this.state.worldDefinition} />
              {stepCanvases}

    );
  }
});

module.exports = WorldPage;<|endoftext|>


<commit_before>/**
 * util-request.js - The utilities for requesting script and style files
 * ref: tests/research/load-js-css/test.html
 */
if (isWebWorker) {
  function requestFromWebWorker(url, callback, charset, crossorigin) {
    // Load with importScripts
    var error
    try {
      importScripts(url)
    } catch (e) {
      error = e
    }
    callback(error)
  }
  // For Developers
  seajs.request = requestFromWebWorker
}
else {
  var doc = document
  var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement
  var baseElement = head.getElementsByTagName("base")[0]

  var currentlyAddingScript

  function request(url, callback, charset, crossorigin) {
    var node = doc.createElement("script")

    if (charset) {
      node.charset = charset
    }

    if (isUndefined(crossorigin)) {
      node.setAttribute("crossorigin", crossorigin)
    }

    addOnload(node, callback, url)

    node.async = true
    node.src = url

    // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
    // the end of the insert execution, so use `currentlyAddingScript` to
    // hold current node, for deriving url in `define` call
    currentlyAddingScript = node

    // ref: #185 & http://dev.jquery.com/ticket/2709
    baseElement ?
        head.insertBefore(node, baseElement) :
        head.appendChild(node)

    currentlyAddingScript = null
  }

  function addOnload(node, callback, url) {
    var supportOnload = "onload" in node

    if (supportOnload) {
      node.onload = onload
      node.onerror = function() {
        emit("error", { uri: url, node: node })
        onload(true)
      }
    }
    else {
      node.onreadystatechange = function() {
        if (/loaded|complete/.test(node.readyState)) {
          onload()
        }
      }
    }

    function onload(error) {
      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      if (!data.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = null

      callback(error)
    }
  }

  // For Developers
  seajs.request = request

}
<commit_msg>Fixed crossorigin logic<commit_after>/**
 * util-request.js - The utilities for requesting script and style files
 * ref: tests/research/load-js-css/test.html
 */
if (isWebWorker) {
  function requestFromWebWorker(url, callback, charset, crossorigin) {
    // Load with importScripts
    var error
    try {
      importScripts(url)
    } catch (e) {
      error = e
    }
    callback(error)
  }
  // For Developers
  seajs.request = requestFromWebWorker
}
else {
  var doc = document
  var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement
  var baseElement = head.getElementsByTagName("base")[0]

  var currentlyAddingScript

  function request(url, callback, charset, crossorigin) {
    var node = doc.createElement("script")

    if (charset) {
      node.charset = charset
    }

    if (!isUndefined(crossorigin)) {
      node.setAttribute("crossorigin", crossorigin)
    }

    addOnload(node, callback, url)

    node.async = true
    node.src = url

    // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
    // the end of the insert execution, so use `currentlyAddingScript` to
    // hold current node, for deriving url in `define` call
    currentlyAddingScript = node

    // ref: #185 & http://dev.jquery.com/ticket/2709
    baseElement ?
        head.insertBefore(node, baseElement) :
        head.appendChild(node)

    currentlyAddingScript = null
  }

  function addOnload(node, callback, url) {
    var supportOnload = "onload" in node

    if (supportOnload) {
      node.onload = onload
      node.onerror = function() {
        emit("error", { uri: url, node: node })
        onload(true)
      }
    }
    else {
      node.onreadystatechange = function() {
        if (/loaded|complete/.test(node.readyState)) {
          onload()
        }
      }
    }

    function onload(error) {
      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      if (!data.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = null

      callback(error)
    }
  }

  // For Developers
  seajs.request = request

}
<|endoftext|>


<commit_before>const Global = require('../global');

module.exports = {
  /**
   * scale in animate
   * @param  {object}  element g element
   * @param  {object}  x to x
   * @param  {object}  y to y
   * @param  {object}  centerX scale centerX
   * @param  {object}  centerY scale centerY
   */
  scaleIn(element, x, y, centerX, centerY) {
    centerX = centerX ? centerX : x;
    centerY = centerY ? centerY : y;
    element.transform([
      [ 't', -centerX, -centerY ],
      [ 's', 0.01, 0.01 ],
      [ 't', x, y ]
    ]);
    element && !element.get('destroyed') && element.animate({
      transform: [
        [ 't', -x, -y ],
        [ 's', 100, 100 ],
        [ 't', centerX, centerY ]
      ]
    }, Global.enterDuration, Global.enterEasing);
  },
  /**
   * scale out animate
   * @param  {object}  element g element
   * @param  {object}  x to x
   * @param  {object}  y to y
   * @param  {object}  centerX scale centerX
   * @param  {object}  centerY scale centerY
   */
  scaleOut(element, x, y, callback) {
    element && !element.get('destroyed') && element.animate({
      transform: [
        [ 't', -x, -y ],
        [ 's', 0.01, 0.01 ],
        [ 't', x, y ]
      ]
    }, Global.leaveDuration, Global.leaveEasing, callback);
  }
};
<commit_msg>Correct the annotation<commit_after>const Global = require('../global');

module.exports = {
  /**
   * scale in animate
   * @param  {object}  element g element
   * @param  {object}  x to x
   * @param  {object}  y to y
   * @param  {object}  centerX scale centerX
   * @param  {object}  centerY scale centerY
   */
  scaleIn(element, x, y, centerX, centerY) {
    centerX = centerX ? centerX : x;
    centerY = centerY ? centerY : y;
    element.transform([
      [ 't', -centerX, -centerY ],
      [ 's', 0.01, 0.01 ],
      [ 't', x, y ]
    ]);
    element && !element.get('destroyed') && element.animate({
      transform: [
        [ 't', -x, -y ],
        [ 's', 100, 100 ],
        [ 't', centerX, centerY ]
      ]
    }, Global.enterDuration, Global.enterEasing);
  },
  /**
   * scale out animate
   * @param  {object}  element g element
   * @param  {object}  x to x
   * @param  {object}  y to y
   * @param  {function} callback callback when animate finshed
   */
  scaleOut(element, x, y, callback) {
    element && !element.get('destroyed') && element.animate({
      transform: [
        [ 't', -x, -y ],
        [ 's', 0.01, 0.01 ],
        [ 't', x, y ]
      ]
    }, Global.leaveDuration, Global.leaveEasing, callback);
  }
};
<|endoftext|>


<commit_before>/**
 * echarts层级查找方法
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, linzhifeng@baidu.com)
 *
 */
define(function() {
    var zrUtil = require('zrender/tool/util');

    /**
     * 获取嵌套选项的基础方法
     * 返回optionTarget中位于optionLocation上的值，如果没有定义，则返回undefined
     */
    function query(optionTarget, optionLocation) {
        if (typeof optionTarget == 'undefined') {
            return;
        }

        if (!optionLocation) {
            return optionTarget;
        }

        optionLocation = optionLocation.split('.');
        var length = optionLocation.length;
        var curIdx = 0;
        while (curIdx < length) {
            optionTarget = optionTarget[optionLocation[curIdx]];
            if (typeof optionTarget == 'undefined') {
                return;
            }
            curIdx++;
        }

        return optionTarget;
    }

    /**
     * 获取多级控制嵌套属性的基础方法
     * 返回ctrList中优先级最高（最靠前）的非undefined属性，ctrList中均无定义则返回undefined
     */
    function deepQuery(ctrList, optionLocation) {
        var finalOption;
        for (var i = 0, l = ctrList.length; i < l; i++) {
            finalOption = query(ctrList[i], optionLocation);
            if (typeof finalOption != 'undefined') {
                return finalOption;
            }
        }
    }

    /**
     * 获取多级控制嵌套属性的基础方法
     * 根据ctrList中优先级合并产出目标属性
     */
    function deepMerge(ctrList, optionLocation) {
        var finalOption;
        var len = ctrList.length;
        while (len--) {
            var tempOption = query(ctrList[len], optionLocation);
            if (typeof tempOption != 'undefined') {
                if (typeof finalOption == 'undefined') {
                    finalOption = zrUtil.clone(tempOption);
                }
                else {
                    zrUtil.merge(
                        finalOption, tempOption, true
                    );
                }
            }
        }

        return finalOption;
    }

    return {
        query : query,
        deepQuery : deepQuery,
        deepMerge : deepMerge
    };
});<commit_msg>添加require参数，解决加载顺序错误导致图表偶尔不出现的问题<commit_after>/**
 * echarts层级查找方法
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, linzhifeng@baidu.com)
 *
 */
define(function(require) {
    var zrUtil = require('zrender/tool/util');

    /**
     * 获取嵌套选项的基础方法
     * 返回optionTarget中位于optionLocation上的值，如果没有定义，则返回undefined
     */
    function query(optionTarget, optionLocation) {
        if (typeof optionTarget == 'undefined') {
            return;
        }

        if (!optionLocation) {
            return optionTarget;
        }

        optionLocation = optionLocation.split('.');
        var length = optionLocation.length;
        var curIdx = 0;
        while (curIdx < length) {
            optionTarget = optionTarget[optionLocation[curIdx]];
            if (typeof optionTarget == 'undefined') {
                return;
            }
            curIdx++;
        }

        return optionTarget;
    }

    /**
     * 获取多级控制嵌套属性的基础方法
     * 返回ctrList中优先级最高（最靠前）的非undefined属性，ctrList中均无定义则返回undefined
     */
    function deepQuery(ctrList, optionLocation) {
        var finalOption;
        for (var i = 0, l = ctrList.length; i < l; i++) {
            finalOption = query(ctrList[i], optionLocation);
            if (typeof finalOption != 'undefined') {
                return finalOption;
            }
        }
    }

    /**
     * 获取多级控制嵌套属性的基础方法
     * 根据ctrList中优先级合并产出目标属性
     */
    function deepMerge(ctrList, optionLocation) {
        var finalOption;
        var len = ctrList.length;
        while (len--) {
            var tempOption = query(ctrList[len], optionLocation);
            if (typeof tempOption != 'undefined') {
                if (typeof finalOption == 'undefined') {
                    finalOption = zrUtil.clone(tempOption);
                }
                else {
                    zrUtil.merge(
                        finalOption, tempOption, true
                    );
                }
            }
        }

        return finalOption;
    }

    return {
        query : query,
        deepQuery : deepQuery,
        deepMerge : deepMerge
    };
});
<|endoftext|>