<commit_before>/*
 * vim: ts=4:sw=4:expandtab
 */
(function () {
    'use strict';
    window.Whisper = window.Whisper || {};

    var Message  = window.Whisper.Message = Backbone.Model.extend({
        database  : Whisper.Database,
        storeName : 'messages',
        initialize: function() {
            this.on('change:attachments', this.updateImageUrl);
            this.on('destroy', this.revokeImageUrl);
        },
        defaults  : function() {
            return {
                timestamp: new Date().getTime(),
                attachments: []
            };
        },
        validate: function(attributes, options) {
            var required = ['conversationId', 'received_at', 'sent_at'];
            var missing = _.filter(required, function(attr) { return !attributes[attr]; });
            if (missing.length) {
                console.log("Message missing attributes: " + missing);
            }
        },
        isEndSession: function() {
            var flag = textsecure.protobuf.DataMessage.Flags.END_SESSION;
            return !!(this.get('flags') & flag);
        },
        isGroupUpdate: function() {
            return !!(this.get('group_update'));
        },
        isIncoming: function() {
            return this.get('type') === 'incoming';
        },
        getDescription: function() {
            if (this.isGroupUpdate()) {
                var group_update = this.get('group_update');
                if (group_update.left) {
                    return group_update.left + ' left the group.';
                }

                var messages = ['Updated the group.'];
                if (group_update.name) {
                    messages.push("Title is now '" + group_update.name + "'.");
                }
                if (group_update.joined) {
                    messages.push(group_update.joined.join(', ') + ' joined the group.');
                }

                return messages.join(' ');
            }
            if (this.isEndSession()) {
                return 'Secure session ended.';
            }
            if (this.isIncoming() && this.hasKeyConflicts()) {
                return 'Received message with unknown identity key.';
            }
            if (this.isIncoming() && this.hasErrors()) {
                return 'Error handling incoming message.';
            }

            return this.get('body');
        },
        getNotificationText: function() {
            var description = this.getDescription();
            if (description) {
                return description;
            }
            if (this.get('attachments').length > 0) {
                return 'Media message';
            }

            return '';
        },
        updateImageUrl: function() {
            this.revokeImageUrl();
            var attachment = this.get('attachments')[0];
            if (attachment) {
                var blob = new Blob([attachment.data], {
                    type: attachment.contentType
                });
                this.imageUrl = URL.createObjectURL(blob);
            } else {
                this.imageUrl = null;
            }
        },
        revokeImageUrl: function() {
            if (this.imageUrl) {
                URL.revokeObjectURL(this.imageUrl);
                this.imageUrl = null;
            }
        },
        getImageUrl: function() {
            if (this.imageUrl === undefined) {
                this.updateImageUrl();
            }
            return this.imageUrl;
        },
        getContact: function() {
            var conversationId = this.get('source');
            if (!this.isIncoming()) {
                conversationId = textsecure.storage.user.getNumber();
            }
            var c = ConversationController.get(conversationId);
            if (!c) {
                c = ConversationController.create({id: conversationId, type: 'private'});
                c.fetch();
            }
            return c;
        },
        isOutgoing: function() {
            return this.get('type') === 'outgoing';
        },
        hasErrors: function() {
            return _.size(this.get('errors')) > 0;
        },
        hasKeyConflicts: function() {
            return _.any(this.get('errors'), function(e) {
                return (e.name === 'IncomingIdentityKeyError' ||
                        e.name === 'OutgoingIdentityKeyError');
            });
        },
        hasKeyConflict: function(number) {
            return _.any(this.get('errors'), function(e) {
                return (e.name === 'IncomingIdentityKeyError' ||
                        e.name === 'OutgoingIdentityKeyError') &&
                        e.number === number;
            });
        },
        getKeyConflict: function(number) {
            return _.find(this.get('errors'), function(e) {
                return (e.name === 'IncomingIdentityKeyError' ||
                        e.name === 'OutgoingIdentityKeyError') &&
                        e.number === number;
            });
        },

        send: function(promise) {
            this.trigger('pending');
            return promise.then(function(result) {
                this.trigger('done');
                if (result.dataMessage) {
                    this.set({dataMessage: result.dataMessage});
                }
                this.save({sent: true});
                this.sendSyncMessage();
            }.bind(this)).catch(function(result) {
                this.trigger('done');
                if (result.dataMessage) {
                    this.set({dataMessage: result.dataMessage});
                }
                this.set({sent: true});

                if (result instanceof Error) {
                    this.saveErrors(result);
                } else {
                    this.saveErrors(result.errors);
                    if (result.successfulNumbers.length > 0) {
                        this.sendSyncMessage();
                    }
                }

            }.bind(this));
        },

        sendSyncMessage: function() {
            var dataMessage = this.get('dataMessage');
            if (this.get('synced') || !dataMessage) {
                return;
            }

            textsecure.messaging.sendSyncMessage(
                dataMessage, this.get('sent_at'), this.get('destination')
            ).then(function() {
                this.save({synced: true, dataMessage: null});
            }.bind(this));
        },

        saveErrors: function(errors) {
            if (!(errors instanceof Array)) {
                errors = [errors];
            }
            errors.forEach(function(e) {
                console.log(e);
                console.log(e.reason, e.stack);
            });
            errors = errors.map(function(e) {
                if (e.constructor === Error ||
                    e.constructor === TypeError ||
                    e.constructor === ReferenceError) {
                    return _.pick(e, 'name', 'message', 'code', 'number', 'reason');
                }
                return e;
            });
            errors = errors.concat(this.get('errors') || []);

            return this.save({errors : errors});
        },

        removeConflictFor: function(number) {
            var errors = _.reject(this.get('errors'), function(e) {
                return e.number === number &&
                    (e.name === 'IncomingIdentityKeyError' ||
                     e.name === 'OutgoingIdentityKeyError');
            });
            this.set({errors: errors});
        },

        removeOutgoingErrors: function(number) {
            var errors = _.partition(this.get('errors'), function(e) {
                return e.number === number &&
                    (e.name === 'OutgoingMessageError' ||
                     e.name === 'SendMessageNetworkError');
            });
            this.set({errors: errors[1]});
            return errors[0][0];
        },

        resend: function(number) {
            var error = this.removeOutgoingErrors(number);
            if (error) {
                var promise = new textsecure.ReplayableError(error).replay();
                this.send(promise);
            }
        },

        resolveConflict: function(number) {
            var error = this.getKeyConflict(number);
            if (error) {
                var promise = new textsecure.ReplayableError(error).replay();
                if (this.isIncoming()) {
                    promise = promise.then(function(dataMessage) {
                        this.removeConflictFor(number);
                        this.handleDataMessage(dataMessage);
                    }.bind(this));
                } else {
                    promise = promise.then(function() {
                        this.removeConflictFor(number);
                        this.save();
                    }.bind(this));
                }
                promise.catch(function(e) {
                    this.saveErrors(e);
                }.bind(this));

                return promise;
            }
        },
        handleDataMessage: function(dataMessage) {
            // This function can be called from the background script on an
            // incoming message or from the frontend after the user accepts an
            // identity key change.
            var message = this;
            var source = message.get('source');
            var type = message.get('type');
            var timestamp = message.get('sent_at');
            var conversationId = message.get('conversationId');
            if (dataMessage.group) {
                conversationId = dataMessage.group.id;
            }
            var conversation = ConversationController.create({id: conversationId});
            conversation.fetch().always(function() {
                var now = new Date().getTime();
                var attributes = { type: 'private' };
                if (dataMessage.group) {
                    var group_update = {};
                    attributes = {
                        type: 'group',
                        groupId: dataMessage.group.id,
                    };
                    if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.UPDATE) {
                        attributes = {
                            type       : 'group',
                            groupId    : dataMessage.group.id,
                            name       : dataMessage.group.name,
                            avatar     : dataMessage.group.avatar,
                            members    : dataMessage.group.members,
                        };
                        group_update = conversation.changedAttributes(_.pick(dataMessage.group, 'name', 'avatar')) || {};
                        var difference = _.difference(dataMessage.group.members, conversation.get('members'));
                        if (difference.length > 0) {
                            group_update.joined = difference;
                        }
                    }
                    else if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.QUIT) {
                        if (source == textsecure.storage.user.getNumber()) {
                            group_update = { left: "You" };
                        } else {
                            group_update = { left: source };
                        }
                        attributes.members = _.without(conversation.get('members'), source);
                    }

                    if (_.keys(group_update).length > 0) {
                        message.set({group_update: group_update});
                    }
                }
                if (type === 'outgoing') {
                    // lazy hack - check for receipts that arrived early.
                    if (dataMessage.group && dataMessage.group.id) {  // group sync
                        var members = conversation.get('members') || [];
                        var receipts = window.receipts.where({ timestamp: timestamp });
                        for (var i in receipts) {
                            if (members.indexOf(receipts[i].get('source')) > -1) {
                                window.receipts.remove(receipts[i]);
                                message.set({
                                    delivered: (message.get('delivered') || 0) + 1
                                });
                            }
                        }
                    } else {
                        var receipt = window.receipts.findWhere({
                            timestamp: timestamp,
                            source: conversationId
                        });
                        if (receipt) {
                            window.receipts.remove(receipt);
                            message.set({
                                delivered: (message.get('delivered') || 0) + 1
                            });
                        }
                    }
                }
                attributes.active_at = now;
                if (type === 'incoming') {
                    attributes.unreadCount = conversation.get('unreadCount') + 1;
                }
                conversation.set(attributes);

                message.set({
                    body           : dataMessage.body,
                    conversationId : conversation.id,
                    attachments    : dataMessage.attachments,
                    decrypted_at   : now,
                    flags          : dataMessage.flags,
                    errors         : []
                });

                var conversation_timestamp = conversation.get('timestamp');
                if (!conversation_timestamp || message.get('sent_at') > conversation_timestamp) {
                    conversation.set({
                        timestamp: message.get('sent_at'),
                        lastMessage: message.getNotificationText()
                    });
                }
                else if (!conversation.get('lastMessage')) {
                    conversation.set({
                        lastMessage: message.getNotificationText()
                    });
                }

                message.save().then(function() {
                    conversation.save().then(function() {
                        conversation.trigger('newmessage', message);
                        conversation.notify(message);
                    });
                });
            });
        }

    });

    Whisper.MessageCollection = Backbone.Collection.extend({
        model      : Message,
        database   : Whisper.Database,
        storeName  : 'messages',
        comparator : 'received_at',
        initialize : function(models, options) {
            if (options) {
                this.conversation = options.conversation;
            }
        },
        destroyAll : function () {
            return Promise.all(this.models.map(function(m) {
                return new Promise(function(resolve, reject) {
                    m.destroy().then(resolve).fail(reject);
                });
            }));
        },

        fetchSentAt: function(timestamp) {
            return this.fetch({
                index: {
                    // 'receipt' index on sent_at
                    name: 'receipt',
                    only: timestamp
                }
            });
        },

        fetchConversation: function(conversationId) {
            return new Promise(function(resolve) {
                var upper;
                if (this.length === 0) {
                    // fetch the most recent messages first
                    upper = Number.MAX_VALUE;
                } else  {
                    // not our first rodeo, fetch older messages.
                    upper = this.at(0).get('received_at');
                }
                var options = {remove: false, limit: 100};
                options.index = {
                    // 'conversation' index on [conversationId, received_at]
                    name  : 'conversation',
                    lower : [conversationId],
                    upper : [conversationId, upper],
                    order : 'desc'
                    // SELECT messages WHERE conversationId = this.id ORDER
                    // received_at DESC
                };
                this.fetch(options).then(resolve);
            }.bind(this));
        },

        hasKeyConflicts: function() {
            return this.any(function(m) { return m.hasKeyConflicts(); });
        }
    });
})();
<commit_msg>Mark message sent iff at least 1 recipient got it<commit_after>/*
 * vim: ts=4:sw=4:expandtab
 */
(function () {
    'use strict';
    window.Whisper = window.Whisper || {};

    var Message  = window.Whisper.Message = Backbone.Model.extend({
        database  : Whisper.Database,
        storeName : 'messages',
        initialize: function() {
            this.on('change:attachments', this.updateImageUrl);
            this.on('destroy', this.revokeImageUrl);
        },
        defaults  : function() {
            return {
                timestamp: new Date().getTime(),
                attachments: []
            };
        },
        validate: function(attributes, options) {
            var required = ['conversationId', 'received_at', 'sent_at'];
            var missing = _.filter(required, function(attr) { return !attributes[attr]; });
            if (missing.length) {
                console.log("Message missing attributes: " + missing);
            }
        },
        isEndSession: function() {
            var flag = textsecure.protobuf.DataMessage.Flags.END_SESSION;
            return !!(this.get('flags') & flag);
        },
        isGroupUpdate: function() {
            return !!(this.get('group_update'));
        },
        isIncoming: function() {
            return this.get('type') === 'incoming';
        },
        getDescription: function() {
            if (this.isGroupUpdate()) {
                var group_update = this.get('group_update');
                if (group_update.left) {
                    return group_update.left + ' left the group.';
                }

                var messages = ['Updated the group.'];
                if (group_update.name) {
                    messages.push("Title is now '" + group_update.name + "'.");
                }
                if (group_update.joined) {
                    messages.push(group_update.joined.join(', ') + ' joined the group.');
                }

                return messages.join(' ');
            }
            if (this.isEndSession()) {
                return 'Secure session ended.';
            }
            if (this.isIncoming() && this.hasKeyConflicts()) {
                return 'Received message with unknown identity key.';
            }
            if (this.isIncoming() && this.hasErrors()) {
                return 'Error handling incoming message.';
            }

            return this.get('body');
        },
        getNotificationText: function() {
            var description = this.getDescription();
            if (description) {
                return description;
            }
            if (this.get('attachments').length > 0) {
                return 'Media message';
            }

            return '';
        },
        updateImageUrl: function() {
            this.revokeImageUrl();
            var attachment = this.get('attachments')[0];
            if (attachment) {
                var blob = new Blob([attachment.data], {
                    type: attachment.contentType
                });
                this.imageUrl = URL.createObjectURL(blob);
            } else {
                this.imageUrl = null;
            }
        },
        revokeImageUrl: function() {
            if (this.imageUrl) {
                URL.revokeObjectURL(this.imageUrl);
                this.imageUrl = null;
            }
        },
        getImageUrl: function() {
            if (this.imageUrl === undefined) {
                this.updateImageUrl();
            }
            return this.imageUrl;
        },
        getContact: function() {
            var conversationId = this.get('source');
            if (!this.isIncoming()) {
                conversationId = textsecure.storage.user.getNumber();
            }
            var c = ConversationController.get(conversationId);
            if (!c) {
                c = ConversationController.create({id: conversationId, type: 'private'});
                c.fetch();
            }
            return c;
        },
        isOutgoing: function() {
            return this.get('type') === 'outgoing';
        },
        hasErrors: function() {
            return _.size(this.get('errors')) > 0;
        },
        hasKeyConflicts: function() {
            return _.any(this.get('errors'), function(e) {
                return (e.name === 'IncomingIdentityKeyError' ||
                        e.name === 'OutgoingIdentityKeyError');
            });
        },
        hasKeyConflict: function(number) {
            return _.any(this.get('errors'), function(e) {
                return (e.name === 'IncomingIdentityKeyError' ||
                        e.name === 'OutgoingIdentityKeyError') &&
                        e.number === number;
            });
        },
        getKeyConflict: function(number) {
            return _.find(this.get('errors'), function(e) {
                return (e.name === 'IncomingIdentityKeyError' ||
                        e.name === 'OutgoingIdentityKeyError') &&
                        e.number === number;
            });
        },

        send: function(promise) {
            this.trigger('pending');
            return promise.then(function(result) {
                this.trigger('done');
                if (result.dataMessage) {
                    this.set({dataMessage: result.dataMessage});
                }
                this.save({sent: true});
                this.sendSyncMessage();
            }.bind(this)).catch(function(result) {
                this.trigger('done');
                if (result.dataMessage) {
                    this.set({dataMessage: result.dataMessage});
                }

                if (result instanceof Error) {
                    this.saveErrors(result);
                } else {
                    this.saveErrors(result.errors);
                    if (result.successfulNumbers.length > 0) {
                        this.set({sent: true});
                        this.sendSyncMessage();
                    }
                }

            }.bind(this));
        },

        sendSyncMessage: function() {
            var dataMessage = this.get('dataMessage');
            if (this.get('synced') || !dataMessage) {
                return;
            }

            textsecure.messaging.sendSyncMessage(
                dataMessage, this.get('sent_at'), this.get('destination')
            ).then(function() {
                this.save({synced: true, dataMessage: null});
            }.bind(this));
        },

        saveErrors: function(errors) {
            if (!(errors instanceof Array)) {
                errors = [errors];
            }
            errors.forEach(function(e) {
                console.log(e);
                console.log(e.reason, e.stack);
            });
            errors = errors.map(function(e) {
                if (e.constructor === Error ||
                    e.constructor === TypeError ||
                    e.constructor === ReferenceError) {
                    return _.pick(e, 'name', 'message', 'code', 'number', 'reason');
                }
                return e;
            });
            errors = errors.concat(this.get('errors') || []);

            return this.save({errors : errors});
        },

        removeConflictFor: function(number) {
            var errors = _.reject(this.get('errors'), function(e) {
                return e.number === number &&
                    (e.name === 'IncomingIdentityKeyError' ||
                     e.name === 'OutgoingIdentityKeyError');
            });
            this.set({errors: errors});
        },

        removeOutgoingErrors: function(number) {
            var errors = _.partition(this.get('errors'), function(e) {
                return e.number === number &&
                    (e.name === 'OutgoingMessageError' ||
                     e.name === 'SendMessageNetworkError');
            });
            this.set({errors: errors[1]});
            return errors[0][0];
        },

        resend: function(number) {
            var error = this.removeOutgoingErrors(number);
            if (error) {
                var promise = new textsecure.ReplayableError(error).replay();
                this.send(promise);
            }
        },

        resolveConflict: function(number) {
            var error = this.getKeyConflict(number);
            if (error) {
                var promise = new textsecure.ReplayableError(error).replay();
                if (this.isIncoming()) {
                    promise = promise.then(function(dataMessage) {
                        this.removeConflictFor(number);
                        this.handleDataMessage(dataMessage);
                    }.bind(this));
                } else {
                    promise = promise.then(function() {
                        this.removeConflictFor(number);
                        this.save();
                    }.bind(this));
                }
                promise.catch(function(e) {
                    this.saveErrors(e);
                }.bind(this));

                return promise;
            }
        },
        handleDataMessage: function(dataMessage) {
            // This function can be called from the background script on an
            // incoming message or from the frontend after the user accepts an
            // identity key change.
            var message = this;
            var source = message.get('source');
            var type = message.get('type');
            var timestamp = message.get('sent_at');
            var conversationId = message.get('conversationId');
            if (dataMessage.group) {
                conversationId = dataMessage.group.id;
            }
            var conversation = ConversationController.create({id: conversationId});
            conversation.fetch().always(function() {
                var now = new Date().getTime();
                var attributes = { type: 'private' };
                if (dataMessage.group) {
                    var group_update = {};
                    attributes = {
                        type: 'group',
                        groupId: dataMessage.group.id,
                    };
                    if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.UPDATE) {
                        attributes = {
                            type       : 'group',
                            groupId    : dataMessage.group.id,
                            name       : dataMessage.group.name,
                            avatar     : dataMessage.group.avatar,
                            members    : dataMessage.group.members,
                        };
                        group_update = conversation.changedAttributes(_.pick(dataMessage.group, 'name', 'avatar')) || {};
                        var difference = _.difference(dataMessage.group.members, conversation.get('members'));
                        if (difference.length > 0) {
                            group_update.joined = difference;
                        }
                    }
                    else if (dataMessage.group.type === textsecure.protobuf.GroupContext.Type.QUIT) {
                        if (source == textsecure.storage.user.getNumber()) {
                            group_update = { left: "You" };
                        } else {
                            group_update = { left: source };
                        }
                        attributes.members = _.without(conversation.get('members'), source);
                    }

                    if (_.keys(group_update).length > 0) {
                        message.set({group_update: group_update});
                    }
                }
                if (type === 'outgoing') {
                    // lazy hack - check for receipts that arrived early.
                    if (dataMessage.group && dataMessage.group.id) {  // group sync
                        var members = conversation.get('members') || [];
                        var receipts = window.receipts.where({ timestamp: timestamp });
                        for (var i in receipts) {
                            if (members.indexOf(receipts[i].get('source')) > -1) {
                                window.receipts.remove(receipts[i]);
                                message.set({
                                    delivered: (message.get('delivered') || 0) + 1
                                });
                            }
                        }
                    } else {
                        var receipt = window.receipts.findWhere({
                            timestamp: timestamp,
                            source: conversationId
                        });
                        if (receipt) {
                            window.receipts.remove(receipt);
                            message.set({
                                delivered: (message.get('delivered') || 0) + 1
                            });
                        }
                    }
                }
                attributes.active_at = now;
                if (type === 'incoming') {
                    attributes.unreadCount = conversation.get('unreadCount') + 1;
                }
                conversation.set(attributes);

                message.set({
                    body           : dataMessage.body,
                    conversationId : conversation.id,
                    attachments    : dataMessage.attachments,
                    decrypted_at   : now,
                    flags          : dataMessage.flags,
                    errors         : []
                });

                var conversation_timestamp = conversation.get('timestamp');
                if (!conversation_timestamp || message.get('sent_at') > conversation_timestamp) {
                    conversation.set({
                        timestamp: message.get('sent_at'),
                        lastMessage: message.getNotificationText()
                    });
                }
                else if (!conversation.get('lastMessage')) {
                    conversation.set({
                        lastMessage: message.getNotificationText()
                    });
                }

                message.save().then(function() {
                    conversation.save().then(function() {
                        conversation.trigger('newmessage', message);
                        conversation.notify(message);
                    });
                });
            });
        }

    });

    Whisper.MessageCollection = Backbone.Collection.extend({
        model      : Message,
        database   : Whisper.Database,
        storeName  : 'messages',
        comparator : 'received_at',
        initialize : function(models, options) {
            if (options) {
                this.conversation = options.conversation;
            }
        },
        destroyAll : function () {
            return Promise.all(this.models.map(function(m) {
                return new Promise(function(resolve, reject) {
                    m.destroy().then(resolve).fail(reject);
                });
            }));
        },

        fetchSentAt: function(timestamp) {
            return this.fetch({
                index: {
                    // 'receipt' index on sent_at
                    name: 'receipt',
                    only: timestamp
                }
            });
        },

        fetchConversation: function(conversationId) {
            return new Promise(function(resolve) {
                var upper;
                if (this.length === 0) {
                    // fetch the most recent messages first
                    upper = Number.MAX_VALUE;
                } else  {
                    // not our first rodeo, fetch older messages.
                    upper = this.at(0).get('received_at');
                }
                var options = {remove: false, limit: 100};
                options.index = {
                    // 'conversation' index on [conversationId, received_at]
                    name  : 'conversation',
                    lower : [conversationId],
                    upper : [conversationId, upper],
                    order : 'desc'
                    // SELECT messages WHERE conversationId = this.id ORDER
                    // received_at DESC
                };
                this.fetch(options).then(resolve);
            }.bind(this));
        },

        hasKeyConflicts: function() {
            return this.any(function(m) { return m.hasKeyConflicts(); });
        }
    });
})();
<|endoftext|>


<commit_before>import { SessionManager } from '/client/imports/modules';
import { ReactivityProvider } from '/client/imports/facades';
import { AceEditor } from 'meteor/arch:ace-editor';
import $ from 'jquery';
import Helper from '/client/imports/helpers/helper';
import { QueryRender } from '../../ui/querying';

const CodeMirror = require('codemirror');
const JSONEditor = require('jsoneditor');

require('datatables.net')(window, $);
require('datatables.net-buttons')(window, $);
require('datatables.net-responsive')(window, $);
require('datatables.net-bs')(window, $);
require('datatables.net-buttons-bs')(window, $);
require('datatables.net-responsive-bs')(window, $);
require('/node_modules/codemirror/mode/javascript/javascript.js');
require('/node_modules/codemirror/addon/fold/brace-fold.js');
require('/node_modules/codemirror/addon/fold/comment-fold.js');
require('/node_modules/codemirror/addon/fold/foldcode.js');
require('/node_modules/codemirror/addon/fold/foldgutter.js');
require('/node_modules/codemirror/addon/fold/indent-fold.js');
require('/node_modules/codemirror/addon/fold/markdown-fold.js');
require('/node_modules/codemirror/addon/fold/xml-fold.js');
require('/node_modules/codemirror/addon/hint/javascript-hint.js');
require('/node_modules/codemirror/addon/hint/show-hint.js');

const UIComponents = function () {
};

UIComponents.prototype = {
  DataTable: {
    attachDeleteTableRowEvent(selector) {
      selector.find('tbody')
        .on('click', 'a.editor_delete', function () {
          selector.DataTable()
            .row($(this)
              .parents('tr'))
            .remove()
            .draw();
        });
    },

    doTableRowSelectable(table, row) {
      if (row.hasClass('selected')) {
        row.removeClass('selected');
      } else {
        table.$('tr.selected')
          .removeClass('selected');
        row.addClass('selected');
      }
    },

    getDatatableLanguageOptions() {
      return {
        emptyTable: Helper.translate({ key: 'emptyTable' }),
        info: Helper.translate({ key: 'info' }),
        infoEmpty: Helper.translate({ key: 'infoEmpty' }),
        infoFiltered: Helper.translate({ key: 'infoFiltered' }),
        infoPostFix: Helper.translate({ key: 'infoPostFix' }),
        thousands: Helper.translate({ key: 'thousands' }),
        lengthMenu: Helper.translate({ key: 'lengthMenu' }),
        loadingRecords: Helper.translate({ key: 'loadingRecords' }),
        processing: Helper.translate({ key: 'processing' }),
        search: Helper.translate({ key: 'dt_search' }),
        zeroRecords: Helper.translate({ key: 'zeroRecords' }),
        paginate: {
          first: Helper.translate({ key: 'first' }),
          last: Helper.translate({ key: 'last' }),
          next: Helper.translate({ key: 'next' }),
          previous: Helper.translate({ key: 'previous' })
        },
        aria: {
          sortAscending: Helper.translate({ key: 'sortAscending' }),
          sortDescending: Helper.translate({ key: 'sortDescending' })
        }
      };
    },

    initiateDatatable({ selector, sessionKey, clickCallback, noDeleteEvent }) {
      const self = this;
      selector.DataTable({
        language: self.getDatatableLanguageOptions()
      });
      selector.find('tbody')
        .on('click', 'tr', function () {
          const table = selector.DataTable();
          self.doTableRowSelectable(table, $(this));

          if (table.row(this)
            .data()) {
            if (sessionKey) {
              SessionManager.set(sessionKey, table.row(this)
                .data());
            }
            if (clickCallback) clickCallback(table, table.row(this));
          }
        });

      if (!noDeleteEvent) this.attachDeleteTableRowEvent(selector);
    },

    setupDatatable({ selectorString, columns, columnDefs = [], data, extraOptions = {}, autoWidth = true, lengthMenu = [5, 10, 20] }) {
      const selector = $(selectorString);
      if ($.fn.dataTable.isDataTable(selectorString)) {
        selector.DataTable()
          .destroy();
      }
      selector.DataTable(Object.assign(extraOptions, {
        language: this.getDatatableLanguageOptions(),
        responsive: true,
        destroy: true,
        stateSave: true,
        autoWidth,
        data,
        columns,
        columnDefs,
        lengthMenu
      }))
        .draw();
    }
  },

  Editor: {
    getAceEditorValue(selector) {
      return AceEditor.instance(selector)
        .getValue();
    },

    setAceEditorValue({ selector, value }) {
      AceEditor.instance(selector, {
        mode: 'javascript',
        theme: 'dawn',
      }, (editor) => {
        editor.$blockScrolling = Infinity;
        editor.setOptions({
          fontSize: '12pt',
          showPrintMargin: false,
        });
        editor.setValue(JSON.stringify(value, null, '\t'), -1);
      });
    },

    setGridEditorValue({ selector, value }) {
      if (!Array.isArray(value)) value = [value];
      // collect all keys
      const allKeys = this.collectAllKeys(value);
      // create HTML table
      let html = '
';
      // table headers
      html += '

';
      allKeys.forEach((key) => {
        html += `<th>${key}</th>`;
      });
      html += '

';
      // data rows
      html += '
';
      value.forEach((row) => {
        html += this.convertObjectToGridRow(row, allKeys);
      });
      html += '

';

      const container = $(`#${selector}`);
      container.html(html);

      const table = container.find('table');
      table.DataTable({
        paging: false
      });
      const self = this;
      table.on('dblclick', 'td[title]', function () {
        self.displayJsonEditorModal(this.getAttribute('title'));
      });
    },

    collectAllKeys(value) {
      const allKeys = new Set();
      value.forEach((row) => {
        Object.keys(row)
          .forEach(k => allKeys.add(k));
      });
      if (allKeys.size === 0) {
        allKeys.add('(empty)');
      }
      return allKeys;
    },

    convertObjectToGridRow(obj, allKeys) {
      let html = '
';
      allKeys.forEach((key) => {
        let val = obj[key];
        if (typeof val === 'undefined') val = '';
        if (val !== null && typeof val === 'object') {
          const valKeys = Object.keys(val);
          if (valKeys.length === 1 && valKeys[0] === '$date') {
            val = val.$date;
          } else {
            val = JSON.stringify(val);
          }
        }
        val = `${val}`;
        if (val.length > 50) {
          html += `<td title="${this.quoteAttr(val)}">${val.substr(0, 47)}...</td>`;
        } else {
          html += `<td>${val}</td>`;
        }
      });
      html += '
';
      return html;
    },

    quoteAttr(s) {
      return `${s}` /* Forces the conversion to string. */
        .replace(/&/g, '&') /* This MUST be the 1st replacement. */
        .replace(/'/g, ''') /* The 4 other predefined entities, required. */
        .replace(/"/g, '"')
        .replace(/</g, '<')
        .replace(/>/g, '>')
        .replace(/\r\n/g, '
') /* Must be before the next replacement. */
        .replace(/[\r\n]/g, '
');
    },

    displayJsonEditorModal(sData) {
      let modal = $('#json-editor-modal');
      if (modal.length === 0) {
        modal = $('
'
          + '
\n'
          + '
'
          + '
'
          + '        <button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>'
          + '
Cell data
'
          + '
'
          + '

'
          + '
'
          + '
'
          + '
');
        $('body')
          .append(modal);

        this.initializeJSONEditor({
          selector: 'json-editor-modal-data',
          options: {
            mode: 'code',
            modes: ['code', 'view'],
            readOnly: true
          }
        });
      }
      modal.modal();
      $('#json-editor-modal-data')
        .data('jsoneditor')
        .set(JSON.parse(sData));
    },

    initializeJSONEditor({ selector, options = {}, setDivData = true }) {
      const editorDiv = $(`#${selector}`);
      let jsonEditor = editorDiv.data('jsoneditor');
      if (!jsonEditor) {
        jsonEditor = new JSONEditor(document.getElementById(selector), Object.assign({
          mode: 'tree',
          modes: ['code', 'form', 'text', 'tree', 'view'],
          search: true,
        }, options));

        if (setDivData) editorDiv.data('jsoneditor', jsonEditor);
      }

      return jsonEditor;
    },

    doCodeMirrorResizable(codeMirror) {
      $('.CodeMirror')
        .resizable({
          resize() {
            codeMirror.setSize($(this)
              .width(), $(this)
              .height());
          },
        });
    },

    initializeCodeMirror({ divSelector, txtAreaId, keepValue = false, height = 100, noResize = false, extraKeysToAppend = {}, autoCompleteListMethod }) {
      const autoCompleteShortcut = ReactivityProvider.findOne(ReactivityProvider.types.Settings).autoCompleteShortcut || 'Ctrl-Space';
      let codeMirror;

      const extraKeys = Object.assign(extraKeysToAppend, {
        'Ctrl-Q': function (cm) {
          cm.foldCode(cm.getCursor());
        },
        'Ctrl-Enter': function () {
          QueryRender.executeQuery();
        }
      });
      extraKeys[autoCompleteShortcut] = 'autocomplete';

      if (!divSelector.data('editor')) {
        codeMirror = CodeMirror.fromTextArea(document.getElementById(txtAreaId), {
          mode: 'javascript',
          theme: 'neat',
          styleActiveLine: true,
          lineNumbers: true,
          lineWrapping: false,
          extraKeys,
          foldGutter: true,
          gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
        });

        if (keepValue) {
          codeMirror.on('change', () => {
            SessionManager.set(SessionManager.strSessionSelectorValue, codeMirror.getValue());
          });
        }

        codeMirror.setSize('%100', height);
        CodeMirror.hint.javascript = (editor) => {
          const cursor = editor.getCursor();
          const currentLine = editor.getLine(cursor.line);
          let start = cursor.ch;
          let end = start;
          while (end < currentLine.length && /[\w.$]+/.test(currentLine.charAt(end))) end += 1;
          while (start && /[\w.$]+/.test(currentLine.charAt(start - 1))) start -= 1;
          const curWord = (start !== end) && currentLine.slice(start, end);
          const list = autoCompleteListMethod ? autoCompleteListMethod(editor.getValue(), curWord) : SessionManager.get(SessionManager.strSessionDistinctFields) || [];
          const regex = new RegExp(`^${curWord}`, 'i');
          return {
            list: (!curWord ? list : list.filter(item => item.match(regex))).sort(),
            from: CodeMirror.Pos(cursor.line, start),
            to: CodeMirror.Pos(cursor.line, end),
          };
        };
        divSelector.data('editor', codeMirror);

        if (!noResize) this.doCodeMirrorResizable(codeMirror);
      } else {
        codeMirror = divSelector.data('editor');
      }

      if (keepValue && SessionManager.get(SessionManager.strSessionSelectorValue)) codeMirror.setValue(SessionManager.get(SessionManager.strSessionSelectorValue));

      codeMirror.refresh();
    },

    setCodeMirrorValue(divSelector, val, txtSelector) {
      if (divSelector.data('editor')) {
        divSelector.data('editor')
          .setValue(val);
      } else if (txtSelector) {
        txtSelector.val(val);
      }
    },

    getCodeMirrorValue(divSelector) {
      if (divSelector.data('editor')) {
        return divSelector.data('editor')
          .getValue();
      }
      return '';
    }
  },

  Checkbox: {
    states: ['check', 'uncheck', 'enable', 'disable'],

    init(selector, withState) {
      selector.iCheck({
        checkboxClass: 'icheckbox_square-green',
      });
      if (withState && this.states.indexOf(withState) !== -1) {
        selector.iCheck(withState);
      }
    },

    getState(selector) {
      return selector.iCheck('update')[0].checked;
    },

    toggleState(selector, state) {
      if (this.states.indexOf(state) === -1) return;
      selector.iCheck(state);
    }
  },

  Combobox: {
    init({
      selector,
      data,
      options = {
        create_option: true,
        allow_single_deselect: true,
        persistent_create_option: true,
        skip_no_results: true,
      },
      sortDataByKey = true,
      comboGroupLabel
    }) {
      let optionsWrapper = selector;
      if (comboGroupLabel) {
        selector.append($(`<optgroup id="optGroup" label="${comboGroupLabel}"></optgroup>`));
        optionsWrapper = selector.find('#optGroup');
      }

      $.each((sortDataByKey ? Helper.sortObjectByKey(data) : data), (key, value) => {
        optionsWrapper.append($('<option></option>')
          .attr('value', key)
          .text(value));
      });

      selector.chosen(options);
    },

    initializeOptionsCombobox(selector, optionEnum, sessionKey) {
      this.init({ selector, data: optionEnum, options: {} });
      this.setOptionsComboboxChangeEvent(selector, sessionKey);
    },

    setOptionsComboboxChangeEvent(cmb, sessionKey = SessionManager.strSessionSelectedOptions) {
      cmb.on('change', (evt, params) => {
        let array = SessionManager.get(sessionKey) || [];
        if (params.deselected) {
          array = array.filter(item => params.deselected.indexOf(item) === -1);
        } else {
          array.push(params.selected);
        }
        SessionManager.set(sessionKey, array);
      });
    },

    initializeCollectionsCombobox(selector) {
      const collectionNames = SessionManager.get(SessionManager.strSessionCollectionNames);
      const data = {};
      collectionNames.forEach((col) => {
        data[col.name] = col.name;
      });

      this.init({ selector, data, sortDataByKey: false, comboGroupLabel: 'Collections' });
    }
  }
};

export default new UIComponents();
<commit_msg>#480 refactoring<commit_after>import { SessionManager } from '/client/imports/modules';
import { ReactivityProvider } from '/client/imports/facades';
import { AceEditor } from 'meteor/arch:ace-editor';
import $ from 'jquery';
import Helper from '/client/imports/helpers/helper';
import { QueryRender } from '../../ui/querying';

const CodeMirror = require('codemirror');
const JSONEditor = require('jsoneditor');

require('datatables.net')(window, $);
require('datatables.net-buttons')(window, $);
require('datatables.net-responsive')(window, $);
require('datatables.net-bs')(window, $);
require('datatables.net-buttons-bs')(window, $);
require('datatables.net-responsive-bs')(window, $);
require('/node_modules/codemirror/mode/javascript/javascript.js');
require('/node_modules/codemirror/addon/fold/brace-fold.js');
require('/node_modules/codemirror/addon/fold/comment-fold.js');
require('/node_modules/codemirror/addon/fold/foldcode.js');
require('/node_modules/codemirror/addon/fold/foldgutter.js');
require('/node_modules/codemirror/addon/fold/indent-fold.js');
require('/node_modules/codemirror/addon/fold/markdown-fold.js');
require('/node_modules/codemirror/addon/fold/xml-fold.js');
require('/node_modules/codemirror/addon/hint/javascript-hint.js');
require('/node_modules/codemirror/addon/hint/show-hint.js');

const UIComponents = function () {
};

UIComponents.prototype = {
  DataTable: {
    attachDeleteTableRowEvent(selector) {
      selector.find('tbody')
        .on('click', 'a.editor_delete', function () {
          selector.DataTable()
            .row($(this)
              .parents('tr'))
            .remove()
            .draw();
        });
    },

    doTableRowSelectable(table, row) {
      if (row.hasClass('selected')) {
        row.removeClass('selected');
      } else {
        table.$('tr.selected')
          .removeClass('selected');
        row.addClass('selected');
      }
    },

    getDatatableLanguageOptions() {
      return {
        emptyTable: Helper.translate({ key: 'emptyTable' }),
        info: Helper.translate({ key: 'info' }),
        infoEmpty: Helper.translate({ key: 'infoEmpty' }),
        infoFiltered: Helper.translate({ key: 'infoFiltered' }),
        infoPostFix: Helper.translate({ key: 'infoPostFix' }),
        thousands: Helper.translate({ key: 'thousands' }),
        lengthMenu: Helper.translate({ key: 'lengthMenu' }),
        loadingRecords: Helper.translate({ key: 'loadingRecords' }),
        processing: Helper.translate({ key: 'processing' }),
        search: Helper.translate({ key: 'dt_search' }),
        zeroRecords: Helper.translate({ key: 'zeroRecords' }),
        paginate: {
          first: Helper.translate({ key: 'first' }),
          last: Helper.translate({ key: 'last' }),
          next: Helper.translate({ key: 'next' }),
          previous: Helper.translate({ key: 'previous' })
        },
        aria: {
          sortAscending: Helper.translate({ key: 'sortAscending' }),
          sortDescending: Helper.translate({ key: 'sortDescending' })
        }
      };
    },

    initiateDatatable({ selector, sessionKey, clickCallback, noDeleteEvent }) {
      const self = this;
      selector.DataTable({
        language: self.getDatatableLanguageOptions()
      });
      selector.find('tbody')
        .on('click', 'tr', function () {
          const table = selector.DataTable();
          self.doTableRowSelectable(table, $(this));

          if (table.row(this)
            .data()) {
            if (sessionKey) {
              SessionManager.set(sessionKey, table.row(this)
                .data());
            }
            if (clickCallback) clickCallback(table, table.row(this));
          }
        });

      if (!noDeleteEvent) this.attachDeleteTableRowEvent(selector);
    },

    setupDatatable({ selectorString, columns, columnDefs = [], data, extraOptions = {}, autoWidth = true, lengthMenu = [5, 10, 20] }) {
      const selector = $(selectorString);
      if ($.fn.dataTable.isDataTable(selectorString)) {
        selector.DataTable()
          .destroy();
      }
      selector.DataTable(Object.assign(extraOptions, {
        language: this.getDatatableLanguageOptions(),
        responsive: true,
        destroy: true,
        stateSave: true,
        autoWidth,
        data,
        columns,
        columnDefs,
        lengthMenu
      }))
        .draw();
    }
  },

  Editor: {
    getAceEditorValue(selector) {
      return AceEditor.instance(selector)
        .getValue();
    },

    setAceEditorValue({ selector, value }) {
      AceEditor.instance(selector, {
        mode: 'javascript',
        theme: 'dawn',
      }, (editor) => {
        editor.$blockScrolling = Infinity;
        editor.setOptions({
          fontSize: '12pt',
          showPrintMargin: false,
        });
        editor.setValue(JSON.stringify(value, null, '\t'), -1);
      });
    },

    setGridEditorValue({ selector, value }) {
      if (!Array.isArray(value)) value = [value];
      // collect all keys
      const allKeys = this.collectAllKeys(value);
      // create HTML table
      let html = '
';
      // table headers
      html += '

';
      allKeys.forEach((key) => {
        html += `<th>${key}</th>`;
      });
      html += '

';
      // data rows
      html += '
';
      value.forEach((row) => {
        html += this.convertObjectToGridRow(row, allKeys);
      });
      html += '

';

      const container = $(`#${selector}`);
      container.html(html);

      const table = container.find('table');
      table.DataTable({
        paging: false
      });
      const self = this;
      table.on('dblclick', 'td[title]', function () {
        self.displayJsonEditorModal(this.getAttribute('title'));
      });
    },

    collectAllKeys(value) {
      const allKeys = new Set();
      value.forEach((row) => {
        Object.keys(row)
          .forEach(k => allKeys.add(k));
      });
      if (allKeys.size === 0) {
        allKeys.add('(empty)');
      }
      return allKeys;
    },

    convertObjectToGridRow(obj, allKeys) {
      let html = '
';
      allKeys.forEach((key) => {
        let val = obj[key];
        if (typeof val === 'undefined') val = '';
        if (val !== null && typeof val === 'object') {
          const valKeys = Object.keys(val);
          if (valKeys.length === 1 && valKeys[0] === '$date') {
            val = val.$date;
          } else {
            val = JSON.stringify(val);
          }
        }
        val = `${val}`;
        if (val.length > 50) {
          html += `<td title="${this.quoteAttr(val)}">${val.substr(0, 47)}...</td>`;
        } else {
          html += `<td>${val}</td>`;
        }
      });
      html += '
';
      return html;
    },

    quoteAttr(s) {
      return `${s}` /* Forces the conversion to string. */
        .replace(/&/g, '&') /* This MUST be the 1st replacement. */
        .replace(/'/g, ''') /* The 4 other predefined entities, required. */
        .replace(/"/g, '"')
        .replace(/</g, '<')
        .replace(/>/g, '>')
        .replace(/\r\n/g, '
') /* Must be before the next replacement. */
        .replace(/[\r\n]/g, '
');
    },

    displayJsonEditorModal(sData) {
      let modal = $('#json-editor-modal');
      if (modal.length === 0) {
        modal = $('
'
          + '
\n'
          + '
'
          + '
'
          + '        <button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>'
          + '
Cell data
'
          + '
'
          + '

'
          + '
'
          + '
'
          + '
');
        $('body')
          .append(modal);

        this.initializeJSONEditor({
          selector: 'json-editor-modal-data',
          options: {
            mode: 'code',
            modes: ['code', 'view'],
            readOnly: true
          }
        });
      }
      modal.modal();
      $('#json-editor-modal-data')
        .data('jsoneditor')
        .set(JSON.parse(sData));
    },

    initializeJSONEditor({ selector, options = {}, setDivData = true }) {
      const editorDiv = $(`#${selector}`);
      let jsonEditor = editorDiv.data('jsoneditor');
      if (!jsonEditor) {
        jsonEditor = new JSONEditor(document.getElementById(selector), Object.assign({
          mode: 'tree',
          modes: ['code', 'form', 'text', 'tree', 'view'],
          search: true,
        }, options));

        if (setDivData) editorDiv.data('jsoneditor', jsonEditor);
      }

      return jsonEditor;
    },

    doCodeMirrorResizable(codeMirror) {
      $('.CodeMirror')
        .resizable({
          resize() {
            codeMirror.setSize($(this)
              .width(), $(this)
              .height());
          },
        });
    },

    initializeCodeMirror({ divSelector, txtAreaId, keepValue = false, height = 100, noResize = false, extraKeysToAppend = {}, autoCompleteListMethod }) {
      const autoCompleteShortcut = ReactivityProvider.findOne(ReactivityProvider.types.Settings).autoCompleteShortcut || 'Ctrl-Space';
      let codeMirror;

      const extraKeys = Object.assign(extraKeysToAppend, {
        'Ctrl-Q': function (cm) {
          cm.foldCode(cm.getCursor());
        },
        'Ctrl-Enter': function () {
          QueryRender.executeQuery();
        }
      });
      extraKeys[autoCompleteShortcut] = 'autocomplete';

      if (!divSelector.data('editor')) {
        codeMirror = CodeMirror.fromTextArea(document.getElementById(txtAreaId), {
          mode: 'javascript',
          theme: 'neat',
          styleActiveLine: true,
          lineNumbers: true,
          lineWrapping: false,
          extraKeys,
          foldGutter: true,
          gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
        });

        if (keepValue) {
          codeMirror.on('change', () => {
            SessionManager.set(SessionManager.strSessionSelectorValue, codeMirror.getValue());
          });
        }

        codeMirror.setSize('%100', height);
        CodeMirror.hint.javascript = (editor) => {
          const cursor = editor.getCursor();
          const currentLine = editor.getLine(cursor.line);
          let start = cursor.ch;
          let end = start;
          while (end < currentLine.length && /[\w.$]+/.test(currentLine.charAt(end))) end += 1;
          while (start && /[\w.$]+/.test(currentLine.charAt(start - 1))) start -= 1;
          const curWord = (start !== end) && currentLine.slice(start, end);
          const list = autoCompleteListMethod ? autoCompleteListMethod(editor.getValue(), curWord) : SessionManager.get(SessionManager.strSessionDistinctFields) || [];
          const regex = new RegExp(`^${curWord}`, 'i');
          return {
            list: (!curWord ? list : list.filter(item => item.match(regex))).sort(),
            from: CodeMirror.Pos(cursor.line, start),
            to: CodeMirror.Pos(cursor.line, end),
          };
        };
        divSelector.data('editor', codeMirror);

        if (!noResize) this.doCodeMirrorResizable(codeMirror);
      } else {
        codeMirror = divSelector.data('editor');
      }

      if (keepValue && SessionManager.get(SessionManager.strSessionSelectorValue)) codeMirror.setValue(SessionManager.get(SessionManager.strSessionSelectorValue));

      codeMirror.refresh();
    },

    setCodeMirrorValue(divSelector, val, txtSelector) {
      if (divSelector.data('editor')) {
        divSelector.data('editor')
          .setValue(val);
      } else if (txtSelector) {
        txtSelector.val(val);
      }
    },

    getCodeMirrorValue(divSelector) {
      if (divSelector.data('editor')) {
        return divSelector.data('editor')
          .getValue();
      }
      return '';
    }
  },

  Checkbox: {
    states: ['check', 'uncheck', 'enable', 'disable'],

    init(selector, withState) {
      selector.iCheck({
        checkboxClass: 'icheckbox_square-green',
      });
      if (withState && this.states.indexOf(withState) !== -1) {
        selector.iCheck(withState);
      }
    },

    getState(selector) {
      return selector.iCheck('update')[0].checked;
    },

    toggleState(selector, state) {
      if (this.states.indexOf(state) === -1) return;
      selector.iCheck(state);
    }
  },

  Combobox: {
    init({
      selector,
      data,
      options = {
        create_option: true,
        allow_single_deselect: true,
        persistent_create_option: true,
        skip_no_results: true,
      },
      sortDataByKey = true,
      comboGroupLabel
    }) {
      selector.empty();
      selector.prepend("<option value=''></option>");

      let optionsWrapper = selector;
      if (comboGroupLabel) {
        selector.append($(`<optgroup id="optGroup" label="${comboGroupLabel}"></optgroup>`));
        optionsWrapper = selector.find('#optGroup');
      }

      $.each((sortDataByKey ? Helper.sortObjectByKey(data) : data), (key, value) => {
        optionsWrapper.append($('<option></option>')
          .attr('value', key)
          .text(value));
      });

      selector.chosen(options);
      selector.trigger('chosen:updated');
    },

    initializeOptionsCombobox(selector, optionEnum, sessionKey) {
      this.init({ selector, data: optionEnum, options: {} });
      this.setOptionsComboboxChangeEvent(selector, sessionKey);
    },

    setOptionsComboboxChangeEvent(cmb, sessionKey = SessionManager.strSessionSelectedOptions) {
      cmb.on('change', (evt, params) => {
        let array = SessionManager.get(sessionKey) || [];
        if (params.deselected) {
          array = array.filter(item => params.deselected.indexOf(item) === -1);
        } else {
          array.push(params.selected);
        }
        SessionManager.set(sessionKey, array);
      });
    },

    initializeCollectionsCombobox(selector) {
      const collectionNames = SessionManager.get(SessionManager.strSessionCollectionNames);
      const data = {};
      collectionNames.forEach((col) => {
        data[col.name] = col.name;
      });

      this.init({ selector, data, sortDataByKey: false, comboGroupLabel: 'Collections' });
    }
  }
};

export default new UIComponents();
<|endoftext|>


<commit_before>Mealpler.controller('DayCtrl', function (MealModel) {
    let day = this;
    let defaultMeal = {
        /*"id": Math.random(),*/
        "name": "",
        "type": "product",
        "quantity": 1,
        "hasIngredients": false
    };

    day.setCurrentMeal = function (meal, date) {
        day.currentMeal = angular.copy(meal);
        day.currentDate = moment(date);
        day.createNewMealItem();
    };
    day.saveCurrentMeal = function () {
        day.mealsList.map(function (a) {
            if (a.id === day.currentMeal.id) {
                day.currentMeal.mealList.forEach(m => a.mealList.push(m));
            }
        });
        MealModel.updateMealInfo(day);
        day.refreshCurrentMeal();
    };

    day.createNewMealItem = function () {
        day.currentMeal.mealList.push(angular.copy(defaultMeal));
    };

    day.refreshCurrentMeal = function () {
        day.currentMeal = {}; day.currentDate = '';
    };

    day.init = function () {
        let list = MealModel.findMealList(day.date);
        day.mealsList = list != null ? list : angular.copy(MealModel.mealsList());
    };

    day.init();
});<commit_msg>MP-4: minor refactoring for DayCtrl (deprecated)<commit_after>Mealpler.controller('DayCtrl', function (MealModel) {
    let day = this;
    let defaultMeal = {
        /*"id": Math.random(),*/
        "name": "",
        "type": "product",
        "quantity": 1,
        "hasIngredients": false
    };

    day.setCurrentMeal = function (meal, date) {
        day.currentMeal = angular.copy(meal);
        day.currentDate = moment(date);
        day.createNewMealItem();
    };
    day.saveCurrentMeal = function () {
        day.mealsList.map(function (a) {
            if (a.id === day.currentMeal.id) {
                day.currentMeal.mealList.forEach(m => a.mealList.push(m));
            }
        });
        MealModel.updateMealInfo(day);
        day.refreshCurrentMeal();
    };

    day.createNewMealItem = function () {
        day.currentMeal.mealList.push(angular.copy(defaultMeal));
    };

    day.refreshCurrentMeal = function () {
        day.currentMeal = {}; day.currentDate = '';
    };

    day.init = function () {
        let list = MealModel.findMealList(day.date);
        day.mealsList = list != null ? list : angular.copy(MealModel.emptyMealsList());
    };

    day.init();
});<|endoftext|>


<commit_before>function responseIsXml (response) {
  if (response && response.config && response.config.responseType) {
    return response.config.responseType === 'xml'
  }
  var contentType = response.headers('content-type')
  if (contentType) {
    return contentType.search(/\Wxml/i) > -1
  } else {
    return false
  }
}

require('../app').factory('sbXmlHttpInterceptor', /* @ngInject */function ($q, x2js) {
  function responseHandler (response) {
    if (response && responseIsXml(response)) {
      response.data = x2js.xml_str2json(response.data)
      return response
    } else {
      return $q.when(response)
    }
  }

  function responseErrorHandler (response) {
    if (response && responseIsXml(response)) {
      response.data = x2js.xml_str2json(response.data)
    }
    return $q.reject(response)
  }

  return {
    response: responseHandler,
    responseError: responseErrorHandler
  }
})
<commit_msg>Fix crash with headers not a function<commit_after>function responseIsXml (response) {
  if (response && response.config && response.config.responseType) {
    return response.config.responseType === 'xml'
  }
  var contentType = response && (typeof response.headers === 'function') && response.headers('content-type')
  if (contentType) {
    return contentType.search(/\Wxml/i) > -1
  } else {
    return false
  }
}

require('../app').factory('sbXmlHttpInterceptor', /* @ngInject */function ($q, x2js) {
  function responseHandler (response) {
    if (response && responseIsXml(response)) {
      response.data = x2js.xml_str2json(response.data)
      return response
    } else {
      return $q.when(response)
    }
  }

  function responseErrorHandler (response) {
    if (response && responseIsXml(response)) {
      response.data = x2js.xml_str2json(response.data)
    }
    return $q.reject(response)
  }

  return {
    response: responseHandler,
    responseError: responseErrorHandler
  }
})
<|endoftext|>


<commit_before>/*!
 * Finds the person of the week and displays their required data from OICR
 * Written by Joshua Ching and Raunaq Suri
*/

$.ajax({
        type: "GET",
        url: "files/IBC List_2014_v1.csv",
        dataType: "text",
        success: function(data) {
            processIBClist(data);
        }
});

function processIBClist(data){
    console.log("process IBC list works");
    console.log(data);
}

var person; //insert name of the person of the week here

var pubMedUrl = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=+ontario+AND+institute+AND+cancer+AND+research" + person;
<commit_msg>stores it in a variable<commit_after>/*!
 * Finds the person of the week and displays their required data from OICR
 * Written by Joshua Ching and Raunaq Suri
*/

var csvList;
$.ajax({
        type: "GET",
        url: "files/IBC List_2014_v1.csv",
        dataType: "text",
        async: false,
        success: function(data) {
            processIBClist(data);
        }
});

function processIBClist(data){
    "use strict";
    console.log("process IBC list works");
    console.log(data);
    csvList = data;

}
alert(csvList);

var person; //insert name of the person of the week here

var pubMedUrl = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=+ontario+AND+institute+AND+cancer+AND+research" + person;
<|endoftext|>


<commit_before>(function (mvc, helpers) {

	Polymer('polymer-view', {
		publish: {
			selected: {
				value: false,
				reflect: true
			},
			controller: ''
		},
		ready: function () {
			var host = this.shadowRoot.host;
			if (host && host['registerView'] && typeof host['registerView'] === 'function') {
				host.registerView(this);
			}

			if (this.controller && typeof this.controller === 'string') {
				this.enqueueForAttach();
				this.forceControllerAttach();
			}
		},
		registerView: function (view) {
			if (!this.views) {
				this.views = [];
			}

			this.views.push(view);
		},
		enqueueForAttach: function () {
			var meta = document.createElement('core-meta');
			meta.type = 'ControllersQueue';
			var registeredMeta = helpers.getMetaData('ControllersQueue', this.controller);
			if (!registeredMeta) {
				meta.views = [];
				meta.views.push(this);
				meta.register(this.controller);
			} else {
				registeredMeta.views.push(this);
			}
		},
		forceControllerAttach: function () {
			var meta = document.createElement('core-meta');
			meta.type = 'Controllers';
			var registeredMeta = helpers.getMetaData('Controllers', this.controller);
			if (registeredMeta) {
				registeredMeta.controller.attachView();
			}
		},
		attachController: function (controller) {
			this._$controller = controller;
			this._$controller._$view = this;
			this._$scope = this.$controller.$scope;
			this.childNodes.array().forEach(function (node) {
				if (!node.tagName) {
					return;
				}

				node.$controller = controller;
				node.$scope = controller.$scope;
			});

			if (this._mapFunctions) {
				this.mapScopeFunctions();
			}
		},
		get $controller() {
			return this._$controller;
		},
		get $scope() {
			return this._$scope;
		}
	});
}(window.polymerMvc, window.polymerMvc.helpers));
<commit_msg>fixed bugs with registering metadata<commit_after>(function (mvc, helpers) {

  Polymer('polymer-view', {
    publish: {
      selected: {
        value: false,
        reflect: true
      },
      controller: ''
    },
    ready: function () {
      var host = this.shadowRoot.host;
      if (host && host.registerView && typeof host.registerView === 'function') {
        host.registerView(this);
      }

      if (this.controller && typeof this.controller === 'string') {
        this.enqueueForAttach();
        this.forceControllerAttach();
      }
    },
    registerView: function (view) {
      if (!this.views) {
        this.views = [];
      }

      this.views.push(view);
    },
    enqueueForAttach: function () {
      var registeredMeta = helpers.getMetaData('ControllersQueue', this.controller);
      if (!registeredMeta) {
        //TODO fix exception
        var meta = document.createElement('core-meta');
        meta.type = 'ControllersQueue';
        meta.views = [];
        meta.views.push(this);
        meta.register(this.controller);
      } else {
        registeredMeta.views.push(this);
      }
    },
    forceControllerAttach: function () {
      var registeredMeta = helpers.getMetaData('Controllers', this.controller);
      if (registeredMeta) {
        registeredMeta.controller.attachView();
      }
    },
    attachController: function (controller) {
      this._$controller = controller;
      this._$controller._$view = this;
      this._$scope = this.$controller.$scope;
      this.childNodes.array().forEach(function (node) {
        if (!node.tagName) {
          return;
        }

        node.$controller = controller;
        node.$scope = controller.$scope;
      });
    },
    get $controller() {
      return this._$controller;
    },
    get $scope() {
      return this._$scope;
    }
  });
}(window.polymerMvc, window.polymerMvc.helpers));<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

var data = require('./OA_PR_JSON.json');
var app = require('../../../server/server');
var _ = require('lodash');
var GeoTweet = app.models.GeoTweet;

var docs = data.docs;

var createGeoTweets = docs.map(doc => {
  var latlng = doc.latLon[0].split(',');
  var lat = +latlng[0],
    lng = +latlng[1];

  return GeoTweet.create({
    username: doc.feedName,
    tweet_id: doc.guid,
    full_tweet: doc.originalTitle,
    post_date: doc.datePublished,
    hashtags: _([doc.feedSource, doc.storySource, doc.all_KeyTag, doc.goWord, doc.locationName])
      .flatten().compact().uniq().value(),
    lat: lat,
    lng: lng
  });
});

Promise.all(createGeoTweets)
.then(() => console.log('done'))
.catch(err => {
  GeoTweet.destroyAll();
  console.error(err);
});
<commit_msg>geocode puerto rico data before loading<commit_after>#!/usr/bin/env node
'use strict';

var data = require('./OA_PR_JSON.json');
var app = require('../../../server/server');
var request = require('request-json');
var client = request.createClient('http://localhost:3001/api/geocoder/');
var _ = require('lodash');
var GeoTweet = app.models.GeoTweet;

var docs = data.docs;

function requestPromise(loc, defaults /*i.e. {latitude: 0, longitude: 0}*/ ) {
  return new Promise((resolve, reject) => {
    client.post('forward-geo/', { address: loc }, (err, _, body) => {
      // console.log(body)
      if (err) {
        console.log(err);
        // return reject(err);
      }
      if (!body.length) {
        return resolve([defaults]); // echo back defaults if no results
      }
      resolve(body);
    });
  });
}

// geocode all locations in doc
function geocode(doc) {
  var locationNames = doc.locationName;
  var latLngs = doc.latLon; // array of strings matching order of location names

  var geocodePromises = locationNames.map((loc,i) => {
    var latlng = latLngs[i].split(','),
      lat = latlng[0],
      lng = latlng[1];

    // use the first result. TODO: use confidence score if available?
    return requestPromise(loc, {latitude: lat, longitude: lng})
    .then(results => {
      doc.geo = results[0]; // add geo result to original doc
      return doc;
    });
  });

  return Promise.all(geocodePromises);
}

function createGeoTweet(doc) {
  var geo = doc.geo;
  GeoTweet.create({
    username: doc.feedName,
    tweet_id: doc.guid,
    full_tweet: doc.originalTitle,
    post_date: doc.datePublished,
    hashtags: _([doc.feedSource, doc.storySource, doc.all_KeyTag, doc.goWord, doc.locationName])
      .flatten().compact().uniq().value(),
    lat: geo.latitude,
    lng: geo.longitude
  });
}

function tweetify(docs) {
  // create geotweet record for each location (dupes tweet text)
  docs.forEach(createGeoTweet);
}

// collect all geocode-to-geotweet promises
var processDocs = docs.map(doc => {
  return geocode(doc)
  .then(tweetify);
});

Promise.all(processDocs)
.then(() => console.log('done'))
.catch(err => {
  console.error(err);
  GeoTweet.destroyAll();
  console.error('Removing geotweets records');
});
<|endoftext|>


<commit_before>// @flow

import type { LoadScriptCallback } from "./types";

export default function loadScript(
  url: string,
  callback: LoadScriptCallback,
  target: ?HTMLIFrameElement
) {
  if (target != null) {
    const targetDocument = target.contentDocument;

    // If we're loading this script into an iframe, wait for it to load,
    // Otherwise the script tag we insert will be removed on-load.
    if (targetDocument.readyState !== "complete") {
      target.addEventListener("load", () => {
        // Use the newly-loaded document.
        // $FlowFixMe We know target isn't null at this point
        loadScriptIntoDocument(url, callback, target.contentDocument);
      });
    } else {
      loadScriptIntoDocument(url, callback, targetDocument);
    }
  } else {
    loadScriptIntoDocument(url, callback, document);
  }
}

function loadScriptIntoDocument(
  url: string,
  callback: LoadScriptCallback,
  targetDocument: Document
) {
  const script = targetDocument.createElement("script");
  script.async = true;
  script.src = url;
  script.onerror = () => {
    callback(false);
  };
  script.onload = () => {
    callback(true);
  };

  // $FlowFixMe
  targetDocument.head.appendChild(script);
}
<commit_msg>Changed syntax to make Flow happier<commit_after>// @flow

import type { LoadScriptCallback } from "./types";

export default function loadScript(
  url: string,
  callback: LoadScriptCallback,
  maybeTarget: ?HTMLIFrameElement
) {
  if (maybeTarget != null) {
    const target = maybeTarget;

    // If we're loading this script into an iframe, wait for it to load,
    // Otherwise the script tag we insert will be removed on-load.
    if (target.contentDocument.readyState !== "complete") {
      target.addEventListener("load", () => {
        // Use the newly-loaded document.
        loadScriptIntoDocument(url, callback, target.contentDocument);
      });
    } else {
      loadScriptIntoDocument(url, callback, target.contentDocument);
    }
  } else {
    loadScriptIntoDocument(url, callback, document);
  }
}

function loadScriptIntoDocument(
  url: string,
  callback: LoadScriptCallback,
  targetDocument: Document
) {
  const script = targetDocument.createElement("script");
  script.async = true;
  script.src = url;
  script.onerror = () => {
    callback(false);
  };
  script.onload = () => {
    callback(true);
  };

  // $FlowFixMe
  targetDocument.head.appendChild(script);
}
<|endoftext|>


<commit_before>'use strict';

var appModule = require('./ka-kaltura-api.module.js');
appModule.run(require('./ka-kaltura-api.run.js'));
appModule.config(require('./ka-kaltura-api.config.js'));

appModule.provider('kaKalturaAPIFacade',require('./services/ka-kaltura-api-facade.provider.js'));

appModule.service('kaRequestsHandlerUtils',require('./services/ka-requests-handler-utils.service.js'));

appModule.config(require('./config/handlers/report-service/get-url-for-report-as-csv.config.js'));
appModule.config(require('./config/handlers/report-service/get-table.config.js'));
<commit_msg>code cleanup<commit_after>'use strict';

var appModule = require('./ka-kaltura-api.module');
appModule.run(require('./ka-kaltura-api.run'));
appModule.config(require('./ka-kaltura-api.config'));

appModule.provider('kaKalturaAPIFacade',require('./services/ka-kaltura-api-facade.provider'));

appModule.service('kaRequestsHandlerUtils',require('./services/ka-requests-handler-utils.service'));

appModule.config(require('./config/handlers/report-service/get-url-for-report-as-csv.config'));
appModule.config(require('./config/handlers/report-service/get-table.config'));
<|endoftext|>