<commit_before>const chat = function () {
    'use strict';
    const buffers = {};
    const recentEntriesCutoff = 100;
    const groupChat = 'group';
    let current = groupChat;

    function addJoin(buddy, timestamp) {
        addEntry(groupChat, new Join(buddy, timestamp));
    }
    function addLeave(buddy, timestamp) {
        addEntry(groupChat, new Leave(buddy, timestamp));
    }
    function addGroupMessage(buddy, timestamp, body) {
        addEntry(groupChat, new Message(buddy, timestamp, body));
    }
    // `sender` can be me or the buddy in this chat.
    function addPrivateMessage(buddy, sender, timestamp, body) {
        addEntry(buddy.id, new Message(sender, timestamp, body));
    }
    function addWarning(buddy, timestamp) {
        // TODO: implement
        throw "Not implemented";
    }
    function addMissingRecipients(buddies) {
        // TODO: implement
        throw "Not implemented";
    }

    function addEntry(chat, entry) {
        if (!(chat in buffers)) {
            buffers[chat] = [];
        }
        if (!(entry instanceof Entry)) {
            throw 'Entry type required';
        }
        buffers[chat].push(entry);

        if (chat === current) {
            const chatWindow = $('#conversationWindow');
            const wasScrolledDown = (Math.ceil(chatWindow[0].scrollHeight - chatWindow[0].scrollTop) === chatWindow[0].clientHeight);

            loadRecentEntries(1);
            $('#conversationWindow .line').last().animate({ 'top': 0 }, 100);

            if (wasScrolledDown) {
                // Scroll the chat window down.
                chatWindow.stop().animate({ scrollTop: chatWindow[0].scrollHeight });

                const chatEntries = chatWindow.children();
                if (chatEntries.length > recentEntriesCutoff) {
                    // Keep the chat window trimmed to `recentEntriesCutoff`.
                    chatEntries.slice(0, chatEntries.length - recentEntriesCutoff).remove();
                }
            }
            // TODO: show indicator for new messages if the user was scrolled up
        } else {
            $('#buddy-' + chat).addClass('newMessage');
        }

        if (entry instanceof Message || entry instanceof Join) {
            Cryptodog.newMessageCount(++Cryptodog.me.newMessages);
        }
        // TODO: Handle audio and desktop notifications
    }

    function switchTo(chat) {
        $('#userInputText').focus();
        if (chat === current) {
            return;
        }
        current = chat;
        if (!(current in buffers)) {
            buffers[current] = [];
        }
        $('#conversationWindow').empty();
        loadRecentEntries();
        $('#conversationWindow .line').addClass('no-animate');
        const last = $('#conversationWindow').children().last();
        if (last.length > 0) {
            last.get(0).scrollIntoView();
        }
    }

    // Render the `recentEntriesCutoff` messages prior to those already displayed.
    function loadPreviousEntries() {
        const numDisplayed = $('#conversationWindow').children().length;
        const prev = buffers[current].slice(0, buffers[current].length - numDisplayed).slice(-recentEntriesCutoff);
        for (let i = prev.length - 1; i >= 0; i--) {
            $('#conversationWindow').prepend(prev[i].render());
        }
        $('#conversationWindow .line').addClass('no-animate');
    }

    // Render the `numEntries` most recent entries, or the `recentEntriesCutoff` most recent if `numEntries` is undefined.
    function loadRecentEntries(numEntries) {
        buffers[current].slice(-(numEntries || recentEntriesCutoff)).forEach(entry => {
            $('#conversationWindow').append(entry.render());
        });
    }

    class Entry {
        constructor(buddy, timestamp) {
            this.buddy = buddy;
            this.timestamp = timestamp;
        }
    }
    class Message extends Entry {
        constructor(buddy, timestamp, body) {
            super(buddy, timestamp);
            this.body = body;
        }

        autolink() {
            const pattern = /(\s*)(https?:\/\/\S+)/gi;
            return this.escapedBody.replace(pattern, '$1$2');
        }

        render() {
            /* HTML-encode the message body before auto-linking.
               Mustache.js also encodes forward slashes, so we unescape them at the end to allow links. */
            this.escapedBody = Mustache.render('{{body}}', this).replace(///g, '/');

            return Mustache.render(Cryptodog.templates.message, {
                nickname: this.buddy.nickname,
                timestamp: this.timestamp,
                // body is not HTML-encoded in the message template.
                body: this.autolink(),
                color: this.buddy.color
            });
        }
    };
    class Join extends Entry {
        render() {
            return Mustache.render(Cryptodog.templates.userJoin, {
                nickname: this.buddy.nickname,
                timestamp: this.timestamp,
                color: this.buddy.color
            });
        }
    }
    class Leave extends Entry {
        render() {
            return Mustache.render(Cryptodog.templates.userLeave, {
                nickname: this.buddy.nickname,
                timestamp: this.timestamp,
            });
        }
    }

    $(document).ready(function () {
        'use strict';
        $('#conversationWindow').scroll(function () {
            if ($('#conversationWindow').scrollTop() === 0) {
                // Prevent auto-scrolling to the top when we prepend the entries.
                $('#conversationWindow').scrollTop(1);
                loadPreviousEntries();
            }
        });

        // Show and hide timestamp when hovering over a message's sender element.
        $('#conversationWindow').on('mouseenter', '.sender', function () {
            $(this).find('.nickname').text($(this).attr('data-timestamp'));
        });
        $('#conversationWindow').on('mouseleave', '.sender', function () {
            $(this).find('.nickname').text($(this).attr('data-sender'));
        });

        $('#userInputSubmit').click(function () {
            $('#userInput').submit();
        });

        let composing = false;
        $('#userInputText').keydown(function (e) {
            if (e.key === 'Tab') {
                if (!$(this).val()) {
                    return;
                }
                const nickComplete = tabComplete($(this).val());
                if (nickComplete) {
                    $(this).val(nickComplete);
                }
                return;
            }
            if (e.key === 'Enter') {
                e.preventDefault();
                $('#userInput').submit();
                composing = false;
                return;
            }

            // XXX: potentially hacky way to determine if key is a printable character
            if (e.key.length !== 1) {
                return;
            }

            if (!composing) {
                composing = true;

                let destination, type;
                if (current === groupChat) {
                    destination = null;
                    type = 'groupchat';
                } else {
                    destination = Cryptodog.getBuddyNicknameByID(current);
                    type = 'chat';
                }

                Cryptodog.xmpp.connection.muc.message(
                    Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                    destination, '', null, type, 'composing'
                );

                window.setTimeout(function (destination, type) {
                    Cryptodog.xmpp.connection.muc.message(
                        Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                        destination, '', null, type, 'paused'
                    );
                    composing = false;
                }, 7000, destination, type);
            }
        });

        $('#userInput').submit(function (e) {
            e.preventDefault();
            const timestamp = new Date(Date.now()).toLocaleTimeString('en-US', { hour12: false });
            const message = $.trim($('#userInputText').val());
            $('#userInputText').val('');
            if (!message.length) {
                return true;
            }

            if (current !== groupChat) {
                const buddy = Cryptodog.buddies[Cryptodog.getBuddyNicknameByID(current)];
                buddy.otr.sendMsg(message);
                addPrivateMessage(buddy, Cryptodog.me, timestamp, message);
                return true;
            }

            if (Object.keys(Cryptodog.buddies).length) {
                const ciphertext = JSON.parse(Cryptodog.multiParty.sendMessage(message));
                let missingRecipients = [];
                for (let b in Cryptodog.buddies) {
                    if (typeof (ciphertext['text'][b]) !== 'object') {
                        missingRecipients.push(b);
                    }
                }
                if (missingRecipients.length) {
                    addMissingRecipients(buddies);
                }
                Cryptodog.xmpp.connection.muc.message(
                    Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                    null, JSON.stringify(ciphertext), null, 'groupchat', 'active');

                addGroupMessage(Cryptodog.me, timestamp, message);
            }
        });

        function tabComplete(input) {
            let nickname, suffix;
            const potentials = [];
            for (nickname in Cryptodog.buddies) {
                if (Cryptodog.buddies.hasOwnProperty(nickname)) {
                    potentials.push({
                        score: nickname.score(input.match(/(\S)+$/)[0], 0.01),
                        value: nickname
                    });
                }
            }
            var largest = potentials[0];

            // find item with largest score
            potentials.forEach(function (item) {
                if (item.score > largest.score) {
                    largest = item;
                }
            }, this);

            if (input.match(/\s/)) {
                suffix = ' ';
            } else {
                suffix = ': ';
            }
            if (largest.score < 0.1) {
                // cut-off matching attempt if all scores are low
                return input;
            }
            return input.replace(/(\S)+$/, largest.value + suffix);
        };
    });

    return {
        switchTo,
        addJoin,
        addLeave,
        addGroupMessage,
        addPrivateMessage,
        addWarning
    };
}();
<commit_msg>Allow sending messages to empty rooms<commit_after>const chat = function () {
    'use strict';
    const buffers = {};
    const recentEntriesCutoff = 100;
    const groupChat = 'group';
    let current = groupChat;

    function addJoin(buddy, timestamp) {
        addEntry(groupChat, new Join(buddy, timestamp));
    }
    function addLeave(buddy, timestamp) {
        addEntry(groupChat, new Leave(buddy, timestamp));
    }
    function addGroupMessage(buddy, timestamp, body) {
        addEntry(groupChat, new Message(buddy, timestamp, body));
    }
    // `sender` can be me or the buddy in this chat.
    function addPrivateMessage(buddy, sender, timestamp, body) {
        addEntry(buddy.id, new Message(sender, timestamp, body));
    }
    function addWarning(buddy, timestamp) {
        // TODO: implement
        throw "Not implemented";
    }
    function addMissingRecipients(buddies) {
        // TODO: implement
        throw "Not implemented";
    }

    function addEntry(chat, entry) {
        if (!(chat in buffers)) {
            buffers[chat] = [];
        }
        if (!(entry instanceof Entry)) {
            throw 'Entry type required';
        }
        buffers[chat].push(entry);

        if (chat === current) {
            const chatWindow = $('#conversationWindow');
            const wasScrolledDown = (Math.ceil(chatWindow[0].scrollHeight - chatWindow[0].scrollTop) === chatWindow[0].clientHeight);

            loadRecentEntries(1);
            $('#conversationWindow .line').last().animate({ 'top': 0 }, 100);

            if (wasScrolledDown) {
                // Scroll the chat window down.
                chatWindow.stop().animate({ scrollTop: chatWindow[0].scrollHeight });

                const chatEntries = chatWindow.children();
                if (chatEntries.length > recentEntriesCutoff) {
                    // Keep the chat window trimmed to `recentEntriesCutoff`.
                    chatEntries.slice(0, chatEntries.length - recentEntriesCutoff).remove();
                }
            }
            // TODO: show indicator for new messages if the user was scrolled up
        } else {
            $('#buddy-' + chat).addClass('newMessage');
        }

        if (entry instanceof Message || entry instanceof Join) {
            Cryptodog.newMessageCount(++Cryptodog.me.newMessages);
        }
        // TODO: Handle audio and desktop notifications
    }

    function switchTo(chat) {
        $('#userInputText').focus();
        if (chat === current) {
            return;
        }
        current = chat;
        if (!(current in buffers)) {
            buffers[current] = [];
        }
        $('#conversationWindow').empty();
        loadRecentEntries();
        $('#conversationWindow .line').addClass('no-animate');
        const last = $('#conversationWindow').children().last();
        if (last.length > 0) {
            last.get(0).scrollIntoView();
        }
    }

    // Render the `recentEntriesCutoff` messages prior to those already displayed.
    function loadPreviousEntries() {
        const numDisplayed = $('#conversationWindow').children().length;
        const prev = buffers[current].slice(0, buffers[current].length - numDisplayed).slice(-recentEntriesCutoff);
        for (let i = prev.length - 1; i >= 0; i--) {
            $('#conversationWindow').prepend(prev[i].render());
        }
        $('#conversationWindow .line').addClass('no-animate');
    }

    // Render the `numEntries` most recent entries, or the `recentEntriesCutoff` most recent if `numEntries` is undefined.
    function loadRecentEntries(numEntries) {
        buffers[current].slice(-(numEntries || recentEntriesCutoff)).forEach(entry => {
            $('#conversationWindow').append(entry.render());
        });
    }

    class Entry {
        constructor(buddy, timestamp) {
            this.buddy = buddy;
            this.timestamp = timestamp;
        }
    }
    class Message extends Entry {
        constructor(buddy, timestamp, body) {
            super(buddy, timestamp);
            this.body = body;
        }

        autolink() {
            const pattern = /(\s*)(https?:\/\/\S+)/gi;
            return this.escapedBody.replace(pattern, '$1$2');
        }

        render() {
            /* HTML-encode the message body before auto-linking.
               Mustache.js also encodes forward slashes, so we unescape them at the end to allow links. */
            this.escapedBody = Mustache.render('{{body}}', this).replace(///g, '/');

            return Mustache.render(Cryptodog.templates.message, {
                nickname: this.buddy.nickname,
                timestamp: this.timestamp,
                // body is not HTML-encoded in the message template.
                body: this.autolink(),
                color: this.buddy.color
            });
        }
    };
    class Join extends Entry {
        render() {
            return Mustache.render(Cryptodog.templates.userJoin, {
                nickname: this.buddy.nickname,
                timestamp: this.timestamp,
                color: this.buddy.color
            });
        }
    }
    class Leave extends Entry {
        render() {
            return Mustache.render(Cryptodog.templates.userLeave, {
                nickname: this.buddy.nickname,
                timestamp: this.timestamp,
            });
        }
    }

    $(document).ready(function () {
        'use strict';
        $('#conversationWindow').scroll(function () {
            if ($('#conversationWindow').scrollTop() === 0) {
                // Prevent auto-scrolling to the top when we prepend the entries.
                $('#conversationWindow').scrollTop(1);
                loadPreviousEntries();
            }
        });

        // Show and hide timestamp when hovering over a message's sender element.
        $('#conversationWindow').on('mouseenter', '.sender', function () {
            $(this).find('.nickname').text($(this).attr('data-timestamp'));
        });
        $('#conversationWindow').on('mouseleave', '.sender', function () {
            $(this).find('.nickname').text($(this).attr('data-sender'));
        });

        $('#userInputSubmit').click(function () {
            $('#userInput').submit();
        });

        let composing = false;
        $('#userInputText').keydown(function (e) {
            if (e.key === 'Tab') {
                if (!$(this).val()) {
                    return;
                }
                const nickComplete = tabComplete($(this).val());
                if (nickComplete) {
                    $(this).val(nickComplete);
                }
                return;
            }
            if (e.key === 'Enter') {
                e.preventDefault();
                $('#userInput').submit();
                composing = false;
                return;
            }

            // XXX: potentially hacky way to determine if key is a printable character
            if (e.key.length !== 1) {
                return;
            }

            if (!composing) {
                composing = true;

                let destination, type;
                if (current === groupChat) {
                    destination = null;
                    type = 'groupchat';
                } else {
                    destination = Cryptodog.getBuddyNicknameByID(current);
                    type = 'chat';
                }

                Cryptodog.xmpp.connection.muc.message(
                    Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                    destination, '', null, type, 'composing'
                );

                window.setTimeout(function (destination, type) {
                    Cryptodog.xmpp.connection.muc.message(
                        Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                        destination, '', null, type, 'paused'
                    );
                    composing = false;
                }, 7000, destination, type);
            }
        });

        $('#userInput').submit(function (e) {
            e.preventDefault();
            const timestamp = new Date(Date.now()).toLocaleTimeString('en-US', { hour12: false });
            const message = $.trim($('#userInputText').val());
            $('#userInputText').val('');
            if (!message.length) {
                return true;
            }

            if (current !== groupChat) {
                const buddy = Cryptodog.buddies[Cryptodog.getBuddyNicknameByID(current)];
                buddy.otr.sendMsg(message);
                addPrivateMessage(buddy, Cryptodog.me, timestamp, message);
                return true;
            }

            if (Object.keys(Cryptodog.buddies).length) {
                const ciphertext = JSON.parse(Cryptodog.multiParty.sendMessage(message));
                let missingRecipients = [];
                for (let b in Cryptodog.buddies) {
                    if (typeof (ciphertext['text'][b]) !== 'object') {
                        missingRecipients.push(b);
                    }
                }
                if (missingRecipients.length) {
                    addMissingRecipients(buddies);
                }
                Cryptodog.xmpp.connection.muc.message(
                    Cryptodog.me.conversation + '@' + Cryptodog.xmpp.currentServer.conference,
                    null, JSON.stringify(ciphertext), null, 'groupchat', 'active');
            }
            addGroupMessage(Cryptodog.me, timestamp, message);
        });

        function tabComplete(input) {
            let nickname, suffix;
            const potentials = [];
            for (nickname in Cryptodog.buddies) {
                if (Cryptodog.buddies.hasOwnProperty(nickname)) {
                    potentials.push({
                        score: nickname.score(input.match(/(\S)+$/)[0], 0.01),
                        value: nickname
                    });
                }
            }
            var largest = potentials[0];

            // find item with largest score
            potentials.forEach(function (item) {
                if (item.score > largest.score) {
                    largest = item;
                }
            }, this);

            if (input.match(/\s/)) {
                suffix = ' ';
            } else {
                suffix = ': ';
            }
            if (largest.score < 0.1) {
                // cut-off matching attempt if all scores are low
                return input;
            }
            return input.replace(/(\S)+$/, largest.value + suffix);
        };
    });

    return {
        switchTo,
        addJoin,
        addLeave,
        addGroupMessage,
        addPrivateMessage,
        addWarning
    };
}();
<|endoftext|>


<commit_before>// Cryptodog XMPP functions and callbacks.

Cryptodog.xmpp = {}
Cryptodog.xmpp.currentStatus = 'online'
Cryptodog.xmpp.connection = null

// Default connection settings.
Cryptodog.xmpp.defaultDomain = 'crypto.cat'
Cryptodog.xmpp.defaultConferenceServer = 'conference.crypto.cat'
Cryptodog.xmpp.defaultRelay = 'https://crypto.cat/http-bind'

Cryptodog.xmpp.domain = Cryptodog.xmpp.defaultDomain
Cryptodog.xmpp.conferenceServer = Cryptodog.xmpp.defaultConferenceServer
Cryptodog.xmpp.relay = Cryptodog.xmpp.defaultRelay

$(window).ready(function() {
'use strict';

// Prepares necessary encryption key operations before XMPP connection.
// Shows a progress bar while doing so.
Cryptodog.xmpp.showKeyPreparationDialog = function(callback) {
	// Key storage currently disabled as we are not yet sure if this is safe to do.
	// Cryptodog.storage.setItem('multiPartyKey', Cryptodog.multiParty.genPrivateKey())
	//else {
	Cryptodog.me.mpPrivateKey = Cryptodog.multiParty.genPrivateKey()
	//}
	Cryptodog.me.mpPublicKey = Cryptodog.multiParty.genPublicKey(
		Cryptodog.me.mpPrivateKey
	)
	Cryptodog.me.mpFingerprint = Cryptodog.multiParty.genFingerprint()
	// If we already have keys, just skip to the callback.
	if (Cryptodog.me.otrKey) {
		callback()
		return
	}
	$('#loginInfo').css({'background-color': '#bb7a20'})
	$('#loginInfo').text(Cryptodog.locale['loginMessage']['generatingKeys'])

	// Add delay to key generation when on the file protocol
	// Since the UI freezes when generating keys without WebWorkers
	if (window.location.protocol === 'file:'){
		setTimeout(function(){Cryptodog.xmpp.prepareKeys(callback)}, 100)
	}
	else {
		Cryptodog.xmpp.prepareKeys(callback)
	}
}

// See above.
Cryptodog.xmpp.prepareKeys = function(callback) {
	// Create DSA key for OTR.
	// file protocol doesn't support WebWorkers
	if (window.location.protocol === 'file:'){
		Cryptodog.me.otrKey = new DSA()
		if (callback) { callback() }
	}
	else {
		DSA.createInWebWorker({
			path: 'js/workers/dsa.js',
			seed: Cryptodog.random.generateSeed
		}, function(key) {
			Cryptodog.me.otrKey = key
			// Key storage currently disabled as we are not yet sure if this is safe to do.
			//	Cryptodog.storage.setItem('myKey', JSON.stringify(Cryptodog.me.otrKey))
			if (callback) { callback() }
		})
	}

}

// Connect anonymously and join conversation.
Cryptodog.xmpp.connect = function() {
	Cryptodog.me.conversation = Strophe.xmlescape($('#conversationName').val())
	Cryptodog.me.nickname = Strophe.xmlescape($('#nickname').val())
	Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.relay)
	Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.domain, null, function(status) {
		if (status === Strophe.Status.CONNECTING) {
			$('#loginInfo').animate({'background-color': '#bb7a20'}, 200)
			$('#loginInfo').text(Cryptodog.locale['loginMessage']['connecting'])
		}
		else if (status === Strophe.Status.CONNECTED) {
			Cryptodog.xmpp.connection.muc.join(
				Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
				Cryptodog.me.nickname,
				function(message) {
					if (Cryptodog.xmpp.onMessage(message)) { return true }
				},
				function(presence) {
					if (Cryptodog.xmpp.onPresence(presence)) { return true }
				}
			)
			Cryptodog.xmpp.onConnected()
			document.title = Cryptodog.me.nickname + '@' + Cryptodog.me.conversation
			$('.conversationName').text(document.title)
			Cryptodog.storage.setItem('myNickname', Cryptodog.me.nickname)
		}
		else if ((status === Strophe.Status.CONNFAIL) || (status === Strophe.Status.DISCONNECTED)) {
			if (Cryptodog.loginError) {
				Cryptodog.xmpp.reconnect()
			}
		}
	})
}

// Executes on successfully completed XMPP connection.
Cryptodog.xmpp.onConnected = function() {
	afterConnect()
	$('#loginInfo').text('✓')
    $('#status').attr('src', 'img/icons/checkmark.svg');
	$('#buddy-groupChat,#status').show()
	$('#buddy-groupChat').insertBefore('#buddiesOnline')
	$('#fill').stop().animate({
		'width': '100%', 'opacity': '1'
	}, 250, 'linear')
	window.setTimeout(function() {
		$('#dialogBoxClose').click()
	}, 400)
	window.setTimeout(function() {
		$('#loginOptions,#languages,#customServerDialog').fadeOut(200)
		$('#version,#logoText,#loginInfo,#info').fadeOut(200)
		$('#header').animate({'background-color': '#444'})
		$('.logo').animate({'margin': '-11px 5px 0 0'})
		$('#login').fadeOut(200, function() {
			$('#conversationInfo').fadeIn()
			$('#buddy-groupChat').click(function() {
				Cryptodog.onBuddyClick($(this))
			})
			$('#buddy-groupChat').click()
			$('#conversationWrapper').fadeIn()
			$('#optionButtons').fadeIn()
			$('#footer').delay(200).animate({'height': 60}, function() {
				$('#userInput').fadeIn(200, function() {
					$('#userInputText').focus()
				})
			})
			$('#buddyWrapper').slideDown()
		})
	}, 800)
	Cryptodog.loginError = true
}

// Reconnect to the same chatroom, on accidental connection loss.
Cryptodog.xmpp.reconnect = function() {
	if (Cryptodog.xmpp.connection) {
	    Cryptodog.xmpp.connection.reset()
	}
	Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.relay)
	Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.domain, null, function(status) {
		if (status === Strophe.Status.CONNECTING) {
			$('.conversationName').animate({'background-color': '#F00'})
		}
		else if (status === Strophe.Status.CONNECTED) {
			afterConnect()
			Cryptodog.xmpp.connection.muc.join(
				Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
				Cryptodog.me.nickname
			)
		}
		else if ((status === Strophe.Status.CONNFAIL) || (status === Strophe.Status.DISCONNECTED)) {
			if (Cryptodog.loginError) {
				window.setTimeout(function() {
					Cryptodog.xmpp.reconnect()
				}, 5000)
			}
		}
	})
}

// Handle incoming messages from the XMPP server.
Cryptodog.xmpp.onMessage = function(message) {
	var nickname = cleanNickname($(message).attr('from'))
	var body = $(message).find('body').text()
	var type = $(message).attr('type')
	// If archived message, ignore.
	if ($(message).find('delay').length !== 0) {
		return true
	}
	//If message is from me, ignore.
	if (nickname === Cryptodog.me.nickname) {
		return true
	}
	// If message is from someone not on buddy list, ignore.
	if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
		return true
	}
	// Check if message has a 'composing' notification.
	if ($(message).find('composing').length && !body.length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).addClass('composing')
		return true
	}
	// Check if message has a 'paused' (stopped writing) notification.
	if ($(message).find('paused').length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
	}
	// Check if message is a group chat message.
	else if (type === 'groupchat' && body.length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
		body = Cryptodog.multiParty.receiveMessage(nickname, Cryptodog.me.nickname, body)
		if (typeof(body) === 'string') {
			Cryptodog.addToConversation(body, nickname, 'groupChat', 'message')
		}
	}
	// Check if this is a private OTR message.
	else if (type === 'chat') {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
		Cryptodog.buddies[nickname].otr.receiveMsg(body)
	}
	return true
}

// Handle incoming presence updates from the XMPP server.
Cryptodog.xmpp.onPresence = function(presence) {
	var status
	var nickname = cleanNickname($(presence).attr('from'))
	// If invalid nickname, do not process
	if ($(presence).attr('type') === 'error') {
		if ($(presence).find('error').attr('code') === '409') {
			// Delay logout in order to avoid race condition with window animation
			window.setTimeout(function() {
				Cryptodog.logout()
				Cryptodog.loginFail(Cryptodog.locale['loginMessage']['nicknameInUse'])
			}, 3000)
			return false
		}
		return true
	}
	// Ignore if presence status is coming from myself
	if (nickname === Cryptodog.me.nickname) {
		return true
	}
	// Detect nickname change (which may be done by non-Cryptodog XMPP clients)
	if ($(presence).find('status').attr('code') === '303') {
		Cryptodog.removeBuddy(nickname)
		return true
	}
	// Detect buddy going offline.
	if ($(presence).attr('type') === 'unavailable') {
		Cryptodog.removeBuddy(nickname)
		return true
	}
	// Create buddy element if buddy is new.
	else if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
		Cryptodog.addBuddy(nickname, null, 'online')
		for (var u = 0; u < 4000; u += 2000) {
			window.setTimeout(Cryptodog.xmpp.sendPublicKey, u, nickname)
		}
	}
	// Handle buddy status change to 'available'.
	else if (
		$(presence).find('show').text() === '' ||
		$(presence).find('show').text() === 'chat'
	) {
		status = 'online'
	}
	// Handle buddy status change to 'away'.
	else {
		status = 'away'
	}
	// Perform status change.
	Cryptodog.buddyStatus(nickname, status)
	return true
}

// Send your own multiparty public key to `nickname`, via XMPP-MUC.
Cryptodog.xmpp.sendPublicKey = function(nickname) {
	Cryptodog.xmpp.connection.muc.message(
		Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
		null, Cryptodog.multiParty.sendPublicKey(nickname), null, 'groupchat', 'active'
	)
}

// Send your current status to the XMPP server.
Cryptodog.xmpp.sendStatus = function() {
        var status = ''
	if (Cryptodog.xmpp.currentStatus === 'away') {
                status = 'away'
	}
	Cryptodog.xmpp.connection.muc.setStatus(
		Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
		Cryptodog.me.nickname, status, status
	)
}

// Executed (manually) after connection.
var afterConnect = function() {
	$('.conversationName').animate({'background-color': '#bb7a20'})
	Cryptodog.xmpp.connection.ibb.addIBBHandler(Cryptodog.otr.ibbHandler)
	/* jshint -W106 */
	Cryptodog.xmpp.connection.si_filetransfer.addFileHandler(Cryptodog.otr.fileHandler)
	/* jshint +W106 */
}

// Clean nickname so that it's safe to use.
var cleanNickname = function(nickname) {
	var clean = nickname.match(/\/([\s\S]+)/)
	if (clean) {
		return clean[1]
	}
	return false
}

})
<commit_msg>Hard tab last commit<commit_after>// Cryptodog XMPP functions and callbacks.

Cryptodog.xmpp = {}
Cryptodog.xmpp.currentStatus = 'online'
Cryptodog.xmpp.connection = null

// Default connection settings.
Cryptodog.xmpp.defaultDomain = 'crypto.cat'
Cryptodog.xmpp.defaultConferenceServer = 'conference.crypto.cat'
Cryptodog.xmpp.defaultRelay = 'https://crypto.cat/http-bind'

Cryptodog.xmpp.domain = Cryptodog.xmpp.defaultDomain
Cryptodog.xmpp.conferenceServer = Cryptodog.xmpp.defaultConferenceServer
Cryptodog.xmpp.relay = Cryptodog.xmpp.defaultRelay

$(window).ready(function() {
'use strict';

// Prepares necessary encryption key operations before XMPP connection.
// Shows a progress bar while doing so.
Cryptodog.xmpp.showKeyPreparationDialog = function(callback) {
	// Key storage currently disabled as we are not yet sure if this is safe to do.
	// Cryptodog.storage.setItem('multiPartyKey', Cryptodog.multiParty.genPrivateKey())
	//else {
	Cryptodog.me.mpPrivateKey = Cryptodog.multiParty.genPrivateKey()
	//}
	Cryptodog.me.mpPublicKey = Cryptodog.multiParty.genPublicKey(
		Cryptodog.me.mpPrivateKey
	)
	Cryptodog.me.mpFingerprint = Cryptodog.multiParty.genFingerprint()
	// If we already have keys, just skip to the callback.
	if (Cryptodog.me.otrKey) {
		callback()
		return
	}
	$('#loginInfo').css({'background-color': '#bb7a20'})
	$('#loginInfo').text(Cryptodog.locale['loginMessage']['generatingKeys'])

	// Add delay to key generation when on the file protocol
	// Since the UI freezes when generating keys without WebWorkers
	if (window.location.protocol === 'file:'){
		setTimeout(function(){Cryptodog.xmpp.prepareKeys(callback)}, 100)
	}
	else {
		Cryptodog.xmpp.prepareKeys(callback)
	}
}

// See above.
Cryptodog.xmpp.prepareKeys = function(callback) {
	// Create DSA key for OTR.
	// file protocol doesn't support WebWorkers
	if (window.location.protocol === 'file:'){
		Cryptodog.me.otrKey = new DSA()
		if (callback) { callback() }
	}
	else {
		DSA.createInWebWorker({
			path: 'js/workers/dsa.js',
			seed: Cryptodog.random.generateSeed
		}, function(key) {
			Cryptodog.me.otrKey = key
			// Key storage currently disabled as we are not yet sure if this is safe to do.
			//	Cryptodog.storage.setItem('myKey', JSON.stringify(Cryptodog.me.otrKey))
			if (callback) { callback() }
		})
	}

}

// Connect anonymously and join conversation.
Cryptodog.xmpp.connect = function() {
	Cryptodog.me.conversation = Strophe.xmlescape($('#conversationName').val())
	Cryptodog.me.nickname = Strophe.xmlescape($('#nickname').val())
	Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.relay)
	Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.domain, null, function(status) {
		if (status === Strophe.Status.CONNECTING) {
			$('#loginInfo').animate({'background-color': '#bb7a20'}, 200)
			$('#loginInfo').text(Cryptodog.locale['loginMessage']['connecting'])
		}
		else if (status === Strophe.Status.CONNECTED) {
			Cryptodog.xmpp.connection.muc.join(
				Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
				Cryptodog.me.nickname,
				function(message) {
					if (Cryptodog.xmpp.onMessage(message)) { return true }
				},
				function(presence) {
					if (Cryptodog.xmpp.onPresence(presence)) { return true }
				}
			)
			Cryptodog.xmpp.onConnected()
			document.title = Cryptodog.me.nickname + '@' + Cryptodog.me.conversation
			$('.conversationName').text(document.title)
			Cryptodog.storage.setItem('myNickname', Cryptodog.me.nickname)
		}
		else if ((status === Strophe.Status.CONNFAIL) || (status === Strophe.Status.DISCONNECTED)) {
			if (Cryptodog.loginError) {
				Cryptodog.xmpp.reconnect()
			}
		}
	})
}

// Executes on successfully completed XMPP connection.
Cryptodog.xmpp.onConnected = function() {
	afterConnect()
	$('#loginInfo').text('✓')
	$('#status').attr('src', 'img/icons/checkmark.svg');
	$('#buddy-groupChat,#status').show()
	$('#buddy-groupChat').insertBefore('#buddiesOnline')
	$('#fill').stop().animate({
		'width': '100%', 'opacity': '1'
	}, 250, 'linear')
	window.setTimeout(function() {
		$('#dialogBoxClose').click()
	}, 400)
	window.setTimeout(function() {
		$('#loginOptions,#languages,#customServerDialog').fadeOut(200)
		$('#version,#logoText,#loginInfo,#info').fadeOut(200)
		$('#header').animate({'background-color': '#444'})
		$('.logo').animate({'margin': '-11px 5px 0 0'})
		$('#login').fadeOut(200, function() {
			$('#conversationInfo').fadeIn()
			$('#buddy-groupChat').click(function() {
				Cryptodog.onBuddyClick($(this))
			})
			$('#buddy-groupChat').click()
			$('#conversationWrapper').fadeIn()
			$('#optionButtons').fadeIn()
			$('#footer').delay(200).animate({'height': 60}, function() {
				$('#userInput').fadeIn(200, function() {
					$('#userInputText').focus()
				})
			})
			$('#buddyWrapper').slideDown()
		})
	}, 800)
	Cryptodog.loginError = true
}

// Reconnect to the same chatroom, on accidental connection loss.
Cryptodog.xmpp.reconnect = function() {
	if (Cryptodog.xmpp.connection) {
	    Cryptodog.xmpp.connection.reset()
	}
	Cryptodog.xmpp.connection = new Strophe.Connection(Cryptodog.xmpp.relay)
	Cryptodog.xmpp.connection.connect(Cryptodog.xmpp.domain, null, function(status) {
		if (status === Strophe.Status.CONNECTING) {
			$('.conversationName').animate({'background-color': '#F00'})
		}
		else if (status === Strophe.Status.CONNECTED) {
			afterConnect()
			Cryptodog.xmpp.connection.muc.join(
				Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
				Cryptodog.me.nickname
			)
		}
		else if ((status === Strophe.Status.CONNFAIL) || (status === Strophe.Status.DISCONNECTED)) {
			if (Cryptodog.loginError) {
				window.setTimeout(function() {
					Cryptodog.xmpp.reconnect()
				}, 5000)
			}
		}
	})
}

// Handle incoming messages from the XMPP server.
Cryptodog.xmpp.onMessage = function(message) {
	var nickname = cleanNickname($(message).attr('from'))
	var body = $(message).find('body').text()
	var type = $(message).attr('type')
	// If archived message, ignore.
	if ($(message).find('delay').length !== 0) {
		return true
	}
	//If message is from me, ignore.
	if (nickname === Cryptodog.me.nickname) {
		return true
	}
	// If message is from someone not on buddy list, ignore.
	if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
		return true
	}
	// Check if message has a 'composing' notification.
	if ($(message).find('composing').length && !body.length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).addClass('composing')
		return true
	}
	// Check if message has a 'paused' (stopped writing) notification.
	if ($(message).find('paused').length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
	}
	// Check if message is a group chat message.
	else if (type === 'groupchat' && body.length) {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
		body = Cryptodog.multiParty.receiveMessage(nickname, Cryptodog.me.nickname, body)
		if (typeof(body) === 'string') {
			Cryptodog.addToConversation(body, nickname, 'groupChat', 'message')
		}
	}
	// Check if this is a private OTR message.
	else if (type === 'chat') {
		$('#buddy-' + Cryptodog.buddies[nickname].id).removeClass('composing')
		Cryptodog.buddies[nickname].otr.receiveMsg(body)
	}
	return true
}

// Handle incoming presence updates from the XMPP server.
Cryptodog.xmpp.onPresence = function(presence) {
	var status
	var nickname = cleanNickname($(presence).attr('from'))
	// If invalid nickname, do not process
	if ($(presence).attr('type') === 'error') {
		if ($(presence).find('error').attr('code') === '409') {
			// Delay logout in order to avoid race condition with window animation
			window.setTimeout(function() {
				Cryptodog.logout()
				Cryptodog.loginFail(Cryptodog.locale['loginMessage']['nicknameInUse'])
			}, 3000)
			return false
		}
		return true
	}
	// Ignore if presence status is coming from myself
	if (nickname === Cryptodog.me.nickname) {
		return true
	}
	// Detect nickname change (which may be done by non-Cryptodog XMPP clients)
	if ($(presence).find('status').attr('code') === '303') {
		Cryptodog.removeBuddy(nickname)
		return true
	}
	// Detect buddy going offline.
	if ($(presence).attr('type') === 'unavailable') {
		Cryptodog.removeBuddy(nickname)
		return true
	}
	// Create buddy element if buddy is new.
	else if (!Cryptodog.buddies.hasOwnProperty(nickname)) {
		Cryptodog.addBuddy(nickname, null, 'online')
		for (var u = 0; u < 4000; u += 2000) {
			window.setTimeout(Cryptodog.xmpp.sendPublicKey, u, nickname)
		}
	}
	// Handle buddy status change to 'available'.
	else if (
		$(presence).find('show').text() === '' ||
		$(presence).find('show').text() === 'chat'
	) {
		status = 'online'
	}
	// Handle buddy status change to 'away'.
	else {
		status = 'away'
	}
	// Perform status change.
	Cryptodog.buddyStatus(nickname, status)
	return true
}

// Send your own multiparty public key to `nickname`, via XMPP-MUC.
Cryptodog.xmpp.sendPublicKey = function(nickname) {
	Cryptodog.xmpp.connection.muc.message(
		Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
		null, Cryptodog.multiParty.sendPublicKey(nickname), null, 'groupchat', 'active'
	)
}

// Send your current status to the XMPP server.
Cryptodog.xmpp.sendStatus = function() {
        var status = ''
	if (Cryptodog.xmpp.currentStatus === 'away') {
                status = 'away'
	}
	Cryptodog.xmpp.connection.muc.setStatus(
		Cryptodog.me.conversation + '@' + Cryptodog.xmpp.conferenceServer,
		Cryptodog.me.nickname, status, status
	)
}

// Executed (manually) after connection.
var afterConnect = function() {
	$('.conversationName').animate({'background-color': '#bb7a20'})
	Cryptodog.xmpp.connection.ibb.addIBBHandler(Cryptodog.otr.ibbHandler)
	/* jshint -W106 */
	Cryptodog.xmpp.connection.si_filetransfer.addFileHandler(Cryptodog.otr.fileHandler)
	/* jshint +W106 */
}

// Clean nickname so that it's safe to use.
var cleanNickname = function(nickname) {
	var clean = nickname.match(/\/([\s\S]+)/)
	if (clean) {
		return clean[1]
	}
	return false
}

})
<|endoftext|>


<commit_before>(function (angular, _) {
	'usr strict';

	var ANIMALS = [
		'cat',
		'chicken',
		'cockerel',
		'cow',
		'dog',
		'donkey',
		'elephant',
		'fox',
		'horse',
		'leopard',
		'lion',
		'lioness',
		'monkey',
		'orangutan',
		'owl',
		'panda',
		'penguin',
		'pig',
		'puma',
		'rabbit',
		'reindeer',
		'sheep',
		'tiger',
		'zebra'
	];

	var SOUNDS_BY_ANIMAL = {
		'cat': 'Meow',
		'chicken': 'Cluck',
		'cockerel': 'Cockadoodle-doo',
		'cow': 'Moo',
		'dog': 'Woof',
		'donkey': 'Eee-aww',
		'elephant': 'Trump',
		'fox': 'Bark',
		'horse': 'Neigh',
		'leopard': 'Growl',
		'lion': 'Roar',
		'lioness': 'Roar',
		'monkey': 'Screech',
		'orangutan': 'Ooo-ooh',
		'owl': 'Twittwoo',
		'panda': 'Growl',
		'penguin': '???',
		'pig': 'Oink',
		'puma': 'Growl',
		'rabbit': 'Snuffle',
		'reindeer': 'Awww',
		'sheep': 'Baaa',
		'tiger': 'Roar',
		'zebra': '???'
	};

	var app = angular.module('FarmYard', ['ngAnimate', 'hmTouchEvents']);

	app.controller('FarmYardCtrl', function ($scope) {
		var SCENE_CACHE = {};

		$scope.position = {x: 0, y: 0};
		$scope.animals = [];

		this.move = function (dx, dy) {
			$scope.position.x += dx;
			$scope.position.y += dy;
			var cache_key = get_cache_key($scope.position.x, $scope.position.y);
			var animal = SCENE_CACHE[cache_key];
			if (typeof animal === 'undefined') {
				SCENE_CACHE[cache_key] = animal = _.sample(ANIMALS);
			}
			$scope.animals.splice(0, 1, animal);
		};

		var get_cache_key = function (x, y) {
			return [x.toString(), y.toString()].join(',');
		};

		this.move(0, 0);
	});

	app.directive('animal', function () {
		return {
			restrict: 'EA',
			scope: {
				name: '=animal'
			},
			templateUrl: '../partials/animal.html',
			controller: function ($scope, $timeout) {
				var timeout_promise;
				$scope.is_speaking = false;

				this.speak = function () {
					$scope.is_speaking = true;
					// TODO: Replace this with listening for the end of the sound
					timeout_promise = $timeout(function () {
						$scope.is_speaking = false;
					}, 1000);
				};
				$scope.$watch('name', function (name) {
					$scope.is_speaking = false;
					$scope.sound = SOUNDS_BY_ANIMAL[name];
				});

			},
			controllerAs: 'animal_ctrl'
		}
	});

}(window.angular, window._));
<commit_msg>Added support for keyboard events to change the animal<commit_after>(function (angular, _) {
	'usr strict';

	var ANIMALS = [
		'cat',
		'chicken',
		'cockerel',
		'cow',
		'dog',
		'donkey',
		'elephant',
		'fox',
		'horse',
		'leopard',
		'lion',
		'lioness',
		'monkey',
		'orangutan',
		'owl',
		'panda',
		'penguin',
		'pig',
		'puma',
		'rabbit',
		'reindeer',
		'sheep',
		'tiger',
		'zebra'
	];

	var SOUNDS_BY_ANIMAL = {
		'cat': 'Meow',
		'chicken': 'Cluck',
		'cockerel': 'Cockadoodle-doo',
		'cow': 'Moo',
		'dog': 'Woof',
		'donkey': 'Eee-aww',
		'elephant': 'Trump',
		'fox': 'Bark',
		'horse': 'Neigh',
		'leopard': 'Growl',
		'lion': 'Roar',
		'lioness': 'Roar',
		'monkey': 'Screech',
		'orangutan': 'Ooo-ooh',
		'owl': 'Twittwoo',
		'panda': 'Growl',
		'penguin': '???',
		'pig': 'Oink',
		'puma': 'Growl',
		'rabbit': 'Snuffle',
		'reindeer': 'Awww',
		'sheep': 'Baaa',
		'tiger': 'Roar',
		'zebra': '???'
	};

	var DELTAS_BY_KEYCODE = {
		37: [-1, 0], // left
		38: [0, -1], // up
		39: [1, 0], // right
		40: [0, 1] // down
	};

	var app = angular.module('FarmYard', ['ngAnimate', 'hmTouchEvents']);

	app.controller('FarmYardCtrl', function ($scope) {
		var SCENE_CACHE = {};

		$scope.position = {x: 0, y: 0};
		$scope.animals = [];

		this.move = function (dx, dy) {
			$scope.position.x += dx;
			$scope.position.y += dy;
			var cache_key = get_cache_key($scope.position.x, $scope.position.y);
			var animal = SCENE_CACHE[cache_key];
			if (typeof animal === 'undefined') {
				SCENE_CACHE[cache_key] = animal = _.sample(ANIMALS);
			}
			$scope.animals.splice(0, 1, animal);
		};

		var get_cache_key = function (x, y) {
			return [x.toString(), y.toString()].join(',');
		};

		var ctrl = this;
		var doc_element = angular.element(document);
		doc_element.on('keydown', function (event) {
			var delta = DELTAS_BY_KEYCODE[event.keyCode];
			if (delta) {
				$scope.$apply(function () {
					ctrl.move.apply(ctrl, delta);
				});
			};

		});
		$scope.$on('$destroy', function () {
			doc_element.off('keydown');
		});

		this.move(0, 0);
	});

	app.directive('animal', function () {
		return {
			restrict: 'EA',
			scope: {
				name: '=animal'
			},
			templateUrl: '../partials/animal.html',
			controller: function ($scope, $timeout) {
				var timeout_promise;
				$scope.is_speaking = false;

				this.speak = function () {
					$scope.is_speaking = true;
					// TODO: Replace this with listening for the end of the sound
					timeout_promise = $timeout(function () {
						$scope.is_speaking = false;
					}, 1000);
				};
				$scope.$watch('name', function (name) {
					$scope.is_speaking = false;
					$scope.sound = SOUNDS_BY_ANIMAL[name];
				});

			},
			controllerAs: 'animal_ctrl'
		}
	});

}(window.angular, window._));
<|endoftext|>


<commit_before>'use strict';
var prev_type = 'undefined', selected = {
	'type': 'undefined',
	'list': []
}, activeMove = false;

function focusTab(id, type) {
	if (type === 'Tab') {
		chrome.tabs.update(parseInt(id, 10), {
			selected: true
		});
	} else {
		chrome.windows.update(parseInt(id, 10), {
			focused: true
		});
	}
}

function getSelected() {
	deSelect();
	$('.selected').each(function (i, item) {
		selected.list.push(id);
	});
}

function restoreSelected() {
	var obj = selected;
	if (obj.list.length !== 0) {
		for (var i = 0; i < obj.list.length; i++) {
			$('#' + obj.list[i]).addClass('selected');
		}
	}
}

function deSelect(reset) {
	if (reset !== true) {
		selected.type = prev_type;
	} else {
		prev_type = 'undefined';
		selected.type = 'undefined';
	}
	selected.list.length = 0;
}

function createNew(type, p) {
	var obj = selected;
	if (type === 'Tab') {
		if (obj.type === 'Window') {
			for (var i = 0; i < obj.list.length; i++) {
				chrome.tabs.create({
					windowId: obj.list[i],
					selected: false
				});
			}
		} else {
			chrome.windows.getCurrent(function (win) {
				chrome.tabs.create({
					windowId: win.id,
					selected: false
				});
			});
		}
	} else {
		chrome.windows.create({
			focused: false,
			incognito: p
		});
	}
}

function closeSelected(all) {
	var obj;
	if (all === true) {
		var currentWin,
		newTab;
		chrome.windows.getCurrent({
			populate: true
		}, function (win) {
			currentWin = win.id;
			chrome.tabs.create({
				windowId: win.id,
				selected: false
			}, function (tab) {
				newTab = tab.id
			});
			for (var i = 0; i < win.tabs.length; i++) {
				var tab_id = win.tabs[i].id;
				if (tab_id !== newTab) {
					chrome.tabs.remove(tab_id);
				}
			};
		});
		chrome.windows.getAll(function (wins) {
			for (var i = 0; i < wins.length; i++) {
				if (currentWin !== wins[i].id) {
					chrome.windows.remove(wins[i].id);
				}
			}
		});
	} else {
		obj = selected;
		if (obj.list.length !== 0) {
			if (obj.type === 'Tab') {
				chrome.tabs.remove(obj.list);
			} else {
				$(obj.list).each(function (i, item) {
					chrome.windows.remove(item);
				});
			}
		}
	}
	deSelect(true);
}

function makePrivate(call) {
	switch (call) {
		case 0:
			var obj = selected;
			if (obj.list.length > 0 && obj.type === 'Window') {
				for (var i = 0; i < obj.list.length; i++) {
					chrome.windows.get(obj.list[i], {
						populate: true
					}, function (old_window) {
						var incognito = true;
						if (old_window.incognito) {
							incognito = false;
						}
						chrome.windows.create({
							incognito: incognito
						}, function (new_window) {
							for (var j = 0; j < old_window.tabs.length; j++) {
								chrome.tabs.create({
									windowId: new_window.id,
									url: old_window.tabs[j].url,
									pinned: old_window.tabs[j].pinned,
									selected: old_window.tabs[j].selected
								});
							}
							chrome.tabs.remove(new_window.tabs[0].id);
							chrome.windows.remove(old_window.id);
						});

					});
				}
			}
			break;
		case 1:
		case 2:
			var bool = true;
			if (call === 2) {
				bool = false
			}
			chrome.windows.getAll({
				populate: true
			},
			function (wins) {
				var temp = [], win;
				for (var i = 0; i < wins.length; i++) {
					temp.push(wins[i]);
					if (bool !== wins[i].incognito) {
						win = temp.pop();
						chrome.windows.create({
							incognito: bool,
							focused: win.focused,
							left: win.left,
							top: win.top,
							width: win.width,
							height: win.height
						}, function (new_win) {
							var tabs = [];
							for (var j = 0; j < win.tabs.length; j++) {
								tabs.push(win.tabs[j]);
								var old_tab = tabs.pop();
								chrome.tabs.create({
									windowId: new_win.id,
									url: old_tab.url,
									pinned: old_tab.pinned,
									selected: old_tab.selected
								});
							}
							chrome.tabs.remove(new_win.tabs[0].id);
							chrome.windows.remove(win.id);
						});
					}
				}
			})
			break;
		default: break;
	}
}

function togglePin(call) {
	if (call !== 0) {
		var bool = true;
		if (call === 2) {
			bool = false
		};
		chrome.windows.getAll({
			populate: true
		}, function (wins) {
			for (var k = 0; k < wins.length; k++) {
				var win = wins[k];
				for (var j = 0; j < win.tabs.length; j++) {
					var tab = win.tabs[j];
					chrome.tabs.update(tab.id, {
						pinned: bool
					});
				}
			}
		});
	} else {
		var obj = selected;
		if (obj.list.length > 0) {
			if (obj.type === 'Tab') {
				for (var j = 0; j < obj.list.length; j++) {
					var pin = true;
					chrome.tabs.get(obj.list[j], function (tab) {
						if (tab.pinned === true) {
							pin = false;
						} else {
							pin = true;
						}
						chrome.tabs.update(tab.id, {
							pinned: pin
						});
					});
				}
			}
		}
	}
}

function splitSelected(parent, id) {
	var obj = selected;
	var tab_ids = obj.list;
	if (id) {
		id = parseInt(id, 10);
	}
	if (tab_ids.length > 0 && obj.type === 'Tab') {
		if (!parent) {
			chrome.windows.create({}, function (new_window) {
				for (var j = 0; j < tab_ids.length; j++) {
					chrome.tabs.move(tab_ids[j], {
						windowId: new_window.id,
						index: -1
					});
				}
			})
		} else {
			var old_win_id, temp = [], tab;
			chrome.windows.get(id, function (new_win) {
				for (var i = 0; i < tab_ids.length; i++) {
					old_win_id = parseInt($('#' + tab_ids[i]).parent().parent().parent().attr('id').split('_')[0], 10);
					temp.push(tab_ids[i]);
					chrome.windows.get(old_win_id, function (old_win) {
						tab = temp.pop();
						if (new_win.incognito || old_win.incognito) {
							chrome.tabs.get(tab, function (old_tab) {
								chrome.tabs.create({
									windowId: new_win.id,
									url: old_tab.url
								})
								chrome.tabs.remove(tab);
							})
						} else {
							chrome.tabs.move(tab, {
								windowId: id,
								index: -1
							}, function () {
								loadData();
							})
						}
					})
				}
			})
		}
	}
}

function moveto() {
	activeMove = true;
	alert("Please select target Window");
}

function reload(bool) {
	if (bool) {
		chrome.windows.getAll({
			populate: true
		}, function (wins) {
			for (var k = 0; k < wins.length; k++) {
				var win = wins[k];
				for (var j = 0; j < win.tabs.length; j++) {
					var tab = win.tabs[j];
					chrome.tabs.reload(tab.id);
				}
			}
		});
	} else {
		var obj = selected;
		if (obj.list.length > 0) {
			var arr = obj.list;
			if (obj.type === 'Tab') {
				for (var i = 0; i < arr.length; i++) {
					chrome.tabs.reload(arr[i]);
				}
			} else {
				for (var i = 0; i < arr.length; i++) {
					chrome.windows.get(arr[i], {
						populate: true
					}, function (win) {
						for (var j = 0; j < win.tabs.length; j++) {
							var tab = win.tabs[j];
							chrome.tabs.reload(tab.id);
						}
					});
				}
			}
		}
	}
}

function clone() {
	var obj = selected;
	if (obj.list.length > 0) {
		if (obj.type === 'Window') {
			for (var i = 0; i < obj.list.length; i++) {
				chrome.windows.get(obj.list[i], {
					populate: true
				}, function (old_win) {
					chrome.windows.create({
						focused: old_win.focused,
						incognito: old_win.incognito
					}, function (new_win) {
						var old_tabs = old_win.tabs;
						for (var j = 0; j < old_tabs.length; j++) {
							chrome.tabs.create({
								windowId: new_win.id,
								url: old_tabs[j].url,
								selected: false
							});
							//chrome.tabs.duplicate(old_tabs[j].id);
						}
						chrome.tabs.remove(new_win.tabs[0].id);
					});
				});
			}
		} else {
			var tab_ids = obj.list;
			for (var i = 0; i < tab_ids.length; i++) {
				chrome.tabs.duplicate(tab_ids[i]);
			}
		}
	}
}

function removeDuplicate() {
	chrome.windows.getAll({
		populate: true
	}, function (wins) {
		for (var k = 0; k < wins.length; k++) {
			var tabs = wins[k].tabs;
			for (var l = 0; l < tabs.length; l++) {
				for (var j = 0; j < tabs.length && j !== l; j++) {
					if (tabs[j].url === tabs[l].url) {
						chrome.tabs.remove(tabs[j].id);
					}
				}
			}
		}
	});
}<commit_msg>-Small fix for Selected items<commit_after>'use strict';
var prev_type = 'undefined', selected = {
	'type': 'undefined',
	'list': []
}, activeMove = false;

function focusTab(id, type) {
	if (type === 'Tab') {
		chrome.tabs.update(parseInt(id, 10), {
			selected: true
		});
	} else {
		chrome.windows.update(parseInt(id, 10), {
			focused: true
		});
	}
}

function getSelected() {
	deSelect();
	$('.selected').each(function (i, item) {
		selected.list.push(parseInt(item.id, 10));
	});
}

function restoreSelected() {
	var obj = selected;
	if (obj.list.length !== 0) {
		for (var i = 0; i < obj.list.length; i++) {
			$('#' + obj.list[i]).addClass('selected');
		}
	}
}

function deSelect(reset) {
	if (reset !== true) {
		selected.type = prev_type;
	} else {
		prev_type = 'undefined';
		selected.type = 'undefined';
	}
	selected.list.length = 0;
}

function createNew(type, p) {
	var obj = selected;
	if (type === 'Tab') {
		if (obj.type === 'Window') {
			for (var i = 0; i < obj.list.length; i++) {
				chrome.tabs.create({
					windowId: obj.list[i],
					selected: false
				});
			}
		} else {
			chrome.windows.getCurrent(function (win) {
				chrome.tabs.create({
					windowId: win.id,
					selected: false
				});
			});
		}
	} else {
		chrome.windows.create({
			focused: false,
			incognito: p
		});
	}
}

function closeSelected(all) {
	var obj;
	if (all === true) {
		var currentWin,
		newTab;
		chrome.windows.getCurrent({
			populate: true
		}, function (win) {
			currentWin = win.id;
			chrome.tabs.create({
				windowId: win.id,
				selected: false
			}, function (tab) {
				newTab = tab.id
			});
			for (var i = 0; i < win.tabs.length; i++) {
				var tab_id = win.tabs[i].id;
				if (tab_id !== newTab) {
					chrome.tabs.remove(tab_id);
				}
			};
		});
		chrome.windows.getAll(function (wins) {
			for (var i = 0; i < wins.length; i++) {
				if (currentWin !== wins[i].id) {
					chrome.windows.remove(wins[i].id);
				}
			}
		});
	} else {
		obj = selected;
		if (obj.list.length !== 0) {
			if (obj.type === 'Tab') {
				chrome.tabs.remove(obj.list);
			} else {
				$(obj.list).each(function (i, item) {
					chrome.windows.remove(item);
				});
			}
		}
	}
	deSelect(true);
}

function makePrivate(call) {
	switch (call) {
		case 0:
			var obj = selected;
			if (obj.list.length > 0 && obj.type === 'Window') {
				for (var i = 0; i < obj.list.length; i++) {
					chrome.windows.get(obj.list[i], {
						populate: true
					}, function (old_window) {
						var incognito = true;
						if (old_window.incognito) {
							incognito = false;
						}
						chrome.windows.create({
							incognito: incognito
						}, function (new_window) {
							for (var j = 0; j < old_window.tabs.length; j++) {
								chrome.tabs.create({
									windowId: new_window.id,
									url: old_window.tabs[j].url,
									pinned: old_window.tabs[j].pinned,
									selected: old_window.tabs[j].selected
								});
							}
							chrome.tabs.remove(new_window.tabs[0].id);
							chrome.windows.remove(old_window.id);
						});

					});
				}
			}
			break;
		case 1:
		case 2:
			var bool = true;
			if (call === 2) {
				bool = false
			}
			chrome.windows.getAll({
				populate: true
			},
			function (wins) {
				var temp = [], win;
				for (var i = 0; i < wins.length; i++) {
					temp.push(wins[i]);
					if (bool !== wins[i].incognito) {
						win = temp.pop();
						chrome.windows.create({
							incognito: bool,
							focused: win.focused,
							left: win.left,
							top: win.top,
							width: win.width,
							height: win.height
						}, function (new_win) {
							var tabs = [];
							for (var j = 0; j < win.tabs.length; j++) {
								tabs.push(win.tabs[j]);
								var old_tab = tabs.pop();
								chrome.tabs.create({
									windowId: new_win.id,
									url: old_tab.url,
									pinned: old_tab.pinned,
									selected: old_tab.selected
								});
							}
							chrome.tabs.remove(new_win.tabs[0].id);
							chrome.windows.remove(win.id);
						});
					}
				}
			})
			break;
		default: break;
	}
}

function togglePin(call) {
	if (call !== 0) {
		var bool = true;
		if (call === 2) {
			bool = false
		};
		chrome.windows.getAll({
			populate: true
		}, function (wins) {
			for (var k = 0; k < wins.length; k++) {
				var win = wins[k];
				for (var j = 0; j < win.tabs.length; j++) {
					var tab = win.tabs[j];
					chrome.tabs.update(tab.id, {
						pinned: bool
					});
				}
			}
		});
	} else {
		var obj = selected;
		if (obj.list.length > 0) {
			if (obj.type === 'Tab') {
				for (var j = 0; j < obj.list.length; j++) {
					var pin = true;
					chrome.tabs.get(obj.list[j], function (tab) {
						if (tab.pinned === true) {
							pin = false;
						} else {
							pin = true;
						}
						chrome.tabs.update(tab.id, {
							pinned: pin
						});
					});
				}
			}
		}
	}
}

function splitSelected(parent, id) {
	var obj = selected;
	var tab_ids = obj.list;
	if (id) {
		id = parseInt(id, 10);
	}
	if (tab_ids.length > 0 && obj.type === 'Tab') {
		if (!parent) {
			chrome.windows.create({}, function (new_window) {
				for (var j = 0; j < tab_ids.length; j++) {
					chrome.tabs.move(tab_ids[j], {
						windowId: new_window.id,
						index: -1
					});
				}
			})
		} else {
			var old_win_id, temp = [], tab;
			chrome.windows.get(id, function (new_win) {
				for (var i = 0; i < tab_ids.length; i++) {
					old_win_id = parseInt($('#' + tab_ids[i]).parent().parent().parent().attr('id').split('_')[0], 10);
					temp.push(tab_ids[i]);
					chrome.windows.get(old_win_id, function (old_win) {
						tab = temp.pop();
						if (new_win.incognito || old_win.incognito) {
							chrome.tabs.get(tab, function (old_tab) {
								chrome.tabs.create({
									windowId: new_win.id,
									url: old_tab.url
								})
								chrome.tabs.remove(tab);
							})
						} else {
							chrome.tabs.move(tab, {
								windowId: id,
								index: -1
							}, function () {
								loadData();
							})
						}
					})
				}
			})
		}
	}
}

function moveto() {
	activeMove = true;
	alert("Please select target Window");
}

function reload(bool) {
	if (bool) {
		chrome.windows.getAll({
			populate: true
		}, function (wins) {
			for (var k = 0; k < wins.length; k++) {
				var win = wins[k];
				for (var j = 0; j < win.tabs.length; j++) {
					var tab = win.tabs[j];
					chrome.tabs.reload(tab.id);
				}
			}
		});
	} else {
		var obj = selected;
		if (obj.list.length > 0) {
			var arr = obj.list;
			if (obj.type === 'Tab') {
				for (var i = 0; i < arr.length; i++) {
					chrome.tabs.reload(arr[i]);
				}
			} else {
				for (var i = 0; i < arr.length; i++) {
					chrome.windows.get(arr[i], {
						populate: true
					}, function (win) {
						for (var j = 0; j < win.tabs.length; j++) {
							var tab = win.tabs[j];
							chrome.tabs.reload(tab.id);
						}
					});
				}
			}
		}
	}
}

function clone() {
	var obj = selected;
	if (obj.list.length > 0) {
		if (obj.type === 'Window') {
			for (var i = 0; i < obj.list.length; i++) {
				chrome.windows.get(obj.list[i], {
					populate: true
				}, function (old_win) {
					chrome.windows.create({
						focused: old_win.focused,
						incognito: old_win.incognito
					}, function (new_win) {
						var old_tabs = old_win.tabs;
						for (var j = 0; j < old_tabs.length; j++) {
							chrome.tabs.create({
								windowId: new_win.id,
								url: old_tabs[j].url,
								selected: false
							});
							//chrome.tabs.duplicate(old_tabs[j].id);
						}
						chrome.tabs.remove(new_win.tabs[0].id);
					});
				});
			}
		} else {
			var tab_ids = obj.list;
			for (var i = 0; i < tab_ids.length; i++) {
				chrome.tabs.duplicate(tab_ids[i]);
			}
		}
	}
}

function removeDuplicate() {
	chrome.windows.getAll({
		populate: true
	}, function (wins) {
		for (var k = 0; k < wins.length; k++) {
			var tabs = wins[k].tabs;
			for (var l = 0; l < tabs.length; l++) {
				for (var j = 0; j < tabs.length && j !== l; j++) {
					if (tabs[j].url === tabs[l].url) {
						chrome.tabs.remove(tabs[j].id);
					}
				}
			}
		}
	});
}<|endoftext|>


<commit_before>$(function() {
	$( '.swipebox' ).swipebox();
	$.getJSON("https://mywebservice.info/beautyUniversity/data_out.php", function(data) {
		var str = "";
		for (var i = data.length - 1; i >= 0; i--) {
			if(data[i]["message"] === "" || data[i]["object_id"] === "") {
				continue;
			}

			str += "<a name='lightbox'  class='swipebox' href='" + "https://graph.facebook.com/" + data[i]["object_id"] + "/picture?type=normal" +"'";
			str += "";
			str += "";
		}

		$("#main-content").append(str);
	 });

	$(window).on('ajaxComplete', function() {
  		setTimeout(function() {
  			$(window).lazyLoadXT();
  		}, 50);
	});
});<commit_msg>modify a tag<commit_after>$(function() {
	$( '.swipebox' ).swipebox();
	$.getJSON("https://mywebservice.info/beautyUniversity/data_out.php", function(data) {
		var str = "";
		for (var i = data.length - 1; i >= 0; i--) {
			if(data[i]["message"] === "" || data[i]["object_id"] === "") {
				continue;
			}

			str += "";
			str += "";
			str += "";
		}

		$("#main-content").append(str);
	 });

	$(window).on('ajaxComplete', function() {
  		setTimeout(function() {
  			$(window).lazyLoadXT();
  		}, 50);
	});
});<|endoftext|>


<commit_before>var HumanizedEditor = (function () {

    var humanizedEditor = {
        init: function () {
            var editor = create();
            configure(editor);
            update(editor);
            onOriginalEditorValueChange(function () { update(editor); });
        }
    };

    var originalEditorSelector = '#postingHtmlBox';

    function create() {
        // Copy original post wrapper and put new editor there.
        // TODO: replace with .after().
        $('.boxes').append('

');
        // Option holder style change a bit later than window resize or media queries applied.
        // It shrinks editor, which is not rerender text wrap after container div is resized.
        // So I add stretched to whole editors wrapper iframe and observe it's windows resize event to rerender editors text wrap.
        $('.editorHolder').append('');
        // Turn off mode switching.
        var modeToggle = $('span.tabs');
        modeToggle.hide();

        var humanizedEditor = ace.edit('humanizedEditorWrapper');

        return humanizedEditor;
    }

    function configure(humanizedEditor) {
        // TODO: Check and fix html-worker loading issue.
        humanizedEditor.getSession().setUseWorker(false);
        humanizedEditor.getSession().setMode('ace/mode/html');
        humanizedEditor.setTheme('ace/theme/monokai');
        humanizedEditor.getSession().setUseWrapMode(true);
        humanizedEditor.setFontSize(13);
        humanizedEditor.setShowPrintMargin(false);
        humanizedEditor.commands.addCommand({
            name: 'insertImage',
            bindKey: { win: 'Ctrl-I' },
            exec: insertImage,
            readonly: false
        });
        humanizedEditor.on('change', function () {
            // Update original textarea value, which will be posted on post Update.
            var humanizedEditorValue = humanizedEditor.getValue();
            $(originalEditorSelector).val(humanizedEditorValue);
        });
        humanizedEditor.on('blur', function () {
            updateOriginalEditorSelection(humanizedEditor);
        });
        $('#holderResizeSignalFrame')[0].contentWindow.window.onresize = function () {
            humanizedEditor.resize(true);
        };
    }

    function update(humanizedEditor) {
        var originalEditorValue = $(originalEditorSelector).val();
        var humanizedEditorValue = humanizedEditor.getValue();
        if (originalEditorValue.length > 0 && originalEditorValue !== humanizedEditorValue) {
            //originalEditorValue = vkbeautify.xml(originalEditorValue);
            humanizedEditor.setValue(originalEditorValue, 1);
        }
    }

    function onOriginalEditorValueChange(callback) {
        var originalEditorObserver = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
                // TODO: Fix. This won't work in case post body is empty and in case of new post in html mode.
                // Posts switch.
                var originalEditorID = 'postingHtmlBox';
                if (mutation.target.id === originalEditorID &&
                        mutation.attributeName === 'disabled' &&
                        mutation.target.disabled === false) {
                    callback();
                }

                // Image insert.
                var insertImageDialogBackgroundClass = 'modal-dialog-bg';
                if (mutation.target.className === insertImageDialogBackgroundClass &&
                        mutation.target.nodeName === 'DIV' &&
                        mutation.attributeName === 'style' &&
                        mutation.target.style.display === 'none') {
                    callback();
                }
            });

        });
        var configuration = { attributes: true, subtree: true };
        originalEditorObserver.observe(document.body, configuration);
    }

    function updateOriginalEditorSelection(humanizedEditor) {
        // Convert selection positions to start and end character indexes.
        var start = humanizedEditor.session.doc.positionToIndex(humanizedEditor.selection.getRange().start)
        var end = humanizedEditor.session.doc.positionToIndex(humanizedEditor.selection.getRange().end)
        $(originalEditorSelector).get(0).setSelectionRange(start, end);
    }

    function insertImage() {
        var insertImageTrigger = document.getElementById('imageUpload');

        var mouseDown = new Event('mousedown', { "bubbles": true });
        var mouseUp = new Event('mouseup', { "bubbles": true });
        var mouseOut = new Event('mouseout', { "bubbles": true });

        insertImageTrigger.dispatchEvent(mouseDown);
        insertImageTrigger.dispatchEvent(mouseUp);
        insertImageTrigger.dispatchEvent(mouseOut);
    }

    return humanizedEditor;
}());

var bloggerObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        if ((mutation.target.id === 'postingHtmlBox' && mutation.attributeName === "disabled" && mutation.target.disabled === false)) {
            // At this moment all data needed for humanized editor creation presented on the page if html mode is on by default.
            var humanizedEditor = Object.create(HumanizedEditor);
            humanizedEditor.init();
            bloggerObserver.disconnect();
        } else if (mutation.target.className === 'state composeMode') {
            // Switch to html mode if compose mode is turned on by default.
            var htmlModeToggle = $('span.tabs button:last-of-type()');
            htmlModeToggle.trigger('click');
            // Now I need to wait until blogger loads html version of a post.
            bloggerObserver.disconnect();
            bloggerObserver = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    if (mutation.target.id === 'blogger-app' && mutation.target.nodeName === 'BODY' && mutation.type === 'childList' && mutation.removedNodes.length === 1 && mutation.removedNodes[0].className === 'gwt-PopupPanel') {
                        // At this moment all data needed for humanized editor creation presented on the page if compose mode is on by default.
                        var humanizedEditor = Object.create(HumanizedEditor);
                        humanizedEditor.init();
                        bloggerObserver.disconnect();
                    }
                });
            });
            var configuration = { subtree: true, childList: true};
            bloggerObserver.observe(document.body, configuration);
        }
    });
});

// TODO: Add attribute filter.
var configuration = { attributes: true, subtree: true};
bloggerObserver.observe(document.body, configuration);
<commit_msg>Fix #5. Clear undo stack after post body load and posts switch.<commit_after>var HumanizedEditor = (function () {

    var humanizedEditor = {
        init: function () {
            var editor = create();
            configure(editor);
            update(editor);
            // Clear first undo stack element to prevent ctrl-z from return editor to it's default value and not initial post body value.
            clearUndoStack(editor);
            onOriginalEditorValueChange(editor);
        }
    };

    var originalEditorSelector = '#postingHtmlBox';

    function create() {
        // Copy original post wrapper and put new editor there.
        // TODO: replace with .after().
        $('.boxes').append('

');
        // Option holder style change a bit later than window resize or media queries applied.
        // It shrinks editor, which is not rerender text wrap after container div is resized.
        // So I add stretched to whole editors wrapper iframe and observe it's windows resize event to rerender editors text wrap.
        $('.editorHolder').append('');
        // Turn off mode switching.
        var modeToggle = $('span.tabs');
        modeToggle.hide();

        var humanizedEditor = ace.edit('humanizedEditorWrapper');

        return humanizedEditor;
    }

    function configure(humanizedEditor) {
        // TODO: Check and fix html-worker loading issue.
        humanizedEditor.getSession().setUseWorker(false);
        humanizedEditor.getSession().setMode('ace/mode/html');
        humanizedEditor.setTheme('ace/theme/monokai');
        humanizedEditor.getSession().setUseWrapMode(true);
        humanizedEditor.setFontSize(13);
        humanizedEditor.setShowPrintMargin(false);
        humanizedEditor.commands.addCommand({
            name: 'insertImage',
            bindKey: { win: 'Ctrl-I' },
            exec: insertImage,
            readonly: false
        });
        humanizedEditor.on('change', function () {
            // Update original textarea value, which will be posted on post Update.
            var humanizedEditorValue = humanizedEditor.getValue();
            $(originalEditorSelector).val(humanizedEditorValue);
        });
        humanizedEditor.on('blur', function () {
            updateOriginalEditorSelection(humanizedEditor);
        });
        $('#holderResizeSignalFrame')[0].contentWindow.window.onresize = function () {
            humanizedEditor.resize(true);
        };
    }

    function update(humanizedEditor) {
        var originalEditorValue = $(originalEditorSelector).val();
        var humanizedEditorValue = humanizedEditor.getValue();
        if (originalEditorValue.length > 0 && originalEditorValue !== humanizedEditorValue) {
            //originalEditorValue = vkbeautify.xml(originalEditorValue);
            humanizedEditor.setValue(originalEditorValue, 1);
        }
    }

    function onOriginalEditorValueChange(humanizedEditor) {
        var originalEditorObserver = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
                // TODO: Fix. This won't work in case post body is empty and in case of new post in html mode.
                // Posts switch.
                var originalEditorID = 'postingHtmlBox';
                if (mutation.target.id === originalEditorID &&
                        mutation.attributeName === 'disabled' &&
                        mutation.target.disabled === false) {
                    update(humanizedEditor);
                    // New post - new undo stack.
                    clearUndoStack(humanizedEditor);
                }

                // Image insert.
                var insertImageDialogBackgroundClass = 'modal-dialog-bg';
                if (mutation.target.className === insertImageDialogBackgroundClass &&
                        mutation.target.nodeName === 'DIV' &&
                        mutation.attributeName === 'style' &&
                        mutation.target.style.display === 'none') {
                    update(humanizedEditor);
                }
            });

        });
        var configuration = { attributes: true, subtree: true };
        originalEditorObserver.observe(document.body, configuration);
    }

    function updateOriginalEditorSelection(humanizedEditor) {
        // Convert selection positions to start and end character indexes.
        var start = humanizedEditor.session.doc.positionToIndex(humanizedEditor.selection.getRange().start)
        var end = humanizedEditor.session.doc.positionToIndex(humanizedEditor.selection.getRange().end)
        $(originalEditorSelector).get(0).setSelectionRange(start, end);
    }

    function clearUndoStack(humanizedEditor) {
        var UndoManager = require('ace/undomanager').UndoManager;
        humanizedEditor.getSession().setUndoManager(new UndoManager());
    }

    function insertImage() {
        var insertImageTrigger = document.getElementById('imageUpload');

        var mouseDown = new Event('mousedown', { "bubbles": true });
        var mouseUp = new Event('mouseup', { "bubbles": true });
        var mouseOut = new Event('mouseout', { "bubbles": true });

        insertImageTrigger.dispatchEvent(mouseDown);
        insertImageTrigger.dispatchEvent(mouseUp);
        insertImageTrigger.dispatchEvent(mouseOut);
    }

    return humanizedEditor;
}());

var bloggerObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        if ((mutation.target.id === 'postingHtmlBox' && mutation.attributeName === "disabled" && mutation.target.disabled === false)) {
            // At this moment all data needed for humanized editor creation presented on the page if html mode is on by default.
            var humanizedEditor = Object.create(HumanizedEditor);
            humanizedEditor.init();
            bloggerObserver.disconnect();
        } else if (mutation.target.className === 'state composeMode') {
            // Switch to html mode if compose mode is turned on by default.
            var htmlModeToggle = $('span.tabs button:last-of-type()');
            htmlModeToggle.trigger('click');
            // Now I need to wait until blogger loads html version of a post.
            bloggerObserver.disconnect();
            bloggerObserver = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    if (mutation.target.id === 'blogger-app' && mutation.target.nodeName === 'BODY' && mutation.type === 'childList' && mutation.removedNodes.length === 1 && mutation.removedNodes[0].className === 'gwt-PopupPanel') {
                        // At this moment all data needed for humanized editor creation presented on the page if compose mode is on by default.
                        var humanizedEditor = Object.create(HumanizedEditor);
                        humanizedEditor.init();
                        bloggerObserver.disconnect();
                    }
                });
            });
            var configuration = { subtree: true, childList: true};
            bloggerObserver.observe(document.body, configuration);
        }
    });
});

// TODO: Add attribute filter.
var configuration = { attributes: true, subtree: true};
bloggerObserver.observe(document.body, configuration);
<|endoftext|>